Skip to content

Commit f52adf7

Browse files
fix(release): skip verification on no-op
1 parent 11bd2e7 commit f52adf7

6 files changed

Lines changed: 41 additions & 4 deletions

File tree

.beads/issues.jsonl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@
127127
{"_type":"issue","id":"haxe.elixir.codex-83h.4","title":"Bind release tag, source commit, ZIP, and hosted asset with repair-only recovery","description":"Replace selected-file and asset-name checks with one cryptographic release identity. Before tagging, fully verify the package and artifact record. After tagging and publication, require prepared HEAD, local tag, remote tag, GitHub Release tag, packaged source metadata, and hosted ZIP digest to agree. Add a protected workflow_dispatch lane that accepts only an existing immutable remote tag and completes an incomplete GitHub Release.","acceptance_criteria":"Local and origin vV resolve to the exact tested SHA; package metadata records that SHA and V; hosted asset state, exact name, size, and digest match the approved local artifact and unexpected custom assets fail; remote version tags are never moved or deleted; immutable GitHub Releases and a v* ruleset are enabled and verified, with dedicated tag-creation identity required where repository ownership supports it; repair refuses branches, arbitrary SHAs, new version calculation, new tags, tag movement, and mismatched bytes; failure-injection covers tag/no-release, draft/partial upload, response loss, final-verifier retry, same-name wrong bytes, and invalid immutable publication.","status":"open","priority":1,"issue_type":"task","owner":"boss@fullofcaffeine.com","created_at":"2026-07-10T18:02:43Z","created_by":"Marcelo Serpa","updated_at":"2026-07-10T18:02:43Z","labels":["provenance","recovery","reference","release","semver","thinking:xhigh"],"dependencies":[{"issue_id":"haxe.elixir.codex-83h.4","depends_on_id":"haxe.elixir.codex-83h","type":"parent-child","created_at":"2026-07-10T12:02:43Z","created_by":"Marcelo Serpa","metadata":"{}"},{"issue_id":"haxe.elixir.codex-83h.4","depends_on_id":"haxe.elixir.codex-83h.2","type":"blocks","created_at":"2026-07-10T12:02:46Z","created_by":"Marcelo Serpa","metadata":"{}"},{"issue_id":"haxe.elixir.codex-83h.4","depends_on_id":"haxe.elixir.codex-83h.3","type":"blocks","created_at":"2026-07-10T12:02:47Z","created_by":"Marcelo Serpa","metadata":"{}"}],"dependency_count":2,"dependent_count":1,"comment_count":0}
128128
{"_type":"issue","id":"haxe.elixir.codex-83h.3","title":"Publish only the exact successfully tested main commit","description":"Replace the privileged workflow_run release workflow, polling of separate workflows, and normal workflow_dispatch bypass with an explicit same-SHA job graph. Consolidate or call the required compiler, package, examples, dogfood, sentinel, and security gates from a push workflow so the publication job receives write authority only after all required jobs for github.sha succeed.","acceptance_criteria":"PRs, fork events, feature pushes, failed or cancelled gates, arbitrary manual refs, and untested SHAs cannot enter normal publication; a main push publishes only github.sha after all declared needs succeed; checkout uses that SHA with full history; no untrusted cache or artifact crosses into the privileged job; publication permissions are minimal and comments are disabled unless justified; release concurrency is one fixed repository group; Actions use reviewed full commit SHAs and Node/Haxe/Elixir/OTP/package/release tooling is exact and locked; npm ci and audit are enforced; workflow-contract fixtures cover the trigger matrix.","status":"open","priority":1,"issue_type":"task","owner":"boss@fullofcaffeine.com","created_at":"2026-07-10T18:02:42Z","created_by":"Marcelo Serpa","updated_at":"2026-07-10T18:02:42Z","labels":["ci","reference","release","security","semver","thinking:xhigh"],"dependencies":[{"issue_id":"haxe.elixir.codex-83h.3","depends_on_id":"haxe.elixir.codex-83h","type":"parent-child","created_at":"2026-07-10T12:02:42Z","created_by":"Marcelo Serpa","metadata":"{}"},{"issue_id":"haxe.elixir.codex-83h.3","depends_on_id":"haxe.elixir.codex-83h.1","type":"blocks","created_at":"2026-07-10T12:02:46Z","created_by":"Marcelo Serpa","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
129129
{"_type":"issue","id":"haxe.elixir.codex-83h.2","title":"Build a reproducible Reflaxe.Elixir artifact without tracked release mutations","description":"Remove @semantic-release/changelog, @semantic-release/git, sync-versions release writes, and generated current-status prose from normal publication. Keep development sentinels in tracked metadata and inject nextRelease.version, tag, and source commit only into a temporary Reflaxe package staging tree. Make the ZIP canonical and validate the exact ZIP rather than rebuilding it during smoke tests.","acceptance_criteria":"Normal prepare leaves git diff empty; one fixed local ZIP path is produced from tracked source commit content; staged haxelib metadata contains the release version and source identity while tracked package files remain development sentinels; canonical ordering, timestamps, modes, locale, timezone, and archiver version make two complete builds byte-identical under varied environments; archive validation rejects missing, altered, duplicate, unexpected, absolute, traversal, symlink, or unsafe-mode entries; isolated haxelib install, compile, Mix/Phoenix dogfood, and package smoke consume the exact ZIP; filename, byte count, and SHA-256 record/sidecar are produced.","status":"open","priority":1,"issue_type":"task","owner":"boss@fullofcaffeine.com","created_at":"2026-07-10T18:02:41Z","created_by":"Marcelo Serpa","updated_at":"2026-07-10T18:02:41Z","labels":["artifact","packaging","reference","release","semver","thinking:xhigh"],"dependencies":[{"issue_id":"haxe.elixir.codex-83h.2","depends_on_id":"haxe.elixir.codex-83h","type":"parent-child","created_at":"2026-07-10T12:02:40Z","created_by":"Marcelo Serpa","metadata":"{}"},{"issue_id":"haxe.elixir.codex-83h.2","depends_on_id":"haxe.elixir.codex-83h.1","type":"blocks","created_at":"2026-07-10T12:02:45Z","created_by":"Marcelo Serpa","metadata":"{}"}],"dependency_count":1,"dependent_count":1,"comment_count":0}
130-
{"_type":"issue","id":"haxe.elixir.codex-83h.1","title":"Replace generated release state with a policy-only SemVer core","description":"Retire release/manifest.json schema v1 as an exact-version and generated-prose owner. Introduce a small version-independent policy contract using a locked standards-tested SemVer library. Real reachable tags own versions. Model initial-development behavior, explicit unsupported channels, and independent durable approval per stable major. The release configuration must load even when tracked development metadata is stale or malformed.","acceptance_criteria":"The manifest contains no mutable current version, generated-file inventory, or posture blocks; fix, feat, and breaking commit sets are tested through the real analyzer; an unapproved 0.x breaking change derives the next minor; an explicitly approved graduation derives 1.0.0 only from a new graduation-authorizing change; stable breaking changes require the target major approval; multiple major approvals coexist; invalid SemVer, unknown majors, prerelease/build policy violations, and unsafe numeric components fail clearly; package metadata cannot prevent configuration loading.","status":"closed","priority":1,"issue_type":"task","owner":"boss@fullofcaffeine.com","created_at":"2026-07-10T18:02:39Z","created_by":"Marcelo Serpa","updated_at":"2026-07-10T20:44:03Z","started_at":"2026-07-10T19:59:37Z","closed_at":"2026-07-10T20:44:03Z","close_reason":"Replaced schema-v1 generated release state with a tag-owned schema-v2 SemVer policy, official analyzer adapter, locked semver validation, per-major approval gates, transitional generator/verifier compatibility, legacy-tag audit handling, docs, and full regression evidence.","labels":["policy","reference","release","semver","thinking:xhigh"],"dependencies":[{"issue_id":"haxe.elixir.codex-83h.1","depends_on_id":"haxe.elixir.codex-83h","type":"parent-child","created_at":"2026-07-10T12:02:39Z","created_by":"Marcelo Serpa","metadata":"{}"}],"dependency_count":0,"dependent_count":2,"comment_count":0}
130+
{"_type":"issue","id":"haxe.elixir.codex-83h.1","title":"Replace generated release state with a policy-only SemVer core","description":"Retire release/manifest.json schema v1 as an exact-version and generated-prose owner. Introduce a small version-independent policy contract using a locked standards-tested SemVer library. Real reachable tags own versions. Model initial-development behavior, explicit unsupported channels, and independent durable approval per stable major. The release configuration must load even when tracked development metadata is stale or malformed.","acceptance_criteria":"The manifest contains no mutable current version, generated-file inventory, or posture blocks; fix, feat, and breaking commit sets are tested through the real analyzer; an unapproved 0.x breaking change derives the next minor; an explicitly approved graduation derives 1.0.0 only from a new graduation-authorizing change; stable breaking changes require the target major approval; multiple major approvals coexist; invalid SemVer, unknown majors, prerelease/build policy violations, and unsafe numeric components fail clearly; package metadata cannot prevent configuration loading.","status":"closed","priority":1,"issue_type":"task","owner":"boss@fullofcaffeine.com","created_at":"2026-07-10T18:02:39Z","created_by":"Marcelo Serpa","updated_at":"2026-07-10T21:22:35Z","started_at":"2026-07-10T19:59:37Z","closed_at":"2026-07-10T21:22:35Z","close_reason":"Completed policy-only SemVer core and fixed the discovered no-op publication regression: published-package verification now runs only for the exact tag created by semantic-release. Full local QA and exact-SHA primary CI are green; workflow regression is covered by guards/tests/docs.","labels":["policy","reference","release","semver","thinking:xhigh"],"dependencies":[{"issue_id":"haxe.elixir.codex-83h.1","depends_on_id":"haxe.elixir.codex-83h","type":"parent-child","created_at":"2026-07-10T12:02:39Z","created_by":"Marcelo Serpa","metadata":"{}"}],"dependency_count":0,"dependent_count":2,"comment_count":0}
131131
{"_type":"issue","id":"haxe.elixir.codex-83h","title":"Adopt the tested-commit SemVer reference release protocol","description":"Replace the completed haxe.elixir.codex-m81 release-commit architecture with the simpler proven protocol from ../haxe.rust: CI tests source commit S, semantic-release derives version V from real tags and a small policy, a deterministic Reflaxe artifact is built from S with V injected only into staging, vV tags S, and GitHub publishes and verifies those exact bytes. Preserve the valid v0.14.x lineage while removing release-time tracked mutations, workflow_run trust ambiguity, and duplicated mutable release truth.","acceptance_criteria":"Normal publication never changes tracked files or creates a release commit; exact version lineage comes from immutable reachable tags; pre-1.0 breaking commits remain minor until explicit per-major approval; the artifact is reproducible and bound to source SHA, tag, size, and SHA-256; only the exact successfully tested main SHA can publish; an existing-tag-only repair path completes partial GitHub publication without deriving a version or moving a tag; repository and host controls are documented and tested; a live release plus a subsequent no-op run records evidence.","notes":"Audit evidence (2026-07-10): release.config.js currently loads mutable version/generation state, runs changelog and git plugins, writes tracked metadata/docs, pushes chore(release) commits, then tags those different commits. v0.14.23 points to release commit 9b256d7e, whose parent is source commit 212be207. .github/workflows/release.yml uses privileged workflow_run plus normal workflow_dispatch, polls other workflows, and does not encode an explicit same-repository push trust boundary. Package creation uses system find/cp/zip timestamps and ordering. Hosted verification downloads and structure-checks a same-name asset but does not bind it to the locally approved SHA-256. Immutable Releases are disabled and no repository rulesets are configured. The focused policy/generation/verification tests pass, showing internal consistency of the predecessor design rather than the target invariants.","status":"open","priority":1,"issue_type":"epic","owner":"boss@fullofcaffeine.com","created_at":"2026-07-10T18:02:38Z","created_by":"Marcelo Serpa","updated_at":"2026-07-10T18:06:56Z","labels":["reference","release","semver","thinking:xhigh"],"dependency_count":0,"dependent_count":0,"comment_count":0}
132132
{"_type":"issue","id":"haxe.elixir.codex-m81.3","title":"Verify prepared release commit, tag, package, and GitHub Release","description":"Extend the in-progress Reflaxe-built zip publication with staged release verification. Verify deterministic repository state and zip contents after the semantic-release release commit but before tag creation, verify the tag before GitHub publication, and verify the published Release plus exact asset afterward.","acceptance_criteria":"Contract-first tests prove failure before implementation; a post-commit prepare verifier prevents metadata/docs/artifact failures from creating a tag; publish verification compares tagged manifest, changelog, generated metadata/docs, and package version/content; success verification checks tag, draft/prerelease state, and exact zip asset; release configuration orders generation, release commit, prepared verification, tag verification, and GitHub publication correctly; partial-publication recovery is documented; package smoke and semantic-release dry-run pass.","status":"closed","priority":1,"issue_type":"task","owner":"boss@fullofcaffeine.com","created_at":"2026-07-09T22:09:07Z","created_by":"Marcelo Serpa","updated_at":"2026-07-10T05:00:54Z","closed_at":"2026-07-10T05:00:54Z","close_reason":"Implemented fail-closed prepared, tagged, and published package verification; added contract tests, release lifecycle ordering guards, recovery documentation, package parity smoke, and semantic-release dry-run coverage.","labels":["artifact","evidence","release","semver","thinking:xhigh","verification"],"dependencies":[{"issue_id":"haxe.elixir.codex-m81.3","depends_on_id":"haxe.elixir.codex-cue","type":"blocks","created_at":"2026-07-09T16:09:38Z","created_by":"Marcelo Serpa","metadata":"{}"},{"issue_id":"haxe.elixir.codex-m81.3","depends_on_id":"haxe.elixir.codex-m81","type":"parent-child","created_at":"2026-07-09T16:09:07Z","created_by":"Marcelo Serpa","metadata":"{}"},{"issue_id":"haxe.elixir.codex-m81.3","depends_on_id":"haxe.elixir.codex-m81.2","type":"blocks","created_at":"2026-07-09T16:09:37Z","created_by":"Marcelo Serpa","metadata":"{}"}],"dependency_count":2,"dependent_count":0,"comment_count":0}
133133
{"_type":"issue","id":"haxe.elixir.codex-m81.2","title":"Generate version metadata and current posture from the release manifest","description":"Turn scripts/release/sync-versions.js into the deterministic generator for every version surface and marker-delimited current-status block. Add read-only check mode and derive semantic-release commit assets from the generator instead of repeating file lists in package.json.","acceptance_criteria":"package.json, package-lock.json, haxelib.json, mix.exs, README badge, scoped HXML versions, and selected current-posture docs are generated from one manifest/version input; check mode compares expected outputs byte-for-byte without mutation; two consecutive generations are byte-identical; missing or duplicate markers and unsafe paths fail clearly; semantic-release configuration derives its release-commit assets from the generator; targeted tests and docs pass.","status":"closed","priority":1,"issue_type":"task","owner":"boss@fullofcaffeine.com","created_at":"2026-07-09T22:09:05Z","created_by":"Marcelo Serpa","updated_at":"2026-07-10T03:24:39Z","closed_at":"2026-07-10T03:24:39Z","close_reason":"Implemented deterministic release generation from release/manifest.json for package, lockfile, Haxelib, Mix, README badge/posture, scoped HXML, and versioning posture. Added read-only byte drift checks, safe-path and marker validation, idempotence/negative tests, official-plugin release.config.js with generated git assets, guard integration, docs, and a successful authenticated semantic-release dry run.","labels":["docs","evidence","generation","release","semver","thinking:high","thinking:xhigh"],"dependencies":[{"issue_id":"haxe.elixir.codex-m81.2","depends_on_id":"haxe.elixir.codex-m81","type":"parent-child","created_at":"2026-07-09T16:09:05Z","created_by":"Marcelo Serpa","metadata":"{}"},{"issue_id":"haxe.elixir.codex-m81.2","depends_on_id":"haxe.elixir.codex-m81.1","type":"blocks","created_at":"2026-07-09T16:09:36Z","created_by":"Marcelo Serpa","metadata":"{}"}],"dependency_count":1,"dependent_count":2,"comment_count":0}

.github/workflows/release.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,18 +204,31 @@ jobs:
204204
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
205205
206206
- name: Release
207+
id: semantic_release
207208
if: steps.release_context.outputs.should_skip != 'true'
208209
env:
209210
GITHUB_TOKEN: ${{ github.token }}
210211
GH_TOKEN: ${{ github.token }}
211212
run: |
212213
set -euo pipefail
214+
before_version="$(node -e "process.stdout.write(require('./scripts/release/sync-versions').latestReachableVersion())")"
213215
echo "[release] Publishing (semantic-release decides whether a new version is required)"
214216
npx semantic-release
217+
after_version="$(node -e "process.stdout.write(require('./scripts/release/sync-versions').latestReachableVersion())")"
218+
published=false
219+
tag=""
220+
if [[ "$after_version" != "$before_version" ]]; then
221+
published=true
222+
tag="v$after_version"
223+
fi
224+
{
225+
echo "published=$published"
226+
echo "tag=$tag"
227+
} >> "$GITHUB_OUTPUT"
215228
216229
- name: Verify published Reflaxe package
217-
if: steps.release_context.outputs.should_skip != 'true'
230+
if: steps.release_context.outputs.should_skip != 'true' && steps.semantic_release.outputs.published == 'true'
218231
env:
219232
GH_TOKEN: ${{ github.token }}
220233
REPO: ${{ github.repository }}
221-
run: scripts/release/verify-published-package.sh
234+
run: scripts/release/verify-published-package.sh "${{ steps.semantic_release.outputs.tag }}"

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* **release:** replace mutable manifest-owned version/generation state with a tag-owned SemVer policy core; delegate Conventional Commit classification to the pinned official analyzer, validate versions with locked `semver`, keep `0.x` breaking changes minor until an independently approved graduation change, and require a durable approval for every stable major.
3434
* **release:** replace the original single graduation-evidence gate with independent, dated per-major approval records; approval remains non-releasing, and a subsequent new breaking commit is required to derive the authorized stable major.
3535
* **release:** keep the existing version metadata, scoped HXML, and current-posture generator as a compatibility bridge while release artifacts migrate to deterministic staging; release policy no longer contains its file inventory or mutable current version.
36+
* **release:** make no-op semantic-release runs skip published-package verification; successful publication passes the exact newly created tag to the verifier instead of inferring a prior release from tracked package metadata.
3637
* **release:** verify the prepared release commit and package before tag creation, verify the tag and tagged generated state before GitHub publication, and validate the downloaded GitHub Release asset afterward; document fail-closed partial-publication recovery.
3738
* **docs:** reconcile current-facing release language with the real pre-1.0 lineage; label old `v1.0.x`/`v1.1.x` milestones as unshipped historical plans and point entrypoints to the canonical generated posture.
3839
* **test:** derive synthetic release versions independently of release policy so generation and staged-verification contracts remain valid after each real publication.

0 commit comments

Comments
 (0)