Skip to content
Merged
1 change: 1 addition & 0 deletions .soldeerignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ CLAUDE.md
/remappings.txt
/slither.config.json
/soldeer.lock
/fixture-lib
/test
7 changes: 7 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ fs_permissions = [
# inherited record check reads that root from contracts forge runs in
# parallel, so a fixture release there would be one they have to fail on.
{ access = "read-write", path = "./test" },
# LibRainDeploySnapshotTest points the aggregate writer here, one directory
# per test. NOT ./src or ./test: the writer names the file it emits
# LibReleasedSuites.sol and that file imports ./Lib<Contract>Released.sol and
# ../abstract/RainDeploySuitesBase.sol, which resolve from src/lib and nowhere
# else, so a copy left behind by a failure under a compiled root fails the
# whole build until it is deleted by hand. Nothing compiles this root.
{ access = "read-write", path = "./fixture-lib" },
# GeneratedSnapshotShapeTest reads the compiler's AST out of the artifact.
{ access = "read", path = "./out" },
]
Expand Down
35 changes: 23 additions & 12 deletions script/Build.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ struct GeneratedContract {
/// deploys.
///
/// - `run()` rewrites the rolling snapshots under `src/generated/candidate/`,
/// the alias libs pointing at them, and the released-suites libs.
/// the alias libs pointing at them, the released-suites libs and the
/// aggregate over them.
/// - `cutRelease()` does the same, freezing the rolling snapshots as
/// `src/generated/<tag>/` in between.
///
Expand All @@ -35,8 +36,8 @@ struct GeneratedContract {
/// The frozen `<tag>/` directories are what
/// `RegistryDeploySuites.releasedSuites()` enumerates.
///
/// `generatedContracts()` is the only list, read by the regeneration, both lib
/// writers and the freeze.
/// `generatedContracts()` is the only list, read by the regeneration, all three
/// lib writers and the freeze.
contract Build is Script, RegistryDeploySuites {
/// Every contract this repo generates deploy pins for.
/// @return contracts The generated contracts.
Expand All @@ -52,8 +53,19 @@ contract Build is Script, RegistryDeploySuites {
});
}

/// @notice Regenerate the rolling snapshots, their alias libs and the
/// released-suites libs.
/// Every generated contract's name, in declaration order — the order the
/// aggregate emits its entries in. Read by the freeze and the aggregate.
/// @return names The contract names.
function generatedContractNames() internal pure returns (string[] memory names) {
GeneratedContract[] memory contracts = generatedContracts();
names = new string[](contracts.length);
for (uint256 i = 0; i < contracts.length; i++) {
names[i] = contracts[i].contractName;
}
}

/// @notice Regenerate the rolling snapshots, their alias libs, the
/// released-suites libs and the aggregate over them.
function run() external {
regenerateCandidates();
regenerateLibs();
Expand All @@ -63,16 +75,14 @@ contract Build is Script, RegistryDeploySuites {
/// `src/generated/<tag>/`, then rewrite the libs from the record, so the
/// release being cut is in them.
function cutRelease() external {
GeneratedContract[] memory contracts = generatedContracts();
string[] memory contractNames = new string[](contracts.length);
for (uint256 i = 0; i < contracts.length; i++) {
contractNames[i] = contracts[i].contractName;
}
LibRainDeploySnapshot.freeze(vm, LibRainDeploySnapshot.LIB_FS_ROOT, regenerateCandidates, contractNames);
LibRainDeploySnapshot.freeze(
vm, LibRainDeploySnapshot.LIB_FS_ROOT, regenerateCandidates, generatedContractNames()
);
regenerateLibs();
}

/// @notice Rewrite every alias lib and every released-suites lib.
/// @notice Rewrite every alias lib, every released-suites lib and the
/// aggregate over them.
function regenerateLibs() internal {
GeneratedContract[] memory contracts = generatedContracts();
for (uint256 i = 0; i < contracts.length; i++) {
Expand All @@ -83,6 +93,7 @@ contract Build is Script, RegistryDeploySuites {
vm, LibRainDeploySnapshot.LIB_FS_ROOT, contracts[i].contractName, contracts[i].candidate.snapshot
);
}
LibRainDeploySnapshot.writeReleasedSuitesAggregate(vm, LibRainDeploySnapshot.LIB_DIR, generatedContractNames());
}

/// @notice Rewrite every `src/generated/candidate/` snapshot from what this
Expand Down
36 changes: 17 additions & 19 deletions src/abstract/RegistryDeploySuites.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ import {
RUNTIME_CODE as MIGRATION_REGISTRY_RUNTIME_CODE_CANDIDATE
} from "../generated/candidate/MigrationRegistry.sol";
import {LibAddressRegistryDeploy} from "../lib/LibAddressRegistryDeploy.sol";
import {LibAddressRegistryReleased} from "../lib/LibAddressRegistryReleased.sol";
import {LibMigrationRegistryDeploy} from "../lib/LibMigrationRegistryDeploy.sol";
import {LibMigrationRegistryReleased} from "../lib/LibMigrationRegistryReleased.sol";
import {LibReleasedSuites} from "../lib/LibReleasedSuites.sol";

/// @title RegistryDeploySuites
/// @notice Everything this repo deploys, declared ONCE.
Expand Down Expand Up @@ -48,8 +47,9 @@ import {LibMigrationRegistryReleased} from "../lib/LibMigrationRegistryReleased.
/// entry and nothing per network at all.
///
/// Half of it is written by hand and half is generated: the candidates below
/// are the declaration, and the released libs come from `script/Build.sol`,
/// which emits them from the frozen record.
/// are the declaration, and the whole released side — the per-contract libs and
/// the aggregate that concatenates them — comes from `script/Build.sol`, which
/// emits it from the frozen record. Nothing about a release is written here.
abstract contract RegistryDeploySuites is RainDeploySuitesBase {
/// @inheritdoc RainDeploySuitesBase
/// @dev Generated by `script/Build.sol` from the frozen
Expand All @@ -59,31 +59,29 @@ abstract contract RegistryDeploySuites is RainDeploySuitesBase {
/// every check quietly stops asking about, while the whole suite stays
/// green.
///
/// One generated lib per deployed contract, concatenated here, because
/// There is one generated lib per deployed contract, because
/// `writeReleasedSuitesLib` emits the releases of ONE contract: a release
/// freezes every contract it names into a single tag directory, so a lib
/// that took the record whole would give another contract's snapshot this
/// contract's suite key and collide with its own entry for that tag.
/// `LibReleasedSuites` is the concatenation of them, generated from the
/// same list that wrote them — so this is one call rather than a summation
/// per contract, and adding a contract does not touch this function at all.
///
/// Both are empty until the first release is cut. The rolling `candidate/`
/// snapshots are not releases and do exist.
function releasedSuites() internal pure override returns (DeploySuite[] memory suites) {
DeploySuite[] memory addressRegistry = LibAddressRegistryReleased.releasedSuites();
DeploySuite[] memory migrationRegistry = LibMigrationRegistryReleased.releasedSuites();

suites = new DeploySuite[](addressRegistry.length + migrationRegistry.length);
for (uint256 i = 0; i < addressRegistry.length; i++) {
suites[i] = addressRegistry[i];
}
for (uint256 i = 0; i < migrationRegistry.length; i++) {
suites[addressRegistry.length + i] = migrationRegistry[i];
}
/// Empty until the first release is cut. The rolling `candidate/` snapshots
/// are not releases and do exist.
function releasedSuites() internal pure override returns (DeploySuite[] memory) {
return LibReleasedSuites.releasedSuites();
}

/// @inheritdoc RainDeploySuitesBase
/// @dev One entry per contract this repo deploys. A third deployed contract
/// is a third named candidate below, a third entry here, and a third entry
/// in `script/Build.sol`'s generated-contract list — nothing else.
/// in `script/Build.sol`'s generated-contract list — nothing else. The
/// released declaration follows from that third entry rather than being a
/// fourth place to edit: `script/Build.sol` generates the per-contract
/// released lib AND the aggregate `releasedSuites()` reads from that one
/// list.
function candidateSuites() internal pure override returns (DeployCandidate[] memory candidates) {
candidates = new DeployCandidate[](2);
candidates[0] = addressRegistryCandidate();
Expand Down
Loading
Loading