Skip to content

Delete legacy forensic audit logs on app startup #3571

Delete legacy forensic audit logs on app startup

Delete legacy forensic audit logs on app startup #3571

Workflow file for this run

name: CI
on:
pull_request:
push:
branches:
- master
merge_group:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
CARGO_TERM_COLOR: always
CARGO_INCREMENTAL: "0"
CARGO_PROFILE_TEST_DEBUG: "0"
jobs:
changes:
name: Classify CI changes
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
run_full: ${{ steps.classify.outputs.run_full }}
run_c: ${{ steps.classify.outputs.run_c }}
run_conformance: ${{ steps.classify.outputs.run_conformance }}
run_ios: ${{ steps.classify.outputs.run_ios }}
run_formal: ${{ steps.classify.outputs.run_formal }}
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
fetch-depth: 0
persist-credentials: false
- name: Classify changed paths
id: classify
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha || github.event.before }}
EVENT_NAME: ${{ github.event_name }}
run: |
set -euo pipefail
if [[ "$EVENT_NAME" == "workflow_dispatch" \
|| -z "$BASE_SHA" \
|| "$BASE_SHA" =~ ^0+$ ]] \
|| ! git cat-file -e "${BASE_SHA}^{commit}"; then
python3 scripts/classify_ci_changes.py --all --github-output "$GITHUB_OUTPUT" </dev/null
else
# Disable rename detection so a code-to-Markdown rename exposes
# both the removed code path and the added documentation path.
git diff --no-renames --name-only -z "$BASE_SHA" "$GITHUB_SHA" \
| python3 scripts/classify_ci_changes.py --github-output "$GITHUB_OUTPUT"
fi
rust-format:
name: Rust format
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal --component rustfmt
rustup default "$toolchain"
rustc --version
cargo --version
- name: Check formatting
run: cargo fmt --all --check
- name: Naming gate (retired legacy tokens)
run: ./scripts/check_legacy_naming.sh
- name: C binding command-surface parity gate
run: python3 scripts/check_c_binding_parity.py
- name: Install example SHA-256 gate
run: |
python3 scripts/check_install_example_sha256.py
python3 scripts/test_install_example_sha256_gate.py
- name: Campaign and QUIC broker image toolchain gate
run: |
./scripts/check_campaign_toolchain.sh
./scripts/tests/test_campaign_toolchain.sh
- name: Agent install documentation gate
run: ./scripts/check_agent_install_docs.sh
- name: CI policy and path-classifier gates
run: |
./scripts/check_convergence_constant_ledger.sh
python3 scripts/test_check_cargo_audit_ci.py
python3 scripts/test_classify_ci_changes.py
rust-audit:
name: Rust audit
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal
rustup default "$toolchain"
rustc --version
cargo --version
- name: Install cargo-audit
uses: taiki-e/install-action@b18fb392e1a1b90971f7c7572c92790fa54f23d5
with:
tool: cargo-audit@0.22.1
- name: Audit Rust dependencies
# Cargo consumes --locked before dispatching to cargo-audit; cargo-audit
# itself does not accept --locked after the subcommand.
run: cargo --locked audit
terminal-harness-installers:
name: Terminal harness installer tests
needs: changes
if: needs.changes.outputs.run_full == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Test Codex, Pi, and OpenCode installers
run: |
set -euo pipefail
integrations/terminal-harness/tests/test_installer.sh codex
integrations/terminal-harness/tests/test_installer.sh pi
integrations/terminal-harness/tests/test_installer.sh opencode
rust-check:
name: Rust check
needs: changes
if: needs.changes.outputs.run_full == 'true'
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal
rustup default "$toolchain"
rustc --version
cargo --version
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Check workspace
run: RUSTFLAGS='-D warnings' cargo check --workspace --all-targets --locked
- name: Check all features
run: RUSTFLAGS='-D warnings' cargo check --workspace --all-targets --all-features --locked
rust-wasm-check:
name: Rust WASM check
needs: changes
if: needs.changes.outputs.run_full == 'true'
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal
rustup default "$toolchain"
rustup target add wasm32-unknown-unknown
rustc --version
cargo --version
clang --version
# Preflight the C toolchain before compiling ring for this target.
clang --print-targets | grep wasm32
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
# Compile-only regression gate. Browser runtime behavior belongs in
# downstream headless-browser acceptance tests.
- name: Build browser WASM libraries
env:
CC_wasm32_unknown_unknown: clang
RUSTFLAGS: -D warnings
run: |
cargo build --locked --target wasm32-unknown-unknown \
-p cgka-traits \
-p cgka-engine \
-p transport-nostr-peeler
rust-clippy:
name: Rust clippy
needs: changes
if: needs.changes.outputs.run_full == 'true'
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal --component clippy
rustup default "$toolchain"
rustc --version
cargo --version
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Run clippy
run: cargo clippy --workspace --all-targets --locked -- -D warnings
- name: Run clippy with all features
run: cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
c-smoke:
name: C bindings smoke
needs: changes
if: needs.changes.outputs.run_c == 'true'
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal
rustup default "$toolchain"
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Install cbindgen
uses: taiki-e/install-action@b18fb392e1a1b90971f7c7572c92790fa54f23d5
with:
tool: cbindgen@0.29.4
- name: Header diff gate
run: |
set -euo pipefail
# Pin the exact version so a newer cbindgen can't reformat the
# checked-in header and fail the diff gate on unrelated PRs.
# RUSTC_BOOTSTRAP=1 lets cbindgen expand the mirror-generating
# macros on stable; it only affects header generation.
RUSTC_BOOTSTRAP=1 cbindgen --config crates/marmot-c/cbindgen.toml --crate marmot-c \
--output crates/marmot-c/include/marmot.h crates/marmot-c
git diff --exit-code crates/marmot-c/include/marmot.h
- name: Alloc-audit tests
run: cargo test -p marmot-c --features alloc-audit --locked
- name: Compile and run debug C smoke test (gcc)
run: ./crates/marmot-c/c-smoke.sh --debug gcc
ios-account-clippy:
name: iOS account keychain clippy
needs: changes
if: needs.changes.outputs.run_ios == 'true'
runs-on: macos-latest
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
shell: bash
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal --component clippy
rustup default "$toolchain"
rustup target add aarch64-apple-ios
rustc --version
cargo --version
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Check iOS account keychain code
run: cargo clippy -p marmot-account --target aarch64-apple-ios --locked -- -D warnings
# The broader integration matrix explicitly enables test-policy-overrides.
# This focused default-feature job proves ordinary debug/release consumers
# reject every non-v1 convergence policy (mdk#970).
convergence-policy-pin:
name: Convergence policy pin (release assertions)
needs: changes
if: needs.changes.outputs.run_full == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal
rustup default "$toolchain"
rustc --version
cargo --version
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Run default-build convergence policy pin tests
# Keep in sync with `just test-convergence-policy-pin`.
run: cargo test -p cgka-engine --test convergence_policy_pin --locked
rust-test:
name: Rust tests (${{ matrix.partition }}/4)
needs: changes
if: needs.changes.outputs.run_full == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
partition: [1, 2, 3, 4]
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal
rustup default "$toolchain"
rustc --version
cargo --version
- name: Install cargo-nextest
uses: taiki-e/install-action@b18fb392e1a1b90971f7c7572c92790fa54f23d5 # nextest
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Run workspace tests
# The subprocess-heavy `wn-cli::cli` suite runs in its own
# `cli-e2e` job so its fan-out never starves these fast unit tests
# (issue #103). `relay_runtime` keeps its own serial job.
run: cargo nextest run --workspace --exclude cgka-conformance-simulator --features wn-cli/test-policy-overrides,cgka-engine/test-crash-hooks --locked --profile ci --partition count:${{ matrix.partition }}/4 -E 'not (package(marmot-app) & binary(relay_runtime)) and not (package(wn-cli) & binary(cli))'
- name: Run doctests
if: matrix.partition == 1
run: cargo test --workspace --exclude cgka-conformance-simulator --doc --locked
- name: Test consent and both diagnostic exporters
if: matrix.partition == 1
run: |
cargo test -p marmot-app --features otlp-export,product-analytics-export --lib product_analytics:: --locked
cargo test -p marmot-app --features otlp-export,product-analytics-export --lib backend_maintenance_collects --locked
cargo test -p marmot-app --features otlp-export,product-analytics-export --test relay_telemetry_otlp --locked
cargo test -p marmot-c --features otlp-export,product-analytics-export,alloc-audit product_analytics --locked
cargo test -p agent-connector --features otlp-export,product-analytics-export usage_diagnostics:: --locked
cli-e2e:
name: CLI e2e (mock relay)
needs: changes
if: needs.changes.outputs.run_full == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal
rustup default "$toolchain"
rustc --version
cargo --version
- name: Install cargo-nextest
uses: taiki-e/install-action@b18fb392e1a1b90971f7c7572c92790fa54f23d5 # nextest
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Run CLI end-to-end tests
# Dedicated job: this subprocess-heavy suite is capped by the `cli-e2e`
# test-group and the `ci` profile adds retries + a hung-test backstop.
# The real-relay test self-skips here; it has its own `Relay CLI E2E`
# job with a docker relay stack.
run: cargo nextest run -p wn-cli --test cli --features test-policy-overrides --locked --profile ci
relay-runtime:
name: Relay runtime tests
needs: changes
if: needs.changes.outputs.run_full == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal
rustup default "$toolchain"
rustc --version
cargo --version
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Run relay runtime tests serially
env:
RUST_BACKTRACE: "1"
run: cargo test -p marmot-app --test relay_runtime --features test-policy-overrides --locked -- --test-threads=1
- name: Run held peer-index upgrade lookup
run: cargo test -p marmot-app --test startup_hydration --features test-policy-overrides --locked -- --exact existing_direct_conversation_is_not_a_miss_while_upgrade_backfill_is_held
relay-e2e:
name: Relay CLI E2E
needs: changes
if: needs.changes.outputs.run_full == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal
rustup default "$toolchain"
rustc --version
cargo --version
- name: Install cargo-nextest
uses: taiki-e/install-action@b18fb392e1a1b90971f7c7572c92790fa54f23d5 # nextest
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Start local relay stack
run: docker compose up -d setup nostr-rs-relay strfry-nostr-relay
- name: Wait for local relays
run: |
docker compose ps
./scripts/wait_for_relays.sh
- name: Run real relay CLI E2E
env:
MDK_E2E_REQUIRE_RELAYS: "1"
run: cargo nextest run -p wn-cli --test cli --features test-policy-overrides --locked --profile ci -E 'test(=real_local_relays_deliver_cli_messages_over_sdk_path)'
- name: Dump local relay logs
if: failure()
run: docker compose logs nostr-rs-relay strfry-nostr-relay || true
- name: Stop local relay stack
if: always()
run: docker compose down -v
conformance:
name: Simulator and vectors
needs: changes
if: needs.changes.outputs.run_conformance == 'true'
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Rust toolchain
run: |
set -euo pipefail
toolchain="$(sed -n 's/^channel = "\(.*\)"/\1/p' rust-toolchain.toml)"
rustup toolchain install "$toolchain" --profile minimal
rustup default "$toolchain"
rustc --version
cargo --version
- name: Install cargo-nextest
uses: taiki-e/install-action@b18fb392e1a1b90971f7c7572c92790fa54f23d5 # nextest
- name: Install just
uses: taiki-e/install-action@b18fb392e1a1b90971f7c7572c92790fa54f23d5 # just
with:
tool: just
- name: Cache Cargo artifacts
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
- name: Run simulator smoke tests
# Multi-minute generated reliability batches and the adversarial
# campaign run in the scheduled Simulator Nightly workflow. Independent
# convergence checks run in their dedicated fast gate below.
run: just simulator-smoke
- name: Validate convergence execution-lane policy
run: just convergence-lane-policy
- name: Validate convergence failure-corpus lifecycle
run: just convergence-failure-corpus
- name: Run focused convergence regressions
run: just focused-convergence-regressions
- name: Run simulator doctests
run: cargo test -p cgka-conformance-simulator --doc --locked
- name: Verify convergence with independent models
run: just convergence-verification-ci
formal:
name: Tamarin proofs
needs: changes
if: needs.changes.outputs.run_formal == 'true'
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install Tamarin dependencies
timeout-minutes: 15
run: |
set -euo pipefail
for attempt in 1 2 3; do
if sudo timeout --kill-after=15s 180s apt-get \
-o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 \
-o Acquire::https::Timeout=30 \
update; then
break
fi
if [[ "$attempt" == 3 ]]; then
echo "apt-get update failed after $attempt attempts" >&2
exit 1
fi
echo "apt-get update attempt $attempt failed; retrying" >&2
done
sudo apt-get \
-o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 \
-o Acquire::https::Timeout=30 \
-o DPkg::Lock::Timeout=60 \
install -y --no-install-recommends graphviz maude
- name: Install Tamarin
env:
TAMARIN_VERSION: 1.12.0
run: |
set -euo pipefail
curl -L --fail --show-error --silent \
"https://github.com/tamarin-prover/tamarin-prover/releases/download/${TAMARIN_VERSION}/tamarin-prover-${TAMARIN_VERSION}-linux64-ubuntu.tar.gz" \
-o /tmp/tamarin-prover.tar.gz
tar -xzf /tmp/tamarin-prover.tar.gz -C /tmp
sudo install -m 0755 /tmp/tamarin-prover /usr/local/bin/tamarin-prover
tamarin-prover --version
- name: Run Tamarin proofs
run: make -C formal/tamarin prove
hermes-installer-auth:
name: Hermes installer auth
needs: changes
if: needs.changes.outputs.run_full == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Check out repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Install system dependencies
timeout-minutes: 15
run: |
set -euo pipefail
venv_check_dir="${RUNNER_TEMP}/hermes-venv-check"
if command -v python3 >/dev/null \
&& command -v git >/dev/null \
&& python3 -m venv "$venv_check_dir"; then
rm -rf "$venv_check_dir"
exit 0
fi
rm -rf "$venv_check_dir"
for attempt in 1 2 3; do
if sudo timeout --kill-after=15s 180s apt-get \
-o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 \
-o Acquire::https::Timeout=30 \
update; then
break
fi
if [[ "$attempt" == 3 ]]; then
echo "apt-get update failed after $attempt attempts" >&2
exit 1
fi
echo "apt-get update attempt $attempt failed; retrying" >&2
done
sudo apt-get \
-o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 \
-o Acquire::https::Timeout=30 \
-o DPkg::Lock::Timeout=60 \
install -y --no-install-recommends python3 python3-venv git
- name: Install uv
uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
- name: Hermes configure_gateway Python unit tests
run: |
set -euo pipefail
env -i HOME="$HOME" PATH="$PATH" USER="${USER:-runner}" \
python3 -m unittest discover -s integrations/hermes/tests/marmot -v
- name: Prepare isolated Hermes environment for authorization E2E
run: |
set -euo pipefail
./scripts/hermes_marmot_dev_setup.sh \
--root "$RUNNER_TEMP/hermes-installer-auth" \
--install-pinned-wheel
- name: Hermes installer unit and mock regressions
run: |
set -euo pipefail
bash -n scripts/install-hermes-marmot.sh
bash -n integrations/hermes/tests/marmot/test_installer_env.sh
bash -n scripts/hermes_marmot_deterministic_e2e.sh
python3 -m py_compile scripts/hermes_marmot_configure_gateway.py integrations/hermes/tests/marmot/e2e_gateway_auth.py
integrations/hermes/tests/marmot/test_dev_scripts.sh
- name: Real Hermes authorization and deterministic Marmot E2E
run: |
set -euo pipefail
# The deterministic runner sources the pinned dev root, runs
# test_installer_env.sh against its real GatewayRunner, then runs the
# adapter E2E. A missing Hermes venv is a hard failure, not a skip.
./scripts/hermes_marmot_deterministic_e2e.sh --root "$RUNNER_TEMP/hermes-installer-auth"
required-ci:
name: Required CI
if: always()
needs:
- changes
- rust-format
- rust-audit
- terminal-harness-installers
- rust-check
- rust-wasm-check
- rust-clippy
- c-smoke
- ios-account-clippy
- convergence-policy-pin
- rust-test
- cli-e2e
- relay-runtime
- relay-e2e
- conformance
- formal
- hermes-installer-auth
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Require every selected core CI job to succeed
env:
NEEDS: ${{ toJSON(needs) }}
run: |
jq --exit-status 'all(.[]; .result == "success" or .result == "skipped")' <<<"$NEEDS"