Files
2026-07-13 12:38:34 +08:00

112 lines
3.3 KiB
Python
Executable File

#!/usr/bin/env python3
"""Bump version, release-tag URLs, and per-platform sha256 in a Composio Homebrew formula.
Used by .github/workflows/cli.bump-homebrew-tap.yml. Reads inputs from env vars
so the bash side has a clean interface; can be run locally for testing:
TAG=@composio/cli@0.2.29 VERSION=0.2.29 \\
DARWIN_ARM=<sha> DARWIN_X86=<sha> LINUX_ARM=<sha> LINUX_X86=<sha> \\
.github/scripts/bump-homebrew-formula.py path/to/Formula/composio.rb
Exits non-zero if any required env var is missing/invalid or if no edits land.
"""
from __future__ import annotations
import os
import re
import sys
REQUIRED_ENV = ("TAG", "VERSION", "DARWIN_ARM", "DARWIN_X86", "LINUX_ARM", "LINUX_X86")
SHA256_RE = re.compile(r"[0-9a-f]{64}")
def main(argv: list[str]) -> int:
if len(argv) != 2:
print(f"usage: {argv[0]} <formula-path>", file=sys.stderr)
return 2
path = argv[1]
missing = [k for k in REQUIRED_ENV if not os.environ.get(k)]
if missing:
print(f"missing env vars: {', '.join(missing)}", file=sys.stderr)
return 2
tag = os.environ["TAG"]
version = os.environ["VERSION"]
shas = {
"darwin-aarch64": os.environ["DARWIN_ARM"],
"darwin-x64": os.environ["DARWIN_X86"],
"linux-aarch64": os.environ["LINUX_ARM"],
"linux-x64": os.environ["LINUX_X86"],
}
for plat, sha in shas.items():
if not SHA256_RE.fullmatch(sha):
print(f"invalid sha for {plat}: {sha!r}", file=sys.stderr)
return 2
with open(path) as f:
src = f.read()
# Bump version line. Lambda avoids replacement-string backreference parsing
# (e.g. a hostile tag containing \g<1>); same defense on tag/sha below.
src, n_ver = re.subn(
r'^(\s*version\s+)"[^"]+"',
lambda m: f'{m.group(1)}"{version}"',
src,
count=1,
flags=re.M,
)
# Bump the release-tag segment of each download URL.
src, n_url = re.subn(
r"(releases/download/)@composio/cli@[^/]+(/composio-)",
lambda m: f"{m.group(1)}{tag}{m.group(2)}",
src,
)
# Bump each sha256 by pairing it with the URL line directly above.
updated_platforms: set[str] = set()
def repl_sha(match: re.Match[str]) -> str:
url_line, sha_line = match.group(1), match.group(2)
for plat, sha in shas.items():
if f"composio-{plat}.zip" in url_line:
updated_platforms.add(plat)
return url_line + re.sub(
r'"[0-9a-f]{64}"',
lambda _m: f'"{sha}"',
sha_line,
)
return match.group(0)
src, n_sha = re.subn(
r'(url\s+"[^"]+"\s*\n)(\s*sha256\s+"[0-9a-f]{64}")',
repl_sha,
src,
)
expected_platforms = set(shas)
if (
n_ver != 1
or n_url != len(shas)
or n_sha != len(shas)
or updated_platforms != expected_platforms
):
print(
"bump produced unexpected edits "
f"(version={n_ver}, url={n_url}, sha={n_sha}, "
f"updated_platforms={sorted(updated_platforms)})",
file=sys.stderr,
)
return 1
with open(path, "w") as f:
f.write(src)
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))