diff --git a/README.rst b/README.rst
index e4300a7..943f8ec 100644
--- a/README.rst
+++ b/README.rst
@@ -36,23 +36,26 @@ Show the CLI help output::
CLI Help output::
- github-backup [-h] [-u USERNAME] [-p PASSWORD] [-t TOKEN_CLASSIC]
- [-f TOKEN_FINE] [--as-app] [-o OUTPUT_DIRECTORY]
- [-l LOG_LEVEL] [-i] [--starred] [--all-starred]
- [--watched] [--followers] [--following] [--all] [--issues]
- [--issue-comments] [--issue-events] [--pulls]
+ github-backup [-h] [-t TOKEN_CLASSIC] [-f TOKEN_FINE] [-q] [--as-app]
+ [-o OUTPUT_DIRECTORY] [-l LOG_LEVEL] [-i]
+ [--incremental-by-files]
+ [--starred] [--all-starred]
+ [--watched] [--followers] [--following] [--all]
+ [--issues] [--issue-comments] [--issue-events] [--pulls]
[--pull-comments] [--pull-commits] [--pull-details]
[--labels] [--hooks] [--milestones] [--repositories]
- [--bare] [--lfs] [--wikis] [--gists] [--starred-gists]
- [--skip-archived] [--skip-existing] [-L [LANGUAGES ...]]
- [-N NAME_REGEX] [-H GITHUB_HOST] [-O] [-R REPOSITORY]
- [-P] [-F] [--prefer-ssh] [-v]
+ [--bare] [--no-prune] [--lfs] [--wikis] [--gists]
+ [--starred-gists] [--skip-archived] [--skip-existing]
+ [-L [LANGUAGES ...]] [-N NAME_REGEX] [-H GITHUB_HOST]
+ [-O] [-R REPOSITORY] [-P] [-F] [--prefer-ssh] [-v]
[--keychain-name OSX_KEYCHAIN_ITEM_NAME]
[--keychain-account OSX_KEYCHAIN_ITEM_ACCOUNT]
[--releases] [--latest-releases NUMBER_OF_LATEST_RELEASES]
- [--skip-prerelease] [--assets] [--skip-assets-on [REPO ...]]
- [--attachments] [--exclude [REPOSITORY [REPOSITORY ...]]
- [--throttle-limit THROTTLE_LIMIT] [--throttle-pause THROTTLE_PAUSE]
+ [--skip-prerelease] [--assets]
+ [--skip-assets-on [SKIP_ASSETS_ON ...]] [--attachments]
+ [--throttle-limit THROTTLE_LIMIT]
+ [--throttle-pause THROTTLE_PAUSE]
+ [--exclude [EXCLUDE ...]]
USER
Backup a github account
@@ -60,27 +63,25 @@ CLI Help output::
positional arguments:
USER github username
- optional arguments:
+ options:
-h, --help show this help message and exit
- -u USERNAME, --username USERNAME
- username for basic auth
- -p PASSWORD, --password PASSWORD
- password for basic auth. If a username is given but
- not a password, the password will be prompted for.
- -f TOKEN_FINE, --token-fine TOKEN_FINE
- fine-grained personal access token or path to token
- (file://...)
- -t TOKEN_CLASSIC, --token TOKEN_CLASSIC
+ -t, --token TOKEN_CLASSIC
personal access, OAuth, or JSON Web token, or path to
token (file://...)
+ -f, --token-fine TOKEN_FINE
+ fine-grained personal access token (github_pat_....),
+ or path to token (file://...)
+ -q, --quiet supress log messages less severe than warning, e.g.
+ info
--as-app authenticate as github app instead of as a user.
- -o OUTPUT_DIRECTORY, --output-directory OUTPUT_DIRECTORY
+ -o, --output-directory OUTPUT_DIRECTORY
directory at which to backup the repositories
- -l LOG_LEVEL, --log-level LOG_LEVEL
+ -l, --log-level LOG_LEVEL
log level to use (default: info, possible levels:
debug, info, warning, error, critical)
-i, --incremental incremental backup
- --incremental-by-files incremental backup using modified time of files
+ --incremental-by-files
+ incremental backup based on modification date of files
--starred include JSON output of starred repositories in backup
--all-starred include starred repositories in backup [*]
--watched include JSON output of watched repositories in backup
@@ -100,20 +101,22 @@ CLI Help output::
--milestones include milestones in backup
--repositories include repository clone in backup
--bare clone bare repositories
+ --no-prune disable prune option for git fetch
--lfs clone LFS repositories (requires Git LFS to be
installed, https://git-lfs.github.com) [*]
--wikis include wiki clone in backup
--gists include gists in backup [*]
--starred-gists include starred gists in backup [*]
+ --skip-archived skip project if it is archived
--skip-existing skip project if a backup directory exists
- -L [LANGUAGES [LANGUAGES ...]], --languages [LANGUAGES [LANGUAGES ...]]
+ -L, --languages [LANGUAGES ...]
only allow these languages
- -N NAME_REGEX, --name-regex NAME_REGEX
+ -N, --name-regex NAME_REGEX
python regex to match names against
- -H GITHUB_HOST, --github-host GITHUB_HOST
+ -H, --github-host GITHUB_HOST
GitHub Enterprise hostname
-O, --organization whether or not this is an organization user
- -R REPOSITORY, --repository REPOSITORY
+ -R, --repository REPOSITORY
name of repository to limit backup to
-P, --private include private repositories [*]
-F, --fork include forked repositories [*]
@@ -128,19 +131,16 @@ CLI Help output::
--releases include release information, not including assets or
binaries
--latest-releases NUMBER_OF_LATEST_RELEASES
- include certain number of the latest releases;
- only applies if including releases
- --skip-prerelease skip prerelease and draft versions; only applies if including releases
+ include certain number of the latest releases; only
+ applies if including releases
+ --skip-prerelease skip prerelease and draft versions; only applies if
+ including releases
--assets include assets alongside release information; only
applies if including releases
- --skip-assets-on [REPO ...]
- skip asset downloads for these repositories (e.g.
- --skip-assets-on repo1 owner/repo2)
- --attachments download user-attachments from issues and pull requests
- to issues/attachments/{issue_number}/ and
- pulls/attachments/{pull_number}/ directories
- --exclude [REPOSITORY [REPOSITORY ...]]
- names of repositories to exclude from backup.
+ --skip-assets-on [SKIP_ASSETS_ON ...]
+ skip asset downloads for these repositories
+ --attachments download user-attachments from issues and pull
+ requests
--throttle-limit THROTTLE_LIMIT
start throttling of GitHub API requests after this
amount of API requests remain
@@ -148,6 +148,8 @@ CLI Help output::
wait this amount of seconds when API request
throttling is active (default: 30.0, requires
--throttle-limit to be set)
+ --exclude [EXCLUDE ...]
+ names of repositories to exclude
Usage Details
@@ -156,13 +158,13 @@ Usage Details
Authentication
--------------
-**Password-based authentication** will fail if you have two-factor authentication enabled, and will `be deprecated `_ by 2023 EOY.
+GitHub requires token-based authentication for API access. Password authentication was `removed in November 2020 `_.
-``--username`` is used for basic password authentication and separate from the positional argument ``USER``, which specifies the user account you wish to back up.
+The positional argument ``USER`` specifies the user or organization account you wish to back up.
-**Classic tokens** are `slightly less secure `_ as they provide very coarse-grained permissions.
+**Fine-grained tokens** (``-f TOKEN_FINE``) are recommended for most use cases, especially long-running backups (e.g. cron jobs), as they provide precise permission control.
-If you need authentication for long-running backups (e.g. for a cron job) it is recommended to use **fine-grained personal access token** ``-f TOKEN_FINE``.
+**Classic tokens** (``-t TOKEN``) are `slightly less secure `_ as they provide very coarse-grained permissions.
Fine Tokens
diff --git a/github_backup/cli.py b/github_backup/cli.py
index 98f8d4a..54849d4 100644
--- a/github_backup/cli.py
+++ b/github_backup/cli.py
@@ -43,7 +43,7 @@ def main():
if args.private and not get_auth(args):
logger.warning(
"The --private flag has no effect without authentication. "
- "Use -t/--token, -f/--token-fine, or -u/--username to authenticate."
+ "Use -t/--token or -f/--token-fine to authenticate."
)
if args.quiet:
diff --git a/github_backup/github_backup.py b/github_backup/github_backup.py
index 34d529a..d62afc3 100644
--- a/github_backup/github_backup.py
+++ b/github_backup/github_backup.py
@@ -7,7 +7,6 @@
import calendar
import codecs
import errno
-import getpass
import json
import logging
import os
@@ -24,7 +23,6 @@
from datetime import datetime
from http.client import IncompleteRead
from urllib.error import HTTPError, URLError
-from urllib.parse import quote as urlquote
from urllib.parse import urlencode, urlparse
from urllib.request import HTTPRedirectHandler, Request, build_opener, urlopen
@@ -149,17 +147,6 @@ def mask_password(url, secret="*****"):
def parse_args(args=None):
parser = argparse.ArgumentParser(description="Backup a github account")
parser.add_argument("user", metavar="USER", type=str, help="github username")
- parser.add_argument(
- "-u", "--username", dest="username", help="username for basic auth"
- )
- parser.add_argument(
- "-p",
- "--password",
- dest="password",
- help="password for basic auth. "
- "If a username is given but not a password, the "
- "password will be prompted for.",
- )
parser.add_argument(
"-t",
"--token",
@@ -533,16 +520,6 @@ def get_auth(args, encode=True, for_git_cli=False):
auth = args.token_classic
else:
auth = "x-access-token:" + args.token_classic
- elif args.username:
- if not args.password:
- args.password = getpass.getpass()
- if encode:
- password = args.password
- else:
- password = urlquote(args.password)
- auth = args.username + ":" + password
- elif args.password:
- raise Exception("You must specify a username for basic auth")
if not auth:
return None
diff --git a/tests/test_all_starred.py b/tests/test_all_starred.py
index f59a67e..0fab048 100644
--- a/tests/test_all_starred.py
+++ b/tests/test_all_starred.py
@@ -46,8 +46,6 @@ def _create_mock_args(self, **overrides):
args.prefer_ssh = False
args.token_classic = None
args.token_fine = None
- args.username = None
- args.password = None
args.as_app = False
args.osx_keychain_item_name = None
args.osx_keychain_item_account = None
diff --git a/tests/test_attachments.py b/tests/test_attachments.py
index 07c1b33..b338caf 100644
--- a/tests/test_attachments.py
+++ b/tests/test_attachments.py
@@ -24,8 +24,6 @@ def attachment_test_setup(tmp_path):
args.as_app = False
args.token_fine = None
args.token_classic = None
- args.username = None
- args.password = None
args.osx_keychain_item_name = None
args.osx_keychain_item_account = None
args.user = "testuser"
diff --git a/tests/test_http_451.py b/tests/test_http_451.py
index 51218d2..d53d65c 100644
--- a/tests/test_http_451.py
+++ b/tests/test_http_451.py
@@ -17,8 +17,6 @@ def test_repository_unavailable_error_raised(self):
args.as_app = False
args.token_fine = None
args.token_classic = None
- args.username = None
- args.password = None
args.osx_keychain_item_name = None
args.osx_keychain_item_account = None
args.throttle_limit = None
@@ -52,8 +50,6 @@ def test_repository_unavailable_error_without_dmca_url(self):
args.as_app = False
args.token_fine = None
args.token_classic = None
- args.username = None
- args.password = None
args.osx_keychain_item_name = None
args.osx_keychain_item_account = None
args.throttle_limit = None
@@ -78,8 +74,6 @@ def test_repository_unavailable_error_with_malformed_json(self):
args.as_app = False
args.token_fine = None
args.token_classic = None
- args.username = None
- args.password = None
args.osx_keychain_item_name = None
args.osx_keychain_item_account = None
args.throttle_limit = None
diff --git a/tests/test_pagination.py b/tests/test_pagination.py
index 75dfccd..831b913 100644
--- a/tests/test_pagination.py
+++ b/tests/test_pagination.py
@@ -45,8 +45,6 @@ def mock_args():
args.as_app = False
args.token_fine = None
args.token_classic = "fake_token"
- args.username = None
- args.password = None
args.osx_keychain_item_name = None
args.osx_keychain_item_account = None
args.throttle_limit = None
diff --git a/tests/test_retrieve_data.py b/tests/test_retrieve_data.py
index c358ff0..adb1152 100644
--- a/tests/test_retrieve_data.py
+++ b/tests/test_retrieve_data.py
@@ -70,8 +70,6 @@ def mock_args(self):
args.as_app = False
args.token_fine = None
args.token_classic = "fake_token"
- args.username = None
- args.password = None
args.osx_keychain_item_name = None
args.osx_keychain_item_account = None
args.throttle_limit = None
@@ -313,8 +311,6 @@ def mock_args(self):
args.as_app = False
args.token_fine = None
args.token_classic = "fake_token"
- args.username = None
- args.password = None
args.osx_keychain_item_name = None
args.osx_keychain_item_account = None
args.throttle_limit = 10 # Throttle when remaining <= 10
@@ -344,8 +340,6 @@ def mock_args(self):
args.as_app = False
args.token_fine = None
args.token_classic = "fake_token"
- args.username = None
- args.password = None
args.osx_keychain_item_name = None
args.osx_keychain_item_account = None
args.throttle_limit = None
diff --git a/tests/test_skip_assets_on.py b/tests/test_skip_assets_on.py
index 2437e05..ce28287 100644
--- a/tests/test_skip_assets_on.py
+++ b/tests/test_skip_assets_on.py
@@ -48,8 +48,6 @@ def _create_mock_args(self, **overrides):
args.prefer_ssh = False
args.token_classic = "test-token"
args.token_fine = None
- args.username = None
- args.password = None
args.as_app = False
args.osx_keychain_item_name = None
args.osx_keychain_item_account = None