chore: import upstream snapshot with attribution
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
This commit is contained in:
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the Discord release announcement JSON payload.
|
||||
|
||||
Reads from environment variables set by the GitHub Actions step:
|
||||
RELEASE_TAG, RELEASE_URL,
|
||||
DISCORD_RELEASES_ROLE_ID, DISCORD_RELEASE_LOGO_EMOJI, DISCORD_RELEASE_LOGO_URL,
|
||||
CHANGELOG_FILE — path to DISCORD_NARRATIVE.md or GENERATED_CHANGELOG.md
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
MAX_CHANGELOG_CHARS = 1400
|
||||
|
||||
tag = os.environ["RELEASE_TAG"]
|
||||
url = os.environ["RELEASE_URL"]
|
||||
role_id = os.environ.get("DISCORD_RELEASES_ROLE_ID", "")
|
||||
logo_emoji = os.environ.get("DISCORD_RELEASE_LOGO_EMOJI", "")
|
||||
logo_url = os.environ.get("DISCORD_RELEASE_LOGO_URL", "")
|
||||
changelog_file = os.environ.get("CHANGELOG_FILE", "")
|
||||
|
||||
if changelog_file and os.path.isfile(changelog_file):
|
||||
with open(changelog_file) as f:
|
||||
changelog = f.read().replace("\r", "").strip()
|
||||
else:
|
||||
changelog = "No changelog available."
|
||||
|
||||
if len(changelog) > MAX_CHANGELOG_CHARS:
|
||||
changelog = changelog[: MAX_CHANGELOG_CHARS - 3] + "..."
|
||||
|
||||
mention = f"<@&{role_id}>\n" if role_id else ""
|
||||
logo_prefix = f"{logo_emoji} " if logo_emoji else ""
|
||||
bt = "`"
|
||||
|
||||
content = (
|
||||
mention + logo_prefix + f"🚀 **opensre {bt}{tag}{bt} is live**\n" + f"🔗 {url}\n\n" + changelog
|
||||
)
|
||||
|
||||
allowed_mentions: dict = {"parse": []}
|
||||
if role_id:
|
||||
allowed_mentions["roles"] = [role_id]
|
||||
|
||||
payload: dict = {"content": content, "allowed_mentions": allowed_mentions}
|
||||
if logo_url:
|
||||
payload["username"] = "OpenSRE"
|
||||
payload["avatar_url"] = logo_url
|
||||
|
||||
json.dump(payload, sys.stdout)
|
||||
@@ -0,0 +1,223 @@
|
||||
"""Assign and notify **new** contributors on `good first issue` threads.
|
||||
|
||||
Here *new* means the commenter has **no merged PRs and no open PRs** in this repo where
|
||||
they are the PR author (GitHub Search API). **One or more merged PRs** means they are not
|
||||
treated as a first-time contributor for this automation (GitHub's ``FIRST_TIME_CONTRIBUTOR``
|
||||
/ ``FIRST_TIMER`` flags are not used).
|
||||
|
||||
Also skips repo insiders (OWNER / MEMBER / COLLABORATOR), bots, closed issues,
|
||||
comments on **pull request** threads (``issue_comment`` fires for PRs too; those
|
||||
use ``issue.pull_request``), and commenters already listed as assignees.
|
||||
|
||||
**One open assignment per eligible new contributor** in this repo: if they already
|
||||
have another **open** issue assigned (Search API), they cannot be auto-assigned here.
|
||||
|
||||
At most **one** auto-assignment per issue: if anyone else is already an assignee,
|
||||
further eligible commenters are skipped (manual pre-assignments count).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
GOOD_FIRST_LABEL = "good first issue"
|
||||
# Do not auto-assign maintainers/collaborators;
|
||||
# eligibility is 0 merged + 0 open PRs as author + not insider.
|
||||
EXCLUDED_COMMENTER_ASSOCIATIONS = frozenset({"OWNER", "MEMBER", "COLLABORATOR"})
|
||||
GITHUB_API = "https://api.github.com"
|
||||
|
||||
|
||||
def _github_api_url(path: str, query: dict[str, str]) -> str:
|
||||
encoded = urllib.parse.urlencode(query)
|
||||
return f"{GITHUB_API}{path}?{encoded}"
|
||||
|
||||
|
||||
def screen_event_without_api(event: dict[str, Any]) -> str | None:
|
||||
"""Return a skip reason before calling the GitHub API, or None if checks should continue."""
|
||||
issue = event.get("issue") or {}
|
||||
comment = event.get("comment") or {}
|
||||
if issue.get("pull_request") is not None:
|
||||
return "comment_on_pull_request"
|
||||
if issue.get("state") != "open":
|
||||
return "issue_not_open"
|
||||
labels = issue.get("labels") or []
|
||||
if not isinstance(labels, list):
|
||||
return "invalid_labels"
|
||||
names = {item.get("name") for item in labels if isinstance(item, dict)}
|
||||
if GOOD_FIRST_LABEL not in names:
|
||||
return "not_good_first_issue"
|
||||
|
||||
c_user = comment.get("user") or {}
|
||||
if c_user.get("type") == "Bot":
|
||||
return "bot_commenter"
|
||||
c_login = c_user.get("login") or ""
|
||||
if not c_login:
|
||||
return "missing_commenter_login"
|
||||
|
||||
c_assoc = comment.get("author_association") or ""
|
||||
if c_assoc in EXCLUDED_COMMENTER_ASSOCIATIONS:
|
||||
return "commenter_repo_insider"
|
||||
|
||||
assignees = issue.get("assignees") or []
|
||||
if isinstance(assignees, list):
|
||||
assigned_logins = {
|
||||
a.get("login") for a in assignees if isinstance(a, dict) and a.get("login")
|
||||
}
|
||||
if c_login in assigned_logins:
|
||||
return "already_assignee"
|
||||
if assigned_logins:
|
||||
return "issue_already_claimed"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def assign_decision(
|
||||
*,
|
||||
skip_reason_pre_api: str | None,
|
||||
merged_pr_count_for_commenter: int,
|
||||
open_pr_count_for_commenter: int,
|
||||
open_assigned_issue_count_for_commenter: int,
|
||||
) -> tuple[bool, str]:
|
||||
"""Return (should_assign_and_comment, skip_reason_or_empty).
|
||||
|
||||
Eligible "new contributor" means ``merged_pr_count_for_commenter == 0`` and
|
||||
``open_pr_count_for_commenter == 0`` (PR author in this repo, via Search API).
|
||||
They must also have no other open issues assigned in this repo
|
||||
(``open_assigned_issue_count_for_commenter == 0``).
|
||||
"""
|
||||
if skip_reason_pre_api is not None:
|
||||
return False, skip_reason_pre_api
|
||||
if open_pr_count_for_commenter > 0:
|
||||
return False, "has_open_prs"
|
||||
if merged_pr_count_for_commenter > 0:
|
||||
return False, "has_merged_prs"
|
||||
if open_assigned_issue_count_for_commenter > 0:
|
||||
return False, "already_has_open_assigned_issue"
|
||||
return True, ""
|
||||
|
||||
|
||||
def _request_json(url: str, token: str) -> Any:
|
||||
parsed = urllib.parse.urlparse(url)
|
||||
if parsed.scheme != "https" or parsed.netloc != "api.github.com":
|
||||
raise ValueError("GitHub API URL must target https://api.github.com")
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
headers={
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28",
|
||||
},
|
||||
method="GET",
|
||||
)
|
||||
with (
|
||||
urllib.request.urlopen(req, timeout=60) as resp
|
||||
): # nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def _search_issue_total_count(query: str, token: str) -> int:
|
||||
url = _github_api_url("/search/issues", {"q": query})
|
||||
try:
|
||||
data = _request_json(url, token)
|
||||
except urllib.error.URLError as exc:
|
||||
print(f"GitHub search failed: {exc}", file=sys.stderr)
|
||||
raise
|
||||
total = data.get("total_count")
|
||||
if not isinstance(total, int):
|
||||
return 0
|
||||
return total
|
||||
|
||||
|
||||
def fetch_merged_pr_count(owner: str, repo: str, login: str, token: str) -> int:
|
||||
q = f"repo:{owner}/{repo} is:pr is:merged author:{login}"
|
||||
return _search_issue_total_count(q, token)
|
||||
|
||||
|
||||
def fetch_open_pr_count(owner: str, repo: str, login: str, token: str) -> int:
|
||||
q = f"repo:{owner}/{repo} is:pr is:open author:{login}"
|
||||
return _search_issue_total_count(q, token)
|
||||
|
||||
|
||||
def fetch_open_assigned_issue_count(owner: str, repo: str, login: str, token: str) -> int:
|
||||
"""Count open issues in this repo where ``login`` is an assignee."""
|
||||
q = f"repo:{owner}/{repo} is:issue is:open assignee:{login}"
|
||||
return _search_issue_total_count(q, token)
|
||||
|
||||
|
||||
def build_assign_notice_body(*, assignee_login: str) -> str:
|
||||
return f"@{assignee_login} You've been **assigned** to this issue. Thanks for picking it up."
|
||||
|
||||
|
||||
def set_github_output(name: str, value: str) -> None:
|
||||
path = os.environ.get("GITHUB_OUTPUT")
|
||||
if not path:
|
||||
return
|
||||
with open(path, "a", encoding="utf-8") as fh:
|
||||
fh.write(f"{name}={value}\n")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
event_path = os.environ.get("GITHUB_EVENT_PATH")
|
||||
repository = os.environ.get("GITHUB_REPOSITORY")
|
||||
token = os.environ.get("GITHUB_TOKEN")
|
||||
if not event_path or not repository or not token:
|
||||
print("Missing GITHUB_EVENT_PATH, GITHUB_REPOSITORY, or GITHUB_TOKEN.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
raw = Path(event_path).read_text(encoding="utf-8")
|
||||
event = json.loads(raw)
|
||||
|
||||
pre = screen_event_without_api(event)
|
||||
merged_count = 0
|
||||
open_count = 0
|
||||
open_assigned_issue_count = 0
|
||||
|
||||
if pre is None:
|
||||
owner, _, repo = repository.partition("/")
|
||||
if not owner or not repo:
|
||||
print("Invalid GITHUB_REPOSITORY.", file=sys.stderr)
|
||||
return 1
|
||||
comment = event.get("comment") or {}
|
||||
c_login = (comment.get("user") or {}).get("login") or ""
|
||||
try:
|
||||
merged_count = fetch_merged_pr_count(owner, repo, c_login, token)
|
||||
open_count = fetch_open_pr_count(owner, repo, c_login, token)
|
||||
open_assigned_issue_count = fetch_open_assigned_issue_count(owner, repo, c_login, token)
|
||||
except urllib.error.URLError as exc:
|
||||
print(f"GitHub API request failed: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
should, reason = assign_decision(
|
||||
skip_reason_pre_api=pre,
|
||||
merged_pr_count_for_commenter=merged_count,
|
||||
open_pr_count_for_commenter=open_count,
|
||||
open_assigned_issue_count_for_commenter=open_assigned_issue_count,
|
||||
)
|
||||
|
||||
if not should:
|
||||
print(f"Skip: {reason}")
|
||||
set_github_output("should_assign", "false")
|
||||
return 0
|
||||
|
||||
comment_user = (event.get("comment") or {}).get("user") or {}
|
||||
login = comment_user.get("login") if isinstance(comment_user, dict) else ""
|
||||
if not isinstance(login, str) or not login:
|
||||
print("Missing commenter login.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
body = build_assign_notice_body(assignee_login=login)
|
||||
Path("assign_comment.md").write_text(body, encoding="utf-8")
|
||||
set_github_output("should_assign", "true")
|
||||
print("Wrote assign_comment.md; assignment will be applied in workflow.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,124 @@
|
||||
"""Write celebrate-merge PR comment body to comment.md (run from Actions after merge)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import random
|
||||
|
||||
discord = os.environ["DISCORD_INVITE_URL"]
|
||||
contributor = os.environ["CONTRIBUTOR_LOGIN"]
|
||||
|
||||
templates: list[str] = [
|
||||
(f"🎉 **MERGED!** @{contributor} just shipped something. The diff gods are pleased. 🙌"),
|
||||
(
|
||||
f"🚀 **Houston, we have a merge.** @{contributor} your PR is in orbit. "
|
||||
"Thanks for launching this one!"
|
||||
),
|
||||
(
|
||||
f"💜 **One more reason the project grows.** Thanks @{contributor} — "
|
||||
"your contribution just landed!"
|
||||
),
|
||||
(
|
||||
f"🎊 **Achievement unlocked: PR Merged.** @{contributor} passed code review, "
|
||||
"survived CI, and shipped. Respect. 🤝"
|
||||
),
|
||||
(
|
||||
f'🔥 **Another one.** @{contributor} said "here\'s a PR" and maintainers said '
|
||||
"\"ship it\". That's how it's done."
|
||||
),
|
||||
(
|
||||
f"🧑💻 **@{contributor} has entered the contributor hall of fame.** "
|
||||
"Merged. Done. Shipped. Go touch grass (then come back with another PR). 🌱"
|
||||
),
|
||||
(
|
||||
f"🎯 **Bullseye.** @{contributor} opened a PR, kept the vibes clean, "
|
||||
"and got it merged. Absolute cinema. 🎬"
|
||||
),
|
||||
(
|
||||
f"⚡ **LGTM → Merged.** @{contributor}, your work is in. "
|
||||
"Every commit counts — thank you for this one."
|
||||
),
|
||||
# new additions
|
||||
(
|
||||
f'😤 **@{contributor} said "I will fix this" and then actually fixed it.** '
|
||||
"Legendary behavior."
|
||||
),
|
||||
(
|
||||
f"🍕 **@{contributor}'s PR:** crispy edges, no unnecessary toppings, delivered on time. "
|
||||
"Understood the assignment. 🔥"
|
||||
),
|
||||
(f"🌊 **Merged.** @{contributor} is now permanently woven into git history. No take-backs. 😄"),
|
||||
(
|
||||
f"🤖 **CI passed. Linter didn't scream. Reviewer typed LGTM.** "
|
||||
f"@{contributor}, every machine in this pipeline just slow-clapped. 🖥️✨"
|
||||
),
|
||||
(f"🧠 **@{contributor} opened a PR.** Maintainers feared them. CI genuflected. It merged. 🚨"),
|
||||
(
|
||||
f"😭 **Clear commit message. Green tests. Kind review.** "
|
||||
f"@{contributor}, stop making the rest of us look bad."
|
||||
),
|
||||
(
|
||||
f"🐸 **Rebase? Handled. Conflicts? Squashed. CI? Vibing.** "
|
||||
f"@{contributor} touched the untouchable and lived. 🫡"
|
||||
),
|
||||
(
|
||||
f"🏆 **@{contributor} did not come to play.** "
|
||||
"PR opened. Review survived. Merged clean. Retire the jersey. 🎽"
|
||||
),
|
||||
(
|
||||
f"🎲 **Researchers are baffled.** @{contributor} opened a PR, got it reviewed without drama, "
|
||||
"and merged clean. This violates known laws of open source. 🔬"
|
||||
),
|
||||
(
|
||||
f"🌮 **@{contributor}'s PR:** showed up unannounced, improved everything, left zero bugs. "
|
||||
"Just like a perfect taco. 🌮"
|
||||
),
|
||||
(
|
||||
f"🐉 **Legend says** enough merged PRs and you ascend. "
|
||||
f"@{contributor} is dangerously close. 🌤️"
|
||||
),
|
||||
(
|
||||
f"🛸 **Aliens watching our repo** just upgraded @{contributor}'s threat level to: "
|
||||
"*do not engage — too competent*. 👽"
|
||||
),
|
||||
(
|
||||
f'🎻 **"The diff was clean, the tests did pass, the reviewer wept."** '
|
||||
f"That poem was about @{contributor}'s PR. 🥹"
|
||||
),
|
||||
(f"🍵 **@{contributor} made tea, opened a PR, and merged before it cooled.** No notes. ☕"),
|
||||
(
|
||||
f"🏄 **Some PRs rot in review for six weeks.** "
|
||||
f'@{contributor}\'s said "not today" and merged like it owned the place. 🌊'
|
||||
),
|
||||
(
|
||||
f"💼 **Interviewer:** describe a time you shipped something impactful.\n\n"
|
||||
f"**@{contributor}:** *points at this PR*\n\n"
|
||||
"**Interviewer:** you're hired. 🤝"
|
||||
),
|
||||
]
|
||||
|
||||
# GIFs are repo-hosted under .github/assets/celebrations/ so GitHub's own CDN serves them.
|
||||
_base = "https://raw.githubusercontent.com/Tracer-Cloud/opensre/main/.github/assets/celebrations"
|
||||
gif_blocks: list[str] = [
|
||||
f"\n\n",
|
||||
f"\n\n",
|
||||
f"\n\n",
|
||||
f"\n\n",
|
||||
f"\n\n",
|
||||
f"\n\n",
|
||||
f"\n\n",
|
||||
f"\n\n",
|
||||
f"\n\n",
|
||||
f"\n\n",
|
||||
]
|
||||
|
||||
head = random.choice(templates) + random.choice(gif_blocks)
|
||||
footer = (
|
||||
"---\n\n"
|
||||
f"👋 **Join us on [Discord - OpenSRE]({discord})** : hang out, contribute, "
|
||||
"or hunt for features and issues. Everyone's welcome."
|
||||
)
|
||||
body = f"{head}\n\n{footer}"
|
||||
|
||||
with open("comment.md", "w", encoding="utf-8") as fh:
|
||||
fh.write(body)
|
||||
Executable
+95
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env bash
|
||||
# Sync Tracer-Cloud/homebrew-tap Formula/opensre.rb version and archive SHAs.
|
||||
#
|
||||
# Usage (CI):
|
||||
# HOMEBREW_TAP_GITHUB_TOKEN=... VERSION=2026.5.29 ASSET_DIR=release-assets ./sync-homebrew-tap-formula.sh
|
||||
#
|
||||
# Local dry-run (no push):
|
||||
# DRY_RUN=1 VERSION=2026.5.29 ASSET_DIR=/path/to/sha256-files ./sync-homebrew-tap-formula.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${VERSION:-}"
|
||||
ASSET_DIR="${ASSET_DIR:-release-assets}"
|
||||
DRY_RUN="${DRY_RUN:-0}"
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "VERSION is required (e.g. 2026.5.29)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for platform in linux-x64 linux-arm64 darwin-x64 darwin-arm64; do
|
||||
sha_file="${ASSET_DIR}/opensre_${VERSION}_${platform}.tar.gz.sha256"
|
||||
if [ ! -f "$sha_file" ]; then
|
||||
echo "Missing SHA256 file: $sha_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
linux_x64_sha="$(awk '{print $1}' "${ASSET_DIR}/opensre_${VERSION}_linux-x64.tar.gz.sha256")"
|
||||
linux_arm64_sha="$(awk '{print $1}' "${ASSET_DIR}/opensre_${VERSION}_linux-arm64.tar.gz.sha256")"
|
||||
darwin_x64_sha="$(awk '{print $1}' "${ASSET_DIR}/opensre_${VERSION}_darwin-x64.tar.gz.sha256")"
|
||||
darwin_arm64_sha="$(awk '{print $1}' "${ASSET_DIR}/opensre_${VERSION}_darwin-arm64.tar.gz.sha256")"
|
||||
|
||||
tap_dir="$(mktemp -d)/homebrew-tap"
|
||||
if [ -n "${HOMEBREW_TAP_GITHUB_TOKEN:-}" ]; then
|
||||
git clone "https://x-access-token:${HOMEBREW_TAP_GITHUB_TOKEN}@github.com/Tracer-Cloud/homebrew-tap.git" "$tap_dir"
|
||||
else
|
||||
git clone "https://github.com/Tracer-Cloud/homebrew-tap.git" "$tap_dir"
|
||||
fi
|
||||
|
||||
python3 - "$tap_dir/Formula/opensre.rb" "$VERSION" "$darwin_arm64_sha" "$darwin_x64_sha" "$linux_arm64_sha" "$linux_x64_sha" <<'PY'
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
formula_path = Path(sys.argv[1])
|
||||
version = sys.argv[2]
|
||||
darwin_arm64_sha = sys.argv[3]
|
||||
darwin_x64_sha = sys.argv[4]
|
||||
linux_arm64_sha = sys.argv[5]
|
||||
linux_x64_sha = sys.argv[6]
|
||||
|
||||
text = formula_path.read_text()
|
||||
text = re.sub(r'version "[^"]+"', f'version "{version}"', text, count=1)
|
||||
replacements = {
|
||||
r'(opensre_#\{version\}_darwin-arm64\.tar\.gz"\n\s*sha256 ")[^"]+(")': darwin_arm64_sha,
|
||||
r'(opensre_#\{version\}_darwin-x64\.tar\.gz"\n\s*sha256 ")[^"]+(")': darwin_x64_sha,
|
||||
r'(opensre_#\{version\}_linux-arm64\.tar\.gz"\n\s*sha256 ")[^"]+(")': linux_arm64_sha,
|
||||
r'(opensre_#\{version\}_linux-x64\.tar\.gz"\n\s*sha256 ")[^"]+(")': linux_x64_sha,
|
||||
}
|
||||
for pattern, sha in replacements.items():
|
||||
text, count = re.subn(pattern, rf'\g<1>{sha}\g<2>', text, count=1)
|
||||
if count != 1:
|
||||
raise SystemExit(f"Failed to update checksum with pattern: {pattern}")
|
||||
formula_path.write_text(text)
|
||||
PY
|
||||
|
||||
cd "$tap_dir"
|
||||
if git diff --quiet -- Formula/opensre.rb; then
|
||||
echo "Homebrew tap formula already up to date for version ${VERSION}."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "=== Formula diff ==="
|
||||
git diff Formula/opensre.rb
|
||||
echo "===================="
|
||||
|
||||
if [ "$DRY_RUN" = "1" ]; then
|
||||
echo "DRY_RUN=1 — not committing or pushing."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "${HOMEBREW_TAP_GITHUB_TOKEN:-}" ]; then
|
||||
echo "HOMEBREW_TAP_GITHUB_TOKEN is required to push." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add Formula/opensre.rb
|
||||
git commit -m "chore: update opensre formula to ${VERSION}"
|
||||
git push origin HEAD:main
|
||||
echo "Pushed homebrew-tap update for version ${VERSION}."
|
||||
Reference in New Issue
Block a user