Compare commits

..

12 Commits

Author SHA1 Message Date
Jose Diaz-Gonzalez
d4055eb99c Release version 0.13.1 2017-04-11 09:40:13 -06:00
Jose Diaz-Gonzalez
d8a330559c Merge pull request #61 from McNetic/fix_empty_updated_at
Fix error when repository has no updated_at value
2017-04-11 09:37:15 -06:00
Nicolai Ehemann
de93824498 Fix error when repository has no updated_at value 2017-04-11 11:10:03 +02:00
Jose Diaz-Gonzalez
2efeaa7580 Release version 0.13.0 2017-04-05 11:49:49 -04:00
Jose Diaz-Gonzalez
647810a2f0 Merge pull request #59 from martintoreilly/master
Add support for storing PAT in OSX keychain
2017-04-05 11:49:24 -04:00
Martin O'Reilly
0dfe5c342a Add OS check for OSX specific keychain args
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
2017-04-05 16:36:52 +01:00
Martin O'Reilly
1d6e1abab1 Add support for storing PAT in OSX keychain
Added additional optional arguments and README guidance for storing
and accessing a Github personal access token (PAT) in the OSX
keychain
2017-04-05 15:17:52 +01:00
Jose Diaz-Gonzalez
dd2b96b172 Release version 0.12.1 2017-03-27 14:55:11 -06:00
Jose Diaz-Gonzalez
7a589f1e63 Merge pull request #57 from acdha/reuse-existing-remotes
Avoid remote branch name churn
2017-03-27 14:54:02 -06:00
Chris Adams
92c619cd01 Avoid remote branch name churn
This avoids the backup output having lots of "[new branch]" messages
because removing the old remote name removed all of the existing branch
references.
2017-03-27 16:26:19 -04:00
Jose Diaz-Gonzalez
9a91dd7733 Merge pull request #55 from amaczuga/master
Fix detection of bare git directories
2016-11-22 13:36:52 -07:00
Andrzej Maczuga
6592bd8196 Fix detection of bare git directories 2016-11-22 20:11:26 +00:00
4 changed files with 114 additions and 10 deletions

View File

@@ -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)
------------------- -------------------

View File

@@ -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

View File

@@ -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,6 +739,15 @@ def fetch_repository(name,
local_dir, local_dir,
skip_existing=False, skip_existing=False,
bare_clone=False): bare_clone=False):
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')) clone_exists = os.path.exists(os.path.join(local_dir, '.git'))
if clone_exists and skip_existing: if clone_exists and skip_existing:
@@ -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))
remotes = subprocess.check_output(['git', 'remote', 'show'],
cwd=local_dir)
remotes = [i.strip() for i in remotes.decode('utf-8')]
if 'origin' not in remotes:
git_command = ['git', 'remote', 'rm', 'origin'] git_command = ['git', 'remote', 'rm', 'origin']
logging_subprocess(git_command, None, cwd=local_dir) logging_subprocess(git_command, None, cwd=local_dir)
git_command = ['git', 'remote', 'add', 'origin', remote_url] git_command = ['git', 'remote', 'add', 'origin', remote_url]
logging_subprocess(git_command, None, cwd=local_dir) logging_subprocess(git_command, None, cwd=local_dir)
git_command = ['git', 'fetch', '--all', '--tags', '--prune'] 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)

View File

@@ -1 +1 @@
__version__ = '0.12.0' __version__ = '0.13.1'