diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2b4553f..a965ce3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,8 +23,6 @@ jobs: task: rainix-rs-static fail-fast: false runs-on: ${{ matrix.os }} - env: - DEPLOYMENT_KEY: ${{ secrets.PRIVATE_KEY }} defaults: run: working-directory: test/fixture @@ -65,8 +63,8 @@ jobs: restore-keys: | foundry-full-${{ runner.os }}- - run: nix develop ../.. --command forge soldeer install + # rainix-sol-artifacts runs hermetically here: with no ETH_RPC_URL the + # task deploys against its own ephemeral anvil, so this job needs no RPC + # or key secrets. - name: Run ${{ matrix.task }} - env: - ETH_RPC_URL: ${{ secrets.CI_DEPLOY_SEPOLIA_RPC_URL || vars.CI_DEPLOY_SEPOLIA_RPC_URL }} - ETHERSCAN_API_KEY: ${{ secrets.EXPLORER_VERIFICATION_KEY }} run: nix develop ../.. --command ${{ matrix.task }} diff --git a/flake.nix b/flake.nix index 5486390..348dd64 100644 --- a/flake.nix +++ b/flake.nix @@ -237,6 +237,39 @@ # Upload all function selectors to the registry. forge selectors up --all + # With no ETH_RPC_URL the task is hermetic: it runs against its own + # ephemeral anvil instance with anvil's first funded dev account as + # the deployment key, so it needs no secrets and no external RPC. + # An explicit ETH_RPC_URL (a real deploy) always wins and leaves + # DEPLOYMENT_KEY untouched. Port 18545 avoids clobbering a dev's + # own anvil on the default 8545. + if [[ -z "''${ETH_RPC_URL:-}" ]]; then + anvil --port 18545 --silent & + anvil_pid=$! + trap 'kill "''${anvil_pid}" 2>/dev/null' EXIT + export ETH_RPC_URL='http://127.0.0.1:18545' + export DEPLOYMENT_KEY='0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' + # Bounded readiness wait: an anvil that died or never bound the + # port must fail here with a clear message, not hang the task + # until the surrounding job's timeout. + anvil_ready="" + for _ in $(seq 1 100); do + if ! kill -0 "''${anvil_pid}" 2>/dev/null; then + echo 'rainix-sol-artifacts: anvil exited during startup' >&2 + exit 1 + fi + if cast chain-id --rpc-url "''${ETH_RPC_URL}" >/dev/null 2>&1; then + anvil_ready=1 + break + fi + sleep 0.2 + done + if [[ -z "''${anvil_ready}" ]]; then + echo "rainix-sol-artifacts: anvil not ready on ''${ETH_RPC_URL} after 20s" >&2 + exit 1 + fi + fi + # Deploy all contracts to testnet. # Assumes the existence of a `Deploy.sol` script in the `script` directory. # Echos the deploy pubkey to stdout to make it easy to add gas to the account. diff --git a/test/bats/task/skip-simulation.test.bats b/test/bats/task/skip-simulation.test.bats index d135ca7..dcdb0e1 100644 --- a/test/bats/task/skip-simulation.test.bats +++ b/test/bats/task/skip-simulation.test.bats @@ -13,12 +13,14 @@ teardown() { } forge_deploy() { - forge script script/Deploy.sol:Deploy \ + # The fixture Deploy.sol reads DEPLOYMENT_KEY itself (consumer convention); + # anvil's first funded dev account. + DEPLOYMENT_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ + forge script script/Deploy.sol:Deploy \ -vvvvv \ --broadcast \ ${DEPLOY_SKIP_SIMULATION:+--skip-simulation} \ --rpc-url http://127.0.0.1:8545 \ - --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ 2>&1 } diff --git a/test/fixture/script/Deploy.sol b/test/fixture/script/Deploy.sol index de64852..3bb4374 100644 --- a/test/fixture/script/Deploy.sol +++ b/test/fixture/script/Deploy.sol @@ -8,8 +8,14 @@ import {Counter} from "../src/Counter.sol"; contract Deploy is Script { function setUp() public {} + /// Reads the deployer key from `DEPLOYMENT_KEY`, the same convention as + /// the consumer `Deploy.sol` scripts `rainix-sol-artifacts` runs, so the + /// fixture exercises the task exactly as consumers do (broadcast included + /// — a bare `vm.broadcast()` would hit foundry's default-sender refusal). function run() public { - vm.broadcast(); + uint256 deployerPrivateKey = vm.envUint("DEPLOYMENT_KEY"); + vm.startBroadcast(deployerPrivateKey); new Counter(); + vm.stopBroadcast(); } }