1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
|
#!/usr/bin/env python3
# Copyright 2023 The Mumble Developers. All rights reserved.
# Use of this source code is governed by a BSD-style license
# that can be found in the LICENSE file at the root of the
# Mumble source tree or at <https://www.mumble.info/LICENSE>.
from typing import List
from typing import Optional
import tarfile
import zipfile
import os
import argparse
import platform
import subprocess
import tempfile
def git(args: List[str]) -> str:
"""Executes git with the given arguments"""
shell: bool = platform.system() == "Windows"
args.insert(0, "git")
p = subprocess.Popen(
args, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
stdout, stderr = p.communicate()
if p.returncode != 0:
raise Exception(
"cmd(): {0} failed with status {1}: {2}".format(args, p.returncode, stderr)
)
return stdout.decode("utf-8").strip()
def get_file_paths(base_dir: str) -> List[str]:
"""Gets a list of all files within the file tree rooted at the given directory. Hidden files are excluded"""
files: List[str] = []
for current_file in os.listdir(base_dir):
if current_file.startswith("."):
# Skip hidden files
continue
full_path: str = os.path.join(base_dir, current_file)
if os.path.isdir(full_path):
files += get_file_paths(full_path)
else:
files.append(full_path)
return files
def create_zip_archive(
files: List[str],
archive_name: str,
rel_to: str,
compression: int = zipfile.ZIP_DEFLATED,
) -> str:
"""Writes the given set of files into a zip archive (using compression, if desired)"""
top_level_dir: str = os.path.basename(archive_name)
archive_name += ".zip"
with zipfile.ZipFile(archive_name, mode="x", compression=compression) as archive:
for current_file in files:
archive.write(
current_file,
arcname=os.path.join(
top_level_dir, os.path.relpath(current_file, rel_to)
),
)
return archive_name
def create_tar_archive(
files: List[str], archive_name: str, rel_to: str, compression: Optional[str] = "gz"
) -> str:
"""Writes the given set of files into a tar archive (using compression, if desired)"""
top_level_dir: str = os.path.basename(archive_name)
archive_name += ".tar"
if compression:
archive_name += "." + compression
with tarfile.open(
archive_name, mode="x:" + (compression if compression else "")
) as archive:
for current_file in files:
archive.add(
current_file,
arcname=os.path.join(
top_level_dir, os.path.relpath(current_file, rel_to)
),
recursive=False,
)
return archive_name
def main() -> None:
parser = argparse.ArgumentParser(
"Creates a source archive of a git repository (including submodules)"
)
parser.add_argument(
"--format",
choices=["tar", "zip"],
default="zip",
help="What kind of archive to create",
)
parser.add_argument(
"--remote",
help="The path/URL to the remote git repository. If not given, the current git repo is used",
)
parser.add_argument(
"--origin-name", default="origin", help="Name of the origin remote"
)
parser.add_argument(
"--revision",
default="master",
help="Name of the revision (tag or branch) for which an archive shall be created",
)
parser.add_argument("--name", help="Name of the archive to be produced")
args = parser.parse_args()
remote: str = args.remote
if not remote:
remote: str = git(["remote", "get-url", args.origin_name])
archive_name: str = args.name if args.name else "mumble-" + args.revision
# Clone the repo into a temp directory
with tempfile.TemporaryDirectory() as tmp_repo:
print("Creating temporary clone at '%s'..." % tmp_repo)
git(
[
"clone",
"--depth",
"1",
"--recurse-submodules",
"--shallow-submodules",
"--branch",
args.revision,
remote,
tmp_repo,
]
)
files: List[str] = get_file_paths(tmp_repo)
print("Archiving repo (%d files & folders)..." % len(files))
if args.format == "zip":
archive_name = create_zip_archive(files, archive_name, rel_to=tmp_repo)
elif args.format == "tar":
archive_name = create_tar_archive(files, archive_name, rel_to=tmp_repo)
else:
raise RuntimeError("Unsupported archive format '%s'" % args.format)
print("Archive created at '%s'" % archive_name)
if __name__ == "__main__":
main()
|