@@ -440,6 +440,12 @@ def parse_args(args=None):
440440 dest = "include_assets" ,
441441 help = "include assets alongside release information; only applies if including releases" ,
442442 )
443+ parser .add_argument (
444+ "--skip-assets-on" ,
445+ dest = "skip_assets_on" ,
446+ nargs = "*" ,
447+ help = "skip asset downloads for these repositories" ,
448+ )
443449 parser .add_argument (
444450 "--attachments" ,
445451 action = "store_true" ,
@@ -561,7 +567,7 @@ def get_github_host(args):
561567
562568
563569def read_file_contents (file_uri ):
564- return open (file_uri [len (FILE_URI_PREFIX ):], "rt" ).readline ().strip ()
570+ return open (file_uri [len (FILE_URI_PREFIX ) :], "rt" ).readline ().strip ()
565571
566572
567573def get_github_repo_url (args , repository ):
@@ -631,7 +637,7 @@ def retrieve_data_gen(args, template, query_args=None, single_request=False):
631637 pass
632638 raise RepositoryUnavailableError (
633639 "Repository unavailable due to legal reasons (HTTP 451)" ,
634- dmca_url = dmca_url
640+ dmca_url = dmca_url ,
635641 )
636642
637643 # Check if we got correct data
@@ -709,7 +715,7 @@ def retrieve_data_gen(args, template, query_args=None, single_request=False):
709715 # Parse Link header: <https://api.github.com/...?per_page=100&after=cursor>; rel="next"
710716 for link in link_header .split ("," ):
711717 if 'rel="next"' in link :
712- next_url = link [link .find ("<" ) + 1 : link .find (">" )]
718+ next_url = link [link .find ("<" ) + 1 : link .find (">" )]
713719 break
714720 if not next_url :
715721 break
@@ -763,9 +769,7 @@ def _get_response(request, auth, template):
763769 return r , errors
764770
765771
766- def _construct_request (
767- per_page , query_args , template , auth , as_app = None , fine = False
768- ):
772+ def _construct_request (per_page , query_args , template , auth , as_app = None , fine = False ):
769773 # If template is already a full URL with query params (from Link header), use it directly
770774 if "?" in template and template .startswith ("http" ):
771775 request_url = template
@@ -1480,9 +1484,11 @@ def download_attachments(
14801484 manifest = {
14811485 "issue_number" : number ,
14821486 "issue_type" : item_type ,
1483- "repository" : f"{ args .user } /{ args .repository } "
1484- if hasattr (args , "repository" ) and args .repository
1485- else args .user ,
1487+ "repository" : (
1488+ f"{ args .user } /{ args .repository } "
1489+ if hasattr (args , "repository" ) and args .repository
1490+ else args .user
1491+ ),
14861492 "manifest_updated_at" : datetime .now (timezone .utc ).isoformat (),
14871493 "attachments" : attachment_metadata_list ,
14881494 }
@@ -1538,9 +1544,7 @@ def retrieve_repositories(args, authenticated_user):
15381544 else :
15391545 repo_path = "{0}/{1}" .format (args .user , args .repository )
15401546 single_request = True
1541- template = "https://{0}/repos/{1}" .format (
1542- get_github_api_host (args ), repo_path
1543- )
1547+ template = "https://{0}/repos/{1}" .format (get_github_api_host (args ), repo_path )
15441548
15451549 repos = retrieve_data (args , template , single_request = single_request )
15461550
@@ -1565,7 +1569,10 @@ def retrieve_repositories(args, authenticated_user):
15651569 repos .extend (gists )
15661570
15671571 if args .include_starred_gists :
1568- if not authenticated_user .get ("login" ) or args .user .lower () != authenticated_user ["login" ].lower ():
1572+ if (
1573+ not authenticated_user .get ("login" )
1574+ or args .user .lower () != authenticated_user ["login" ].lower ()
1575+ ):
15691576 logger .warning (
15701577 "Cannot retrieve starred gists for '%s'. GitHub only allows access to the authenticated user's starred gists." ,
15711578 args .user ,
@@ -1673,9 +1680,11 @@ def backup_repositories(args, output_directory, repositories):
16731680
16741681 include_gists = args .include_gists or args .include_starred_gists
16751682 include_starred = args .all_starred and repository .get ("is_starred" )
1676- if (args .include_repository or args .include_everything ) or (
1677- include_gists and repository .get ("is_gist" )
1678- ) or include_starred :
1683+ if (
1684+ (args .include_repository or args .include_everything )
1685+ or (include_gists and repository .get ("is_gist" ))
1686+ or include_starred
1687+ ):
16791688 repo_name = (
16801689 repository .get ("name" )
16811690 if not repository .get ("is_gist" )
@@ -1735,7 +1744,9 @@ def backup_repositories(args, output_directory, repositories):
17351744 include_assets = args .include_assets or args .include_everything ,
17361745 )
17371746 except RepositoryUnavailableError as e :
1738- logger .warning (f"Repository { repository ['full_name' ]} is unavailable (HTTP 451)" )
1747+ logger .warning (
1748+ f"Repository { repository ['full_name' ]} is unavailable (HTTP 451)"
1749+ )
17391750 if e .dmca_url :
17401751 logger .warning (f"DMCA notice: { e .dmca_url } " )
17411752 logger .info (f"Skipping remaining resources for { repository ['full_name' ]} " )
@@ -1795,7 +1806,11 @@ def backup_issues(args, repo_cwd, repository, repos_template):
17951806 modified = os .path .getmtime (issue_file )
17961807 modified = datetime .fromtimestamp (modified ).strftime ("%Y-%m-%dT%H:%M:%SZ" )
17971808 if modified > issue ["updated_at" ]:
1798- logger .info ("Skipping issue {0} because it wasn't modified since last backup" .format (number ))
1809+ logger .info (
1810+ "Skipping issue {0} because it wasn't modified since last backup" .format (
1811+ number
1812+ )
1813+ )
17991814 continue
18001815
18011816 if args .include_issue_comments or args .include_everything :
@@ -1869,7 +1884,11 @@ def backup_pulls(args, repo_cwd, repository, repos_template):
18691884 modified = os .path .getmtime (pull_file )
18701885 modified = datetime .fromtimestamp (modified ).strftime ("%Y-%m-%dT%H:%M:%SZ" )
18711886 if modified > pull ["updated_at" ]:
1872- logger .info ("Skipping pull request {0} because it wasn't modified since last backup" .format (number ))
1887+ logger .info (
1888+ "Skipping pull request {0} because it wasn't modified since last backup" .format (
1889+ number
1890+ )
1891+ )
18731892 continue
18741893 if args .include_pull_comments or args .include_everything :
18751894 template = comments_regular_template .format (number )
@@ -1919,9 +1938,11 @@ def backup_milestones(args, repo_cwd, repository, repos_template):
19191938 elif written_count == 0 :
19201939 logger .info ("{0} milestones unchanged, skipped write" .format (total ))
19211940 else :
1922- logger .info ("Saved {0} of {1} milestones to disk ({2} unchanged)" .format (
1923- written_count , total , total - written_count
1924- ))
1941+ logger .info (
1942+ "Saved {0} of {1} milestones to disk ({2} unchanged)" .format (
1943+ written_count , total , total - written_count
1944+ )
1945+ )
19251946
19261947
19271948def backup_labels (args , repo_cwd , repository , repos_template ):
@@ -1975,6 +1996,20 @@ def backup_releases(args, repo_cwd, repository, repos_template, include_assets=F
19751996 )
19761997 releases = releases [: args .number_of_latest_releases ]
19771998
1999+ # Check if this repo should skip asset downloads (case-insensitive)
2000+ skip_assets = False
2001+ if include_assets :
2002+ repo_name = repository .get ("name" , "" ).lower ()
2003+ repo_full_name = repository .get ("full_name" , "" ).lower ()
2004+ skip_repos = [r .lower () for r in (args .skip_assets_on or [])]
2005+ skip_assets = repo_name in skip_repos or repo_full_name in skip_repos
2006+ if skip_assets :
2007+ logger .info (
2008+ "Skipping assets for {0} ({1} releases) due to --skip-assets-on" .format (
2009+ repository .get ("name" ), len (releases )
2010+ )
2011+ )
2012+
19782013 # for each release, store it
19792014 written_count = 0
19802015 for release in releases :
@@ -1986,7 +2021,7 @@ def backup_releases(args, repo_cwd, repository, repos_template, include_assets=F
19862021 if json_dump_if_changed (release , output_filepath ):
19872022 written_count += 1
19882023
1989- if include_assets :
2024+ if include_assets and not skip_assets :
19902025 assets = retrieve_data (args , release ["assets_url" ])
19912026 if len (assets ) > 0 :
19922027 # give release asset files somewhere to live & download them (not including source archives)
@@ -2008,9 +2043,11 @@ def backup_releases(args, repo_cwd, repository, repos_template, include_assets=F
20082043 elif written_count == 0 :
20092044 logger .info ("{0} releases unchanged, skipped write" .format (total ))
20102045 else :
2011- logger .info ("Saved {0} of {1} releases to disk ({2} unchanged)" .format (
2012- written_count , total , total - written_count
2013- ))
2046+ logger .info (
2047+ "Saved {0} of {1} releases to disk ({2} unchanged)" .format (
2048+ written_count , total , total - written_count
2049+ )
2050+ )
20142051
20152052
20162053def fetch_repository (
@@ -2024,9 +2061,12 @@ def fetch_repository(
20242061):
20252062 if bare_clone :
20262063 if os .path .exists (local_dir ):
2027- clone_exists = subprocess .check_output (
2028- ["git" , "rev-parse" , "--is-bare-repository" ], cwd = local_dir
2029- ) == b"true\n "
2064+ clone_exists = (
2065+ subprocess .check_output (
2066+ ["git" , "rev-parse" , "--is-bare-repository" ], cwd = local_dir
2067+ )
2068+ == b"true\n "
2069+ )
20302070 else :
20312071 clone_exists = False
20322072 else :
@@ -2047,7 +2087,9 @@ def fetch_repository(
20472087 )
20482088 else :
20492089 logger .info (
2050- "Skipping {0} (repository not accessible - may be empty, private, or credentials invalid)" .format (name )
2090+ "Skipping {0} (repository not accessible - may be empty, private, or credentials invalid)" .format (
2091+ name
2092+ )
20512093 )
20522094 return
20532095
0 commit comments