Skip to content

docs(release-process): fix wrong monorepo paths, invalid bash, and stale release-script flags - #2295

Merged
palango merged 5 commits into
mainfrom
GigaHierz/2139-release-process-fixes
Sep 3, 2026
Merged

docs(release-process): fix wrong monorepo paths, invalid bash, and stale release-script flags#2295
palango merged 5 commits into
mainfrom
GigaHierz/2139-release-process-fixes

Conversation

@GigaHierz

@GigaHierz GigaHierz commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

The hole, and the fix

/contribute-to-celo/release-process/smart-contracts could not be followed end to end. Two layers were broken.

The commands did not exist. The four Truffle release scripts this page invoked were deleted from celo-monorepo. packages/protocol/package.json on master defines only the :foundry variants:

Page said Now
yarn release:verify-deployed release:verify-deployed:foundry
yarn release:make release:make:foundry
yarn release:check-versions release:check-versions:foundry
yarn release:verify-release deleted with no :foundry sibling — verify-deployed:foundry takes -p

check-versions is the one that hides. The package.json key still exists, so a grep finds it, but check-versions.sh calls build_tag and release-lib.sh now defines only build_tag_foundry. Under set -euo pipefail it dies before compiling anything.

The flags and filenames hanging off them were also wrong, which is what the first commit fixed: the packages/what-is-celo/... path that 404s, NETWORK=${"anvil"|...} which was a shell syntax error, the -p/-r flags the Foundry scripts hard-error on, the mandatory -k, and the -l libraries filename that only validates against the previous release.

Also in this pass:

  • Pre-audit tag corrected to the core-contracts.v${N}.pre-audit family. The celo-core-contracts-v${N} family stops at v7 upstream.
  • Dropped "(Use -d to dry-run the deploy)". -d belonged to the deleted Truffle script; getopts 'b:k:i:l:n:p:r:u:sa:' has no d. -u takes an RPC URL, so a local anvil fork is the way to rehearse.
  • PRIVATE_KEY quoted and given a visible placeholder. Empty and unquoted, -k $PRIVATE_KEY word-split to a bare trailing -k and getopts bailed before the script's own "Need to set the private key" message could print.
  • The celocli governance:propose block now defines N, NETWORK and RELEASE_CANDIDATE. It used two of them undefined, so copied on its own it resolved to proposal--.json.
  • The verify step names upgrade_proposal.json, the artifact actually fetched, instead of a proposal.json that appears nowhere else on the page.
  • forge, cast and jq added to prerequisites — every rewritten block runs through forge build.
  • # Run from packages/protocol added to every block that needs it; the scripts source relative paths and -i takes a repo-relative path.
  • Links RELEASE_PROCESS_FOUNDRY.md, the release owners' own doc, whose opening table is exactly this retired-to-Foundry mapping.

Corrections to the previous version of this body

Six statements in the earlier body did not hold. Recording them rather than deleting them, since reviewers approve on the strength of the body:

  1. ${N-1} is "the literal N" — it is ${parameter-word}: unset yields core-contracts.v1, and with N=18 it yields core-contracts.v18, the release being prepared rather than the previous one. Silently wrong rather than obviously wrong, which is a stronger argument for the fix than the one originally made.
  2. The OpenZeppelin link row was inverted. main carries the version-less URL, which returns 200. The /1.x/ variant 404s. Acting on the row as written would have replaced a working link with a dead one.
  3. "Four reverts" — one. On three of those four lines the other branch's head is byte-identical to its merge base, so a merge would revert nothing. Only the anvil option genuinely conflicts.
  4. "Only the first block defined anything" — four of the five later blocks defined their own variables. What none defined was N, and $PRIVATE_KEY was not on the page at all before this PR.
  5. "Each block now stands alone" — was false for the celocli propose block until this commit.
  6. export const N = "N" was inherited — it arrived in b555aecb (the Docusaurus removal), so the Mintlify migration wrote it. Removing it is still safe: every ${N} on the page sits inside inline code or a fence, and MDX evaluates neither.

