Skip to content

ci: attach release assets before publishing and verify the set - #237

Merged
re-gius merged 6 commits into
masterfrom
re-gius/release-asset-publication-order
Aug 18, 2026
Merged

ci: attach release assets before publishing and verify the set#237
re-gius merged 6 commits into
masterfrom
re-gius/release-asset-publication-order

Conversation

@re-gius

@re-gius re-gius commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Description

Releases were occasionally. published without ABI assets because assets were being attached to an already-published release. This repository has immutable releases enabled, so GitHub refuses that upload. Both publish workflows now create the release as a draft, verify its asset set, and publish only afterwards, with a guard that rejects a pre-published version before the build rather than after it. Releases are also cut from the Actions tab with a version input, so nobody has to create a tag or a release by hand.

Type

  • Bug fix
  • Feature
  • Breaking change
  • Documentation
  • Chore
  • Refactor
  • Security

Scope

  • Registration
  • Resolver
  • Store
  • Proof of Personhood
  • Deployment scripts
  • Tests

Related Issues

Part of #222 — the release-workflow half. The SDK half (dotns-sdk's scripts/sync-abis.mjs keeping stale ABIs when assets are missing) is a separate PR in that repository.

Unblocks #118, which cannot attach deployments.json as a release asset until the publication order is fixed. Downstream of that, #221 and #223 stop transcribing addresses by hand

Fixes

Changes, applied to both publish-release.yml and publish-prerelease.yml:

  • Guard, first step. If a release for the tag exists and is not a draft, fail immediately with an explanation. Previously this case consumed the whole build and died at the last step.
  • Extract step reads the contract list from the new .github/abi-contracts.txt and writes release/expected-assets.txt recording what the build produced.
  • Verify step diffs the draft's actual assets against that file plus the zip, and refuses to publish on any mismatch.
  • Publish step runs gh release edit --draft=false, so publication happens only after verification. softprops keeps draft: true.
  • workflow_dispatch with a version input, alongside the existing tag-push trigger. A Resolve release tag step takes the version from either source, validates it against the same pattern as the trigger, and exports RELEASE_TAG for the downstream steps. The input is read through an env var rather than interpolated into the script.
  • Concurrency keyed on inputs.version || github.ref, because github.ref is the branch on a dispatch and two versions from one branch would otherwise queue behind each other.

.github/abi-contracts.txt. The 32-name list was duplicated verbatim in both workflows, so a new contract had to be added twice or the release silently omitted it, and the verification step would have needed a third copy to check against. It now lives in one file that both read, and verification compares against what the build actually emitted rather than against a list.

See #222 for more context.

Checklist

Code

  • Follows project style
  • forge build passes
  • forge test passes
  • No new compiler warnings

Testing

  • New tests added for changed behavior
  • Fuzz tests added where applicable
  • Invariant tests verified

Security

  • No new selfdestruct or delegatecall
  • Access control reviewed
  • No storage layout conflicts (for upgradeable contracts)

Documentation

  • NatSpec updated on changed interfaces
  • README updated if needed

Breaking Changes

  • No breaking changes
  • Breaking changes documented below

Breaking changes:

Breaking changes: no API or contract change, but the release process changes. Releases can no longer be created through the GitHub UI's release form or with gh release create, because doing so publishes immediately and the workflow can then no longer attach assets. Cutting a release is now either "Publish Release Package" from the Actions tab with a version input, or pushing a matching tag; both run the same workflow, which creates, verifies and publishes. The guard turns the old habit into an immediate failure.

How to test

Dispatch the workflow from the Actions tab.
Not yet exercised end to end. It should be tested on a fork, not here.

Notes

A release from current master is still needed.

@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

CI Summary

Check Result
Deploy Contracts Reproduces DEPLOYMENTS.md; resume verified
PR Title PR Title Valid
Labels Unknown
Secret Scan Passed - No secrets detected

Deploy Contracts

Deployed addresses vs DEPLOYMENTS.md

Expected is the committed manifest; actual is this CI deployment of the same pipeline.

