Repository navigation
fix(foundry-test): stop DeployPlatform/UpgradePlatform tests racing on DEPLOYMENTS_NETWORK - #311
Conversation
…n DEPLOYMENTS_NETWORK PR No. 254 added two tests in different contracts that vm.setEnv the process-wide DEPLOYMENTS_NETWORK to different values. forge runs test contracts in parallel, so the Upgrade test could switch it between the Deploy test's setEnv and its write, and the Deploy test then read a file that was never written. develop went red on cf80216 and PR No. 304 was dropped from the merge queue by the same failure. - DeployPlatform: _writeDeployments now delegates to a new _writeDeploymentsTo(d, deployer, outPath); the real script path still resolves the file through DeploymentsPath, so its output is unchanged. - Both harnesses expose _writeDeploymentsTo, and both tests pass their own file path explicitly. No test sets DEPLOYMENTS_NETWORK or GIT_COMMIT_HASH any more (the upgrade test already passed the commit as an argument), which also takes DeploymentsPathTest's env read out of the race. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reviewed against Claimed: Verified: CI Solidity check green at Claimed: Verified:
The output path Claimed: Verified: Running that exact command in the review worktree: no output. Confirmed — neither Claimed: The Verified: In the old test, Claimed: Verified: That test reads No |
Files changed (3) — as of
|
| Field | Value |
|---|---|
| Change | Modified |
| Lines | +10/-1 |
| Diff (what exactly is in this PR) | _writeDeployments(d, deployer) is now a one-line wrapper that calls the new _writeDeploymentsTo(d, deployer, _deploymentsFile("")). The writing logic (resolving impl addresses, building the JSON, calling vm.writeJson) moves into _writeDeploymentsTo, which takes an explicit outPath. run() is unchanged — it still calls _writeDeployments(d, msg.sender). |
| Functionality — how & why | How: The wrapper pattern preserves the production call path (run() → _writeDeployments → _deploymentsFile("")) while exposing _writeDeploymentsTo to tests with their own pre-determined path. Why: vm.setEnv changes a process-wide environment variable. Forge runs test contracts in parallel, so when DeployPlatformScriptTest set DEPLOYMENTS_NETWORK=test-impl-metadata and UpgradePlatformScriptTest set it to test-upgrade-metadata, either test could read the other contract's value between the setEnv and the subsequent _deploymentsFile("") call — making the Deploy test read a file that was never written. Extracting the path as an argument eliminates the shared-state dependency entirely. |
Diff vs current develop HEAD |
None — branch cut from current develop tip |
| Recommended merge proposal | Merged as-is — run() call path verified unchanged, 20/20 focused tests pass locally |
| Actual merge proposal | Soon |
| Pending proposal | None |
| Local merge conflict | No |
| GitHub merge conflict | No |
foundry/test/DeployPlatformScript.t.sol
| Field | Value |
|---|---|
| Change | Modified |
| Lines | +19/-22 |
| Diff (what exactly is in this PR) | DeployPlatformHarness.writeDeployments → writeDeploymentsTo(d, deployer, path) exposing _writeDeploymentsTo. test_writeDeployments_writesImplAndCodehashMetadata: removes vm.setEnv("DEPLOYMENTS_NETWORK", "test-impl-metadata"), constructs the path directly as a string literal, and passes it to harness.writeDeploymentsTo(d, deployer, path). Docstring updated to match. |
| Functionality — how & why | How: The test no longer touches the process environment. The output path foundry/deployments/test-impl-metadata.json is the same file as before, now named explicitly rather than derived from _deploymentsFile("") via a setEnv. Why: Removes this contract's contribution to the DEPLOYMENTS_NETWORK race. |
Diff vs current develop HEAD |
None — branch cut from current develop tip |
| Recommended merge proposal | Merged as-is |
| Actual merge proposal | Soon |
| Pending proposal | None |
| Local merge conflict | No |
| GitHub merge conflict | No |
foundry/test/UpgradePlatformScript.t.sol
| Field | Value |
|---|---|
| Change | Modified |
| Lines | +14/-24 |
| Diff (what exactly is in this PR) | DeployHarness.writeDeployments → writeDeploymentsTo(d, deployer_, outPath). test_writeUpgradeMetadata_writesImplCodehashAndCommit: removes vm.setEnv("DEPLOYMENTS_NETWORK", "test-upgrade-metadata"), removes vm.setEnv("GIT_COMMIT_HASH", commit) (commit was already passed as a direct argument — the setEnv was redundant), constructs the path explicitly, passes it to deployHarness.writeDeploymentsTo. assertEq message updated from "must match GIT_COMMIT_HASH" to "must match the commit passed in". Docstring "MUST go into this function" constraint removed. |
| Functionality — how & why | How: Same strategy as DeployPlatformScript.t.sol — path passed explicitly, no env writes. The GIT_COMMIT_HASH setEnv removal is a pure cleanup; the function under test takes the commit as an argument and never reads that env var. Why: This contract was the other source of the race: setting DEPLOYMENTS_NETWORK=test-upgrade-metadata could clobber DeployPlatformScriptTest's env before its write, making the Deploy test read the wrong file. |
Diff vs current develop HEAD |
None — branch cut from current develop tip |
| Recommended merge proposal | Merged as-is |
| Actual merge proposal | Soon |
| Pending proposal | None |
| Local merge conflict | No |
| GitHub merge conflict | No |
Verification
Deep verification detail in the separate verification comment. Summary: forge test --match-contract 'DeployPlatformScriptTest|UpgradePlatformScriptTest|DeploymentsPathTest' in the review worktree — 20 passed, 0 failed. run() call path verified unchanged. No setEnv("DEPLOYMENTS_NETWORK" or setEnv("GIT_COMMIT_HASH" remaining in foundry/test/. CI all green at run 41483946043.
Local vs. GitHub agree: git merge-tree --write-tree exit 0 matches GitHub mergeable: MERGEABLE.
robertocarlous
left a comment
There was a problem hiding this comment.
Verified at 62fb279: run() output path unchanged, 20/20 focused forge tests pass locally, no setEnv in test/ remaining, CI all green.
8b46e4e
Actual outcome — PR No. 311 merged as-is through the merge queueThis replaces the predictions in Robbert's pre-merge review comment above with what actually happened when the PR landed. One commit on
Files unchanged from the PR (3 of 3)
Verification
Local vs. GitHub agree: yes. The merge was clean, as both predicted. |
Summary
This fixes the red
developoncf80216, which PR No. 254 introduced (diagnosis, Discussion 170 log).DeployPlatformScriptTest.test_writeDeployments_writesImplAndCodehashMetadataandUpgradePlatformScriptTest.test_writeUpgradeMetadata_writesImplCodehashAndCommitbothvm.setEnvthe process-wideDEPLOYMENTS_NETWORK, to different values. forge runs test contracts in parallel, so the Upgrade test could switch the variable between the Deploy test'ssetEnvand its write. The Deploy test then readtest-impl-metadata.json, a file that was never written. This failed 2 of 3 runs:developpush run 41476524829 and the merge-queue run that dropped PR No. 304.Changes
foundry/script/DeployPlatform.s.sol:_writeDeployments(d, deployer)now just calls a new_writeDeploymentsTo(d, deployer, outPath)with_deploymentsFile(""). The script's real output path and JSON are unchanged.foundry/test/DeployPlatformScript.t.sol,foundry/test/UpgradePlatformScript.t.sol: both harnesses expose_writeDeploymentsTo, and both tests pass their own file explicitly (deployments/test-impl-metadata.json,deployments/test-upgrade-metadata.json).DEPLOYMENTS_NETWORKorGIT_COMMIT_HASHany more. The upgrade test already passed the commit hash as an argument, so itssetEnv("GIT_COMMIT_HASH", …)was redundant.DeploymentsPathTest.test_deploymentsFileUsesPrefixAndChainNetworkout of the race, because it readsDEPLOYMENTS_NETWORKtwice.Verification
forge build(real via_ir profile) and the fullforge test: 518 passed, 0 failed on the fixed branch.forge test --match-contract 'DeployPlatformScriptTest|UpgradePlatformScriptTest|DeploymentsPathTest'run back to back 8 times: 20/20 passed every time. Before the fix the same race failed 2 of 3 CI runs.grep -rn 'setEnv("DEPLOYMENTS_NETWORK"\|setEnv("GIT_COMMIT_HASH"' foundry/test: no hits.foundry/script/**changed, so per MAINTAINING No. 4.2 the reviewer checks locally thatDeployPlatform.run()still writesdeployments/<network>.json(the wrapper passes_deploymentsFile("")through unchanged).Fixes the
developregression from PR No. 254. Owner per MAINTAINING No. 4.6: @robertocarlous / @Santiagocetran, so a review from either of you unblocks the merge queue.🤖 Generated with Claude Code