mirror of
https://github.com/josegonzalez/python-github-backup.git
synced 2025-12-05 16:18:02 +01:00
Compare commits
130 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcf21f7a2e | ||
|
|
36812a332b | ||
|
|
0e0197149e | ||
|
|
eb545c1c2f | ||
|
|
2e72797984 | ||
|
|
68fe29d1e1 | ||
|
|
3dc3691770 | ||
|
|
5b0608ce14 | ||
|
|
1ce8455860 | ||
|
|
dcb89a5c33 | ||
|
|
b0bfffde1a | ||
|
|
0f3aaa6fc2 | ||
|
|
c39ec9c549 | ||
|
|
e981ce3ff9 | ||
|
|
22d8f8e649 | ||
|
|
aaefac1a66 | ||
|
|
cb66375e1e | ||
|
|
24d7aa83df | ||
|
|
c8c71239c7 | ||
|
|
6ca8030648 | ||
|
|
53f6650f61 | ||
|
|
548a2ec405 | ||
|
|
871d69b99a | ||
|
|
ca3c4fa64b | ||
|
|
0846e7d8e5 | ||
|
|
503444359d | ||
|
|
04c70ce277 | ||
|
|
e774c70275 | ||
|
|
ba46cb87e8 | ||
|
|
883407f8ca | ||
|
|
aacb252e57 | ||
|
|
2623167110 | ||
|
|
f6ad296730 | ||
|
|
c8eef58d76 | ||
|
|
8eb154a540 | ||
|
|
2e9db92b68 | ||
|
|
09bbcfc7b1 | ||
|
|
4e14f5a2c6 | ||
|
|
b474e1654f | ||
|
|
71d70265cc | ||
|
|
2309b0cb76 | ||
|
|
1e14a4eecd | ||
|
|
56d3fd75bf | ||
|
|
c3e470b34e | ||
|
|
4948178a63 | ||
|
|
88de80c480 | ||
|
|
15eeff7879 | ||
|
|
4bb71db468 | ||
|
|
17af2cbc28 | ||
|
|
e0d66daadb | ||
|
|
1971c97b5d | ||
|
|
b1b3df692d | ||
|
|
8d7311efbf | ||
|
|
8449d6352d | ||
|
|
d8c228c83e | ||
|
|
4a134ae2ec | ||
|
|
5cb7c6ad2e | ||
|
|
75382afeae | ||
|
|
f325daa875 | ||
|
|
2cc34de2a3 | ||
|
|
dea87873f9 | ||
|
|
0288b5f553 | ||
|
|
02a07d3f0d | ||
|
|
24a7b1f885 | ||
|
|
22fa2eb97e | ||
|
|
cb147cf6d0 | ||
|
|
298724acfc | ||
|
|
65d541f577 | ||
|
|
8b08685678 | ||
|
|
b18ba6de28 | ||
|
|
358d1e3d3e | ||
|
|
1cd04281e9 | ||
|
|
6630b2b82e | ||
|
|
391f2ba305 | ||
|
|
1f0bf50381 | ||
|
|
eb44c735eb | ||
|
|
caff40e65b | ||
|
|
bba39fb4c8 | ||
|
|
093db93994 | ||
|
|
d835d47c17 | ||
|
|
2cd9061c46 | ||
|
|
0cc50bc4cb | ||
|
|
436e8df0ac | ||
|
|
9812988a4a | ||
|
|
1eccebcb83 | ||
|
|
122eb56aa1 | ||
|
|
a0fdae3314 | ||
|
|
80fa92664c | ||
|
|
7b69394488 | ||
|
|
d1d3d84d95 | ||
|
|
fff2aa4075 | ||
|
|
8eba46d8a7 | ||
|
|
9dc3458dba | ||
|
|
e9d7692123 | ||
|
|
a1ef61f87c | ||
|
|
6b62973997 | ||
|
|
b25af67898 | ||
|
|
0380fb8e35 | ||
|
|
f62fe5e6c9 | ||
|
|
c97598c914 | ||
|
|
c488b0adf9 | ||
|
|
888815c271 | ||
|
|
66e11aa532 | ||
|
|
d1874c0bd9 | ||
|
|
4c07bd1310 | ||
|
|
fd2d398025 | ||
|
|
53d2ceec10 | ||
|
|
421a7ec62b | ||
|
|
ec43649bcd | ||
|
|
e869844dba | ||
|
|
0857a37440 | ||
|
|
585af4c4e3 | ||
|
|
41ec01d5cb | ||
|
|
7dc22358df | ||
|
|
b855bcabf6 | ||
|
|
3c3262ed69 | ||
|
|
42b836f623 | ||
|
|
09f4168db6 | ||
|
|
3e9a4fa0d8 | ||
|
|
ab18e96ea8 | ||
|
|
eb88def888 | ||
|
|
7fe6541291 | ||
|
|
c8b8b270f6 | ||
|
|
a97f15b519 | ||
|
|
500c97c60e | ||
|
|
31a6e52a5e | ||
|
|
4c5187bcff | ||
|
|
2de69beffa | ||
|
|
96592295e1 | ||
|
|
bd65c3d5d6 |
15
.github/dependabot.yml
vendored
Normal file
15
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: pip
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "13:00"
|
||||||
|
groups:
|
||||||
|
python-packages:
|
||||||
|
patterns:
|
||||||
|
- "*"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
6
.github/workflows/automatic-release.yml
vendored
6
.github/workflows/automatic-release.yml
vendored
@@ -15,7 +15,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -27,9 +27,9 @@ jobs:
|
|||||||
git config --local user.email "action@github.com"
|
git config --local user.email "action@github.com"
|
||||||
git config --local user.name "GitHub Action"
|
git config --local user.name "GitHub Action"
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.8'
|
python-version: '3.12'
|
||||||
- name: Install prerequisites
|
- name: Install prerequisites
|
||||||
run: pip install -r release-requirements.txt
|
run: pip install -r release-requirements.txt
|
||||||
- name: Execute release
|
- name: Execute release
|
||||||
|
|||||||
77
.github/workflows/docker.yml
vendored
Normal file
77
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
|
||||||
|
name: Create and publish a Docker image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'main'
|
||||||
|
- 'dev'
|
||||||
|
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
- 'v*.*'
|
||||||
|
- 'v*.*.*'
|
||||||
|
- '*'
|
||||||
|
- '*.*'
|
||||||
|
- '*.*.*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
|
- 'dev'
|
||||||
|
|
||||||
|
|
||||||
|
env:
|
||||||
|
REGISTRY: ghcr.io
|
||||||
|
IMAGE_NAME: ${{ github.repository }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push-image:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to the Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.REGISTRY }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||||
|
tags: |
|
||||||
|
type=semver,pattern={{version}}
|
||||||
|
type=semver,pattern={{major}}.{{minor}}
|
||||||
|
type=semver,pattern={{major}}
|
||||||
|
type=sha
|
||||||
|
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
push: true
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
15
.github/workflows/lint.yml
vendored
15
.github/workflows/lint.yml
vendored
@@ -5,16 +5,16 @@ name: "lint"
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- '*'
|
- "*"
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- "main"
|
||||||
- 'master'
|
- "master"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
name: lint
|
name: lint
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -22,11 +22,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.8"
|
python-version: "3.12"
|
||||||
cache: "pip"
|
cache: "pip"
|
||||||
- run: pip install -r release-requirements.txt
|
- run: pip install -r release-requirements.txt && pip install wheel
|
||||||
- run: flake8 --ignore=E501,E203,W503
|
- run: flake8 --ignore=E501,E203,W503
|
||||||
- run: black .
|
- run: black .
|
||||||
- run: rst-lint README.rst
|
- run: rst-lint README.rst
|
||||||
|
- run: python setup.py sdist bdist_wheel && twine check dist/*
|
||||||
|
|||||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -18,13 +18,13 @@ pkg
|
|||||||
|
|
||||||
# Debian Files
|
# Debian Files
|
||||||
debian/files
|
debian/files
|
||||||
debian/python-aws-hostname*
|
debian/python-github-backup*
|
||||||
|
|
||||||
# Sphinx build
|
# Sphinx build
|
||||||
doc/_build
|
doc/_build
|
||||||
|
|
||||||
# Generated man page
|
# Generated man page
|
||||||
doc/aws_hostname.1
|
doc/github_backup.1
|
||||||
|
|
||||||
# Annoying macOS files
|
# Annoying macOS files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
@@ -35,3 +35,10 @@ doc/aws_hostname.1
|
|||||||
.atom
|
.atom
|
||||||
|
|
||||||
README
|
README
|
||||||
|
|
||||||
|
# RSA
|
||||||
|
id_rsa
|
||||||
|
id_rsa.pub
|
||||||
|
|
||||||
|
# Virtual env
|
||||||
|
venv
|
||||||
|
|||||||
1230
CHANGES.rst
1230
CHANGES.rst
File diff suppressed because it is too large
Load Diff
25
README.rst
25
README.rst
@@ -49,7 +49,9 @@ CLI Help output::
|
|||||||
[-P] [-F] [--prefer-ssh] [-v]
|
[-P] [-F] [--prefer-ssh] [-v]
|
||||||
[--keychain-name OSX_KEYCHAIN_ITEM_NAME]
|
[--keychain-name OSX_KEYCHAIN_ITEM_NAME]
|
||||||
[--keychain-account OSX_KEYCHAIN_ITEM_ACCOUNT]
|
[--keychain-account OSX_KEYCHAIN_ITEM_ACCOUNT]
|
||||||
[--releases] [--assets] [--exclude [REPOSITORY [REPOSITORY ...]]
|
[--releases] [--latest-releases NUMBER_OF_LATEST_RELEASES]
|
||||||
|
[--skip-prerelease] [--assets]
|
||||||
|
[--exclude [REPOSITORY [REPOSITORY ...]]
|
||||||
[--throttle-limit THROTTLE_LIMIT] [--throttle-pause THROTTLE_PAUSE]
|
[--throttle-limit THROTTLE_LIMIT] [--throttle-pause THROTTLE_PAUSE]
|
||||||
USER
|
USER
|
||||||
|
|
||||||
@@ -124,6 +126,10 @@ CLI Help output::
|
|||||||
keychain that holds the personal access or OAuth token
|
keychain that holds the personal access or OAuth token
|
||||||
--releases include release information, not including assets or
|
--releases include release information, not including assets or
|
||||||
binaries
|
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
|
||||||
--assets include assets alongside release information; only
|
--assets include assets alongside release information; only
|
||||||
applies if including releases
|
applies if including releases
|
||||||
--exclude [REPOSITORY [REPOSITORY ...]]
|
--exclude [REPOSITORY [REPOSITORY ...]]
|
||||||
@@ -161,7 +167,7 @@ Customise the permissions for your use case, but for a personal account full bac
|
|||||||
|
|
||||||
**User permissions**: Read access to followers, starring, and watching.
|
**User permissions**: Read access to followers, starring, and watching.
|
||||||
|
|
||||||
**Repository permissions**: Read access to code, commit statuses, issues, metadata, pages, pull requests, and repository hooks.
|
**Repository permissions**: Read access to contents, issues, metadata, pull requests, and webhooks.
|
||||||
|
|
||||||
|
|
||||||
Prefer SSH
|
Prefer SSH
|
||||||
@@ -206,13 +212,20 @@ When you use the ``--lfs`` option, you will need to make sure you have Git LFS i
|
|||||||
Instructions on how to do this can be found on https://git-lfs.github.com.
|
Instructions on how to do this can be found on https://git-lfs.github.com.
|
||||||
|
|
||||||
|
|
||||||
|
Run in Docker container
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
To run the tool in a Docker container use the following command:
|
||||||
|
|
||||||
|
sudo docker run --rm -v /path/to/backup:/data --name github-backup ghcr.io/josegonzalez/python-github-backup -o /data $OPTIONS $USER
|
||||||
|
|
||||||
Gotchas / Known-issues
|
Gotchas / Known-issues
|
||||||
======================
|
======================
|
||||||
|
|
||||||
All is not everything
|
All is not everything
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
The ``--all`` argument does not include; cloning private repos (``-P, --private``), cloning forks (``-F, --fork``) cloning starred repositories (``--all-starred``), ``--pull-details``, cloning LFS repositories (``--lfs``), cloning gists (``--starred-gists``) or cloning starred gist repos (``--starred-gists``). See examples for more.
|
The ``--all`` argument does not include: cloning private repos (``-P, --private``), cloning forks (``-F, --fork``), cloning starred repositories (``--all-starred``), ``--pull-details``, cloning LFS repositories (``--lfs``), cloning gists (``--gists``) or cloning starred gist repos (``--starred-gists``). See examples for more.
|
||||||
|
|
||||||
Cloning all starred size
|
Cloning all starred size
|
||||||
------------------------
|
------------------------
|
||||||
@@ -241,12 +254,6 @@ It's therefore recommended to only use the incremental argument if the output/re
|
|||||||
|
|
||||||
This is due to needing the correct permission for ``--hooks`` on public repos.
|
This is due to needing the correct permission for ``--hooks`` on public repos.
|
||||||
|
|
||||||
2. **Releases blocking**
|
|
||||||
|
|
||||||
A known ``--releases`` (required for ``--assets``) error will sometimes block the backup.
|
|
||||||
|
|
||||||
If you're backing up a lot of repositories with releases e.g. an organisation or ``--all-starred``. You may need to remove ``--releases`` (and therefore ``--assets``) to complete a backup. Documented in `issue 209 <https://github.com/josegonzalez/python-github-backup/issues/209>`_.
|
|
||||||
|
|
||||||
|
|
||||||
"bare" is actually "mirror"
|
"bare" is actually "mirror"
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import os, sys, logging
|
import logging
|
||||||
|
import os
|
||||||
logging.basicConfig(
|
import sys
|
||||||
format='%(asctime)s.%(msecs)03d: %(message)s',
|
|
||||||
datefmt='%Y-%m-%dT%H:%M:%S',
|
|
||||||
level=logging.INFO
|
|
||||||
)
|
|
||||||
|
|
||||||
from github_backup.github_backup import (
|
from github_backup.github_backup import (
|
||||||
backup_account,
|
backup_account,
|
||||||
@@ -20,6 +16,12 @@ from github_backup.github_backup import (
|
|||||||
retrieve_repositories,
|
retrieve_repositories,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
format="%(asctime)s.%(msecs)03d: %(message)s",
|
||||||
|
datefmt="%Y-%m-%dT%H:%M:%S",
|
||||||
|
level=logging.INFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
@@ -29,7 +31,7 @@ def main():
|
|||||||
|
|
||||||
output_directory = os.path.realpath(args.output_directory)
|
output_directory = os.path.realpath(args.output_directory)
|
||||||
if not os.path.isdir(output_directory):
|
if not os.path.isdir(output_directory):
|
||||||
logger.info('Create output directory {0}'.format(output_directory))
|
logger.info("Create output directory {0}".format(output_directory))
|
||||||
mkdir_p(output_directory)
|
mkdir_p(output_directory)
|
||||||
|
|
||||||
if args.lfs_clone:
|
if args.lfs_clone:
|
||||||
@@ -41,10 +43,10 @@ def main():
|
|||||||
logger.root.setLevel(log_level)
|
logger.root.setLevel(log_level)
|
||||||
|
|
||||||
if not args.as_app:
|
if not args.as_app:
|
||||||
logger.info('Backing up user {0} to {1}'.format(args.user, output_directory))
|
logger.info("Backing up user {0} to {1}".format(args.user, output_directory))
|
||||||
authenticated_user = get_authenticated_user(args)
|
authenticated_user = get_authenticated_user(args)
|
||||||
else:
|
else:
|
||||||
authenticated_user = {'login': None}
|
authenticated_user = {"login": None}
|
||||||
|
|
||||||
repositories = retrieve_repositories(args, authenticated_user)
|
repositories = retrieve_repositories(args, authenticated_user)
|
||||||
repositories = filter_repositories(args, repositories)
|
repositories = filter_repositories(args, repositories)
|
||||||
@@ -52,7 +54,7 @@ def main():
|
|||||||
backup_account(args, output_directory)
|
backup_account(args, output_directory)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
try:
|
try:
|
||||||
main()
|
main()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = "0.44.1"
|
__version__ = "0.48.0"
|
||||||
|
|||||||
@@ -15,9 +15,11 @@ import platform
|
|||||||
import re
|
import re
|
||||||
import select
|
import select
|
||||||
import socket
|
import socket
|
||||||
|
import ssl
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime
|
||||||
from http.client import IncompleteRead
|
from http.client import IncompleteRead
|
||||||
from urllib.error import HTTPError, URLError
|
from urllib.error import HTTPError, URLError
|
||||||
from urllib.parse import quote as urlquote
|
from urllib.parse import quote as urlquote
|
||||||
@@ -35,6 +37,23 @@ FNULL = open(os.devnull, "w")
|
|||||||
FILE_URI_PREFIX = "file://"
|
FILE_URI_PREFIX = "file://"
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
https_ctx = ssl.create_default_context()
|
||||||
|
if not https_ctx.get_ca_certs():
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
warnings.warn(
|
||||||
|
"\n\nYOUR DEFAULT CA CERTS ARE EMPTY.\n"
|
||||||
|
+ "PLEASE POPULATE ANY OF:"
|
||||||
|
+ "".join(
|
||||||
|
["\n - " + x for x in ssl.get_default_verify_paths() if type(x) is str]
|
||||||
|
)
|
||||||
|
+ "\n",
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
import certifi
|
||||||
|
|
||||||
|
https_ctx = ssl.create_default_context(cafile=certifi.where())
|
||||||
|
|
||||||
|
|
||||||
def logging_subprocess(
|
def logging_subprocess(
|
||||||
popenargs, stdout_log_level=logging.DEBUG, stderr_log_level=logging.ERROR, **kwargs
|
popenargs, stdout_log_level=logging.DEBUG, stderr_log_level=logging.ERROR, **kwargs
|
||||||
@@ -376,6 +395,19 @@ def parse_args(args=None):
|
|||||||
dest="include_releases",
|
dest="include_releases",
|
||||||
help="include release information, not including assets or binaries",
|
help="include release information, not including assets or binaries",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--latest-releases",
|
||||||
|
type=int,
|
||||||
|
default=0,
|
||||||
|
dest="number_of_latest_releases",
|
||||||
|
help="include certain number of the latest releases; only applies if including releases",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--skip-prerelease",
|
||||||
|
action="store_true",
|
||||||
|
dest="skip_prerelease",
|
||||||
|
help="skip prerelease and draft versions; only applies if including releases",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--assets",
|
"--assets",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@@ -652,7 +684,7 @@ def _get_response(request, auth, template):
|
|||||||
while True:
|
while True:
|
||||||
should_continue = False
|
should_continue = False
|
||||||
try:
|
try:
|
||||||
r = urlopen(request)
|
r = urlopen(request, context=https_ctx)
|
||||||
except HTTPError as exc:
|
except HTTPError as exc:
|
||||||
errors, should_continue = _request_http_error(exc, auth, errors) # noqa
|
errors, should_continue = _request_http_error(exc, auth, errors) # noqa
|
||||||
r = exc
|
r = exc
|
||||||
@@ -763,14 +795,21 @@ class S3HTTPRedirectHandler(HTTPRedirectHandler):
|
|||||||
return request
|
return request
|
||||||
|
|
||||||
|
|
||||||
def download_file(url, path, auth):
|
def download_file(url, path, auth, as_app=False, fine=False):
|
||||||
# Skip downloading release assets if they already exist on disk so we don't redownload on every sync
|
# Skip downloading release assets if they already exist on disk so we don't redownload on every sync
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
return
|
return
|
||||||
|
|
||||||
request = Request(url)
|
request = _construct_request(
|
||||||
|
per_page=100,
|
||||||
|
page=1,
|
||||||
|
query_args={},
|
||||||
|
template=url,
|
||||||
|
auth=auth,
|
||||||
|
as_app=as_app,
|
||||||
|
fine=fine,
|
||||||
|
)
|
||||||
request.add_header("Accept", "application/octet-stream")
|
request.add_header("Accept", "application/octet-stream")
|
||||||
request.add_header("Authorization", "Basic ".encode("ascii") + auth)
|
|
||||||
opener = build_opener(S3HTTPRedirectHandler)
|
opener = build_opener(S3HTTPRedirectHandler)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -912,11 +951,15 @@ def filter_repositories(args, unfiltered_repositories):
|
|||||||
if r.get("language") and r.get("language").lower() in languages
|
if r.get("language") and r.get("language").lower() in languages
|
||||||
] # noqa
|
] # noqa
|
||||||
if name_regex:
|
if name_regex:
|
||||||
repositories = [r for r in repositories if name_regex.match(r["name"])]
|
repositories = [
|
||||||
|
r for r in repositories if "name" not in r or name_regex.match(r["name"])
|
||||||
|
]
|
||||||
if args.skip_archived:
|
if args.skip_archived:
|
||||||
repositories = [r for r in repositories if not r.get("archived")]
|
repositories = [r for r in repositories if not r.get("archived")]
|
||||||
if args.exclude:
|
if args.exclude:
|
||||||
repositories = [r for r in repositories if r["name"] not in args.exclude]
|
repositories = [
|
||||||
|
r for r in repositories if "name" not in r or r["name"] not in args.exclude
|
||||||
|
]
|
||||||
|
|
||||||
return repositories
|
return repositories
|
||||||
|
|
||||||
@@ -1189,8 +1232,11 @@ def backup_hooks(args, repo_cwd, repository, repos_template):
|
|||||||
template = "{0}/{1}/hooks".format(repos_template, repository["full_name"])
|
template = "{0}/{1}/hooks".format(repos_template, repository["full_name"])
|
||||||
try:
|
try:
|
||||||
_backup_data(args, "hooks", template, output_file, hook_cwd)
|
_backup_data(args, "hooks", template, output_file, hook_cwd)
|
||||||
except SystemExit:
|
except Exception as e:
|
||||||
|
if "404" in str(e):
|
||||||
logger.info("Unable to read hooks, skipping")
|
logger.info("Unable to read hooks, skipping")
|
||||||
|
else:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def backup_releases(args, repo_cwd, repository, repos_template, include_assets=False):
|
def backup_releases(args, repo_cwd, repository, repos_template, include_assets=False):
|
||||||
@@ -1206,8 +1252,24 @@ def backup_releases(args, repo_cwd, repository, repos_template, include_assets=F
|
|||||||
release_template = "{0}/{1}/releases".format(repos_template, repository_fullname)
|
release_template = "{0}/{1}/releases".format(repos_template, repository_fullname)
|
||||||
releases = retrieve_data(args, release_template, query_args=query_args)
|
releases = retrieve_data(args, release_template, query_args=query_args)
|
||||||
|
|
||||||
# for each release, store it
|
if args.skip_prerelease:
|
||||||
|
releases = [r for r in releases if not r["prerelease"] and not r["draft"]]
|
||||||
|
|
||||||
|
if args.number_of_latest_releases and args.number_of_latest_releases < len(
|
||||||
|
releases
|
||||||
|
):
|
||||||
|
releases.sort(
|
||||||
|
key=lambda item: datetime.strptime(
|
||||||
|
item["created_at"], "%Y-%m-%dT%H:%M:%SZ"
|
||||||
|
),
|
||||||
|
reverse=True,
|
||||||
|
)
|
||||||
|
releases = releases[: args.number_of_latest_releases]
|
||||||
|
logger.info("Saving the latest {0} releases to disk".format(len(releases)))
|
||||||
|
else:
|
||||||
logger.info("Saving {0} releases to disk".format(len(releases)))
|
logger.info("Saving {0} releases to disk".format(len(releases)))
|
||||||
|
|
||||||
|
# for each release, store it
|
||||||
for release in releases:
|
for release in releases:
|
||||||
release_name = release["tag_name"]
|
release_name = release["tag_name"]
|
||||||
release_name_safe = release_name.replace("/", "__")
|
release_name_safe = release_name.replace("/", "__")
|
||||||
@@ -1227,7 +1289,9 @@ def backup_releases(args, repo_cwd, repository, repos_template, include_assets=F
|
|||||||
download_file(
|
download_file(
|
||||||
asset["url"],
|
asset["url"],
|
||||||
os.path.join(release_assets_cwd, asset["name"]),
|
os.path.join(release_assets_cwd, asset["name"]),
|
||||||
get_auth(args),
|
get_auth(args, encode=not args.as_app),
|
||||||
|
as_app=args.as_app,
|
||||||
|
fine=True if args.token_fine is not None else False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1284,10 +1348,12 @@ def fetch_repository(
|
|||||||
git_command = ["git", "remote", "set-url", "origin", remote_url]
|
git_command = ["git", "remote", "set-url", "origin", remote_url]
|
||||||
logging_subprocess(git_command, cwd=local_dir)
|
logging_subprocess(git_command, cwd=local_dir)
|
||||||
|
|
||||||
|
git_command = ["git", "fetch", "--all", "--force", "--tags", "--prune"]
|
||||||
|
if no_prune:
|
||||||
|
git_command.pop()
|
||||||
|
logging_subprocess(git_command, cwd=local_dir)
|
||||||
if lfs_clone:
|
if lfs_clone:
|
||||||
git_command = ["git", "lfs", "fetch", "--all", "--prune"]
|
git_command = ["git", "lfs", "fetch", "--all", "--prune"]
|
||||||
else:
|
|
||||||
git_command = ["git", "fetch", "--all", "--force", "--tags", "--prune"]
|
|
||||||
if no_prune:
|
if no_prune:
|
||||||
git_command.pop()
|
git_command.pop()
|
||||||
logging_subprocess(git_command, cwd=local_dir)
|
logging_subprocess(git_command, cwd=local_dir)
|
||||||
|
|||||||
7
python-github-backup.code-workspace
Executable file
7
python-github-backup.code-workspace
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"path": "."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,38 +1,39 @@
|
|||||||
autopep8==2.0.4
|
autopep8==2.3.1
|
||||||
black==23.11.0
|
black==24.10.0
|
||||||
bleach==6.0.0
|
bleach==6.2.0
|
||||||
certifi==2023.7.22
|
certifi==2024.12.14
|
||||||
charset-normalizer==3.1.0
|
charset-normalizer==3.4.1
|
||||||
click==8.1.7
|
click==8.1.8
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
docutils==0.20.1
|
docutils==0.21.2
|
||||||
flake8==6.1.0
|
flake8==7.1.1
|
||||||
gitchangelog==3.0.4
|
gitchangelog==3.0.4
|
||||||
idna==3.4
|
idna==3.10
|
||||||
importlib-metadata==6.6.0
|
importlib-metadata==8.5.0
|
||||||
jaraco.classes==3.2.3
|
jaraco.classes==3.4.0
|
||||||
keyring==23.13.1
|
keyring==25.6.0
|
||||||
markdown-it-py==2.2.0
|
markdown-it-py==3.0.0
|
||||||
mccabe==0.7.0
|
mccabe==0.7.0
|
||||||
mdurl==0.1.2
|
mdurl==0.1.2
|
||||||
more-itertools==9.1.0
|
more-itertools==10.5.0
|
||||||
mypy-extensions==1.0.0
|
mypy-extensions==1.0.0
|
||||||
packaging==23.2
|
packaging==24.2
|
||||||
pathspec==0.11.2
|
pathspec==0.12.1
|
||||||
pkginfo==1.9.6
|
pkginfo==1.12.0
|
||||||
platformdirs==4.1.0
|
platformdirs==4.3.6
|
||||||
pycodestyle==2.11.1
|
pycodestyle==2.12.1
|
||||||
pyflakes==3.1.0
|
pyflakes==3.2.0
|
||||||
Pygments==2.15.1
|
Pygments==2.18.0
|
||||||
readme-renderer==37.3
|
readme-renderer==44.0
|
||||||
requests==2.31.0
|
requests==2.32.3
|
||||||
requests-toolbelt==1.0.0
|
requests-toolbelt==1.0.0
|
||||||
restructuredtext-lint==1.4.0
|
restructuredtext-lint==1.4.0
|
||||||
rfc3986==2.0.0
|
rfc3986==2.0.0
|
||||||
rich==13.3.5
|
rich==13.9.4
|
||||||
six==1.16.0
|
setuptools==75.6.0
|
||||||
tqdm==4.65.0
|
six==1.17.0
|
||||||
twine==4.0.2
|
tqdm==4.67.1
|
||||||
urllib3==2.0.7
|
twine==6.0.1
|
||||||
|
urllib3==2.3.0
|
||||||
webencodings==0.5.1
|
webencodings==0.5.1
|
||||||
zipp==3.15.0
|
zipp==3.21.0
|
||||||
|
|||||||
8
setup.py
8
setup.py
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from github_backup import __version__
|
from github_backup import __version__
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -39,10 +40,11 @@ setup(
|
|||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Topic :: System :: Archiving :: Backup",
|
"Topic :: System :: Archiving :: Backup",
|
||||||
"License :: OSI Approved :: MIT License",
|
"License :: OSI Approved :: MIT License",
|
||||||
"Programming Language :: Python :: 3.5",
|
|
||||||
"Programming Language :: Python :: 3.6",
|
|
||||||
"Programming Language :: Python :: 3.7",
|
|
||||||
"Programming Language :: Python :: 3.8",
|
"Programming Language :: Python :: 3.8",
|
||||||
|
"Programming Language :: Python :: 3.9",
|
||||||
|
"Programming Language :: Python :: 3.10",
|
||||||
|
"Programming Language :: Python :: 3.11",
|
||||||
|
"Programming Language :: Python :: 3.12",
|
||||||
],
|
],
|
||||||
description="backup a github user or organization",
|
description="backup a github user or organization",
|
||||||
long_description=open_file("README.rst").read(),
|
long_description=open_file("README.rst").read(),
|
||||||
|
|||||||
Reference in New Issue
Block a user