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
58 changes: 57 additions & 1 deletion scripts/tests/actions-lock-update-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,28 @@ if [ "${2:-}" = "--verify-local" ]; then
.github/workflows/actions.lock
sed -i 's#uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1#uses: actions/checkout@v7.0.1#' \
.github/workflows/ci.yml
exit
case "${FAKE_VERIFY_FINDING:-}" in
reusable-exact)
printf '%s\n' '{"valid":false,"findings":[{"workflow":".github/workflows/reusable.yml","category":"stale","dependency":"hyperpolymath/standards@abc123"}]}'
Comment thread
hyperpolymath marked this conversation as resolved.
exit 1
;;
reusable-wrong-ref)
printf '%s\n' '{"valid":false,"findings":[{"workflow":".github/workflows/reusable.yml","category":"stale","dependency":"hyperpolymath/standards@wrong456"}]}'
exit 1
;;
reusable-non-stale)
printf '%s\n' '{"valid":false,"findings":[{"workflow":".github/workflows/reusable.yml","category":"missing","dependency":"hyperpolymath/standards@abc123"}]}'
exit 1
;;
malformed-success)
printf '%s\n' 'not valid JSON'
exit 0
;;
*)
printf '%s\n' '{"valid":true,"findings":[]}'
exit
;;
esac
fi
sed -i '1i# This workflow is managed by gh actions-lock.' .github/workflows/ci.yml
sed -i 's#actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1#actions/checkout@v7.0.1#' \
Expand Down Expand Up @@ -75,6 +96,41 @@ cmp -s "$WORK/ci.before-verify" .github/workflows/ci.yml
cmp -s "$WORK/lock.before-verify" .github/workflows/actions.lock
echo "PASS: Actions lock verification restores tool-authored workflow edits"

cat > .github/workflows/reusable.yml <<'EOF'
# SPDX-License-Identifier: MPL-2.0
name: Reusable caller
on: push
permissions: {}
jobs:
governance:
uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@abc123
EOF

FAKE_VERIFY_FINDING=reusable-exact GH_BIN="$WORK/bin/fake-gh" \
bash "$UPDATE" --verify-local .github/workflows >/dev/null
echo "PASS: exact reusable-workflow dependency is accepted"
Comment thread
hyperpolymath marked this conversation as resolved.

if FAKE_VERIFY_FINDING=reusable-wrong-ref GH_BIN="$WORK/bin/fake-gh" \
bash "$UPDATE" --verify-local .github/workflows >/dev/null 2>&1; then
echo "FAIL: wrong reusable-workflow ref was accepted" >&2
exit 1
fi
echo "PASS: wrong reusable-workflow ref remains blocking"

if FAKE_VERIFY_FINDING=reusable-non-stale GH_BIN="$WORK/bin/fake-gh" \
bash "$UPDATE" --verify-local .github/workflows >/dev/null 2>&1; then
echo "FAIL: non-stale reusable-workflow finding was accepted" >&2
exit 1
fi
echo "PASS: non-stale reusable-workflow finding remains blocking"

if FAKE_VERIFY_FINDING=malformed-success GH_BIN="$WORK/bin/fake-gh" \
bash "$UPDATE" --verify-local .github/workflows >/dev/null 2>&1; then
echo "FAIL: malformed successful verifier output was accepted" >&2
exit 1
fi
echo "PASS: malformed verifier output fails closed"

# A failed refresh must restore both authored workflows and the previous
# lockfile; this is the production failure mode that left the original checkout
# half-rewritten when DNS resolution failed.
Expand Down
67 changes: 66 additions & 1 deletion scripts/update-actions-lock.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,71 @@ restore_workflows() {
done
}

workflow_references_reusable_dependency() {
workflow=$1
dependency=$2
repo=${dependency%@*}
ref=${dependency#*@}

[[ -f "$workflow" ]] || return 1
awk -v prefix="$repo/.github/workflows/" -v suffix="@$ref" '
/^[[:space:]]*uses:[[:space:]]*/ {
value = $0
sub(/^[[:space:]]*uses:[[:space:]]*/, "", value)
sub(/[[:space:]]*#.*/, "", value)
sub(/[[:space:]]*$/, "", value)
if (index(value, prefix) == 1 &&
length(value) >= length(suffix) &&
substr(value, length(value) - length(suffix) + 1) == suffix) {
found = 1
}
}
END { exit(found ? 0 : 1) }
' "$workflow"
}

verify_lock_coverage() {
# gh-actions-lock v0.1.6 does not recognise reusable-workflow `uses:`
# paths. GitHub's startup enforcement nevertheless requires callers to
# carry the reusable repository and its transitive actions in actions.lock.
# Accept only the tool's `stale` false positive when the named workflow
# contains the exact owner/repo/.github/workflows/file@ref dependency.
# Every other finding, including a wrong ref, remains blocking.
command -v jq >/dev/null 2>&1 || {
"$GH_BIN" actions-lock --verify-local
return
}

set +e
result=$("$GH_BIN" actions-lock --verify-local --json=valid,findings)
status=$?
set -e

if ! printf '%s' "$result" | jq -e '.valid != null and (.findings | type == "array")' >/dev/null 2>&1; then
printf '%s\n' "$result"
[[ "$status" -ne 0 ]] && return "$status"
return 1
fi
if printf '%s' "$result" | jq -e '.valid == true and (.findings | length == 0)' >/dev/null; then
return 0
fi

remaining=0
while IFS=$'\t' read -r category workflow dependency; do
if [[ "$category" = stale ]] &&
workflow_references_reusable_dependency "$workflow" "$dependency"; then
echo "Accepted reusable-workflow lock coverage: $workflow -> $dependency"
else
remaining=$((remaining + 1))
fi
done < <(printf '%s' "$result" | jq -r '.findings[] | [.category, .workflow, .dependency] | @tsv')

if [[ "$remaining" -ne 0 ]]; then
printf '%s\n' "$result"
return 1
fi
}

cleanup() {
status=$?
if [ "$COMPLETE" != true ]; then
Expand Down Expand Up @@ -62,6 +127,6 @@ fi
# Despite its name, --verify-local can migrate local `./` action paths to an
# invalid `$/` spelling. Treat verification as mutating and restore authored
# workflow bytes afterward too.
"$GH_BIN" actions-lock --verify-local
verify_lock_coverage
restore_workflows
COMPLETE=true
Loading