Contract Expected Actual Match
Create3Factory 0x8533c79E058c5a6489CAFeCA86dc600E029D75f5 0x8533c79E058c5a6489CAFeCA86dc600E029D75f5 match
DotnsContentResolver 0x7F74D7CD50f5a834270E2ad395a01b01891AB37d 0x7F74D7CD50f5a834270E2ad395a01b01891AB37d match
DotnsNameEscrow 0x4881Afb78e7C908cAe818168B926229D93376520 0x4881Afb78e7C908cAe818168B926229D93376520 match
DotnsPopController 0xCC932348606cc1f3318cADeC5A5Cd2CA447f8a4b 0xCC932348606cc1f3318cADeC5A5Cd2CA447f8a4b match
DotnsPopResolver 0xDaC984884EcA8Fc44011f1D6C49B27828390A72B 0xDaC984884EcA8Fc44011f1D6C49B27828390A72B match
DotnsProtocolRegistry 0xD19e3D0C97CF501125a04A97405e3e6592fa846E 0xD19e3D0C97CF501125a04A97405e3e6592fa846E match
DotnsRegistrar 0x4f06E818Ba3d987704fd91cf3d868E4b019106Ab 0x4f06E818Ba3d987704fd91cf3d868E4b019106Ab match
DotnsRegistrarController 0xBdaA01bD1bA67d709F2b1fF286Da0d854977EA30 0xBdaA01bD1bA67d709F2b1fF286Da0d854977EA30 match
DotnsRegistry 0xf34054fd76BbF85f216cf9908226D5f0A72E50CA 0xf34054fd76BbF85f216cf9908226D5f0A72E50CA match
DotnsResolver 0xbd1165E549DF96F083c0A16f61590927bC187009 0xbd1165E549DF96F083c0A16f61590927bC187009 match
DotnsReverseResolver 0xee3883d7eB60Ee9BCD7F3bcD8f2f05302A9Cc035 0xee3883d7eB60Ee9BCD7F3bcD8f2f05302A9Cc035 match
LabelStoreBeacon 0xb57Ebc2e7085616d4906D1fE49af1cE13f7dffeF 0xb57Ebc2e7085616d4906D1fE49af1cE13f7dffeF match
Multicall3 0xB4468000abD87D3c56cbFBd153161223D7b109e5 0xB4468000abD87D3c56cbFBd153161223D7b109e5 match
PopRules 0x747B456bE03aec0b42bd85C51513730FBD45DA31 0x747B456bE03aec0b42bd85C51513730FBD45DA31 match
RootGatewayDispatcher 0xa889CCA3Fb4B07b98a11cc54C10f13dDA20bc3db 0xa889CCA3Fb4B07b98a11cc54C10f13dDA20bc3db match
StoreFactory 0x709A027F446a9e2a4BB9cb9a9c754435b19e32B7 0x709A027F446a9e2a4BB9cb9a9c754435b19e32B7 match
UserStoreBeacon 0xb7C995601679840d36F37E86DB2d7dF30797eC5C 0xb7C995601679840d36F37E86DB2d7dF30797eC5C match
_seed 0x0000000000000000000000000000000000000000 0x0000000000000000000000000000000000000000 match

View full logs

Labels

other, P0, type: docs, type: bug

@re-gius re-gius changed the title CI: attach release assets before publishing + verify ci: attach release assets before publishing + verify Aug 17, 2026
@re-gius re-gius changed the title ci: attach release assets before publishing + verify ci: attach release assets before publishing and verify the set Aug 17, 2026
@re-gius re-gius added P0 Highest Priority type: bug Bug report labels Aug 17, 2026
@re-gius
re-gius marked this pull request as ready for review August 17, 2026 13:47

@sphamjoli sphamjoli left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superseded by the inline review below.

@sphamjoli
sphamjoli dismissed their stale review August 17, 2026 19:52

Superseded by an inline review referencing the specific lines.

@sphamjoli sphamjoli left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work, the draft, verify, publish order is the right call for immutable releases. Reading the version through an env var and checking it before use keeps injection out, and building the draft only after forge test passes is spot on.

One thing to sort before merge: the concurrency key does not quite do what its comment says, detail is on the line. Could we also prove it in CI on this PR rather than a manual dispatch? Wire the beta workflow to run against this branch so it cuts a v0.5.5-rc pre-release here, then we confirm from the run that the pre-release has every ABI plus the zip, it published cleanly, and creating the tag did not start a second release run. The rest of the inline notes are small hardening bits, not blockers.

