134 lines
4.3 KiB
Python
Executable File
134 lines
4.3 KiB
Python
Executable File
#!/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")
|