Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 68 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ jobs:
commands: ${{ needs.plan.outputs.test-command }}
component: ${{ inputs.component }}
args: ${{ inputs.args }}
scope: full
scope: ${{ inputs.scope }}
auto-setup: ${{ inputs.auto-setup }}
php-version: ${{ inputs.php-version }}
node-version: ${{ inputs.node-version }}
Expand Down Expand Up @@ -860,6 +860,12 @@ jobs:
with:
name: homeboy-test-inventory-failure-${{ github.run_attempt }}
path: test-inventory-failure
- name: Download baseline Test inventory failure provenance
if: needs.candidate-test-plan.result == 'success' && needs.baseline-test-plan.outputs.bootstrap-baseline-red == 'true'
uses: actions/download-artifact@v7
with:
name: homeboy-baseline-test-inventory-failure-${{ github.run_attempt }}
path: baseline-test-inventory-failure
- name: Report candidate Test inventory generation failure
if: needs.candidate-test-plan.result != 'success'
run: |
Expand All @@ -869,10 +875,33 @@ jobs:
echo "::error::Test inventory generation failed for '${command}' (inventory=${{ needs.candidate-test-plan.result }}; results=${results}). Inspect Plan candidate Test shards for the underlying command failure."
find test-inventory-failure/homeboy-ci-results -type f -maxdepth 1 -print -exec cat {} \; 2>/dev/null || true
exit 1
- name: Report baseline Test inventory generation failure
if: needs.candidate-test-plan.result == 'success' && github.event_name == 'pull_request' && inputs.differential-gating == 'true' && needs.baseline-test-plan.result != 'success'
- name: Reconcile baseline Test bootstrap failure
if: needs.candidate-test-plan.result == 'success' && needs.baseline-test-plan.outputs.bootstrap-baseline-red == 'true'
env:
REPOSITORY: ${{ github.repository }}
CANDIDATE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
BASE_SHA: ${{ github.event.pull_request.base.sha || github.sha }}
COMMAND: ${{ needs.plan.outputs.test-command }}
RUN_ATTEMPT: ${{ github.run_attempt }}
run: |
echo '::error::Baseline Test inventory generation failed; baseline shard artifacts were not downloaded. Inspect Plan baseline Test shards for the underlying command failure.'
provenance=baseline-test-inventory-failure/provenance.json
jq -e --arg repository "${REPOSITORY}" --arg candidate_sha "${CANDIDATE_SHA}" --arg base_sha "${BASE_SHA}" --arg command "${COMMAND}" --argjson run_attempt "${RUN_ATTEMPT}" '
.schema == "homeboy/test-inventory-provenance/v1"
and .phase == "baseline"
and .repository == $repository
and .candidate_sha == $candidate_sha
and .base_sha == $base_sha
and .command == $command
and .inventory_outcome == "failure"
and .planner_outcome == "skipped"
and (.run_attempt | type == "number" and . > 0 and . <= $run_attempt)
and (.results | type == "object")
' "${provenance}" >/dev/null
echo "::warning::Test bootstrap_baseline_red: immutable baseline inventory failed before baseline shards could be planned. Candidate shards remain mandatory; inspect Plan baseline Test shards and the retained baseline inventory provenance."
- name: Fail unresolved baseline Test planning failure
if: needs.candidate-test-plan.result == 'success' && github.event_name == 'pull_request' && inputs.differential-gating == 'true' && needs.baseline-test-plan.result != 'success' && needs.baseline-test-plan.outputs.bootstrap-baseline-red != 'true'
run: |
echo '::error::Baseline Test planning failed outside the classified inventory-bootstrap path; no differential verdict can be produced.'
exit 1
- uses: actions/download-artifact@v7
if: needs.candidate-test-plan.result == 'success'
Expand Down Expand Up @@ -934,6 +963,11 @@ jobs:
env:
COMMAND: ${{ needs.plan.outputs.test-command }}
run: RESULTS="$(cat homeboy-ci-results/results.json)" COMMANDS="${COMMAND}" OPERATIONS_RESULTS='' PR_ACTIVE='' bash .homeboy-action/scripts/core/enforce-final-status.sh
- name: Enforce candidate Test result after bootstrap baseline red
if: needs.candidate-test-plan.result == 'success' && needs.baseline-test-plan.outputs.bootstrap-baseline-red == 'true'
env:
COMMAND: ${{ needs.plan.outputs.test-command }}
run: RESULTS="$(cat homeboy-ci-results/results.json)" COMMANDS="${COMMAND}" OPERATIONS_RESULTS='' PR_ACTIVE='' bash .homeboy-action/scripts/core/enforce-final-status.sh

