Antithesis #9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "Antithesis" | |
| env: | |
| TESTNET: testnets/cardano_node_governance | |
| DURATION: 2 | |
| on: | |
| workflow_dispatch: | |
| schedule: | |
| # run once a week | |
| - cron: '5 1 * * 1' | |
| jobs: | |
| run-cardano-node-governance: | |
| runs-on: ubuntu-latest | |
| env: | |
| MOOG_GITHUB_PAT: ${{ secrets.MOOG_GITHUB_PAT }} | |
| MOOG_WALLET_FILE: wallet.json | |
| MOOG_WALLET_PASSPHRASE: ${{ secrets.MOOG_WALLET_PASSPHRASE }} | |
| # Same secret reused: the wallet mnemonics and the SSH key share one | |
| # passphrase in this setup. | |
| MOOG_SSH_PASSWORD: ${{ secrets.MOOG_WALLET_PASSPHRASE }} | |
| MOOG_MPFS_HOST: ${{ vars.MOOG_MPFS_HOST }} | |
| MOOG_TOKEN_ID: ${{ vars.MOOG_TOKEN_ID }} | |
| MOOG_PLATFORM: github | |
| # saratomaz, not cfhal - cfhal is only role-registered against | |
| # cardano-foundation/* repos; saratomaz holds the register-role | |
| # for input-output-hk/cardano-node-tests-antithesis (and matches | |
| # the wallet/SSH key configured via MOOG_REQUESTER_WALLET here). | |
| MOOG_REQUESTER: saratomaz | |
| timeout-minutes: 200 | |
| # contents:write to push the branch pinning the freshly fetched | |
| # cardano-node image (the image push itself uses GHCR_PAT, | |
| # not this token). | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Install Nix | |
| uses: cachix/install-nix-action@v31 | |
| with: | |
| extra_nix_config: | | |
| extra-trusted-public-keys = hydra.iohk.io:f/Ea+s+dFdN+3Y/G+FDgSq+a5NEWhJGzdjvKNGv0/EQ= | |
| extra-substituters = https://cache.iog.io/ | |
| # Pushes to ghcr.io/saratomaz/... (same namespace as gov-cli / | |
| # gov-configurator), matching the account registered with moog. That | |
| # namespace isn't owned by this repo, so the auto-generated | |
| # GITHUB_TOKEN (scoped to this org repo) can't write to it - needs a | |
| # personal PAT with write:packages instead (GHCR_PAT, separate from | |
| # MOOG_GITHUB_PAT which moog itself uses). | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: saratomaz | |
| password: ${{ secrets.GHCR_PAT }} | |
| # cardano-node isn't built here: Hydra (IntersectMBO's own CI) builds | |
| # every master commit and caches it at cache.iog.io. `--max-jobs 0` | |
| # forbids a local build, so this only succeeds if Hydra has already | |
| # cached the current tip - which is normally the case within minutes | |
| # of a merge, but can occasionally lag on a very fresh commit. On | |
| # failure we fall back to whatever digest is already pinned in | |
| # docker-compose.yaml instead of failing the run. | |
| - name: Fetch cardano-node master from Hydra cache | |
| id: node-image | |
| continue-on-error: true | |
| run: | | |
| set -euo pipefail | |
| LOCKED_URL=$(nix flake metadata github:IntersectMBO/cardano-node/master --json | jq -r .url) | |
| echo "building: $LOCKED_URL" | |
| nix build --print-out-paths --builders "" --max-jobs 0 \ | |
| --out-link ./cardano-node-image \ | |
| "${LOCKED_URL}#packages.x86_64-linux.dockerImage/node" | |
| nix profile install nixpkgs#skopeo | |
| # The runner's /etc/containers/registries.conf is legacy v1 format, | |
| # which this skopeo (via nixpkgs) refuses to read ("registries.conf | |
| # must be in v2 format but is in v1"). Point it at a minimal v2 | |
| # config instead of touching the system file. | |
| REGISTRIES_CONF="$(mktemp)" | |
| echo 'unqualified-search-registries = ["docker.io"]' > "$REGISTRIES_CONF" | |
| export CONTAINERS_REGISTRIES_CONF="$REGISTRIES_CONF" | |
| DEST="ghcr.io/saratomaz/cardano-node-tests-antithesis/cardano-node-master:${GITHUB_RUN_ID}" | |
| skopeo copy "docker-archive:./cardano-node-image" "docker://${DEST}" | |
| DIGEST=$(skopeo inspect --no-tags "docker://${DEST}" | jq -r .Digest) | |
| echo "image=ghcr.io/saratomaz/cardano-node-tests-antithesis/cardano-node-master@${DIGEST}" >> "$GITHUB_OUTPUT" | |
| - name: Pin fetched image into docker-compose.yaml | |
| id: pin | |
| if: steps.node-image.outcome == 'success' | |
| env: | |
| NEW_IMAGE: ${{ steps.node-image.outputs.image }} | |
| run: | | |
| set -euo pipefail | |
| sed -i "s|ghcr.io/intersectmbo/cardano-node@sha256:[0-9a-f]*|${NEW_IMAGE}|g" \ | |
| "$TESTNET/docker-compose.yaml" | |
| if git diff --quiet; then | |
| echo "cardano-node master image unchanged, nothing to pin" | |
| exit 0 | |
| fi | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| BRANCH="antithesis/governance-master-${GITHUB_RUN_ID}" | |
| git checkout -b "$BRANCH" | |
| git add "$TESTNET/docker-compose.yaml" | |
| git commit -m "Pin cardano-node master (Hydra build, run ${GITHUB_RUN_ID}) for Antithesis" | |
| git push origin "$BRANCH" | |
| echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - name: Install moog | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| # Pinned, NOT latest: v2.0.0+ added a client-side precondition that | |
| # probes GET /status on the MPFS host as part of an in-progress | |
| # "mpfs-v2" migration (moog#96, moog#101 - still open). Production | |
| # mpfs.plutimus.com still only serves the legacy API, so /status | |
| # 404s and every moog command fails immediately. v0.5.1.5 is the | |
| # last release before that migration work and works against the | |
| # current production server. Bump this once moog#101 lands and a | |
| # new release is confirmed compatible with mpfs.plutimus.com. | |
| # | |
| # The portable Linux requester binary is the statically-linked musl | |
| # tarball (the old `*-linux64.tar.gz` asset was dropped at the 0.5.x | |
| # packaging change). | |
| TAG=v0.5.1.5 | |
| echo "moog release: $TAG" | |
| gh release download "$TAG" -R cardano-foundation/moog \ | |
| -p 'moog-*-x86_64-linux-musl.tar.gz' --clobber | |
| TARBALL=$(ls moog-*-x86_64-linux-musl.tar.gz | grep -vE 'moog-(agent|oracle)-' | head -1) | |
| echo "using $TARBALL" | |
| tar xzf "$TARBALL" | |
| sudo install -m 0755 moog /usr/local/bin/moog | |
| moog --version | |
| - name: Configure moog wallet | |
| run: printf '%s' "${{ secrets.MOOG_REQUESTER_WALLET }}" | base64 --decode > "$MOOG_WALLET_FILE" | |
| - name: Submit test | |
| id: request | |
| run: | | |
| set -euo pipefail | |
| COMMIT="${{ steps.pin.outputs.sha || github.sha }}" | |
| # Captured (instead of piped straight into jq) so the raw response | |
| # is visible in the log if it's ever shaped unexpectedly. | |
| FACTS=$(moog facts test-runs --whose "$MOOG_REQUESTER") | |
| echo "$FACTS" | |
| TRY=$(echo "$FACTS" | jq \ | |
| --arg commit "$COMMIT" \ | |
| --arg directory "$TESTNET" \ | |
| --arg platform "$MOOG_PLATFORM" \ | |
| --arg repository "$GITHUB_REPOSITORY" \ | |
| --arg requester "$MOOG_REQUESTER" \ | |
| '(if type == "array" then . else [.] end) | |
| | map(select( | |
| type == "object" | |
| and .key.type == "test-run" | |
| and .key.commitId == $commit | |
| and .key.directory == $directory | |
| and .key.platform == $platform | |
| and ((.key.repository.organization + "/" + .key.repository.repo) == $repository) | |
| and .key.requester == $requester | |
| )) | length') | |
| TRY=$((TRY + 1)) # start with try=1 | |
| echo "TRY=$TRY for $TESTNET" | |
| RESULT=$(moog requester create-test -d "$TESTNET" \ | |
| -c "$COMMIT" \ | |
| -r "$GITHUB_REPOSITORY" \ | |
| --try "$TRY" \ | |
| -t "$DURATION") | |
| echo "$RESULT" | |
| # -e fails the step if the field is missing/null; -r unwraps the | |
| # string quotes. | |
| ID=$(echo "$RESULT" | jq -er .value.testRunId) | |
| TXID=$(echo "$RESULT" | jq -er .txHash) | |
| echo "id=$ID" >> "$GITHUB_OUTPUT" | |
| echo "txHash=$TXID" >> "$GITHUB_OUTPUT" | |
| - name: Wait for results | |
| run: timeout $(((DURATION + 1) * 3600)) ./scripts/wait-for-test.sh "${{ steps.request.outputs.id }}" | |
| # wait-for-test.sh only reports pass/fail, not the report link. The | |
| # report url on the test-run fact is encrypted; decrypting it needs the | |
| # wallet/passphrase already in this job's env (same as the other moog | |
| # calls above). | |
| - name: Report Antithesis result link | |
| if: always() | |
| run: | | |
| set -euo pipefail | |
| RESULT=$(moog facts test-runs --test-run-id "${{ steps.request.outputs.id }}") | |
| OUTCOME=$(echo "$RESULT" | jq -r '.[0].value.outcome // "unknown"') | |
| URL=$(echo "$RESULT" | jq -r '.[0].value.url // empty') | |
| echo "outcome: $OUTCOME" | |
| { | |
| echo "### Antithesis test run" | |
| echo "- outcome: **$OUTCOME**" | |
| if [ -n "$URL" ]; then | |
| echo "- report: $URL" | |
| fi | |
| } >> "$GITHUB_STEP_SUMMARY" |