Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 10 additions & 68 deletions bin/github-backup
Original file line number Diff line number Diff line change
@@ -1,76 +1,18 @@
#!/usr/bin/env python
"""
Backwards-compatible wrapper script.

import logging
import os
import sys

from github_backup.github_backup import (
backup_account,
backup_repositories,
check_git_lfs_install,
filter_repositories,
get_auth,
get_authenticated_user,
logger,
mkdir_p,
parse_args,
retrieve_repositories,
)

# INFO and DEBUG go to stdout, WARNING and above go to stderr
log_format = logging.Formatter(
fmt="%(asctime)s.%(msecs)03d: %(message)s",
datefmt="%Y-%m-%dT%H:%M:%S",
)

stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setLevel(logging.DEBUG)
stdout_handler.addFilter(lambda r: r.levelno < logging.WARNING)
stdout_handler.setFormatter(log_format)

stderr_handler = logging.StreamHandler(sys.stderr)
stderr_handler.setLevel(logging.WARNING)
stderr_handler.setFormatter(log_format)

logging.basicConfig(level=logging.INFO, handlers=[stdout_handler, stderr_handler])

The recommended way to run github-backup is via the installed command
(pip install github-backup) or python -m github_backup.

def main():
args = parse_args()
This script is kept for backwards compatibility with existing installations
that may reference this path directly.
"""

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."
)

if args.quiet:
logger.setLevel(logging.WARNING)

output_directory = os.path.realpath(args.output_directory)
if not os.path.isdir(output_directory):
logger.info("Create output directory {0}".format(output_directory))
mkdir_p(output_directory)

if args.lfs_clone:
check_git_lfs_install()

if args.log_level:
log_level = logging.getLevelName(args.log_level.upper())
if isinstance(log_level, int):
logger.root.setLevel(log_level)

if not args.as_app:
logger.info("Backing up user {0} to {1}".format(args.user, output_directory))
authenticated_user = get_authenticated_user(args)
else:
authenticated_user = {"login": None}

repositories = retrieve_repositories(args, authenticated_user)
repositories = filter_repositories(args, repositories)
backup_repositories(args, output_directory, repositories)
backup_account(args, output_directory)
import sys

from github_backup.cli import main
from github_backup.github_backup import logger

if __name__ == "__main__":
try:
Expand Down
13 changes: 13 additions & 0 deletions github_backup/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Allow running as: python -m github_backup"""

import sys

from github_backup.cli import main
from github_backup.github_backup import logger

if __name__ == "__main__":
try:
main()
except Exception as e:
logger.error(str(e))
sys.exit(1)
82 changes: 82 additions & 0 deletions github_backup/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/usr/bin/env python
"""Command-line interface for github-backup."""

import logging
import os
import sys

from github_backup.github_backup import (
backup_account,
backup_repositories,
check_git_lfs_install,
filter_repositories,
get_auth,
get_authenticated_user,
logger,
mkdir_p,
parse_args,
retrieve_repositories,
)

# INFO and DEBUG go to stdout, WARNING and above go to stderr
log_format = logging.Formatter(
fmt="%(asctime)s.%(msecs)03d: %(message)s",
datefmt="%Y-%m-%dT%H:%M:%S",
)

stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setLevel(logging.DEBUG)
stdout_handler.addFilter(lambda r: r.levelno < logging.WARNING)
stdout_handler.setFormatter(log_format)

stderr_handler = logging.StreamHandler(sys.stderr)
stderr_handler.setLevel(logging.WARNING)
stderr_handler.setFormatter(log_format)

logging.basicConfig(level=logging.INFO, handlers=[stdout_handler, stderr_handler])


def main():
"""Main entry point for github-backup CLI."""
args = parse_args()

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."
)

if args.quiet:
logger.setLevel(logging.WARNING)

output_directory = os.path.realpath(args.output_directory)
if not os.path.isdir(output_directory):
logger.info("Create output directory {0}".format(output_directory))
mkdir_p(output_directory)

if args.lfs_clone:
check_git_lfs_install()

if args.log_level:
log_level = logging.getLevelName(args.log_level.upper())
if isinstance(log_level, int):
logger.root.setLevel(log_level)

if not args.as_app:
logger.info("Backing up user {0} to {1}".format(args.user, output_directory))
authenticated_user = get_authenticated_user(args)
else:
authenticated_user = {"login": None}

repositories = retrieve_repositories(args, authenticated_user)
repositories = filter_repositories(args, repositories)
backup_repositories(args, output_directory, repositories)
backup_account(args, output_directory)


if __name__ == "__main__":
try:
main()
except Exception as e:
logger.error(str(e))
sys.exit(1)
12 changes: 6 additions & 6 deletions github_backup/github_backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,7 @@ def download_attachment_file(url, path, auth, as_app=False, fine=False):
bytes_downloaded += len(chunk)

# Atomic rename to final location
os.rename(temp_path, path)
os.replace(temp_path, path)

metadata["size_bytes"] = bytes_downloaded
metadata["success"] = True
Expand Down Expand Up @@ -1459,7 +1459,7 @@ def download_attachments(

# Rename to add extension (already atomic from download)
try:
os.rename(filepath, final_filepath)
os.replace(filepath, final_filepath)
metadata["saved_as"] = os.path.basename(final_filepath)
except Exception as e:
logger.warning(
Expand Down Expand Up @@ -1490,7 +1490,7 @@ def download_attachments(
manifest_path = os.path.join(attachments_dir, "manifest.json")
with open(manifest_path + ".temp", "w") as f:
json.dump(manifest, f, indent=2)
os.rename(manifest_path + ".temp", manifest_path) # Atomic write
os.replace(manifest_path + ".temp", manifest_path) # Atomic write
logger.debug(
"Wrote manifest for {0} #{1}: {2} attachments".format(
item_type_display, number, len(attachment_metadata_list)
Expand Down Expand Up @@ -1811,7 +1811,7 @@ def backup_issues(args, repo_cwd, repository, repos_template):

with codecs.open(issue_file + ".temp", "w", encoding="utf-8") as f:
json_dump(issue, f)
os.rename(issue_file + ".temp", issue_file) # Unlike json_dump, this is atomic
os.replace(issue_file + ".temp", issue_file) # Atomic write


def backup_pulls(args, repo_cwd, repository, repos_template):
Expand Down Expand Up @@ -1886,7 +1886,7 @@ def backup_pulls(args, repo_cwd, repository, repos_template):

with codecs.open(pull_file + ".temp", "w", encoding="utf-8") as f:
json_dump(pull, f)
os.rename(pull_file + ".temp", pull_file) # Unlike json_dump, this is atomic
os.replace(pull_file + ".temp", pull_file) # Atomic write


def backup_milestones(args, repo_cwd, repository, repos_template):
Expand Down Expand Up @@ -2203,5 +2203,5 @@ def json_dump_if_changed(data, output_file_path):
temp_file = output_file_path + ".temp"
with codecs.open(temp_file, "w", encoding="utf-8") as f:
f.write(new_content)
os.rename(temp_file, output_file_path) # Atomic on POSIX systems
os.replace(temp_file, output_file_path) # Atomic write
return True
6 changes: 5 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ def open_file(fname):
author="Jose Diaz-Gonzalez",
author_email="github-backup@josediazgonzalez.com",
packages=["github_backup"],
scripts=["bin/github-backup"],
entry_points={
"console_scripts": [
"github-backup=github_backup.cli:main",
],
},
url="http://github.com/josegonzalez/python-github-backup",
license="MIT",
classifiers=[
Expand Down