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
86 changes: 86 additions & 0 deletions .github/workflows/foundation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,39 @@ permissions:
contents: read

jobs:
classify:
name: classify
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
web: ${{ steps.impact.outputs.web }}
firmware: ${{ steps.impact.outputs.firmware }}
pages: ${{ steps.impact.outputs.pages }}
security_release_shared: ${{ steps.impact.outputs.security_release_shared }}
snapshot_contract: ${{ steps.impact.outputs.snapshot_contract }}
snapshot_build: ${{ steps.impact.outputs.snapshot_build }}
process_docs_only: ${{ steps.impact.outputs.process_docs_only }}
uncertain: ${{ steps.impact.outputs.uncertain }}
steps:
- name: Checkout complete history for deterministic diff
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Classify changed paths fail-safe
id: impact
shell: bash
run: >-
python3 scripts/ci_change_impact.py
--event-file "$GITHUB_EVENT_PATH"
--event-name "$GITHUB_EVENT_NAME"
--github-output "$GITHUB_OUTPUT"

web:
name: web
needs: classify
if: needs.classify.outputs.web == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
Expand Down Expand Up @@ -72,6 +103,57 @@ jobs:
cat /tmp/qr-smoke-dom.html
exit 1
fi
- name: Build production-equivalent Web smoke
working-directory: web
shell: bash
run: |
set -euo pipefail
version="$(python3 - <<'PY'
import json
from pathlib import Path
print(json.loads(Path("../firmware/release-profile.json").read_text(encoding="utf-8"))["firmware_version"])
PY
)"
VITE_M5AUTH_WEB_VERSION="$version" \
VITE_M5AUTH_WEB_BUILD_COMMIT="$GITHUB_SHA" \
VITE_M5AUTH_WEB_EXACT_RELEASE=false \
VITE_M5AUTH_FLASH_ENABLED=true \
npx --no-install vite build --mode production-smoke
- name: Test production-equivalent routes and same-origin firmware assets on Chrome
working-directory: web
shell: bash
run: |
set -euo pipefail
smoke_url="http://127.0.0.1:4174/m5authenticator/tests/browser/production-site-smoke.html"
npx --no-install vite preview --mode production-smoke --host 127.0.0.1 --port 4174 --strictPort >/tmp/m5auth-production-smoke-vite.log 2>&1 &
vite_pid=$!
trap 'kill "$vite_pid" 2>/dev/null || true' EXIT

for _ in {1..30}; do
if curl -fsS "$smoke_url" >/dev/null; then
break
fi
sleep 1
done
curl -fsS "$smoke_url" >/dev/null

google-chrome \
--headless=new \
--no-sandbox \
--disable-gpu \
--virtual-time-budget=120000 \
--dump-dom \
"$smoke_url" \
>/tmp/production-site-smoke-dom.html

if ! grep -q 'data-status="pass"' /tmp/production-site-smoke-dom.html \
|| ! grep -q 'data-stage="complete"' /tmp/production-site-smoke-dom.html \
|| ! grep -q 'PRODUCTION_SITE_SMOKE_PASS' /tmp/production-site-smoke-dom.html; then
cat /tmp/production-site-smoke-dom.html
cat /tmp/m5auth-production-smoke-vite.log
exit 1
fi

- name: Build Web App
working-directory: web
run: npm run build
Expand All @@ -80,6 +162,8 @@ jobs:

web-qr-windows:
name: web QR Windows Chrome
needs: classify
if: needs.classify.outputs.web == 'true'
runs-on: windows-latest
timeout-minutes: 10
steps:
Expand Down Expand Up @@ -154,6 +238,8 @@ jobs:

firmware:
name: firmware
needs: classify
if: needs.classify.outputs.firmware == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
Expand Down
46 changes: 42 additions & 4 deletions .github/workflows/issue117-screen-snapshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,47 @@ on:
- "tests/screen_snapshot_*"
- "tests/esp_idf_image_pin_contract_test.py"
- "scripts/esp_idf_build_image.py"
- "scripts/ci_change_impact.py"
- "tools/diagnostics/screen_snapshot.py"
- "scripts/windows/**"
- "docs/testing/screen-snapshot-diagnostics.md"
- "docs/testing/screen-snapshot-usb-tx-investigation.md"
- "AGENTS.md"
- ".github/workflows/issue117-screen-snapshot.yml"
- ".github/workflows/security.yml"

permissions:
contents: read

jobs:
test-screen-snapshot-profile:
name: test-only screen snapshot profile
classify:
name: classify snapshot impact
runs-on: ubuntu-latest
timeout-minutes: 15
timeout-minutes: 5
outputs:
snapshot_contract: ${{ steps.impact.outputs.snapshot_contract }}
snapshot_build: ${{ steps.impact.outputs.snapshot_build }}
uncertain: ${{ steps.impact.outputs.uncertain }}
steps:
- name: Checkout complete history for deterministic diff
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Classify snapshot impact fail-safe
id: impact
run: >-
python3 scripts/ci_change_impact.py
--event-file "$GITHUB_EVENT_PATH"
--event-name "$GITHUB_EVENT_NAME"
--github-output "$GITHUB_OUTPUT"

