diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 037960b8..0f9e243b 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -17,15 +17,23 @@ 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. + +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 `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) 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.