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
27 changes: 27 additions & 0 deletions .github/workflows/notify-main-failure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,36 @@ jobs:
WORKFLOW_REF: ${{ github.workflow_ref }}
BRANCH: ${{ github.ref_name }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
run: |
set -uo pipefail

# A RE-RUN is the most common way a failure gets fixed, and it was the
# one case this could never report. Attempts share a run id, so the
# failing attempt is the very run the history lookup below excludes as
# "the current one" — it would then find some older, green run and
# conclude nothing had broken. Check the previous attempt first.
if [ "${RUN_ATTEMPT:-1}" -gt 1 ]; then
PREV_ATTEMPT=$((RUN_ATTEMPT - 1))
if LAST=$(gh api "repos/${REPO}/actions/runs/${RUN_ID}/attempts/${PREV_ATTEMPT}" \
--jq '.conclusion // ""' 2>&1); then
case "${LAST}" in
failure|timed_out|startup_failure)
echo "should_post=true" >> "$GITHUB_OUTPUT"
echo "prev_conclusion=${LAST}" >> "$GITHUB_OUTPUT"
echo "Attempt ${PREV_ATTEMPT} concluded '${LAST}', this attempt recovered it."
exit 0
;;
*)
echo "should_post=false" >> "$GITHUB_OUTPUT"
echo "Attempt ${PREV_ATTEMPT} concluded '${LAST:-unknown}', not a recovery."
exit 0
;;
esac
fi
echo "Could not read attempt ${PREV_ATTEMPT}, falling back to run history: ${LAST}"
fi

# Match the workflow FILE rather than its display name, which a
# `run-name:` override changes; fall back to the name if the ref does
# not resolve to a path inside this repo.
Expand Down
11 changes: 9 additions & 2 deletions .github/workflows/workflow-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,20 @@ jobs:
GH_TOKEN: ${{ github.token }}
run: |
set -uo pipefail
# The shared policy travels with the scripts, so every repo audits under
# the same rules and a decision recorded once is not re-litigated per
# repo. A repo with its own .github/zizmor.yml still wins, since zizmor
# prefers a repo-local config over --config.
CFG=(--config .ci-shared/zizmor.yml)
[ -f .github/zizmor.yml ] && CFG=()

if [ "$BLOCKING" = "true" ]; then
uvx "zizmor@${VERSION}" --persona="$PERSONA" .github/workflows
uvx "zizmor@${VERSION}" "${CFG[@]}" --persona="$PERSONA" .github/workflows
else
# Advisory: report and move on. The findings land in the log and the
# step summary either way, so turning this into a gate later is a
# one-line input change rather than a discovery exercise.
uvx "zizmor@${VERSION}" --persona="$PERSONA" .github/workflows | tee zizmor.out || true
uvx "zizmor@${VERSION}" "${CFG[@]}" --persona="$PERSONA" .github/workflows | tee zizmor.out || true
{
echo "### zizmor (advisory)"
echo
Expand Down
31 changes: 31 additions & 0 deletions zizmor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Shared zizmor policy for every repo that calls workflow-lint.yml.
#
# One file rather than seven, for the same reason the pipelines were unified: a
# per-repo copy drifts, and the drift is invisible until the signal is wrong.
#
# ONE audit is turned down here, and only partly, because it flags a DECISION
# rather than a defect. Everything else stays on: if a finding survives this file
# it is worth reading, which is the only way the report keeps meaning anything.
#
# Deliberately NOT suppressed, though it is the largest remaining group:
# `secrets-inherit`. `secrets: inherit` is our standard everywhere, so it will
# report on nearly every caller — but narrowing those to explicit secret lists is
# a legitimate hardening step nobody has decided against yet, and silencing it
# here would quietly make that decision. Suppress it when someone chooses to.

rules:
# In-org references float on `@main` deliberately (decision 11.BH). 101
# composite-action call sites already do, and keeping 150+ SHA pins bumped as
# the reusables evolve costs more than it buys — so the control moved to the
# source instead, where it is one gate rather than 150: `main` in
# mindsdb/github-actions requires a code-owner review, dismisses stale
# approvals, and admits no bypass actors.
#
# THIRD-PARTY actions get no such exemption. A tag there is mutable and an
# upstream account compromise reaches our pipelines with the caller's secrets in
# scope, which is a real finding and tracked as ENG-1053 hardening item 1.
unpinned-uses:
config:
policies:
mindsdb/*: ref-pin
"*": hash-pin
Loading