| 12
 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")
 |