Skip to content

fix: require canonical governance and normal-merge authority #8811

fix: require canonical governance and normal-merge authority

fix: require canonical governance and normal-merge authority #8811

Workflow file for this run

name: ci
on:
pull_request:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.repository }}-${{ github.event_name == 'pull_request' && github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
verify:
name: verify
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read
env:
GIT_CONFIG_COUNT: "1"
GIT_CONFIG_KEY_0: init.defaultBranch
GIT_CONFIG_VALUE_0: main
steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
persist-credentials: false
- name: verify exact checkout
shell: bash
env:
NOEMA_EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
run: |
set -euo pipefail
if [[ ! "$NOEMA_EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then
printf '::error::Invalid expected head SHA.\n'
exit 1
fi
test "$(git rev-parse HEAD)" = "$NOEMA_EXPECTED_HEAD_SHA"
- name: setup node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: "24.19.0"
cache: npm
- name: verify package-manager toolchain
shell: bash
run: |
set -euo pipefail
test "$(node --version)" = "v24.19.0"
test "$(npm --version)" = "11.17.0"
- name: verify live pull-request base before lockfile control
if: github.event_name == 'pull_request'
shell: bash
env:
GH_TOKEN: ${{ github.token }}
NOEMA_PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
NOEMA_EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
if [[ ! "$NOEMA_EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then
printf '::error::Invalid expected head SHA.\n'
exit 1
fi
if [ -z "$NOEMA_PR_BASE_REF" ]; then
printf '::error::Pull-request base ref is unavailable.\n'
exit 1
fi
repository_name="${GITHUB_REPOSITORY#*/}"
resolve_live_base_sha() {
local output=""
local graphql_transient_exhausted="false"
for attempt in 1 2 3; do
if output="$(
gh api graphql \
-f query='query($owner:String!,$name:String!,$qualifiedName:String!){repository(owner:$owner,name:$name){ref(qualifiedName:$qualifiedName){target{oid}}}}' \
-F owner="$GITHUB_REPOSITORY_OWNER" \
-F name="$repository_name" \
-F qualifiedName="refs/heads/${NOEMA_PR_BASE_REF}" \
--jq '.data.repository.ref.target.oid' \
2>&1
)"; then
printf '%s\n' "$output"
return 0
fi
if printf '%s\n' "$output" | grep -Eq '\(HTTP (502|503|504)\)$'; then
if [ "$attempt" -lt 3 ]; then
sleep "$attempt"
continue
fi
graphql_transient_exhausted="true"
break
fi
printf '::error::Live pull-request base resolution failed after attempt %s.\n' "$attempt" >&2
return 1
done
if [ "$graphql_transient_exhausted" != "true" ]; then
return 1
fi
if output="$(
gh api --method GET "repos/${GITHUB_REPOSITORY}/git/ref/heads/${NOEMA_PR_BASE_REF}" \
--jq '.object.sha' \
2>&1
)"; then
printf '%s\n' "$output"
return 0
fi
printf '::error::Live pull-request base REST fallback failed.\n' >&2
return 1
}
live_base_sha="$(resolve_live_base_sha)"
if [[ ! "$live_base_sha" =~ ^[0-9a-f]{40}$ ]]; then
printf '::error::Live pull-request base ref did not resolve to a full commit SHA.\n'
exit 1
fi
if ! git merge-base --is-ancestor "$live_base_sha" "$NOEMA_EXPECTED_HEAD_SHA"; then
printf '::error::Pull-request head does not contain the current live base %s.\n' "$live_base_sha"
exit 1
fi
printf 'NOEMA_LIVE_BASE_SHA=%s\n' "$live_base_sha" >> "$GITHUB_ENV"
- name: verify lockfile change control
if: github.event_name == 'pull_request'
shell: bash
run: |
set -euo pipefail
if [[ ! "$NOEMA_LIVE_BASE_SHA" =~ ^[0-9a-f]{40}$ ]]; then
printf '::error::Invalid live pull-request base SHA.\n'
exit 1
fi
base_lock="$RUNNER_TEMP/noema-package-lock-base.json"
git show "${NOEMA_LIVE_BASE_SHA}:package-lock.json" >"$base_lock"
NOEMA_LOCKFILE_BASE_PATH="$base_lock" \
NOEMA_LOCKFILE_BASE_SHA="$NOEMA_LIVE_BASE_SHA" \
node --input-type=module <<'NODE'
import { runLockfileChangeControl } from "./scripts/lockfile-change-control.mjs";
const result = runLockfileChangeControl();
if (!result.passed) {
for (const failure of result.failures) {
console.error(`::error::${failure}`);
}
process.exit(1);
}
console.log(`Lockfile change control passed for ${result.changedPackages.length} changed package node(s).`);
NODE
- name: regenerate canonical lockfile in disposable workspace
id: regenerate_lockfile
shell: bash
run: |
set -euo pipefail
regeneration_root="$RUNNER_TEMP/noema-lockfile-regeneration"
rm -rf "$regeneration_root"
mkdir -p "$regeneration_root"
cp package.json package-lock.json .npmrc "$regeneration_root/"
(
cd "$regeneration_root"
npm install \
--package-lock-only \
--ignore-scripts \
--no-audit \
--no-fund \
--legacy-peer-deps=false \
--install-links=false
)
cp "$regeneration_root/package-lock.json" "$RUNNER_TEMP/noema-package-lock-regenerated.json"
if cmp --silent package-lock.json "$RUNNER_TEMP/noema-package-lock-regenerated.json"; then
printf 'match=true\n' >> "$GITHUB_OUTPUT"
else
printf 'match=false\n' >> "$GITHUB_OUTPUT"
diff -u package-lock.json "$RUNNER_TEMP/noema-package-lock-regenerated.json" \
> "$RUNNER_TEMP/noema-package-lock-regeneration.diff" || true
fi
- name: upload regenerated lockfile evidence
if: ${{ !cancelled() && steps.regenerate_lockfile.outcome != 'skipped' }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: noema-lockfile-regeneration-${{ github.event.pull_request.head.sha || github.sha }}
path: |
${{ runner.temp }}/noema-package-lock-regenerated.json
${{ runner.temp }}/noema-package-lock-regeneration.diff
if-no-files-found: error
retention-days: 1
- name: require committed lockfile reproducibility
if: steps.regenerate_lockfile.outputs.match != 'true'
shell: bash
run: |
printf '::error::package-lock.json is not the canonical output of the pinned Node/npm toolchain.\n'
exit 1
- name: install
run: npm ci --legacy-peer-deps=false --install-links=false
- name: release typecheck
run: npm run typecheck
- name: release tests
shell: bash
run: |
set -euo pipefail
log="$RUNNER_TEMP/noema-release-tests.log"
rm -f coverage/coverage-final.json
if npm run test -- --reporter=dot --coverage.reporter=json --coverage.reporter=text >"$log" 2>&1; then
exit 0
fi
tail -c 32768 "$log" | tail -n 160
if [ -f coverage/coverage-final.json ]; then
node --input-type=module <<'NODE'
import { readFileSync } from "node:fs";
import { relative } from "node:path";
const report = JSON.parse(readFileSync("coverage/coverage-final.json", "utf8"));
let diagnosticCount = 0;
outer: for (const file of Object.keys(report).sort()) {
const coverage = report[file];
const statementMap = coverage?.statementMap ?? {};
for (const [statementId, hits] of Object.entries(coverage?.s ?? {})) {
if (hits !== 0) continue;
const location = statementMap[statementId];
if (!location?.start) continue;
const path = relative(process.cwd(), file) || file;
console.error(
`Uncovered statement ${path}:${location.start.line}:${location.start.column}`,
);
diagnosticCount += 1;
if (diagnosticCount >= 64) break outer;
}
const branchMap = coverage?.branchMap ?? {};
for (const [branchId, hits] of Object.entries(coverage?.b ?? {})) {
if (!Array.isArray(hits)) continue;
const branch = branchMap[branchId];
const locations = branch?.locations ?? [];
for (const [armIndex, armHits] of hits.entries()) {
if (armHits !== 0) continue;
const location = locations[armIndex] ?? branch?.loc;
const line = branch?.line ?? branch?.loc?.start?.line ?? location?.line ?? location?.start?.line;
if (!Number.isInteger(line)) continue;
const column = branch?.loc?.start?.column ?? location?.start?.column ?? 0;
const path = relative(process.cwd(), file) || file;
console.error(
`Uncovered branch ${path}:${line}:${column} branch=${branchId} arm=${armIndex}`,
);
diagnosticCount += 1;
if (diagnosticCount >= 64) break outer;
}
}
}
NODE
fi
exit 1
- name: release security scan
run: npm run security:scan
- name: release KPI verification
run: npm run kpi:verify
- name: release dependency license inventory
run: npm run release:dependency-license-inventory
- name: release acquisition manifest
run: npm run acquisition:manifest
- name: release acquisition integrity
run: npm run acquisition:integrity
- name: refuse pull-request base drift after verification
if: github.event_name == 'pull_request'
shell: bash
env:
GH_TOKEN: ${{ github.token }}
NOEMA_PR_BASE_REF: ${{ github.event.pull_request.base.ref }}
run: |
set -euo pipefail
if [[ ! "$NOEMA_LIVE_BASE_SHA" =~ ^[0-9a-f]{40}$ ]]; then
printf '::error::Initial live pull-request base SHA is unavailable.\n'
exit 1
fi
if [ -z "$NOEMA_PR_BASE_REF" ]; then
printf '::error::Pull-request base ref is unavailable.\n'
exit 1
fi
repository_name="${GITHUB_REPOSITORY#*/}"
resolve_live_base_sha() {
local output=""
local graphql_transient_exhausted="false"
for attempt in 1 2 3; do
if output="$(
gh api graphql \
-f query='query($owner:String!,$name:String!,$qualifiedName:String!){repository(owner:$owner,name:$name){ref(qualifiedName:$qualifiedName){target{oid}}}}' \
-F owner="$GITHUB_REPOSITORY_OWNER" \
-F name="$repository_name" \
-F qualifiedName="refs/heads/${NOEMA_PR_BASE_REF}" \
--jq '.data.repository.ref.target.oid' \
2>&1
)"; then
printf '%s\n' "$output"
return 0
fi
if printf '%s\n' "$output" | grep -Eq '\(HTTP (502|503|504)\)$'; then
if [ "$attempt" -lt 3 ]; then
sleep "$attempt"
continue
fi
graphql_transient_exhausted="true"
break
fi
printf '::error::Live pull-request base resolution failed after attempt %s.\n' "$attempt" >&2
return 1
done
if [ "$graphql_transient_exhausted" != "true" ]; then
return 1
fi
if output="$(
gh api --method GET "repos/${GITHUB_REPOSITORY}/git/ref/heads/${NOEMA_PR_BASE_REF}" \
--jq '.object.sha' \
2>&1
)"; then
printf '%s\n' "$output"
return 0
fi
printf '::error::Live pull-request base REST fallback failed.\n' >&2
return 1
}
live_base_sha="$(resolve_live_base_sha)"
if [[ ! "$live_base_sha" =~ ^[0-9a-f]{40}$ ]]; then
printf '::error::Live pull-request base ref did not resolve to a full commit SHA.\n'
exit 1
fi
if [ "$live_base_sha" != "$NOEMA_LIVE_BASE_SHA" ]; then
printf '::error::Pull-request base branch advanced during verification from %s to %s.\n' \
"$NOEMA_LIVE_BASE_SHA" "$live_base_sha"
exit 1
fi
test "$live_base_sha" = "$NOEMA_LIVE_BASE_SHA"