Comment thread .github/workflows/publish-release.yml Outdated
# Two runs for the same version would race to attach assets to the same draft. On a
# dispatch `github.ref` is the branch, so key on the requested version instead.
concurrency:
group: ${{ github.workflow }}-${{ inputs.version || github.ref }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: this does not serialise the two triggers for the same version, which is what the comment above it promises. On a tag push inputs.version is empty, so the group is ...-refs/tags/v0.5.5; on a dispatch it is ...-v0.5.5. The two strings differ, so a dispatch and a tag push for the same version are not mutually exclusive: both clear the pre-published guard while neither is published, both build, both attach to the same draft tag, and both reach publish, where immutable releases fail the second one or the asset sets race. Use github.ref_name instead of github.ref. On a tag push it is the tag v0.5.5; on a dispatch inputs.version wins. Both then collapse to ...-v0.5.5.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ca36d0c , both workflows now key on inputs.version || github.ref_name, so a dispatch and a tag push for the same version collapse to the same group.

# Two runs for the same version would race to attach assets to the same draft. On a
# dispatch `github.ref` is the branch, so key on the requested version instead.
concurrency:
group: ${{ github.workflow }}-${{ inputs.version || github.ref }}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same blocking issue as the release workflow. Switch github.ref to github.ref_name so a dispatch and a tag push for the same pre-release version share one concurrency group.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in ca36d0c as well

else
TAG="$GITHUB_REF_NAME"
fi
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+-.+$ ]]; then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-.+$ accepts anything after the hyphen. Not exploitable, since $RELEASE_TAG is quoted everywhere downstream and a newline cannot reach $GITHUB_ENV through the anchored match, but it lets junk suffixes through. Tighten to -[0-9A-Za-z.]+$.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tightened to -[0-9A-Za-z.]+$ as suggested ca36d0c

Comment thread .github/workflows/publish-release.yml Outdated
)

for name in "${contracts[@]}"; do
while IFS= read -r name || [ -n "$name" ]; do

@sphamjoli sphamjoli Aug 17, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardening: a CRLF-saved abi-contracts.txt puts a trailing carriage return in name, breaks the artifact path, and fails the release. Add a pre-commit hook that rejects CR line endings in this file so a bad checkout cannot reach the workflow. Pinning it to LF in .gitattributes covers the checkout side too, and a run-loop strip (name="${name%$'\r'}") is a last resort, but the hook stops it at the source.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added all three layers in ca36d0c : validate_abi_contracts in scripts/shell/pre-commit.sh rejects a CR at commit time, a new .gitattributes pins the file to eol=lf on checkout, and the loop keeps ${name%$'\r'} as the last resort.

Comment thread README.md Outdated

Do not create releases through the GitHub UI's release form, or with `gh release create`. Both publish immediately, and because this repository has immutable releases enabled, a published release can no longer accept assets: only its title and notes stay editable. A release made that way carries no ABIs at all. The workflow rejects an already-published version before building, so the mistake fails in seconds rather than silently shipping an empty release.

If a run fails partway, re-run it from the Actions tab; the draft is updated rather than duplicated. If the version has already been published, use a different one, since its assets cannot be changed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

softprops never deletes assets, so this holds only when the asset set is unchanged. Remove a contract from abi-contracts.txt and re-run against an existing draft, and the stale asset survives; the verify step then refuses to publish, which is correct but not what this line implies. Add a note to delete the draft first when the contract list changed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in ca36d0c

@sphamjoli

sphamjoli commented Aug 17, 2026

Copy link
Copy Markdown
Member

Could we also please prove it in CI on this PR rather than a manual dispatch? Wire the beta workflow to run against this branch so it cuts a v0.5.5-rc pre-release automatically here, and we read the result straight from the PR run: the pre-release has every ABI plus the zip, it published cleanly, and creating the tag did not start a second release run. Both workflows share the fix, so a green pre-release covers the real release too. Thanks for putting this together!

@re-gius

re-gius commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Could we also please prove it in CI on this PR rather than a manual dispatch? Wire the beta workflow to run against this branch so it cuts a v0.5.5-rc pre-release automatically here, and we read the result straight from the PR run: the pre-release has every ABI plus the zip, it published cleanly, and creating the tag did not start a second release run. Both workflows share the fix, so a green pre-release covers the real release too. Thanks for putting this together!

Pre-release successfully created, you can find it in https://github.com/paritytech/dotns/releases
The description still has broken URLs, but I fixed them in da753f0 . Apart from that, all assets are available and verification passed, tag is expanded correctly.

@re-gius
re-gius requested a review from sphamjoli August 18, 2026 09:42
@re-gius
re-gius merged commit 7bfac8a into master Aug 18, 2026
7 checks passed
@re-gius
re-gius deleted the re-gius/release-asset-publication-order branch August 18, 2026 11:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

other P0 Highest Priority type: bug Bug report type: docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Release workflow publishes no ABI assets, and the SDK silently keeps stale ones

2 participants