Skip to content

ci(release): remind the owner to sign each release (#1327) - #1463

Closed
ajianaz wants to merge 9 commits into
developfrom
ci/release-signature-reminder
Closed

ajianaz wants to merge 9 commits into
developfrom
ci/release-signature-reminder

Conversation

@ajianaz

@ajianaz ajianaz commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator

What

Adds an automatic reminder for the manual signing step introduced in #1462.

  • .github/workflows/release-signature-reminder.yml: runs after the Release workflow succeeds, every 6 hours, and on demand. Permissions are contents: read and issues: write only.
  • scripts/release-signature-reminder.sh: it looks at the latest published, non-prerelease release that carries checksums-sha256.txt.bundle.
    • If it has no checksums-sha256.txt.minisig, the script opens the issue Sign release vX.Y.Z (minisign) with the exact commands (git pull, scripts/sign-release.sh vX.Y.Z), assigned to the release signer (default ajianaz). It re-pings at most once every 12 hours.
    • Once the signature is on the release, it closes the issue.
    • A reminder for a release that is no longer the latest is closed as superseded, because uteke upgrade only installs the latest release.
  • scripts/tests/release-signature-reminder.test.sh: 69 checks using a stub gh.
  • Docs: AGENTS.md "Release procedure" step 6 and the CHANGELOG.

CI signs nothing and holds no signing secret. The minisign key stays offline by design: CI signing would only be as trustworthy as the workflow, which is what cosign keyless already covers.

Why

Since #1462, uteke upgrade fails closed for any release without a .minisig, and the owner signs offline after each release. A manual step that nothing reminds you of gets forgotten, and then uteke upgrade refuses the newest release for everyone.

Testing

  • bash scripts/tests/release-signature-reminder.test.sh: 69 checks pass. bash scripts/tests/sign-release.test.sh: passes. bash -n on all scripts is clean. The workflow YAML parses; actionlint and shellcheck are not installed here.
  • An independent security review found two real issues, both fixed:
    • The issue lookup used the search index. A freshly created reminder can be unsearchable for minutes, so a second run could create a duplicate. It now lists open issues directly (no --search), and a test fails if a listing call uses --search.
    • Comments by anyone reset the "last activity" clock, so a stranger could suppress the re-ping. Only comments by trusted authors count now.
  • Trust boundary: the script only acts on issues and comments authored by the Actions bot or the release signer. A look-alike issue by a stranger is ignored entirely and does not stop the genuine reminder being created. The bot is trusted only when is_bot is true (gh prints bots as app/<name>).
  • Other hardening, each with a test:
    • the tag is validated with ^v[0-9]+\.[0-9]+\.[0-9]+$ before it reaches any command or issue title
    • REMINDER_AFTER_HOURS=08 works
    • 404 detection matches HTTP 404 only; a 5xx or rate limit leaves an issue open
    • the comment and the close are a single gh issue close --comment call
    • if issue creation with the assignee fails, the script re-lists before retrying, so it cannot create a duplicate
  • The workflow checkout uses the default branch, never the triggering branch. It uses no ${{ }} of event data inside run:, and concurrency prevents overlapping runs.
  • Not run yet: the workflow itself cannot run before it is on the default branch. After merge: gh workflow run release-signature-reminder.yml, expecting an issue for v0.21.0 (it has a bundle and no .minisig), then check the issue author with gh issue view --json author (it should be app/github-actions).

Notes

  • After this merges the workflow will open Sign release v0.21.0 (minisign). That is correct: v0.21.0 is unsigned, so uteke upgrade refuses it. When v0.21.1 is published the v0.21.0 reminder is closed as superseded.
  • "Latest" follows GitHub's definition (by creation date), which is also what uteke upgrade installs.

Comment thread scripts/release-signature-reminder.sh Fixed
@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

🔍 Cora AI Code Review

⚠️ Review could not complete. LLM API error after retries: LLM API returned status 504: {"is_bifrost_error":true,"status_code":504,"error":{"error":"fasthttp: timeout","type":"request_timed_out","message":"request timed out (default is 300 seconds). You can increase it by setting the default_request_timeout_in_seconds in the network_config or in UI - Providers \u003e Provider Name \u003e Network Config."},"extra_fields":{"routing_info":{"provider":"glm","model":"glm-5.3","key":"b5"},"provider":"glm","original_model_requested":"glm-5.3","resolved_model_used":"glm-5.3","request_type":"chat_compl… [truncated]


Review powered by cora-code · BYOK · MIT

Same filter and behaviour (all 69 reminder test checks pass). It also reads as
the jq program it is, instead of a quoted string with an inline pipeline.
Comment thread scripts/release-signature-reminder.sh Outdated
# The jq program is kept in a variable (a quoted here-document) so it reads as the
# jq program it is; it only ever receives the issue list as JSON input.
STALE_FILTER="$(cat <<'JQ'
[.[] | select(.title | test("^Sign release v[0-9]+\\.[0-9]+\\.[0-9]+ \\(minisign\\)$"))
…in (#1327)

Found by a local Cora review: only the first argument was validated, and gh
calls inside the while-read loop inherited the here-document as stdin. Reject
any unknown argument (exit 2, nothing mutating) and give those calls
</dev/null. Three new test checks.
@ajianaz

ajianaz commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

Review evidence: why the Cora CI job cannot complete on this PR

CI: Cora Review reports "Review could not complete" on every attempt since the static-scanner finding was fixed (and a re-run gave the same result): LLM API returned status 504 ... request timed out (default is 300 seconds), from the gateway to provider glm, model glm-5.3.

Cause, reproduced locally with the same gateway and model (cora review, no upload):

  • the whole PR diff (5 files, ~490 lines) times out at 300 s with the same 504;
  • a 2-line diff finishes in 13 s with 0 findings, so the gateway is healthy and the limit is the request time for large diffs on glm-5.3;
  • reviewing per file succeeds for the smaller parts but release-signature-reminder.sh alone also exceeds 290 s on glm-5.3.

Real verdicts obtained locally (per file):

File Model Result
workflow + docs glm-5.3 2 info (signer handle in workflow env is intentional and documented; the workflow_run trigger names the "Release" workflow)
scripts/tests/release-signature-reminder.test.sh glm-5.3 1 minor + 3 info (test-harness robustness)
scripts/release-signature-reminder.sh glm-5.3-flash and glm-5-turbo no critical or major; minors: extra CLI arguments ignored (fixed), gh could read the here-document through stdin (fixed), a reminder stays open when the latest release has no bundle (documented edge case)

Earlier blocker, resolved: the static scanner flagged the stale-pass jq filter as "SQL injection via string concatenation" (alerts #783, #784). It is a jq program fed only by --arg and JSON input, not SQL. The filter was moved into a variable and its select(...) line was split, with no behaviour change (all reminder tests pass). The latest analysis uploaded for this PR still shows the old alert instance because no newer analysis could be produced while the LLM step times out.

What would make CI conclusive: raise default_request_timeout_in_seconds on the gateway above 300 s, or point the CORA_MODEL secret to a faster model (glm-5.3-flash finished the script review in ~4 minutes locally). Neither is a repo change.

@ajianaz

ajianaz commented Oct 15, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by the four smaller PRs #1472, #1473, #1474 and #1476 (this one was too large for the Cora reviewer to finish).

@ajianaz ajianaz closed this Oct 15, 2026
@ajianaz
ajianaz deleted the ci/release-signature-reminder branch October 15, 2026 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants