File: create_source_archive.py

package info (click to toggle)
mumble 1.5.735-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 90,008 kB
  • sloc: cpp: 556,921; ansic: 81,662; python: 3,606; sh: 659; makefile: 506; asm: 371; cs: 306; sql: 228; javascript: 143; perl: 80; xml: 13
file content (164 lines) | stat: -rwxr-xr-x 4,951 bytes parent folder | download | duplicates (2)
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()