tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

publish_to_maven_local_if_modified.py (4465B)


      1 #!/usr/bin/env python3
      2 # This Source Code Form is subject to the terms of the Mozilla Public
      3 # License, v. 2.0. If a copy of the MPL was not distributed with this
      4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      5 
      6 
      7 # Purpose: Publish android packages to local maven repo, but only if changed since last publish.
      8 # Dependencies: None
      9 # Usage: ./automation/publish_to_maven_local_if_modified.py
     10 
     11 import argparse
     12 import hashlib
     13 import os
     14 import subprocess
     15 import sys
     16 import time
     17 from pathlib import Path
     18 
     19 
     20 def fatal_err(msg):
     21    print(f"\033[31mError: {msg}\033[0m")
     22    sys.exit(1)
     23 
     24 
     25 def run_cmd_checked(*args, **kwargs):
     26    """Run a command, throwing an exception if it exits with non-zero status."""
     27    return subprocess.run(*args, check=True, **kwargs)
     28 
     29 
     30 def find_project_root():
     31    """Find the absolute path of the project repository root."""
     32    # As a convention, we expect this file in [project-root]/automation/.
     33    automation_dir = Path(__file__).parent
     34 
     35    # Therefore the automation dir's parent is the project root we're looking for.
     36    return automation_dir.parent
     37 
     38 
     39 LAST_CONTENTS_HASH_FILE = ".lastAutoPublishContentsHash"
     40 
     41 GITIGNORED_FILES_THAT_AFFECT_THE_BUILD = ["local.properties"]
     42 
     43 parser = argparse.ArgumentParser(
     44    description="Publish android packages to local maven repo, but only if changed since last publish"
     45 )
     46 parser.parse_args()
     47 
     48 root_dir = find_project_root()
     49 if str(root_dir) != os.path.abspath(os.curdir):
     50    fatal_err(
     51        f"This only works if run from the repo root ({root_dir!r} != {os.path.abspath(os.curdir)!r})"
     52    )
     53 
     54 # Calculate a hash reflecting the current state of the repo.
     55 
     56 contents_hash = hashlib.sha256()
     57 
     58 contents_hash.update(
     59    run_cmd_checked(["git", "rev-parse", "HEAD"], capture_output=True).stdout
     60 )
     61 contents_hash.update(b"\x00")
     62 
     63 # Get a diff of all tracked (staged and unstaged) files.
     64 
     65 changes = run_cmd_checked(["git", "diff", "HEAD", "."], capture_output=True).stdout
     66 contents_hash.update(changes)
     67 contents_hash.update(b"\x00")
     68 
     69 # But unfortunately it can only tell us the names of untracked
     70 # files, and it won't tell us anything about files that are in
     71 # .gitignore but can still affect the build.
     72 
     73 untracked_files = []
     74 
     75 # Get a list of all untracked files sans standard exclusions.
     76 
     77 # -o is for getting other (i.e. untracked) files
     78 # --exclude-standard is to handle standard Git exclusions: .git/info/exclude, .gitignore in each directory,
     79 # and the user's global exclusion file.
     80 changes_others = run_cmd_checked(
     81    ["git", "ls-files", "-o", "--exclude-standard"], capture_output=True
     82 ).stdout
     83 changes_lines = iter(ln.strip() for ln in changes_others.split(b"\n"))
     84 
     85 try:
     86    ln = next(changes_lines)
     87    while ln:
     88        untracked_files.append(ln)
     89        ln = next(changes_lines)
     90 except StopIteration:
     91    pass
     92 
     93 # Then, account for some excluded files that we care about.
     94 untracked_files.extend(GITIGNORED_FILES_THAT_AFFECT_THE_BUILD)
     95 
     96 # Finally, get hashes of everything.
     97 # Skip files that don't exist, e.g. missing GITIGNORED_FILES_THAT_AFFECT_THE_BUILD. `hash-object` errors out if it gets
     98 # a non-existent file, so we hope that disk won't change between this filter and the cmd run just below.
     99 filtered_untracked = [nm for nm in untracked_files if os.path.isfile(nm)]
    100 # Reading contents of the files is quite slow when there are lots of them, so delegate to `git hash-object`.
    101 git_hash_object_cmd = ["git", "hash-object"]
    102 git_hash_object_cmd.extend(filtered_untracked)
    103 changes_untracked = run_cmd_checked(git_hash_object_cmd, capture_output=True).stdout
    104 contents_hash.update(changes_untracked)
    105 contents_hash.update(b"\x00")
    106 
    107 contents_hash = contents_hash.hexdigest()
    108 
    109 # If the contents hash has changed since last publish, re-publish.
    110 last_contents_hash = ""
    111 try:
    112    with open(LAST_CONTENTS_HASH_FILE) as f:
    113        last_contents_hash = f.read().strip()
    114 except FileNotFoundError:
    115    pass
    116 
    117 if contents_hash == last_contents_hash:
    118    print("Contents have not changed, no need to publish")
    119 else:
    120    print("Contents have changed, publishing")
    121    if sys.platform.startswith("win"):
    122        run_cmd_checked(
    123            ["gradlew.bat", "publishToMavenLocal", f"-Plocal={time.time_ns()}"],
    124            shell=True,
    125        )
    126    else:
    127        run_cmd_checked([
    128            "./gradlew",
    129            "publishToMavenLocal",
    130            f"-Plocal={time.time_ns()}",
    131        ])
    132    with open(LAST_CONTENTS_HASH_FILE, "w") as f:
    133        f.write(contents_hash)
    134        f.write("\n")