name: ci
on:
push:
branches: [main]
pull_request:
schedule:
- cron: "0 6 * * 1"
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 - id: attest
uses: ./
with:
anywhere: ci-fmt ci-shellcheck
- id: fmt
if: ${{ !contains(fromJSON(steps.attest.outputs.gates), 'ci-fmt') }}
run: cargo fmt --all -- --check
- id: clippy
if: ${{ !contains(fromJSON(steps.attest.outputs.gates), 'ci-clippy') }}
run: cargo clippy --all-targets -- -D warnings
- id: shellcheck
if: ${{ !contains(fromJSON(steps.attest.outputs.gates), 'ci-shellcheck') }}
run: shellcheck verify.sh sign/sign.sh tests/*.sh
- uses: ./sign
with:
key: ${{ secrets.ATTEST_SIGNING_KEY }}
gates: >-
${{ steps.fmt.outcome == 'success' && 'ci-fmt' || '' }}
${{ steps.clippy.outcome == 'success' && 'ci-clippy' || '' }}
${{ steps.shellcheck.outcome == 'success' && 'ci-shellcheck' || '' }}
conformance:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
timeout-minutes: 15
permissions:
contents: write
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 - id: attest
uses: ./
- id: test
if: ${{ !contains(fromJSON(steps.attest.outputs.gates), 'pre-push-cargo-test') }}
run: cargo test
shell: bash
- id: conformance
if: ${{ !contains(fromJSON(steps.attest.outputs.gates), 'ci-conformance') }}
run: make conformance sign-test
shell: bash
- uses: ./sign
with:
key: ${{ secrets.ATTEST_SIGNING_KEY }}
gates: >-
${{ steps.test.outcome == 'success' && 'pre-push-cargo-test' || '' }}
${{ steps.conformance.outcome == 'success' && 'ci-conformance' || '' }}
compat:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 - run: make compat
action:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 - name: isolate from the real attestations
run: |
set -eu
git remote remove origin
git update-ref -d refs/notes/amont-attest || true
# `git notes add` writes an object and needs a committer identity;
# a bare runner has none.
git config user.email ci@example.org
git config user.name CI
- id: bare
uses: ./
- name: nothing covered, and the step succeeded
run: |
set -eu
[ "${{ steps.bare.outputs.gates }}" = "[]" ] || {
echo "expected [], got: ${{ steps.bare.outputs.gates }}"; exit 1; }
[ -z "${{ steps.bare.outputs.covered }}" ] || {
echo "expected empty covered"; exit 1; }
- name: sign this exact tree by hand
run: |
set -eu
ssh-keygen -q -t ed25519 -N '' -C ci@example.org -f "$RUNNER_TEMP/k"
printf '%s namespaces="amont-attest" %s\n' ci@example.org "$(cat "$RUNNER_TEMP/k.pub")" \
> "$RUNNER_TEMP/ci-signers"
tree=$(git rev-parse 'HEAD^{tree}')
printf 'amont-attest-v2\ntree %s\ngates pre-push-cargo-test pre-push-clippy\nplatform x86_64-linux\namont ci\n' \
"$tree" > "$RUNNER_TEMP/p"
ssh-keygen -Y sign -n amont-attest -f "$RUNNER_TEMP/k" "$RUNNER_TEMP/p"
git notes --ref amont-attest add -f \
-m "$(printf '%s\n\n%s' "$(cat "$RUNNER_TEMP/p")" "$(cat "$RUNNER_TEMP/p.sig")")" "$tree"
- id: signed
uses: ./
with:
signers: ${{ runner.temp }}/ci-signers
- name: the attestation is reported, exactly
run: |
set -eu
got='${{ steps.signed.outputs.gates }}'
want='["pre-push-cargo-test","pre-push-clippy"]'
[ "$got" = "$want" ] || { echo "want $want, got $got"; exit 1; }
- name: a covered gate is skipped
if: ${{ contains(fromJSON(steps.signed.outputs.gates), 'pre-push-cargo-test') }}
run: echo "correctly identified as covered"
- name: an uncovered gate still runs
if: ${{ !contains(fromJSON(steps.signed.outputs.gates), 'pre-push-audit-rust') }}
run: echo "correctly identified as NOT covered"
- id: nokey
uses: ./sign
with:
key: ""
gates: ci-lint-only
- name: an empty key signs nothing and succeeds
run: |
set -eu
[ '${{ steps.nokey.outputs.status }}' = no-key ] || exit 1
[ '${{ steps.nokey.outputs.signed }}' = false ] || exit 1
- id: readkey
run: |
set -eu
{
echo 'key<<ATTEST_EOF'
cat "$RUNNER_TEMP/k"
echo 'ATTEST_EOF'
} >> "$GITHUB_OUTPUT"
- id: appended
uses: ./sign
with:
key: ${{ steps.readkey.outputs.key }}
gates: ci-lint-only
platform: aarch64-macos
push: false
- name: the block was appended locally
run: |
set -eu
[ '${{ steps.appended.outputs.status }}' = local ] || { echo 'status: ${{ steps.appended.outputs.status }}'; exit 1; }
n=$(git notes --ref amont-attest show "$(git rev-parse 'HEAD^{tree}')" | grep -c '^-----BEGIN SSH SIGNATURE-----$')
[ "$n" = 2 ] || { echo "expected 2 blocks, got $n"; exit 1; }
- id: strict
uses: ./
with:
signers: ${{ runner.temp }}/ci-signers
- id: loose
uses: ./
with:
signers: ${{ runner.temp }}/ci-signers
anywhere: ci-lint-only
- id: osonly
uses: ./
with:
signers: ${{ runner.temp }}/ci-signers
platform: macos
- name: strict, anywhere and os-only read the two blocks as specified
run: |
set -eu
[ '${{ steps.strict.outputs.gates }}' = '["pre-push-cargo-test","pre-push-clippy"]' ] || {
echo 'strict: ${{ steps.strict.outputs.gates }}'; exit 1; }
[ '${{ steps.loose.outputs.gates }}' = '["pre-push-cargo-test","pre-push-clippy","ci-lint-only"]' ] || {
echo 'anywhere: ${{ steps.loose.outputs.gates }}'; exit 1; }
[ '${{ steps.osonly.outputs.gates }}' = '["ci-lint-only"]' ] || {
echo 'os-only: ${{ steps.osonly.outputs.gates }}'; exit 1; }
- name: re-sign with only a prefix-colliding gate
run: |
set -eu
tree=$(git rev-parse 'HEAD^{tree}')
printf 'amont-attest-v2\ntree %s\ngates pre-push-cargo-test-slow\nplatform x86_64-linux\namont ci\n' \
"$tree" > "$RUNNER_TEMP/p2"
ssh-keygen -Y sign -n amont-attest -f "$RUNNER_TEMP/k" "$RUNNER_TEMP/p2"
git notes --ref amont-attest add -f \
-m "$(printf '%s\n\n%s' "$(cat "$RUNNER_TEMP/p2")" "$(cat "$RUNNER_TEMP/p2.sig")")" "$tree"
- id: collide
uses: ./
with:
signers: ${{ runner.temp }}/ci-signers
- name: a merely-prefix-matching gate does NOT count as covered
run: |
set -eu
[ '${{ steps.collide.outputs.gates }}' = '["pre-push-cargo-test-slow"]' ] || exit 1
# the legacy string form WOULD have matched — that is the defect:
case '${{ steps.collide.outputs.covered }}' in
*pre-push-cargo-test*) echo "substring match, as expected of the old form" ;;
esac
- name: and the array form correctly says it is not covered
if: ${{ !contains(fromJSON(steps.collide.outputs.gates), 'pre-push-cargo-test') }}
run: echo "the real suite would still run — correct"
msrv:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 - run: |
v=$(awk -F'"' '/^rust-version/ {print $2}' Cargo.toml)
rustup toolchain install "$v" --profile minimal
cargo "+$v" check --locked