What this does NOT do / residual risk

Documentation only; no script is changed. The page is verified against celo-monorepo master — if a release is cut from a branch where these scripts differ, the page will drift again. The underlying fragility is that the page hand-copies command names from another repo with nothing checking them; RELEASE_PROCESS_FOUNDRY.md is now linked so there is at least an authoritative anchor.

tags:view and the two celocli commands were already correct and are unchanged.

Judgement calls

  • release:verify-releaserelease:verify-deployed:foundry -p … rather than dropping the section. RELEASE_PROCESS_FOUNDRY.md maps it that way and verify-deployed-forge.sh accepts both -p and -i (getopts 'b:n:fl:i:p:'), so the substitution is mechanical. Reversal: one line.
  • Kept the -u/anvil pointer instead of deleting the dry-run sentence outright, since rehearsing a mainnet deploy is the reader's actual goal. Reversal: half a sentence.
  • Did not add a warning about a mainnet deployer key in argv. It is a real exposure — argv is world-readable in /proc on Linux — but it is the upstream script's interface, not this page's, and inventing guidance here felt like the wrong place. Happy to add a sentence if you want it.

Issues

Refs #2139 — that PR touches the same file; see below.

Stacking / conflicts

Branched off main, currently 2 behind (from #2305 and #2286), no conflict. No other open PR touches this file. #2139 overlaps on one line (the anvil option); whichever lands second takes the other's wording.

Verification evidence

Every command on the page checked against celo-monorepo master:

$ gh api .../packages/protocol/package.json | jq '.scripts | keys[] | select(contains("release"))'
release:check-opcodes
release:check-versions
release:check-versions:foundry
release:determine-release-version
release:make:foundry
release:verify-deployed:foundry

Every flag checked against the scripts' own getopts:

check-versions-foundry.sh   getopts 'a:b:r:l:i'            page uses -a -b
verify-deployed-forge.sh    getopts 'b:n:fl:i:p:'          page uses -n -b -f -p -i
make-release-foundry.sh     getopts 'b:k:i:l:n:p:r:u:sa:'  page uses -b -n -r -i -l -k

The check-versions breakage, confirmed directly:

release-lib.sh:29    function build_tag_foundry() {     # no build_tag
check-versions.sh:45 build_tag $BRANCH $LOG_FILE
check-versions.sh:47 build_tag $NEW_BRANCH $LOG_FILE
$ mint broken-links
success no broken links found

No dead script names remain:

$ grep -nE 'release:(verify-deployed|make|check-versions|verify-release)([^:]|$)' <page> | grep -v ':foundry'
(no matches)

Remaining ops steps

  • none

Questions for the maintainer

Do you want the sentence warning that a mainnet deployer key passed via -k lands in argv? It is the upstream interface, so I left it out — see judgement calls.

@palango

palango commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Most of this is right, and more of it is right than the body claims. You wrote that you could not verify the flag and filename semantics against the scripts, so I did: they are correct, including one subtlety I doubt was luck. What stops me approving is the layer underneath. The commands those flags hang off were deleted from celo-monorepo, so the page still cannot be followed end to end.

The blocker: four dead script names

packages/protocol/package.json on master defines only release:check-versions, release:check-versions:foundry, release:make:foundry, release:verify-deployed:foundry, release:check-opcodes and release:determine-release-version. The Truffle entries went in 6ce74bb32, and they are absent at tag core-contracts.v17 too, so this is not master-only drift.

line page says needs to be
95, 191 yarn release:verify-deployed release:verify-deployed:foundry
145 yarn release:make release:make:foundry
129 yarn release:check-versions release:check-versions:foundry
180 yarn release:verify-release deleted, no :foundry sibling

129 is the one that hides. The package.json key still exists, so a grep finds it, but check-versions.sh calls build_tag and release-lib.sh now defines only build_tag_foundry. Under set -euo pipefail it dies before compiling anything.

For 180, packages/protocol/RELEASE_PROCESS_FOUNDRY.md maps release:verify-release to "use verify-deployed with --proposal", and verify-deployed-forge.sh accepts both -p and -i, so the substitution is mechanical:

yarn release:verify-deployed:foundry -p "upgrade_proposal.json" -b $RELEASE_CANDIDATE -n $NETWORK -f -i initialization_data.json

Read that file before the next push. Its opening table is this exact mapping, and it would have caught all four. The page probably ought to link it, since it is the release owners' own doc. Stale prose mentions of the old names sit on 87, 98, 132, 170 and 185.

What's right

  • packages/what-is-celo/... 404s; packages/protocol/releaseData/initializationData/ holds release1.json through release17.json. Your read of the bad find-and-replace is right.
  • .circleci is gone, and .github/workflows/celo-monorepo.yml:32 is RELEASE_TAG: core-contracts.v17.
  • NETWORK=${"anvil"|"celo-sepolia"|"mainnet"} really was a syntax error, and all three names in the replacement comment resolve in truffle-config-parent.js.
  • Dropping -p: make-release-foundry.sh:57 hard-errors on any -p path and derives proposal-$NETWORK-$BRANCH.json itself at line 61. Dropping -r: same shape in check-versions-foundry.sh, which builds report-$BRANCH-$NEW_BRANCH.json. Adding -k: getopts 'b:k:i:l:n:p:r:u:sa:', mandatory at line 49.
  • -l "$NETWORK-$PREVIOUS_RELEASE-libraries.json" is not merely plausible, it is the only value that passes. validate_libraries_filename rejects anything that is not the previous release's file.

The fix here is four script renames and a tag. No command needs reworking.

Smaller things

Line 56 still carries the wrong tag. You corrected the file path in that sentence and left celo-core-contracts-v${N}.pre-audit sitting next to it. That family stops at v7 upstream, and every pre-audit tag since is core-contracts.v${N}.pre-audit, which is what step 2 two lines above already creates.

On line 136, "(Use -d to dry-run the deploy)" now contradicts the -k you added on 145. -d belonged to the deleted Truffle script. The Foundry one has no dry-run at all, so drop the parenthetical or point at -u against a local anvil fork instead.

Line 156 is the one block that never got a preamble, and this PR is what broke it. Main had the literal "proposal.json". You replaced that with "proposal-$NETWORK-$RELEASE_CANDIDATE.json" and defined neither variable, so copied on its own it resolves to proposal--.json. The block was copy-safe before and is not now.

Lines 144 and 145: PRIVATE_KEY= empty plus unquoted -k $PRIVATE_KEY word-splits down to a bare trailing -k, and getopts bails before the script's own "Need to set the private key" message ever prints. Quoting fixes that. A visible placeholder would be better still, and a mainnet deployer key in argv deserves a sentence of warning.

Line 171 says "the proposal.json you fetched in the step prior", naming a file that now appears nowhere else on the page. The artifact fetched in that step is upgrade_proposal.json.

Line 69, the prerequisites callout, still says yarn install plus yarn build is a complete setup. Every block you rewrote runs through forge build, and the deploy block wants cast and jq. It predates you, but this PR is what moved the page onto the Foundry contract.

One nit: only the first block says # Run from packages/protocol. The other five need it too, since the scripts source relative paths and -i takes a repo-relative path.

The body

Six statements do not hold, and reviewers approve on the strength of the body:

  1. ${N-1} is not "the literal N". It is ${parameter-word}: unset gives core-contracts.v1, and with N=18 it gives core-contracts.v18, the release being prepared. Silently wrong rather than obviously wrong, which is a better argument for your fix than the one you made.
  2. The OpenZeppelin row is inverted. Main has the version-less URL and it returns 200. Fix smart contracts release process docs #2139's branch is the one carrying /1.x/, which 404s. Anyone acting on the row as written would swap a working link for a dead one.
  3. Of the four claimed reverts, one is real. Fix smart contracts release process docs #2139 touches a single file, and on three of those four lines its head is byte-identical to its merge base, so a merge would revert nothing. Only the anvil option genuinely conflicts.
  4. "Only the first block on the page defined anything" is not what the diff shows. Four of the five later blocks defined their own variables. What none of them defined was N, and $PRIVATE_KEY was not on the page at all before this PR.
  5. "Each block now stands alone" is false for line 156, per above.
  6. export const N = "N" arrived in b555aecb "remove: removed docusaurus (#2040)", so the Mintlify migration wrote it rather than inheriting it. Removing it is still safe: every ${N} on the page sits inside an inline-code span or a fence, and MDX evaluates neither.

Happy to push the renames myself if that is easier than another round.

…ale release-script flags

Supersedes #2139, which was opened in February and has since diverged from main:
merging it as-is would revert four fixes main has picked up (the `description`
frontmatter, the pinned celo-monorepo setup link, the OpenZeppelin proxies link,
and the anvil network option). This applies only the parts of #2139 that main
still needs, on top of current main.

Verified against celo-org/celo-monorepo:

- `packages/what-is-celo/about-celo-l1/protocol/releaseData/` 404s — a bad
  find-and-replace from an old docs migration. The real path is
  `packages/protocol/releaseData/initializationData/`, which exists.
- `.circleci/` 404s; the repo moved to GitHub Actions. `RELEASE_TAG` is defined
  at `.github/workflows/celo-monorepo.yml:32`.

Bash that could not run:

- `${N-1}` is parameter expansion with a default, not arithmetic — it evaluates
  to "N" whenever N is unset, so `PREVIOUS_RELEASE` came out as
  `core-contracts.vN`. Now `$((N-1))`.
- `NETWORK=${"anvil"|"celo-sepolia"|"mainnet"}` is a syntax error. Now a real
  assignment with the options in a comment.
- Six blocks used `$N`, `$PREVIOUS_RELEASE`, `$RELEASE_CANDIDATE` or
  `$PRIVATE_KEY` without defining them. Each block now stands alone
  (AGENTS.md section 5). The examples use N=18, the next release after the
  `core-contracts.v17` currently pinned in the workflow.

Release script flags and outputs brought in line with the current
implementation: `-r`/`-p` no longer name files that the scripts write
themselves, `-k` is required by `release:make`, and the generated filenames
(`report-*`, `*-libraries.json`, `proposal-*`) are stated where they are
produced and used.

Also drops the `export const N = "N"` Docusaurus leftover and three typos
(Vaildator, valildator, EXCECUTED).

Verified: `mintlify broken-links` reports no broken links.

Co-Authored-By: martinvol <martinvol@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@GigaHierz
GigaHierz force-pushed the GigaHierz/2139-release-process-fixes branch 2 times, most recently from a6c8ead to 06307b2 Compare September 2, 2026 12:32
The four Truffle release scripts this page invoked were deleted from
celo-monorepo, so the process could not be followed end to end:

  release:verify-deployed  -> release:verify-deployed:foundry
  release:make             -> release:make:foundry
  release:check-versions   -> release:check-versions:foundry
  release:verify-release   -> deleted; verify-deployed:foundry takes -p

check-versions is the one that hides: the package.json key still exists, so
grep finds it, but check-versions.sh calls build_tag and release-lib.sh now
defines only build_tag_foundry, so it dies under set -euo pipefail before
compiling anything.

Also:
- correct the pre-audit tag to the core-contracts.v${N} family
- drop "(Use -d to dry-run)": the Foundry script has no -d; -u takes an RPC
  URL, so a local anvil fork is the way to rehearse
- quote $PRIVATE_KEY and give it a visible placeholder — empty and unquoted,
  it word-split to a bare trailing -k and getopts bailed before the script's
  own "Need to set the private key" message could print
- define N, NETWORK and RELEASE_CANDIDATE in the celocli propose block, which
  used two of them undefined and resolved to proposal--.json
- name upgrade_proposal.json in the verify step instead of a proposal.json
  that appears nowhere else on the page
- add forge, cast and jq to the prerequisites, and the packages/protocol
  working-directory comment to every block that needs it
- link RELEASE_PROCESS_FOUNDRY.md, which maps each retired command

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@GigaHierz

Copy link
Copy Markdown
Contributor Author

All of it addressed, and I verified your findings independently before acting on them rather than taking them on trust.

The blocker — four dead script names. Confirmed against packages/protocol/package.json on master: only release:check-versions, :check-versions:foundry, :make:foundry, :verify-deployed:foundry, :check-opcodes and :determine-release-version exist. All four renamed, release:verify-release mapped to verify-deployed:foundry -p per RELEASE_PROCESS_FOUNDRY.md, and the stale prose mentions on 87, 98, 132, 170 and 185 with them.

Your check-versions subtlety checks out exactly as described:

release-lib.sh:29     function build_tag_foundry() {      # build_tag is gone
check-versions.sh:45  build_tag $BRANCH $LOG_FILE
check-versions.sh:47  build_tag $NEW_BRANCH $LOG_FILE

The key surviving in package.json is what makes it dangerous — a grep confirms the command and it still dies before compiling.

Smaller ones, all taken: pre-audit tag corrected to the core-contracts.v${N} family; -d dropped (no d in getopts 'b:k:i:l:n:p:r:u:sa:') and replaced with a pointer at -u against a local anvil fork; PRIVATE_KEY quoted with a visible placeholder; the celocli propose block now defines N, NETWORK and RELEASE_CANDIDATE so it no longer resolves to proposal--.json; upgrade_proposal.json named in the verify step; forge, cast and jq in the prerequisites; the working-directory comment on every block that needs it; and RELEASE_PROCESS_FOUNDRY.md linked — you were right that its opening table would have caught all four, and it belongs on the page.

The body. All six were wrong and I have rewritten it, keeping the corrections visible rather than quietly deleting them. Two worth calling out: the ${N-1} reading is a better argument for the fix than the one originally made, since N=18 silently yields core-contracts.v18 — the release being prepared — rather than failing loudly. And the inverted OpenZeppelin row would have had someone swap a working link for a 404.

Every command and flag now checked against upstream:

check-versions-foundry.sh   getopts 'a:b:r:l:i'            page uses -a -b
verify-deployed-forge.sh    getopts 'b:n:fl:i:p:'          page uses -n -b -f -p -i
make-release-foundry.sh     getopts 'b:k:i:l:n:p:r:u:sa:'  page uses -b -n -r -i -l -k

$ grep -nE 'release:(verify-deployed|make|check-versions|verify-release)([^:]|$)' <page> | grep -v ':foundry'
(no matches)

$ mint broken-links
success no broken links found

One thing I left out, deliberately. You noted a mainnet deployer key in argv deserves a warning. I quoted $PRIVATE_KEY and gave it a placeholder, but did not add the sentence — argv exposure is the upstream script's interface rather than this page's, and I would rather you decide whether the docs are the right place to say it. It is one sentence if you want it.

The deeper problem stands: this page hand-copies command names from another repo with nothing checking them, which is how four names went stale unnoticed. Linking RELEASE_PROCESS_FOUNDRY.md gives it an anchor but not a test.

palango
palango previously approved these changes Sep 3, 2026

@palango palango left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Blocker closed. Re-checked against celo-monorepo master rather than against your summary: packages/protocol/package.json defines exactly six release:* scripts, all four names on the page resolve to one of them, and grepping the head for a non-:foundry variant returns nothing.

The check-versions subtlety holds. release-lib.sh:29 defines build_tag_foundry and nothing else, and check-versions.sh:45 and :47 call bare build_tag. RELEASE_PROCESS_FOUNDRY.md returns 200 and its opening table maps release:verify-release to verify-deployed with a proposal, which is what the page now does. Flags match all three getopts strings: 'a:b:r:l:i', 'b:n:fl:i:p:', 'b:k:i:l:n:p:r:u:sa:'.

The -u pointer is better than the sentence it replaced. make-release-foundry.sh:14 documents it as "Custom RPC URL (optional, overrides network default)", assigned at :38 and threaded through :67 and :83-84, so an anvil fork genuinely rehearses the deploy.

The rest verified: pre-audit tag against RELEASE_TAG: core-contracts.v17 at celo-monorepo.yml:32, PRIVATE_KEY quoted with a visible placeholder, the three variables in the propose block, upgrade_proposal.json in the verify step, forge/cast/jq in the prerequisites, and the run-from comment on all six packages/protocol blocks and correctly absent from the two celocli ones. On the body I spot-checked the correction that could have done damage: the version-less OpenZeppelin URL returns 200 and /upgrades-plugins/1.x/proxies 404s, so it now reads the right way round.

On your question, add the sentence. -k "$PRIVATE_KEY" puts a mainnet deployer key in a command line that is world-readable through /proc on Linux and lands in shell history. That it is the upstream script's interface is the reason to warn, not the reason to stay quiet: this page is where the reader meets that interface, and it is this page telling them to run it against mainnet.

Two residuals, neither blocking. Fold them in with that sentence or leave them for the next pass:

  • $YOUR_ADDRESS on line 162 is still undefined. It predates this PR, but correction 5 says the propose block now stands alone, and with one undefined variable it does not quite.
  • The tags:view block carries # Run from packages/protocol. That one is git for-each-ref and works anywhere in the repo; it is the only block where the comment does no work.

Your point about the deeper problem stands. Hand-copied command names from another repo with nothing checking them is how four went stale, and a link is an anchor rather than a test. Worth its own issue.

…UR_ADDRESS, drop a stray run-from comment

Three follow-ups from review:

- Add a Warning after the release:make:foundry block. The script's -k flag puts
  the deployer key on the command line, which is readable by other processes
  (/proc on Linux) and lands in shell history. This page is where a release
  engineer meets that interface and it tells them to run it against mainnet,
  so the page carries the warning.
- Define YOUR_ADDRESS in the celocli governance:propose block so it stands
  alone like the others.
- Drop the 'Run from packages/protocol' comment on the tags:view block. That
  one is git for-each-ref and works from anywhere in the repo; it was the only
  block where the comment did no work.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@GigaHierz

Copy link
Copy Markdown
Contributor Author

Added the sentence, and folded in both residuals, in 2bb252cf as a plain commit on top.

  • A <Warning> under the release:make:foundry block: -k "$PRIVATE_KEY" puts the deployer key on the command line, readable by other processes via /proc on Linux and written to shell history; run from a dedicated deploy machine and clear it afterwards. Your reasoning is the one I went with: the page is where the reader meets that interface and it tells them to run it against mainnet, so the page carries the warning.
  • YOUR_ADDRESS="0x..." is now defined inside the governance:propose block, so it stands alone like the six others.
  • The # Run from packages/protocol comment is gone from the tags:view block only. The other six keep it.

mint broken-links green on the new head. Since this repo dismisses approvals on push, re-requesting you.

The hand-copied script names problem is filed as #2310, with your two remedies (a script check against package.json and the getopts strings, or a recorded recurring check) as the options.

@GigaHierz
GigaHierz requested a review from palango September 3, 2026 09:46
@palango
palango enabled auto-merge (squash) September 3, 2026 09:54
@palango
palango merged commit f61c905 into main Sep 3, 2026
5 checks passed
@palango
palango deleted the GigaHierz/2139-release-process-fixes branch September 3, 2026 10:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants