From 02c943d8b6461d84399c09f092321a09cc80ef2b Mon Sep 17 00:00:00 2001 From: jQuinRivero Date: Tue, 11 Aug 2026 13:48:21 -0300 Subject: [PATCH 1/2] ci: publish stable hastegeo wheel automatically on merge to main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merging a PR into `main` that touches `hastelib/` produced a validated wheel artifact but never published it. The `publish-stable` job in `hastegeo-publish.yml` was gated on two repository variables (`HASTEGEO_PUBLISH_ENABLED` and `HASTEGEO_RELEASE_APPROVAL_CONFIGURED`) plus the protected `hastegeo-release` environment. Neither variable was ever set, so the job was silently skipped on every merge and stable publication has been off since the pipeline shipped — stable stops at 1.0.26 while twelve 1.0.27rcN prereleases accumulated. The per-release approval was a rollout gate, not a permanent control. It is also redundant: the `main` ruleset is active with no bypass actors and requires an approving review plus CodeQL and gitleaks, so nothing reaches `main` unreviewed. The PR review that lands the commit is the release approval. Drop the `HASTEGEO_RELEASE_APPROVAL_CONFIGURED` condition and the `environment: hastegeo-release` line from `publish-stable`. `HASTEGEO_PUBLISH_ENABLED` remains as the kill switch. `HASTEGEO_RELEASE_APPROVAL_CONFIGURED` is intentionally left unset: it also gates the destructive RC deletion job in `rc-cleanup.yml`, which keeps the protected environment and its reviewer requirement. Behavior change: every merge touching `hastelib/` now cuts a patch release. Use `workflow_dispatch` with an explicit `bump` or `set_version` for minor/major. Update the workflow policy test accordingly — `hastegeo-build.yml` runs that suite as a build step, so leaving it asserting the old gate would break the wheel build itself. `test_scheduled_cleanup_is_report_only` continues to assert that RC deletion keeps the protected environment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d383b5e1-d1c6-4f7b-b8b1-0ef6b8f69c78 --- .github/workflows/README.md | 14 +++++++++----- .github/workflows/hastegeo-publish.yml | 12 +++++++++--- hastelib/README.md | 6 ++++-- hastelib/tests/build/test_release_workflows.py | 12 +++++++++--- 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 037960b8..629a241d 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -17,15 +17,19 @@ This directory contains GitHub Actions workflows for the HASTE project, focused `training_image_tag`, and `imageprep_image_tag` on the PR. Stable releases create `hastegeo-vX.Y.Z` tags, making reruns of the same source -commit a no-op. Stable publication additionally requires repository variables -`HASTEGEO_PUBLISH_ENABLED=true` and -`HASTEGEO_RELEASE_APPROVAL_CONFIGURED=true`. +commit a no-op. Stable publication is automatic: merging a PR into `main` that +touches `hastelib/` publishes the next patch wheel, because the review required +to land that commit is the release approval. Set `HASTEGEO_PUBLISH_ENABLED=true` +to enable it; set it to anything else to halt stable publication. Use +`workflow_dispatch` with an explicit `bump` or `set_version` for minor/major +releases. RC publication is automatic unless `HASTEGEO_RC_PUBLISH_ENABLED=false`. Fork PRs remain build-only. The RC image environment is selected through `HASTEGEO_RC_ENVIRONMENT`, keeping environment names out of the workflow. -Before enabling stable publication, repository administrators must configure a -protected release environment with required reviewers and self-review disabled. +The protected `hastegeo-release` environment and the +`HASTEGEO_RELEASE_APPROVAL_CONFIGURED` variable still gate the destructive RC +deletion job in `rc-cleanup.yml`. ## Docker Build and Push Workflow diff --git a/.github/workflows/hastegeo-publish.yml b/.github/workflows/hastegeo-publish.yml index 2ed69275..9341e0a5 100644 --- a/.github/workflows/hastegeo-publish.yml +++ b/.github/workflows/hastegeo-publish.yml @@ -159,15 +159,21 @@ jobs: --channel rc \ --source-sha "${{ needs.prepare.outputs.source_sha }}" + # Stable publication is automatic on merge to the default branch: the PR + # review required to land the commit there is the release approval. A second + # per-release sign-off would re-approve an already-reviewed commit. + # HASTEGEO_PUBLISH_ENABLED remains the kill switch. + # + # HASTEGEO_RELEASE_APPROVAL_CONFIGURED is deliberately NOT read here. It still + # gates the destructive RC deletion job in rc-cleanup.yml, which keeps the + # protected hastegeo-release environment. publish-stable: needs: prepare if: >- needs.prepare.outputs.channel == 'release' && needs.prepare.outputs.already_published != 'true' && - vars.HASTEGEO_PUBLISH_ENABLED == 'true' && - vars.HASTEGEO_RELEASE_APPROVAL_CONFIGURED == 'true' + vars.HASTEGEO_PUBLISH_ENABLED == 'true' runs-on: ubuntu-latest - environment: hastegeo-release permissions: actions: read contents: write diff --git a/hastelib/README.md b/hastelib/README.md index f6d1ab37..34f9a88c 100644 --- a/hastelib/README.md +++ b/hastelib/README.md @@ -29,8 +29,10 @@ version when it builds an RC or stable wheel. `hatch build` is build-only. GitHub Actions resolves the version, builds and tests without write credentials, then passes the wheel to a separate trusted -publisher behind the protected `hastegeo-release` environment. Stable releases -create a `hastegeo-vX.Y.Z` source tag so reruns are idempotent. +publisher that runs from the default branch. Merging a PR into `main` that +touches `hastelib/` publishes the next stable patch wheel — the review required +to land the commit is the release approval. Stable releases create a +`hastegeo-vX.Y.Z` source tag so reruns are idempotent. ## License diff --git a/hastelib/tests/build/test_release_workflows.py b/hastelib/tests/build/test_release_workflows.py index 21d665e1..a676be39 100644 --- a/hastelib/tests/build/test_release_workflows.py +++ b/hastelib/tests/build/test_release_workflows.py @@ -91,7 +91,7 @@ def test_privileged_publisher_is_default_branch_workflow_run(self): self.assertIn("PR head changed after the wheel build", workflow) self.assertIn("PR #$PR_NUMBER is not open", workflow) - def test_rc_is_automatic_but_stable_is_approval_gated(self): + def test_rc_and_stable_are_both_automatic_but_kill_switched(self): workflow = ( REPO_ROOT / ".github/workflows/hastegeo-publish.yml" ).read_text(encoding="utf-8") @@ -107,8 +107,14 @@ def test_rc_is_automatic_but_stable_is_approval_gated(self): )[0] self.assertNotIn("environment:", rc_block) self.assertIn("HASTEGEO_RC_PUBLISH_ENABLED", rc_block) - self.assertIn("environment: hastegeo-release", stable_block) - self.assertIn("environment: hastegeo-release", workflow) + # Merging to the default branch is the release approval, so stable + # publication carries no protected environment. + # HASTEGEO_PUBLISH_ENABLED stays as the kill switch. + self.assertNotIn("environment:", stable_block) + self.assertIn("HASTEGEO_PUBLISH_ENABLED", stable_block) + self.assertNotIn( + "HASTEGEO_RELEASE_APPROVAL_CONFIGURED", stable_block + ) self.assertIn("contents: write", workflow) self.assertNotIn("--clobber", publisher) From 71698a83f564f2381c31245a86ab6182e9182f58 Mon Sep 17 00:00:00 2001 From: jQuinRivero Date: Tue, 11 Aug 2026 14:06:29 -0300 Subject: [PATCH 2/2] docs: align hastegeo CI spec with automatic stable publication Address PR #120 review feedback. The spec is the source of truth in this repo, but the workflow change left it asserting a control that no longer exists. Update README.md, design.md, plan.md, and test-plan.md to record that stable publication is automatic on merge to main, gated only by HASTEGEO_PUBLISH_ENABLED, and that the protected hastegeo-release environment plus HASTEGEO_RELEASE_APPROVAL_CONFIGURED now scope to the destructive RC deletion job only. The superseded bring-up gate is recorded rather than deleted. Also remove an incorrect instruction from .github/workflows/README.md: it told operators to use workflow_dispatch with bump/set_version for minor/major releases, but hastegeo-publish.yml only accepts push and same-repository pull_request upstream events, and its trusted resolver receives neither input. That path publishes nothing. Document the gap explicitly instead. --- .github/workflows/README.md | 14 +++++++---- spec/features/hastegeo-ci-pipeline/README.md | 4 +++- spec/features/hastegeo-ci-pipeline/design.md | 13 ++++++----- spec/features/hastegeo-ci-pipeline/plan.md | 23 +++++++++++++------ .../hastegeo-ci-pipeline/test-plan.md | 3 ++- 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 629a241d..0f9e243b 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -18,11 +18,15 @@ This directory contains GitHub Actions workflows for the HASTE project, focused Stable releases create `hastegeo-vX.Y.Z` tags, making reruns of the same source commit a no-op. Stable publication is automatic: merging a PR into `main` that -touches `hastelib/` publishes the next patch wheel, because the review required -to land that commit is the release approval. Set `HASTEGEO_PUBLISH_ENABLED=true` -to enable it; set it to anything else to halt stable publication. Use -`workflow_dispatch` with an explicit `bump` or `set_version` for minor/major -releases. +touches `hastelib/` publishes the next **patch** wheel, because the review +required to land that commit is the release approval. Set +`HASTEGEO_PUBLISH_ENABLED=true` to enable it; set it to anything else to halt +stable publication. + +Minor and major stable releases have no automated path today. The publisher only +accepts `push` and same-repository `pull_request` upstream events, so a +`workflow_dispatch` build is not published even though `hastegeo-build.yml` +accepts `bump` and `set_version` inputs. RC publication is automatic unless `HASTEGEO_RC_PUBLISH_ENABLED=false`. Fork PRs remain build-only. The RC image environment is selected through diff --git a/spec/features/hastegeo-ci-pipeline/README.md b/spec/features/hastegeo-ci-pipeline/README.md index 49c63ff5..4c1c1c14 100644 --- a/spec/features/hastegeo-ci-pipeline/README.md +++ b/spec/features/hastegeo-ci-pipeline/README.md @@ -73,5 +73,7 @@ Function deployments can be reported as successful. | Stable source tags | Main workflow reruns are idempotent | | PEP 440 `rcN` | `rc01` is normalized to `rc1` by Python packaging | | Automatic trusted RC publication | Fast dev/test handoff without exposing PR jobs to credentials | -| Protected stable environment | Human approval limits stable-release blast radius | +| Stable publishes on merge to `main` | The branch ruleset's required review is the release approval; a second per-release sign-off re-approves an already-reviewed commit | +| `HASTEGEO_PUBLISH_ENABLED` kill switch | Stable publication can be halted without a code change | +| Protected environment retained for RC deletion | Human approval limits destructive-automation blast radius | | Scheduled cleanup is report-only | Destructive automation is deferred until observed safely | diff --git a/spec/features/hastegeo-ci-pipeline/design.md b/spec/features/hastegeo-ci-pipeline/design.md index 86d844a4..0d569ef1 100644 --- a/spec/features/hastegeo-ci-pipeline/design.md +++ b/spec/features/hastegeo-ci-pipeline/design.md @@ -5,8 +5,9 @@ The pipeline separates untrusted source execution from trusted repository and Azure mutations. A read-only job builds and validates the wheel, passes it as an Actions artifact, and a trusted default-branch workflow validates and -automatically publishes same-repository PR RCs. Stable releases remain -approval-gated. +automatically publishes same-repository PR RCs. Stable releases publish +automatically on merge to `main`, gated only by the `HASTEGEO_PUBLISH_ENABLED` +kill switch — the branch ruleset's required PR review is the release approval. ## Architecture @@ -36,7 +37,7 @@ Trusted publish job [contents:write, trusted base ref only] +--> deploy workflow uses the same wheel + both image tags Stable push: - protected hastegeo-release approval + HASTEGEO_PUBLISH_ENABLED kill switch -> publish X.Y.Z + create/reconcile hastegeo-vX.Y.Z tag ``` @@ -140,8 +141,8 @@ retain file must exist, and any GitHub query/delete failure aborts the run. |---|---| | `HASTEGEO_RC_ENVIRONMENT` repository variable | Selects the environment providing RC ACR OIDC/secrets | | `HASTEGEO_RC_PUBLISH_ENABLED` repository variable | RC kill switch (`false` disables; absent enables) | -| `hastegeo-release` environment | Required reviewer approval for stable releases | -| `HASTEGEO_PUBLISH_ENABLED` repository variable | Stable publication enablement | -| `HASTEGEO_RELEASE_APPROVAL_CONFIGURED` repository variable | Admin confirmation that required reviewers are active | +| `hastegeo-release` environment | Required reviewer approval for destructive RC deletion (`rc-cleanup.yml`) | +| `HASTEGEO_PUBLISH_ENABLED` repository variable | Stable publication enablement / kill switch | +| `HASTEGEO_RELEASE_APPROVAL_CONFIGURED` repository variable | Admin confirmation that required reviewers are active; gates RC deletion only | | `HASTE_BUMP` | Patch(default), minor, or major target | | `HASTE_SET_VERSION` | Exact version override | diff --git a/spec/features/hastegeo-ci-pipeline/plan.md b/spec/features/hastegeo-ci-pipeline/plan.md index cd691727..6ec1cfe6 100644 --- a/spec/features/hastegeo-ci-pipeline/plan.md +++ b/spec/features/hastegeo-ci-pipeline/plan.md @@ -10,7 +10,7 @@ | 2 | Add trusted wheel validator/publisher | `backend-dev` | complete | | 3 | Split read-only build and trusted publish workflow jobs | `backend-dev` | complete | | 3 | Add automatic trusted RC publication | `backend-dev` | in-progress | -| 3 | Keep protected environment gate for stable releases | `backend-dev` | blocked: repository admin required | +| 3 | Publish stable automatically on merge to `main` | `backend-dev` | complete | | 4 | Connect RC publication to coherent ACR image tags | `backend-dev` | complete | | 4 | Harden Function wheel resolution and deployment failure handling | `backend-dev` | complete | | 5 | Make cleanup report-only and fail-closed | `backend-dev` | complete | @@ -27,14 +27,23 @@ ## Rollout Gate -Stable publication jobs use `HASTEGEO_PUBLISH_ENABLED == 'true'` and the -protected `hastegeo-release` environment. They also require -`HASTEGEO_RELEASE_APPROVAL_CONFIGURED == 'true'`, which an administrator sets -only after required reviewers are active. Merge with publication disabled, -inspect the first build artifacts, configure required reviewers, then enable -and manually approve the first release. +Stable publication is gated solely by `HASTEGEO_PUBLISH_ENABLED == 'true'`. +Merging a PR into `main` is the release approval: the branch ruleset is active +with no bypass actors and requires an approving review plus passing status +checks, so no commit reaches `main` unreviewed. + +Superseded: stable publication originally also required +`HASTEGEO_RELEASE_APPROVAL_CONFIGURED == 'true'` and the protected +`hastegeo-release` environment. That was a bring-up gate — merge with +publication disabled, inspect artifacts, then enable — and it was never turned +on, so no stable wheel published for the pipeline's whole life. Both controls +remain in force for the destructive RC deletion job in `rc-cleanup.yml`. Same-repository PR RCs publish automatically from trusted `workflow_run` code and build final development ACR images once. The target environment is selected by `HASTEGEO_RC_ENVIRONMENT`. Set `HASTEGEO_RC_PUBLISH_ENABLED=false` only as an emergency kill switch. + +Known gap: minor and major stable releases have no automated path. The publisher +accepts only `push` and same-repository `pull_request` upstream events, so a +`workflow_dispatch` build carrying `bump` or `set_version` is never published. diff --git a/spec/features/hastegeo-ci-pipeline/test-plan.md b/spec/features/hastegeo-ci-pipeline/test-plan.md index 750a5c0a..64788d11 100644 --- a/spec/features/hastegeo-ci-pipeline/test-plan.md +++ b/spec/features/hastegeo-ci-pipeline/test-plan.md @@ -47,7 +47,8 @@ - Build job permissions are `contents: read`. - Build checkout sets `persist-credentials: false`. - Build job receives no `GH_TOKEN`, Azure secrets, or OIDC permission. -- Publish job uses `contents: write`, protected environment, and no PR checkout. +- Publish job uses `contents: write` and no PR checkout. Stable publication + carries no protected environment; the destructive RC deletion job does. - Image job executes trusted inline Azure commands rather than PR shell scripts. - Fork PR condition skips publish and image jobs. - Every external action is pinned to a full SHA.