mirror of
https://github.com/josegonzalez/python-github-backup.git
synced 2025-12-05 16:18:02 +01:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4055eb99c | ||
|
|
d8a330559c | ||
|
|
de93824498 | ||
|
|
2efeaa7580 | ||
|
|
647810a2f0 | ||
|
|
0dfe5c342a | ||
|
|
1d6e1abab1 | ||
|
|
dd2b96b172 | ||
|
|
7a589f1e63 | ||
|
|
92c619cd01 | ||
|
|
9a91dd7733 | ||
|
|
6592bd8196 |
37
CHANGES.rst
37
CHANGES.rst
@@ -1,6 +1,43 @@
|
|||||||
Changelog
|
Changelog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
0.13.1 (2017-04-11)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- Fix error when repository has no updated_at value. [Nicolai Ehemann]
|
||||||
|
|
||||||
|
0.13.0 (2017-04-05)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- Add OS check for OSX specific keychain args. [Martin O'Reilly]
|
||||||
|
|
||||||
|
Keychain arguments are only supported on Mac OSX.
|
||||||
|
Added check for operating system so we give a
|
||||||
|
"Keychain arguments are only supported on Mac OSX"
|
||||||
|
error message rather than a "No password item matching the
|
||||||
|
provided name and account could be found in the osx keychain"
|
||||||
|
error message
|
||||||
|
|
||||||
|
|
||||||
|
- Add support for storing PAT in OSX keychain. [Martin O'Reilly]
|
||||||
|
|
||||||
|
Added additional optional arguments and README guidance for storing
|
||||||
|
and accessing a Github personal access token (PAT) in the OSX
|
||||||
|
keychain
|
||||||
|
|
||||||
|
|
||||||
|
0.12.1 (2017-03-27)
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
- Avoid remote branch name churn. [Chris Adams]
|
||||||
|
|
||||||
|
This avoids the backup output having lots of "[new branch]" messages
|
||||||
|
because removing the old remote name removed all of the existing branch
|
||||||
|
references.
|
||||||
|
|
||||||
|
|
||||||
|
- Fix detection of bare git directories. [Andrzej Maczuga]
|
||||||
|
|
||||||
0.12.0 (2016-11-22)
|
0.12.0 (2016-11-22)
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
|||||||
23
README.rst
23
README.rst
@@ -34,6 +34,8 @@ CLI Usage is as follows::
|
|||||||
[-L [LANGUAGES [LANGUAGES ...]]] [-N NAME_REGEX]
|
[-L [LANGUAGES [LANGUAGES ...]]] [-N NAME_REGEX]
|
||||||
[-H GITHUB_HOST] [-O] [-R REPOSITORY] [-P] [-F]
|
[-H GITHUB_HOST] [-O] [-R REPOSITORY] [-P] [-F]
|
||||||
[--prefer-ssh] [-v]
|
[--prefer-ssh] [-v]
|
||||||
|
[--keychain-name OSX_KEYCHAIN_ITEM_NAME]
|
||||||
|
[--keychain-account OSX_KEYCHAIN_ITEM_ACCOUNT]
|
||||||
USER
|
USER
|
||||||
|
|
||||||
Backup a github account
|
Backup a github account
|
||||||
@@ -83,6 +85,12 @@ CLI Usage is as follows::
|
|||||||
-F, --fork include forked repositories
|
-F, --fork include forked repositories
|
||||||
--prefer-ssh Clone repositories using SSH instead of HTTPS
|
--prefer-ssh Clone repositories using SSH instead of HTTPS
|
||||||
-v, --version show program's version number and exit
|
-v, --version show program's version number and exit
|
||||||
|
--keychain-name OSX_KEYCHAIN_ITEM_NAME
|
||||||
|
OSX ONLY: name field of password item in OSX keychain
|
||||||
|
that holds the personal access or OAuth token
|
||||||
|
--keychain-account OSX_KEYCHAIN_ITEM_ACCOUNT
|
||||||
|
OSX ONLY: account field of password item in OSX
|
||||||
|
keychain that holds the personal access or OAuth token
|
||||||
|
|
||||||
|
|
||||||
The package can be used to backup an *entire* organization or repository, including issues and wikis in the most appropriate format (clones for wikis, json files for issues).
|
The package can be used to backup an *entire* organization or repository, including issues and wikis in the most appropriate format (clones for wikis, json files for issues).
|
||||||
@@ -91,3 +99,18 @@ Authentication
|
|||||||
==============
|
==============
|
||||||
|
|
||||||
Note: Password-based authentication will fail if you have two-factor authentication enabled.
|
Note: Password-based authentication will fail if you have two-factor authentication enabled.
|
||||||
|
|
||||||
|
Using the Keychain on Mac OSX
|
||||||
|
=============================
|
||||||
|
Note: On Mac OSX the token can be stored securely in the user's keychain. To do this:
|
||||||
|
|
||||||
|
1. Open Keychain from "Applications -> Utilities -> Keychain Access"
|
||||||
|
2. Add a new password item using "File -> New Password Item"
|
||||||
|
3. Enter a name in the "Keychain Item Name" box. You must provide this name to github-backup using the --keychain-name argument.
|
||||||
|
4. Enter an account name in the "Account Name" box, enter your Github username as set above. You must provide this name to github-backup using the --keychain-account argument.
|
||||||
|
5. Enter your Github personal access token in the "Password" box
|
||||||
|
|
||||||
|
Note: When you run github-backup, you will be asked whether you want to allow "security" to use your confidential information stored in your keychain. You have two options:
|
||||||
|
|
||||||
|
1. **Allow:** In this case you will need to click "Allow" each time you run `github-backup`
|
||||||
|
2. **Always Allow:** In this case, you will not be asked for permission when you run `github-backup` in future. This is less secure, but is required if you want to schedule `github-backup` to run automatically
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import select
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import platform
|
||||||
try:
|
try:
|
||||||
# python 3
|
# python 3
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
@@ -251,13 +252,37 @@ def parse_args():
|
|||||||
parser.add_argument('-v', '--version',
|
parser.add_argument('-v', '--version',
|
||||||
action='version',
|
action='version',
|
||||||
version='%(prog)s ' + __version__)
|
version='%(prog)s ' + __version__)
|
||||||
|
parser.add_argument('--keychain-name',
|
||||||
|
dest='osx_keychain_item_name',
|
||||||
|
help='OSX ONLY: name field of password item in OSX keychain that holds the personal access or OAuth token')
|
||||||
|
parser.add_argument('--keychain-account',
|
||||||
|
dest='osx_keychain_item_account',
|
||||||
|
help='OSX ONLY: account field of password item in OSX keychain that holds the personal access or OAuth token')
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
def get_auth(args, encode=True):
|
def get_auth(args, encode=True):
|
||||||
auth = None
|
auth = None
|
||||||
|
|
||||||
if args.token:
|
if args.osx_keychain_item_name:
|
||||||
|
if not args.osx_keychain_item_account:
|
||||||
|
log_error('You must specify both name and account fields for osx keychain password items')
|
||||||
|
else:
|
||||||
|
if platform.system() != 'Darwin':
|
||||||
|
log_error("Keychain arguments are only supported on Mac OSX")
|
||||||
|
try:
|
||||||
|
with open(os.devnull,'w') as devnull:
|
||||||
|
token = (subprocess.check_output([
|
||||||
|
'security','find-generic-password',
|
||||||
|
'-s',args.osx_keychain_item_name,
|
||||||
|
'-a',args.osx_keychain_item_account,
|
||||||
|
'-w' ], stderr=devnull).strip())
|
||||||
|
auth = token + ':' + 'x-oauth-basic'
|
||||||
|
except:
|
||||||
|
log_error('No password item matching the provided name and account could be found in the osx keychain.')
|
||||||
|
elif args.osx_keychain_item_account:
|
||||||
|
log_error('You must specify both name and account fields for osx keychain password items')
|
||||||
|
elif args.token:
|
||||||
_path_specifier = 'file://'
|
_path_specifier = 'file://'
|
||||||
if args.token.startswith(_path_specifier):
|
if args.token.startswith(_path_specifier):
|
||||||
args.token = open(args.token[len(_path_specifier):],
|
args.token = open(args.token[len(_path_specifier):],
|
||||||
@@ -495,7 +520,7 @@ def backup_repositories(args, output_directory, repositories):
|
|||||||
repos_template = 'https://{0}/repos'.format(get_github_api_host(args))
|
repos_template = 'https://{0}/repos'.format(get_github_api_host(args))
|
||||||
|
|
||||||
if args.incremental:
|
if args.incremental:
|
||||||
last_update = max(repository['updated_at'] for repository in repositories) # noqa
|
last_update = max(list(repository['updated_at'] for repository in repositories) or [time.strftime('%Y-%m-%dT%H:%M:%SZ', time.localtime())]) # noqa
|
||||||
last_update_path = os.path.join(output_directory, 'last_update')
|
last_update_path = os.path.join(output_directory, 'last_update')
|
||||||
if os.path.exists(last_update_path):
|
if os.path.exists(last_update_path):
|
||||||
args.since = open(last_update_path).read().strip()
|
args.since = open(last_update_path).read().strip()
|
||||||
@@ -714,7 +739,16 @@ def fetch_repository(name,
|
|||||||
local_dir,
|
local_dir,
|
||||||
skip_existing=False,
|
skip_existing=False,
|
||||||
bare_clone=False):
|
bare_clone=False):
|
||||||
clone_exists = os.path.exists(os.path.join(local_dir, '.git'))
|
if bare_clone:
|
||||||
|
if os.path.exists(local_dir):
|
||||||
|
clone_exists = subprocess.check_output(['git',
|
||||||
|
'rev-parse',
|
||||||
|
'--is-bare-repository'],
|
||||||
|
cwd=local_dir) == "true\n"
|
||||||
|
else:
|
||||||
|
clone_exists = False
|
||||||
|
else:
|
||||||
|
clone_exists = os.path.exists(os.path.join(local_dir, '.git'))
|
||||||
|
|
||||||
if clone_exists and skip_existing:
|
if clone_exists and skip_existing:
|
||||||
return
|
return
|
||||||
@@ -732,11 +766,21 @@ def fetch_repository(name,
|
|||||||
|
|
||||||
if clone_exists:
|
if clone_exists:
|
||||||
log_info('Updating {0} in {1}'.format(name, local_dir))
|
log_info('Updating {0} in {1}'.format(name, local_dir))
|
||||||
git_command = ['git', 'remote', 'rm', 'origin']
|
|
||||||
logging_subprocess(git_command, None, cwd=local_dir)
|
remotes = subprocess.check_output(['git', 'remote', 'show'],
|
||||||
git_command = ['git', 'remote', 'add', 'origin', remote_url]
|
cwd=local_dir)
|
||||||
logging_subprocess(git_command, None, cwd=local_dir)
|
remotes = [i.strip() for i in remotes.decode('utf-8')]
|
||||||
git_command = ['git', 'fetch', '--all', '--tags', '--prune']
|
|
||||||
|
if 'origin' not in remotes:
|
||||||
|
git_command = ['git', 'remote', 'rm', 'origin']
|
||||||
|
logging_subprocess(git_command, None, cwd=local_dir)
|
||||||
|
git_command = ['git', 'remote', 'add', 'origin', remote_url]
|
||||||
|
logging_subprocess(git_command, None, cwd=local_dir)
|
||||||
|
else:
|
||||||
|
git_command = ['git', 'remote', 'set-url', 'origin', remote_url]
|
||||||
|
logging_subprocess(git_command, None, cwd=local_dir)
|
||||||
|
|
||||||
|
git_command = ['git', 'fetch', '--all', '--force', '--tags', '--prune']
|
||||||
logging_subprocess(git_command, None, cwd=local_dir)
|
logging_subprocess(git_command, None, cwd=local_dir)
|
||||||
else:
|
else:
|
||||||
log_info('Cloning {0} repository from {1} to {2}'.format(
|
log_info('Cloning {0} repository from {1} to {2}'.format(
|
||||||
@@ -744,7 +788,7 @@ def fetch_repository(name,
|
|||||||
masked_remote_url,
|
masked_remote_url,
|
||||||
local_dir))
|
local_dir))
|
||||||
if bare_clone:
|
if bare_clone:
|
||||||
git_command = ['git', 'clone', '--bare', remote_url, local_dir]
|
git_command = ['git', 'clone', '--mirror', remote_url, local_dir]
|
||||||
else:
|
else:
|
||||||
git_command = ['git', 'clone', remote_url, local_dir]
|
git_command = ['git', 'clone', remote_url, local_dir]
|
||||||
logging_subprocess(git_command, None)
|
logging_subprocess(git_command, None)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = '0.12.0'
|
__version__ = '0.13.1'
|
||||||
|
|||||||
Reference in New Issue
Block a user