diff --git a/.github/actions/otel-job-id/action.yml b/.github/actions/otel-job-id/action.yml new file mode 100644 index 000000000..91f3e47a4 --- /dev/null +++ b/.github/actions/otel-job-id/action.yml @@ -0,0 +1,52 @@ +name: Resolve OTEL job id +description: >- + Append ci.job_id to SPINIFEX_OTEL_ATTRS so telemetry emitted by this job can + be pivoted straight back to its GitHub job log. Must run before the step that + bakes SPINIFEX_OTEL_ATTRS into a VM's telemetry.env, and requires + `actions: read` on the calling job. + +inputs: + job-name: + description: >- + Rendered name of the calling job, exactly as it appears in the Actions UI. + github.job is only the YAML key and no context exposes the numeric id, so + the id has to be looked up by name. + required: true + token: + description: Token used for the Actions jobs API lookup. + required: false + default: ${{ github.token }} + +runs: + using: composite + steps: + - name: Resolve CI job id (best-effort) + shell: bash + env: + GH_TOKEN: ${{ inputs.token }} + JOB_NAME: ${{ inputs.job-name }} + REPO: ${{ github.repository }} + RUN_ID: ${{ github.run_id }} + run: | + # Never fail the caller: on any lookup error or name mismatch ci.job_id + # is simply omitted and the run proceeds with the labels it already has. + set +e + + if [ -z "${SPINIFEX_OTEL_ATTRS:-}" ]; then + echo "SPINIFEX_OTEL_ATTRS is unset or empty — telemetry is disabled, nothing to label" + exit 0 + fi + + # env.JOB_NAME rather than shell interpolation into the jq filter: job + # names carry quotes, slashes and non-ASCII separators that would + # otherwise break the expression. + JOB_ID=$(gh api "repos/${REPO}/actions/runs/${RUN_ID}/jobs" --paginate \ + --jq '.jobs[] | select(.name == env.JOB_NAME) | .id' 2>/dev/null | head -n1) + + if [ -n "$JOB_ID" ]; then + echo "SPINIFEX_OTEL_ATTRS=${SPINIFEX_OTEL_ATTRS},ci.job_id=${JOB_ID}" >> "$GITHUB_ENV" + echo "resolved ci.job_id=${JOB_ID} for job '${JOB_NAME}'" + else + echo "::warning::could not resolve numeric job id for '${JOB_NAME}' — continuing without ci.job_id" + fi + exit 0 diff --git a/.github/scripts/suite-sets.sh b/.github/scripts/suite-sets.sh new file mode 100644 index 000000000..87559e402 --- /dev/null +++ b/.github/scripts/suite-sets.sh @@ -0,0 +1,58 @@ +# shellcheck shell=bash +# shellcheck disable=SC2034 # every variable here is consumed by the sourcing workflow +# Source from a workflow `run:` block: +# . "${GITHUB_WORKSPACE}/spinifex/.github/scripts/suite-sets.sh" +# +# Which e2e suites each topology can run. Sourced by every workflow that drives +# suites (e2e.yml, e2e-suites.yml, e2e-nightly.yml) so the lists cannot drift +# apart — they previously disagreed three ways. +# +# Eligibility is a property of the suite, not of the workflow: a suite belongs +# to a topology when all of its assertions are reachable there. lb is +# multi-only for exactly that reason — on a single node every internet-facing +# subtest skips for want of a peer, so a single-node lb run reports green +# having exercised a fraction of the suite. + +# Suites runnable against a one-node environment. +# +# partialblock gates merges because it earned it, not because it is new: on one +# node, same binary and workload, it fails against the pre-fix engine (1758 of +# 8192 halves reverted a generation) and passes against the fixed one (0 of +# 8192). A suite that has never been shown to go red for the reason it claims +# does not belong here. +E2E_SUITES_SINGLE="single iam cert eks ecs storagegrowth partialblock" + +# Suites runnable against a multi-node environment. +E2E_SUITES_MULTI="multinode lb cert" + +# grep -xE alternation form, for narrowing a requested set to the eligible one. +E2E_SUITES_SINGLE_RE="$(printf '%s' "${E2E_SUITES_SINGLE}" | tr ' ' '|')" +E2E_SUITES_MULTI_RE="$(printf '%s' "${E2E_SUITES_MULTI}" | tr ' ' '|')" + +# e2e-nightly runs a narrower set than the two sets above, deliberately: its +# ~19 cells each have a 35-minute budget and exist to prove that every +# install / network / host-OS permutation boots and serves. Suite breadth is +# the PR gate's job, and it already runs on every push. These are subsets, and +# e2e_suites_assert_subset below enforces that. +E2E_SUITES_NIGHTLY_SINGLE="single cert" +E2E_SUITES_NIGHTLY_MULTI="multinode cert lb" + +# Fail loudly when a narrowed list names a suite its topology cannot run. This +# is the guard that stops the lists drifting again: a suite added to a nightly +# subset without also being declared eligible above stops the job here rather +# than silently skipping half its assertions in CI. +# e2e_suites_assert_subset