snapshot-contract:
name: snapshot contract
needs: classify
if: needs.classify.outputs.snapshot_contract == 'true'
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -48,6 +74,18 @@ jobs:
tests/screen_snapshot_tx_atomicity_test.py
tests/screen_snapshot_usage_contract_test.py
tests/screen_snapshot_windows_scripts_contract_test.py

snapshot-build:
name: test-only screen snapshot profile
needs: classify
if: needs.classify.outputs.snapshot_build == 'true'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Verify immutable ESP-IDF build image contract
run: python3 -m unittest tests/esp_idf_image_pin_contract_test.py
- name: Build diagnostics-ON profile with ESP-IDF 5.5.5
Expand Down
66 changes: 63 additions & 3 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ name: Pages

on:
push:
branches:
- main
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
candidate_sha:
description: "PRE-RELEASE CANDIDATE: exact 40-character current main SHA to deploy"
required: true
type: string
candidate_ack:
description: "I understand this pre-release candidate overwrites the public Pages site"
required: true
type: boolean

permissions:
contents: read
Expand All @@ -28,6 +35,48 @@ jobs:
with:
persist-credentials: false
fetch-depth: 0
- name: Authorize exact-main manual candidate deployment
if: github.event_name == 'workflow_dispatch'
shell: bash
env:
CANDIDATE_SHA: ${{ inputs.candidate_sha }}
CANDIDATE_ACK: ${{ inputs.candidate_ack }}
run: |
set -euo pipefail
if [ "$GITHUB_REF" != "refs/heads/main" ]; then
echo "Manual Pages candidate must be invoked from refs/heads/main."
exit 1
fi
if [ "$CANDIDATE_ACK" != "true" ]; then
echo "Manual Pages candidate requires explicit pre-release acknowledgement."
exit 1
fi
if ! [[ "$CANDIDATE_SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then
echo "candidate_sha must be an exact 40-character commit SHA."
exit 1
fi

git fetch --force --no-tags origin main
MAIN_SHA="$(git rev-parse refs/remotes/origin/main)"
if [ "$GITHUB_SHA" != "$MAIN_SHA" ]; then
echo "Workflow source SHA $GITHUB_SHA is not fresh current main $MAIN_SHA."
exit 1
fi
if [ "${CANDIDATE_SHA,,}" != "${MAIN_SHA,,}" ]; then
echo "candidate_sha $CANDIDATE_SHA does not equal fresh current main $MAIN_SHA."
exit 1
fi

{
echo "## PRE-RELEASE Pages candidate"
echo ""
echo "- source ref: `$GITHUB_REF`"
echo "- source SHA: `$GITHUB_SHA`"
echo "- candidate SHA: `$CANDIDATE_SHA`"
echo "- current main: `$MAIN_SHA`"
echo "- this deployment is a mutable public candidate, not an immutable GitHub Release"
} >> "$GITHUB_STEP_SUMMARY"

- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
Expand Down Expand Up @@ -64,7 +113,8 @@ jobs:
exit 1
fi
EXACT_RELEASE=false
if git tag --points-at "$GITHUB_SHA" --list "$EXPECTED_TAG" | grep -Fxq "$EXPECTED_TAG"; then
if [ "$GITHUB_EVENT_NAME" != "workflow_dispatch" ] \
&& git tag --points-at "$GITHUB_SHA" --list "$EXPECTED_TAG" | grep -Fxq "$EXPECTED_TAG"; then
EXACT_RELEASE=true
fi
{
Expand All @@ -73,6 +123,16 @@ jobs:
echo "VITE_M5AUTH_WEB_EXACT_RELEASE=$EXACT_RELEASE"
echo "M5AUTH_EXACT_RELEASE=$EXACT_RELEASE"
} >> "$GITHUB_ENV"

if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
{
echo ""
echo "### Candidate build identity"
echo "- Web/Firmware version: `v$VERSION`"
echo "- build commit: `$GITHUB_SHA`"
echo "- exact release: `false`"
} >> "$GITHUB_STEP_SUMMARY"
fi
- name: Install exact Web dependencies
working-directory: web
run: npm ci --ignore-scripts --no-audit --no-fund
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ jobs:
run: python3 -m unittest tests/esp_idf_image_pin_contract_test.py
- name: Verify CI supply-chain privilege and workspace boundaries
run: python3 -m unittest tests/ci_supply_chain_boundary_test.py
- name: Verify CI change-impact routing and Pages cadence
run: python3 -m unittest tests/ci_change_impact_test.py
- name: Verify protected-main release authorization
run: python3 -m unittest tests/release_authorization_test.py
- name: Verify signed release attestation provenance contract
Expand Down
Loading
Loading