Skip to content

ci: add public release finalizer#179

Merged
msheth-circle merged 19 commits into
circlefin:mainfrom
sdaveas-circle:ci-public-release-finalizer
Jul 17, 2026
Merged

ci: add public release finalizer#179
msheth-circle merged 19 commits into
circlefin:mainfrom
sdaveas-circle:ci-public-release-finalizer

Conversation

@sdaveas-circle

@sdaveas-circle sdaveas-circle commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the public-side release finalizer automation for Arc Node:

  • validates merged release PRs before finalization
  • creates or updates the public release branch for `vX.Y.0` releases
  • creates final public release tags
  • requires the PR to carry the `release` label before finalization runs
  • chains production final tags into the existing binary release workflow
  • keeps binary artifact publisher manually callable from the Actions UI with an explicit tag
  • artifact publishing no longer relies on `v*` tag-push triggers, avoiding duplicate publishes when the finalizer creates a tag

Safety

  • The workflow only finalizes after PR merge.
  • It ignores fork-origin PRs and only finalizes branches from this repository.
  • It only accepts `sync/*` Copybara branches with Git-valid branch names, matching the private handoff output such as `sync/v0_8_0`.
  • Only `v*` production tags are accepted; the finalizer targets `main` and `release/*` branches only.
  • Binary publishing only runs on a successful finalize, not on test or dry-run triggers.

Validation

  • `bash -n .github/scripts/finalize-release.sh scripts/release-config.sh`
  • `actionlint .github/workflows/finalize-release.yaml .github/workflows/release-binaries.yaml .github/workflows/build-docker.yaml`
  • `git diff --check`
  • Docker tag helper smoke test for `v0.8.0` and `v1.2.3-rc.1`; confirms `test-v0.8.0` and `test/v0.8.0` are rejected
  • public finalizer validation smoke for `sync/v0_8_1` branch shape
  • invalid Copybara branch rejection smoke for `sync/bad..branch`

sdaveas-circle and others added 19 commits June 19, 2026 11:56
Patch releases finalize from the release/X.Y branch itself, so the
previous local `./` reference loaded build-docker.yaml from whatever
branch triggered finalize-release.yaml. Pinning to @main keeps
job_workflow_ref stable across patch (release/X.Y-triggered) and
minor/major (main-triggered) releases, so Cloudsmith OIDC trust can
anchor on a single main-pinned claim instead of a release/* wildcard.
Docker images are published manually via build-docker.yaml's
workflow_dispatch trigger instead of automatically on release-PR merge.
Automating this required either a Cloudsmith OIDC claim that release/*
branches can't satisfy (Cloudsmith rejects wildcards) or loosening the
trust condition to accept any workflow calling build-docker.yaml from
any branch, which is broader than intended.
- collapse resolve_namespace() to production-only constants
- remove release-namespace action input (no longer needed)
- remove test-main and test-release/** branch triggers
- tighten tag regex to ^v in release-binaries.yaml
- simplify release_docker_image_version_from_tag to vX.Y.Z only
- restore top-level concurrency in release-binaries.yaml
- align actions/checkout pin to v6.0.3 across finalize-release.yaml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix git option injection: add -- before tag/branch args in git tag and
  git push calls (git check-ref-format accepts --force-style names)
- Move inputs.mode out of inline bash interpolation into env var (MODE)
  per GitHub Actions injection guidance
- Move write_outputs to after all git mutations in finalize mode
- Align SHORT_HASH to bash slice in manifest job (was --short=8, which
  can return 9+ chars; build job already used ${SHA::8})
- Add note when Release kind is promoted from patch to minor (PATCH=0)
- Add comments documenting Copybara branch coupling and extract_body_field
  single-line guarantee

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move inputs.repository-path out of inline bash interpolation into env
  var (REPOSITORY_PATH) to match MODE/PR_BODY/etc pattern
- Use refs/tags/${TAG} in git rev-list to disambiguate tag from branch
  if names collide (unambiguous ref path)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…cker images

The finalize-release job's condition never checked github.event.action,
so relabeling an already-merged sync/* PR (a lower bar than merge rights)
could re-enter the tag/branch creation and binary release chain long
after merge. Add the missing action == 'closed' guard, matching the
validate job's existing check.

Also add a reminder comment on the release PR when finalize succeeds,
since Docker image publishing can't be auto-chained (Cloudsmith OIDC
can't trust a release/* wildcard claim) and was otherwise left as an
easy-to-forget manual step despite docs/installation.md documenting
Docker images as a primary install path.
resolve_namespace(), the TAG_PREFIXES array, and tag_prefixes_for_error()
were vestiges of a dropped test-namespace feature: NAMESPACE was always
"production" and RELEASE_REF_PREFIX always "". Collapse them to plain
constants and drop the now-unused release-config.sh helpers that only
resolve_namespace() called.

Also normalize the Release kind PR-body field (trim + lowercase) since
it can be hand-edited, and exact-match case-sensitivity was an easy
foot-gun.
release-binaries.yaml's version-resolution steps captured the entire
anchored tag regex, so VERSION was always identical to TAG; use TAG
directly instead of the redundant alias. Drop the release job's
job-level concurrency group, which the workflow's top-level
release-binaries-${tag} group already fully serializes.

build-docker.yaml's image_version/release_tag outputs were computed
but never consumed anywhere (no job-level outputs block, nothing reads
them) — drop the dead output writes, keep the tag-format validation
that runs alongside them.
tr -d '[:space:]' removed internal whitespace too, so a typo like
"min or" silently became a valid "minor" instead of failing loudly.
Trim only leading/trailing whitespace, keeping internal-whitespace
typos as hard errors.
…-release.sh

The namespace-collapse refactor removed the last two functions
finalize-release.sh called from release-config.sh, leaving the
source (and the SCRIPT_DIR/REPO_ROOT plumbing that located it) dead.
release-config.sh is still sourced where it's actually used, in
build-docker.yaml.
@msheth-circle
msheth-circle merged commit 745ba4e into circlefin:main Jul 17, 2026
13 checks passed
@github-actions github-actions Bot added the pending-import Merged PR awaiting reverse-sync to upstream label Jul 17, 2026
@circle-github-action-bot circle-github-action-bot added import-conflict Import failed 3-way merge; manual resolution required and removed pending-import Merged PR awaiting reverse-sync to upstream labels Jul 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

import-conflict Import failed 3-way merge; manual resolution required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants