Compare commits

...

21 Commits
0.5.0 ... 0.7.0

Author SHA1 Message Date
Jose Diaz-Gonzalez
d0e239b3ef Release version 0.7.0 2016-02-02 14:52:07 -05:00
Jose Diaz-Gonzalez
29c9373d9d Merge pull request #32 from albertyw/soft-fail-hooks
Softly fail if not able to read hooks
2016-01-29 03:36:48 -05:00
Jose Diaz-Gonzalez
eb8b22c81c Merge pull request #33 from albertyw/update-readme
Add note about 2-factor auth in readme
2016-01-29 03:35:28 -05:00
Jose Diaz-Gonzalez
03739ce1be Merge pull request #31 from albertyw/fix-private-repos
Fix reading user's private repositories
2016-01-29 03:34:21 -05:00
Albert Wang
d2bb205b4b Add note about 2-factor auth 2016-01-29 00:33:53 -08:00
Albert Wang
17141c1bb6 Softly fail if not able to read hooks 2016-01-29 00:20:53 -08:00
Albert Wang
d362adbbca Make user repository search go through endpoint capable of reading private repositories 2016-01-28 22:52:58 -08:00
Jose Diaz-Gonzalez
89df625e04 Merge pull request #28 from alexmojaki/getpass
Prompt for password if only username given
2016-01-15 10:34:29 -05:00
Alex Hall
675484a215 Update README with new CLI usage 2016-01-12 14:40:29 +02:00
Alex Hall
325f77dcd9 Prompt for password if only username given 2016-01-12 11:18:26 +02:00
Jose Diaz-Gonzalez
f12e9167aa Release version 0.6.0 2015-11-10 15:36:20 -05:00
Jose Diaz-Gonzalez
816447af19 Force proper remote url 2015-11-10 15:36:12 -05:00
Jose Diaz-Gonzalez
d9e15e2be2 Merge pull request #24 from eht16/add_backup_hooks
Add backup hooks
2015-10-21 16:47:26 -04:00
Enrico Tröger
534145d178 Improve error handling in case of HTTP errors
In case of a HTTP status code 404, the returned 'r' was never assigned.
In case of URL errors which are not timeouts, we probably should bail
out.
2015-10-21 22:40:34 +02:00
Enrico Tröger
fe162eedd5 Add --hooks to also include web hooks into the backup 2015-10-21 22:39:45 +02:00
Jose Diaz-Gonzalez
53a9a22afb Merge pull request #22 from eht16/issue_17_create_output_directory
Create the user specified output directory if it does not exist
2015-10-16 15:05:29 -04:00
Jose Diaz-Gonzalez
2aa7d4cf1e Merge pull request #21 from eht16/fix_get_response_missing_auth
Add missing auth argument to _get_response()
2015-10-16 15:05:14 -04:00
Jose Diaz-Gonzalez
804843c128 Merge pull request #20 from eht16/improve_error_msg_on_non_existing_repo
Add repository URL to error message for non-existing repositories
2015-10-16 15:05:05 -04:00
Enrico Tröger
5fc27a4d42 Create the user specified output directory if it does not exist
Fixes #17.
2015-10-16 14:16:47 +02:00
Enrico Tröger
c8b3f048f5 Add repository URL to error message for non-existing repositories
This makes it easier for the user to identify which repository does not
exist or is not initialised, i.e. whether it is the main repository or
the wiki repository and which clone URL was used to check.
2015-10-16 14:09:13 +02:00
Enrico Tröger
2d98251992 Add missing auth argument to _get_response()
When running unauthenticated and Github starts rate-limiting the client,
github-backup crashes because the used auth variable in _get_response()
was not available. This change should fix it.
2015-10-16 14:00:56 +02:00
4 changed files with 123 additions and 28 deletions

View File

@@ -1,6 +1,53 @@
Changelog
=========
0.7.0 (2016-02-02)
------------------
- Softly fail if not able to read hooks. [Albert Wang]
- Add note about 2-factor auth. [Albert Wang]
- Make user repository search go through endpoint capable of reading
private repositories. [Albert Wang]
- Prompt for password if only username given. [Alex Hall]
0.6.0 (2015-11-10)
------------------
- Force proper remote url. [Jose Diaz-Gonzalez]
- Improve error handling in case of HTTP errors. [Enrico Tröger]
In case of a HTTP status code 404, the returned 'r' was never assigned.
In case of URL errors which are not timeouts, we probably should bail
out.
- Add --hooks to also include web hooks into the backup. [Enrico Tröger]
- Create the user specified output directory if it does not exist.
[Enrico Tröger]
Fixes #17.
- Add missing auth argument to _get_response() [Enrico Tröger]
When running unauthenticated and Github starts rate-limiting the client,
github-backup crashes because the used auth variable in _get_response()
was not available. This change should fix it.
- Add repository URL to error message for non-existing repositories.
[Enrico Tröger]
This makes it easier for the user to identify which repository does not
exist or is not initialised, i.e. whether it is the main repository or
the wiki repository and which clone URL was used to check.
0.5.0 (2015-10-10)
------------------

View File

@@ -23,15 +23,14 @@ CLI Usage is as follows::
Github Backup [-h] [-u USERNAME] [-p PASSWORD] [-t TOKEN]
[-o OUTPUT_DIRECTORY] [--starred] [--watched] [--all]
[--issues] [--issue-comments] [--issue-events] [--pulls]
[--pull-comments] [--pull-commits] [--repositories]
[--wikis] [--skip-existing]
[-L [LANGUAGES [LANGUAGES ...]]] [-N NAME_REGEX]
[-H GITHUB_HOST] [-O] [-R REPOSITORY] [-P] [-F]
[--prefer-ssh] [-v]
[--pull-comments] [--pull-commits] [--labels] [--hooks]
[--milestones] [--repositories] [--wikis]
[--skip-existing] [-L [LANGUAGES [LANGUAGES ...]]]
[-N NAME_REGEX] [-H GITHUB_HOST] [-O] [-R REPOSITORY]
[-P] [-F] [--prefer-ssh] [-v]
USER
Backup a github users account
Backup a github account
positional arguments:
USER github username
@@ -41,7 +40,8 @@ CLI Usage is as follows::
-u USERNAME, --username USERNAME
username for basic auth
-p PASSWORD, --password PASSWORD
password for basic auth
password for basic auth. If a username is given but
not a password, the password will be prompted for.
-t TOKEN, --token TOKEN
personal access or OAuth token
-o OUTPUT_DIRECTORY, --output-directory OUTPUT_DIRECTORY
@@ -55,6 +55,10 @@ CLI Usage is as follows::
--pulls include pull requests in backup
--pull-comments include pull request review comments in backup
--pull-commits include pull request commits in backup
--labels include labels in backup
--hooks include hooks in backup (works only when
authenticated)
--milestones include milestones in backup
--repositories include repository clone in backup
--wikis include wiki clone in backup
--skip-existing skip project if a backup directory exists
@@ -64,7 +68,7 @@ CLI Usage is as follows::
python regex to match names against
-H GITHUB_HOST, --github-host GITHUB_HOST
GitHub Enterprise hostname
-O, --organization whether or not this is a query for an organization
-O, --organization whether or not this is an organization user
-R REPOSITORY, --repository REPOSITORY
name of repository to limit backup to
-P, --private include private repositories
@@ -74,3 +78,8 @@ CLI Usage is as follows::
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).
Authentication
==============
Note: Password-based authentication will fail if you have two-factor authentication enabled.

View File

