File: publish_to_maven_local_if_modified.py

package info (click to toggle)
thunderbird 1%3A143.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 4,703,968 kB
  • sloc: cpp: 7,770,492; javascript: 5,943,842; ansic: 3,918,754; python: 1,418,263; xml: 653,354; asm: 474,045; java: 183,079; sh: 111,238; makefile: 20,410; perl: 14,359; objc: 13,059; yacc: 4,583; pascal: 3,405; lex: 1,720; ruby: 999; exp: 762; sql: 715; awk: 580; php: 436; lisp: 430; sed: 69; csh: 10
file content (133 lines) | stat: -rwxr-xr-x 4,451 bytes parent folder | download | duplicates (13)
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
#!/usr/bin/env python3
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.


# Purpose: Publish android packages to local maven repo, but only if changed since last publish.
# Dependencies: None
# Usage: ./automation/publish_to_maven_local_if_modified.py

import argparse
import hashlib
import os
import subprocess
import sys
import time
from pathlib import Path


def fatal_err(msg):
    print(f"\033[31mError: {msg}\033[0m")
    exit(1)


def run_cmd_checked(*args, **kwargs):
    """Run a command, throwing an exception if it exits with non-zero status."""
    kwargs["check"] = True
    return subprocess.run(*args, **kwargs)


def find_project_root():
    """Find the absolute path of the project repository root."""
    # As a convention, we expect this file in [project-root]/automation/.
    automation_dir = Path(__file__).parent

    # Therefore the automation dir's parent is the project root we're looking for.
    return automation_dir.parent


LAST_CONTENTS_HASH_FILE = ".lastAutoPublishContentsHash"

GITIGNORED_FILES_THAT_AFFECT_THE_BUILD = ["local.properties"]

parser = argparse.ArgumentParser(
    description="Publish android packages to local maven repo, but only if changed since last publish"
)
parser.parse_args()

root_dir = find_project_root()
if str(root_dir) != os.path.abspath(os.curdir):
    fatal_err(
        f"This only works if run from the repo root ({root_dir!r} != {os.path.abspath(os.curdir)!r})"
    )

# Calculate a hash reflecting the current state of the repo.

contents_hash = hashlib.sha256()

contents_hash.update(
    run_cmd_checked(["git", "rev-parse", "HEAD"], capture_output=True).stdout
)
contents_hash.update(b"\x00")

# Get a diff of all tracked (staged and unstaged) files.

changes = run_cmd_checked(["git", "diff", "HEAD", "."], capture_output=True).stdout
contents_hash.update(changes)
contents_hash.update(b"\x00")

# But unfortunately it can only tell us the names of untracked
# files, and it won't tell us anything about files that are in
# .gitignore but can still affect the build.

untracked_files = []

# Get a list of all untracked files sans standard exclusions.

# -o is for getting other (i.e. untracked) files
# --exclude-standard is to handle standard Git exclusions: .git/info/exclude, .gitignore in each directory,
# and the user's global exclusion file.
changes_others = run_cmd_checked(
    ["git", "ls-files", "-o", "--exclude-standard"], capture_output=True
).stdout
changes_lines = iter(ln.strip() for ln in changes_others.split(b"\n"))

try:
    ln = next(changes_lines)
    while ln:
        untracked_files.append(ln)
        ln = next(changes_lines)
except StopIteration:
    pass

# Then, account for some excluded files that we care about.
untracked_files.extend(GITIGNORED_FILES_THAT_AFFECT_THE_BUILD)

# Finally, get hashes of everything.
# Skip files that don't exist, e.g. missing GITIGNORED_FILES_THAT_AFFECT_THE_BUILD. `hash-object` errors out if it gets
# a non-existent file, so we hope that disk won't change between this filter and the cmd run just below.
filtered_untracked = [nm for nm in untracked_files if os.path.isfile(nm)]
# Reading contents of the files is quite slow when there are lots of them, so delegate to `git hash-object`.
git_hash_object_cmd = ["git", "hash-object"]
git_hash_object_cmd.extend(filtered_untracked)
changes_untracked = run_cmd_checked(git_hash_object_cmd, capture_output=True).stdout
contents_hash.update(changes_untracked)
contents_hash.update(b"\x00")

contents_hash = contents_hash.hexdigest()

# If the contents hash has changed since last publish, re-publish.
last_contents_hash = ""
try:
    with open(LAST_CONTENTS_HASH_FILE) as f:
        last_contents_hash = f.read().strip()
except FileNotFoundError:
    pass

if contents_hash == last_contents_hash:
    print("Contents have not changed, no need to publish")
else:
    print("Contents have changed, publishing")
    if sys.platform.startswith("win"):
        run_cmd_checked(
            ["gradlew.bat", "publishToMavenLocal", f"-Plocal={time.time_ns()}"],
            shell=True,
        )
    else:
        run_cmd_checked(
            ["./gradlew", "publishToMavenLocal", f"-Plocal={time.time_ns()}"]
        )
    with open(LAST_CONTENTS_HASH_FILE, "w") as f:
        f.write(contents_hash)
        f.write("\n")