mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-13 00:34:43 +00:00
292 lines
13 KiB
YAML
292 lines
13 KiB
YAML
name: Canonical Governance
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- main
|
|
pull_request:
|
|
branches:
|
|
- main
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
governance:
|
|
runs-on: ubuntu-24.04
|
|
# The release-control audits resolve the workspace layout from the
|
|
# local checkout: the repo directory must be named exactly "pulse"
|
|
# (the canonical repo id) and the evidence repos must be checked out
|
|
# as siblings, mirroring <workspace>/repos/<repo-id> on dev machines.
|
|
defaults:
|
|
run:
|
|
working-directory: repos/pulse
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
with:
|
|
persist-credentials: false
|
|
fetch-depth: 0
|
|
path: repos/pulse
|
|
|
|
- name: Checkout pulse-pro evidence repo
|
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
with:
|
|
repository: rcourtman/pulse-pro
|
|
token: ${{ secrets.WORKFLOW_PAT }}
|
|
persist-credentials: false
|
|
fetch-depth: 1
|
|
path: repos/pulse-pro
|
|
|
|
- name: Checkout pulse-enterprise evidence repo
|
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
with:
|
|
repository: rcourtman/pulse-enterprise
|
|
token: ${{ secrets.WORKFLOW_PAT }}
|
|
persist-credentials: false
|
|
fetch-depth: 1
|
|
path: repos/pulse-enterprise
|
|
|
|
- name: Resolve pulse-mobile evidence ref
|
|
id: pulse-mobile-ref
|
|
shell: bash
|
|
env:
|
|
CANDIDATE_REF: ${{ github.head_ref }}
|
|
GH_TOKEN: ${{ secrets.WORKFLOW_PAT }}
|
|
run: |
|
|
set -euo pipefail
|
|
resolved_ref="main"
|
|
if [ -n "${CANDIDATE_REF}" ]; then
|
|
matching_ref="$(gh api \
|
|
"repos/rcourtman/pulse-mobile/git/matching-refs/heads/${CANDIDATE_REF}" \
|
|
--jq '.[].ref' 2>/dev/null \
|
|
| grep --fixed-strings --line-regexp -- "refs/heads/${CANDIDATE_REF}" \
|
|
|| true)"
|
|
if [ -n "${matching_ref}" ]; then
|
|
resolved_ref="${CANDIDATE_REF}"
|
|
fi
|
|
fi
|
|
echo "ref=${resolved_ref}" >> "${GITHUB_OUTPUT}"
|
|
echo "Using pulse-mobile ref: ${resolved_ref}"
|
|
|
|
- name: Checkout pulse-mobile evidence repo
|
|
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
|
with:
|
|
repository: rcourtman/pulse-mobile
|
|
ref: ${{ steps.pulse-mobile-ref.outputs.ref }}
|
|
token: ${{ secrets.WORKFLOW_PAT }}
|
|
persist-credentials: false
|
|
fetch-depth: 1
|
|
path: repos/pulse-mobile
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
|
with:
|
|
go-version-file: repos/pulse/go.mod
|
|
cache: true
|
|
cache-dependency-path: repos/pulse/go.sum
|
|
|
|
- name: Set up Node.js for frontend governance
|
|
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
|
with:
|
|
node-version: '24'
|
|
cache: npm
|
|
cache-dependency-path: repos/pulse/frontend-modern/package-lock.json
|
|
|
|
- name: Install locked frontend governance dependencies
|
|
working-directory: repos/pulse/frontend-modern
|
|
env:
|
|
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
|
|
run: npm ci --ignore-scripts --no-audit --no-fund
|
|
|
|
- name: Stub embedded frontend assets for Go tests
|
|
run: bash scripts/ensure_test_assets.sh
|
|
|
|
- name: Check Pulse Mobile compatibility contract
|
|
run: |
|
|
python3 scripts/release_control/generate_mobile_compatibility_test.py
|
|
python3 scripts/release_control/mobile_compatibility_test.py
|
|
python3 scripts/release_control/generate_mobile_compatibility.py --check --mobile-repo ../pulse-mobile
|
|
python3 scripts/release_control/mobile_compatibility.py \
|
|
--mobile-repo ../pulse-mobile \
|
|
--evidence-out "${RUNNER_TEMP}/pulse-mobile-compatibility.json" \
|
|
--pretty
|
|
go test ./internal/api \
|
|
-run 'Test(RelayMobileRuntimeRouteInventory|RelayMobileAccessScopeAllowsGovernedMobileRuntimeEndpoints|RelayMobileAccessScopeDeniesAdjacentAIRoutes|Contract_PatrolAttentionRoutesAcceptMobileRelayCapability)$' \
|
|
-count=1
|
|
go test ./internal/relay \
|
|
-run 'Test(CanonicalActionOutcomeNotificationMatrix|NewPatrolFindingNotification|NewApprovalRequestNotification|NewActionDecisionNotificationUsesCanonicalActionIdentity|NewFixCompletedNotification|NewActionOutcomeNotificationIsHonestAboutVerification)$' \
|
|
-count=1
|
|
|
|
- name: Upload Pulse Mobile compatibility evidence
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: pulse-mobile-compatibility
|
|
path: ${{ runner.temp }}/pulse-mobile-compatibility.json
|
|
if-no-files-found: error
|
|
|
|
- name: Determine governance diff range
|
|
id: diff
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
range="${{ github.event.pull_request.base.sha }}...${{ github.sha }}"
|
|
elif [ "${{ github.event_name }}" = "push" ]; then
|
|
range="${{ github.event.before }}...${{ github.sha }}"
|
|
elif git rev-parse --verify HEAD^ >/dev/null 2>&1; then
|
|
range="HEAD^...HEAD"
|
|
else
|
|
range=""
|
|
fi
|
|
echo "range=${range}" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Run canonical completion guard against changed commits
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
range="${{ steps.diff.outputs.range }}"
|
|
if [ -z "${range}" ]; then
|
|
printf '' | python3 scripts/release_control/canonical_completion_guard.py --files-from-stdin
|
|
exit 0
|
|
fi
|
|
base="${range%%...*}"
|
|
head_sha="${range##*...}"
|
|
# Evaluate each commit the way the commit-time hook did: its own
|
|
# file list against its own parent. A range-wide evaluation
|
|
# cannot honor per-commit Contract-Neutral trailers (recorded by
|
|
# .husky/prepare-commit-msg when the author sets
|
|
# PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT), so bypassed commits that
|
|
# passed locally would fail here.
|
|
if ! commits=$(git rev-list --reverse --no-merges "${base}..${head_sha}" 2>/dev/null); then
|
|
echo "Range base ${base} unavailable (force push?); evaluating ${head_sha} against its parent."
|
|
commits="${head_sha}"
|
|
fi
|
|
if [ -z "${commits}" ]; then
|
|
echo "No commits to evaluate in range ${range}."
|
|
exit 0
|
|
fi
|
|
status=0
|
|
for commit in ${commits}; do
|
|
if ! git rev-parse --verify --quiet "${commit}^" >/dev/null; then
|
|
echo "Skipping root commit ${commit} (no parent to diff against)."
|
|
continue
|
|
fi
|
|
reason=$(git log -1 --format='%(trailers:key=Contract-Neutral,valueonly,separator=; )' "${commit}" | tr '\n' ' ')
|
|
echo "::group::canonical completion guard @ ${commit}"
|
|
# Pass the parent as the diff base so the guard compares
|
|
# contract texts parent-vs-commit; the CI index equals HEAD,
|
|
# so the default index comparison would misreport contract
|
|
# updates in earlier commits as insubstantial.
|
|
if ! git diff-tree --no-commit-id --name-only -r "${commit}" \
|
|
| PULSE_ALLOW_CONTRACT_NEUTRAL_COMMIT="${reason}" python3 scripts/release_control/canonical_completion_guard.py \
|
|
--files-from-stdin --diff-base "${commit}^"; then
|
|
echo "Canonical completion guard failed for commit ${commit}."
|
|
status=1
|
|
fi
|
|
if ! git diff-tree --no-commit-id --name-only -r "${commit}" \
|
|
| python3 scripts/release_control/browser_verification_guard.py \
|
|
--files-from-stdin --commit "${commit}"; then
|
|
echo "Browser verification guard failed for commit ${commit}."
|
|
status=1
|
|
fi
|
|
echo "::endgroup::"
|
|
done
|
|
exit ${status}
|
|
|
|
- name: Run status audit
|
|
env:
|
|
PULSE_REPO_ROOT_PULSE: ${{ github.workspace }}/repos/pulse
|
|
PULSE_REPO_ROOT_PULSE_PRO: ${{ github.workspace }}/repos/pulse-pro
|
|
PULSE_REPO_ROOT_PULSE_ENTERPRISE: ${{ github.workspace }}/repos/pulse-enterprise
|
|
PULSE_REPO_ROOT_PULSE_MOBILE: ${{ github.workspace }}/repos/pulse-mobile
|
|
run: python3 scripts/release_control/status_audit.py --check
|
|
|
|
- name: Validate Pulse Intelligence release-gate schema
|
|
run: python3 scripts/release_control/pulse_intelligence_gate.py --validate-only --matrix docs/release-control/v6/internal/pulse-intelligence-release-gate.json
|
|
|
|
- name: Run Pulse Intelligence release-gate unit tests
|
|
run: python3 scripts/release_control/pulse_intelligence_gate_test.py
|
|
|
|
- name: Run control plane audit
|
|
env:
|
|
PULSE_REPO_ROOT_PULSE: ${{ github.workspace }}/repos/pulse
|
|
PULSE_REPO_ROOT_PULSE_PRO: ${{ github.workspace }}/repos/pulse-pro
|
|
PULSE_REPO_ROOT_PULSE_ENTERPRISE: ${{ github.workspace }}/repos/pulse-enterprise
|
|
PULSE_REPO_ROOT_PULSE_MOBILE: ${{ github.workspace }}/repos/pulse-mobile
|
|
run: python3 scripts/release_control/control_plane_audit.py --check
|
|
|
|
- name: Run registry audit
|
|
run: python3 scripts/release_control/registry_audit.py --check
|
|
|
|
- name: Run canonical mutation registry audits
|
|
run: go test ./internal/mutationregistry ./internal/ai/tools -run 'Test(EveryRegisteredMutationHasDisposition|InfrastructureAPIRoutesResolveToRegistry|TransportCommandCatalogsResolveToRegistry|PatrolJobRegistrationResolvesToRegistry|RuntimeCandidateAuditNegativeFixtures|ActionRouteMethodAuthorityIsExactAndLookalikesFailClosed|NonAdmittingTransportMessagesCannotCarryDispatchAuthority|UnknownTransportLookalikeFailsClosed|RegisteredModelMutationSchemasResolveToClosedRegistry|RetiredMutationAliasesCannotShadowExtensions)' -count=1
|
|
|
|
- name: Run contract audit
|
|
run: python3 scripts/release_control/contract_audit.py --check
|
|
|
|
- name: Run canonical completion guard unit tests
|
|
run: python3 scripts/release_control/canonical_completion_guard_test.py
|
|
|
|
- name: Run browser verification guard unit tests
|
|
run: python3 scripts/release_control/browser_verification_guard_test.py
|
|
|
|
- name: Run control plane audit unit tests
|
|
run: python3 scripts/release_control/control_plane_audit_test.py
|
|
|
|
- name: Run contract audit unit tests
|
|
run: python3 scripts/release_control/contract_audit_test.py
|
|
|
|
- name: Run staged Go formatter unit tests
|
|
run: python3 scripts/release_control/format_staged_go_test.py
|
|
|
|
- name: Run governance stage guard unit tests
|
|
run: python3 scripts/release_control/governance_stage_guard_test.py
|
|
|
|
- name: Run registry audit unit tests
|
|
run: python3 scripts/release_control/registry_audit_test.py
|
|
|
|
- name: Run repo file IO unit tests
|
|
run: python3 scripts/release_control/repo_file_io_test.py
|
|
|
|
- name: Run release promotion policy unit tests
|
|
run: python3 scripts/release_control/release_promotion_policy_test.py
|
|
|
|
- name: Run release container identity verifier unit tests
|
|
run: python3 scripts/release_control/verify_release_container_images_test.py
|
|
|
|
- name: Run stable container alias verifier unit tests
|
|
run: python3 scripts/release_control/verify_stable_container_aliases_test.py
|
|
|
|
- name: Run immutable release integrity unit tests
|
|
run: python3 scripts/release_control/verify_github_release_integrity_test.py
|
|
|
|
- name: Run immutable release setting unit tests
|
|
run: python3 scripts/release_control/check_github_release_immutability_test.py
|
|
|
|
- name: Run status audit unit tests
|
|
run: python3 scripts/release_control/status_audit_test.py
|
|
|
|
- name: Run subsystem contract helper unit tests
|
|
run: python3 scripts/release_control/subsystem_contracts_test.py
|
|
|
|
- name: Run subsystem lookup unit tests
|
|
run: python3 scripts/release_control/subsystem_lookup_test.py
|
|
|
|
- name: Run repo governance guardrail tests
|
|
env:
|
|
PULSE_REPO_ROOT_PULSE: ${{ github.workspace }}/repos/pulse
|
|
PULSE_REPO_ROOT_PULSE_PRO: ${{ github.workspace }}/repos/pulse-pro
|
|
PULSE_REPO_ROOT_PULSE_ENTERPRISE: ${{ github.workspace }}/repos/pulse-enterprise
|
|
PULSE_REPO_ROOT_PULSE_MOBILE: ${{ github.workspace }}/repos/pulse-mobile
|
|
run: go test ./internal/repoctl -count=1
|
|
|
|
- name: Run active-target automated readiness assertion proofs
|
|
run: python3 scripts/release_control/readiness_assertion_guard.py --active-target --proof-type automated
|
|
|
|
- name: Run active-target hybrid readiness assertion proofs
|
|
run: python3 scripts/release_control/readiness_assertion_guard.py --active-target --proof-type hybrid
|
|
|
|
- name: Run readiness assertion guard unit tests
|
|
run: python3 scripts/release_control/readiness_assertion_guard_test.py
|