baseline-test-plan:
name: Plan baseline Test shards
Expand All @@ -942,6 +976,7 @@ jobs:
runs-on: ubuntu-latest
outputs:
test-matrix: ${{ steps.plan.outputs.matrix }}
bootstrap-baseline-red: ${{ steps.bootstrap-baseline-red.outputs.value }}
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -1006,6 +1041,35 @@ jobs:
bash .homeboy-action/scripts/core/shard-tests.sh plan
jq -cn --arg command "${TEST_COMMAND}" --slurpfile plan homeboy-test-shard-plan.json '{include:[$plan[0].shards[] | {command:$command,shard_id:.id}]}' >> /dev/null
echo "matrix=$(jq -cn --arg command "${TEST_COMMAND}" --slurpfile plan homeboy-test-shard-plan.json '{include:[$plan[0].shards[] | {command:$command,shard_id:.id}]}')" >> "${GITHUB_OUTPUT}"
- name: Classify baseline inventory bootstrap failure
id: bootstrap-baseline-red
if: always() && steps.inventory.outcome == 'failure' && steps.plan.outcome == 'skipped'
run: echo 'value=true' >> "${GITHUB_OUTPUT}"
- name: Write baseline Test inventory failure provenance
if: always() && steps.bootstrap-baseline-red.outputs.value == 'true'
env:
REPOSITORY: ${{ github.repository }}
CANDIDATE_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
BASE_SHA: ${{ github.event.pull_request.base.sha || github.sha }}
COMMAND: ${{ needs.plan.outputs.test-command }}
INVENTORY_OUTCOME: ${{ steps.inventory.outcome }}
PLANNER_OUTCOME: ${{ steps.plan.outcome }}
RESULTS: ${{ steps.inventory.outputs.results }}
RUN_ATTEMPT: ${{ github.run_attempt }}
run: |
mkdir -p homeboy-baseline-test-inventory-failure
inventory_payload="${RESULTS-}"
[ -n "${inventory_payload}" ] || inventory_payload='{}'
jq -cn --arg schema 'homeboy/test-inventory-provenance/v1' --arg phase baseline --arg repository "${REPOSITORY}" --arg candidate_sha "${CANDIDATE_SHA}" --arg base_sha "${BASE_SHA}" --arg command "${COMMAND}" --arg inventory_outcome "${INVENTORY_OUTCOME}" --arg planner_outcome "${PLANNER_OUTCOME}" --argjson run_attempt "${RUN_ATTEMPT}" --argjson results "${inventory_payload}" \
'{schema:$schema,phase:$phase,repository:$repository,candidate_sha:$candidate_sha,base_sha:$base_sha,command:$command,inventory_outcome:$inventory_outcome,planner_outcome:$planner_outcome,run_attempt:$run_attempt,results:$results}' > homeboy-baseline-test-inventory-failure/provenance.json
cp -R homeboy-ci-results homeboy-baseline-test-inventory-failure/homeboy-ci-results 2>/dev/null || true
- name: Upload baseline Test inventory failure provenance
if: always() && steps.bootstrap-baseline-red.outputs.value == 'true'
uses: actions/upload-artifact@v7
with:
name: homeboy-baseline-test-inventory-failure-${{ github.run_attempt }}
path: homeboy-baseline-test-inventory-failure
if-no-files-found: error
- name: Fail baseline Test inventory generation
if: always() && (steps.inventory.outcome != 'success' || steps.plan.outcome != 'success')
run: |
Expand Down
14 changes: 12 additions & 2 deletions scripts/core/test-test-shards.sh
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,12 @@ grep -F 'baseline_result="$(jq -r --arg command "${COMMAND}"' "${WORKFLOW}" >/de
grep -F 'result_filename="$(command_result_filename "${COMMAND}")"' "${WORKFLOW}" >/dev/null || { printf 'FAIL: differential baseline lookup does not use the canonical result filename\n'; exit 1; }
# shellcheck disable=SC2016
grep -F '[ -f "homeboy-baseline-results/${result_filename}" ] && structured_output=true' "${WORKFLOW}" >/dev/null || { printf 'FAIL: differential baseline lookup does not fail closed when its declared result is absent\n'; exit 1; }
if [ "$(grep -c 'scope: full' "${WORKFLOW}")" -lt 4 ]; then
printf 'FAIL: candidate and baseline shard planning and replay do not use their manifests as the sole test selector\n'; exit 1
candidate_inventory="$(sed -n '/name: Configure candidate Test shards/,/name: Plan candidate Test shards/p' "${WORKFLOW}")"
baseline_inventory="$(sed -n '/name: Configure baseline Test shards/,/name: Plan baseline Test shards/p' "${WORKFLOW}")"
printf '%s\n' "${candidate_inventory}" | grep -F 'scope: ${{ inputs.scope }}' >/dev/null || { printf 'FAIL: candidate Test inventory does not forward caller scope\n'; exit 1; }
printf '%s\n' "${baseline_inventory}" | grep -F 'scope: full' >/dev/null || { printf 'FAIL: immutable baseline Test inventory is not full scope\n'; exit 1; }
if [ "$(grep -c 'scope: full' "${WORKFLOW}")" -lt 3 ]; then
printf 'FAIL: immutable baseline inventory and shard replays do not use full scope\n'; exit 1
fi
grep -F 'prepare-test-shard-workspace.sh prepare .homeboy-action' "${WORKFLOW}" >/dev/null || { printf 'FAIL: candidate binary setup does not isolate the action checkout\n'; exit 1; }
grep -F "steps.prepare-binary-workspace.outcome == 'success'" "${WORKFLOW}" >/dev/null || { printf 'FAIL: candidate binary setup does not always restore its exact Git exclusions\n'; exit 1; }
Expand All @@ -250,4 +254,10 @@ grep -F "::error::Test inventory generation failed for" "${WORKFLOW}" >/dev/null
if grep -A4 'name: Report candidate Test inventory generation failure' "${WORKFLOW}" | grep -F 'homeboy-test-shard-plan-' >/dev/null; then
printf 'FAIL: inventory failure reporting attempts to read a missing shard plan artifact\n'; exit 1
fi
grep -F 'bootstrap-baseline-red: ${{ steps.bootstrap-baseline-red.outputs.value }}' "${WORKFLOW}" >/dev/null || { printf 'FAIL: baseline inventory failures are not classified for reconciliation\n'; exit 1; }
grep -F 'name: homeboy-baseline-test-inventory-failure-${{ github.run_attempt }}' "${WORKFLOW}" >/dev/null || { printf 'FAIL: classified baseline inventory failures are not retained as artifacts\n'; exit 1; }
grep -F 'Test bootstrap_baseline_red: immutable baseline inventory failed' "${WORKFLOW}" >/dev/null || { printf 'FAIL: Test does not publish the baseline bootstrap warning\n'; exit 1; }
grep -F "needs.baseline-test-plan.outputs.bootstrap-baseline-red == 'true'" "${WORKFLOW}" >/dev/null || { printf 'FAIL: Test does not restrict the nonblocking path to classified baseline inventory failures\n'; exit 1; }
grep -F 'name: Enforce candidate Test result after bootstrap baseline red' "${WORKFLOW}" >/dev/null || { printf 'FAIL: bootstrap baseline warning can bypass candidate Test enforcement\n'; exit 1; }
grep -F "needs.baseline-test-plan.result == 'success'" "${WORKFLOW}" >/dev/null || { printf 'FAIL: baseline shards can run without a successful baseline inventory plan\n'; exit 1; }
printf 'PASS: policy waits for sharded Test reconciliation while preserving unsharded behavior\n'
4 changes: 2 additions & 2 deletions scripts/release/test-release-workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ assert_not_contains 'docs/CHANGELOG.md' "${WORKFLOW}" "release workflow does not
# GitHub-hosted workflow dependencies must use Node 24-capable action majors (#321).
assert_count 'actions/checkout@v6' '18' "${CI_WORKFLOW}" "reusable CI workflow uses checkout v6 for every checkout"
assert_count 'actions/cache@v5' '3' "${CI_WORKFLOW}" "reusable CI workflow uses cache v5"
assert_count 'actions/upload-artifact@v7' '8' "${CI_WORKFLOW}" "reusable CI workflow uses artifact upload v7"
assert_count 'actions/download-artifact@v7' '7' "${CI_WORKFLOW}" "reusable CI workflow uses artifact download v7"
assert_count 'actions/upload-artifact@v7' '9' "${CI_WORKFLOW}" "reusable CI workflow uses artifact upload v7"
assert_count 'actions/download-artifact@v7' '8' "${CI_WORKFLOW}" "reusable CI workflow uses artifact download v7"
assert_count 'actions/create-github-app-token@v3' '3' "${CI_WORKFLOW}" "reusable CI workflow uses app-token v3"
assert_not_contains 'actions/checkout@v4' "${CI_WORKFLOW}" "reusable CI workflow has no checkout v4 references"
assert_not_contains 'actions/cache@v4' "${CI_WORKFLOW}" "reusable CI workflow has no cache v4 references"
Expand Down
Loading