@@ -7,6 +7,7 @@ import base64
import calendar
import codecs
import errno
import getpass
import json
import logging
import os
@@ -109,7 +110,9 @@ def parse_args():
parser.add_argument('-p',
'--password',
dest='password',
help='password for basic auth')
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',
dest='token',
@@ -159,6 +162,10 @@ def parse_args():
action='store_true',
dest='include_labels',
help='include labels in backup')
parser.add_argument('--hooks',
action='store_true',
dest='include_hooks',
help='include hooks in backup (works only when authenticated)')
parser.add_argument('--milestones',
action='store_true',
dest='include_milestones',
@@ -215,17 +222,18 @@ def parse_args():
def get_auth(args):
auth = None
if args.token:
auth = base64.b64encode(args.token + ':' + 'x-oauth-basic')
elif args.username and args.password:
auth = base64.b64encode(args.username + ':' + args.password)
elif args.username and not args.password:
log_error('You must specify a password for basic auth')
elif args.password and not args.username:
return base64.b64encode(args.token + ':' + 'x-oauth-basic')
if args.username:
if not args.password:
args.password = getpass.getpass()
return base64.b64encode(args.username + ':' + args.password)
if args.password:
log_error('You must specify a username for basic auth')
return auth
return None
def get_github_api_host(args):
@@ -256,7 +264,7 @@ def retrieve_data(args, template, query_args=None, single_request=False):
while True:
page = page + 1
request = _construct_request(per_page, page, query_args, template, auth) # noqa
r, errors = _get_response(request, template)
r, errors = _get_response(request, auth, template)
status_code = int(r.getcode())
@@ -289,7 +297,7 @@ def get_query_args(query_args=None):
return query_args
def _get_response(request, template):
def _get_response(request, auth, template):
retry_timeout = 3
errors = []
# We'll make requests in a loop so we can
@@ -300,8 +308,11 @@ def _get_response(request, template):
r = urllib2.urlopen(request)
except urllib2.HTTPError as exc:
errors, should_continue = _request_http_error(exc, auth, errors) # noqa
r = exc
except urllib2.URLError:
should_continue = _request_url_error(template, retry_timeout)
if not should_continue:
raise
if should_continue:
continue
@@ -371,9 +382,8 @@ def _request_url_error(template, retry_timeout):
def retrieve_repositories(args):
log_info('Retrieving repositories')
single_request = False
template = 'https://{0}/users/{1}/repos'.format(
get_github_api_host(args),
args.user)
template = 'https://{0}/user/repos'.format(
get_github_api_host(args))
if args.organization:
template = 'https://{0}/orgs/{1}/repos'.format(
get_github_api_host(args),
@@ -391,6 +401,9 @@ def retrieve_repositories(args):
def filter_repositories(args, repositories):
log_info('Filtering repositories')
repositories = [r for r in repositories if r['owner']['login'] == args.user]
name_regex = None
if args.name_regex:
name_regex = re.compile(args.name_regex)
@@ -450,6 +463,9 @@ def backup_repositories(args, output_directory, repositories):
if args.include_labels or args.include_everything:
backup_labels(args, repo_cwd, repository, repos_template)
if args.include_hooks or args.include_everything:
backup_hooks(args, repo_cwd, repository, repos_template)
def backup_issues(args, repo_cwd, repository, repos_template):
has_issues_dir = os.path.isdir('{0}/issues/.git'.format(repo_cwd))
@@ -575,6 +591,25 @@ def backup_labels(args, repo_cwd, repository, repos_template):
label_cwd)
def backup_hooks(args, repo_cwd, repository, repos_template):
auth = get_auth(args)
if not auth:
log_info("Skipping hooks since no authentication provided")
return
hook_cwd = os.path.join(repo_cwd, 'hooks')
output_file = '{0}/hooks.json'.format(hook_cwd)
template = '{0}/{1}/hooks'.format(repos_template,
repository['full_name'])
try:
_backup_data(args,
'hooks',
template,
output_file,
hook_cwd)
except SystemExit:
log_info("Unable to read hooks, skipping")
def fetch_repository(name, remote_url, local_dir, skip_existing=False):
clone_exists = os.path.exists(os.path.join(local_dir, '.git'))
@@ -586,11 +621,15 @@ def fetch_repository(name, remote_url, local_dir, skip_existing=False):
stderr=FNULL,
shell=True)
if initalized == 128:
log_info("Skipping {0} since it's not initalized".format(name))
log_info("Skipping {0} ({1}) since it's not initalized".format(name, remote_url))
return
if clone_exists:
log_info('Updating {0} in {1}'.format(name, local_dir))
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)
git_command = ['git', 'fetch', '--all', '--tags', '--prune']
logging_subprocess(git_command, None, cwd=local_dir)
else:
@@ -651,8 +690,8 @@ def main():
output_directory = os.path.realpath(args.output_directory)
if not os.path.isdir(output_directory):
log_error('Specified output directory is not a directory: {0}'.format(
output_directory))
log_info('Create output directory {0}'.format(output_directory))
mkdir_p(output_directory)
log_info('Backing up user {0} to {1}'.format(args.user, output_directory))

View File

@@ -1 +1 @@
__version__ = '0.5.0'
__version__ = '0.7.0'