nigig-org/.forgejo/workflows/nigig-site.yml
Arena Agent 5d2d890f70
Some checks failed
doc-engine / engine (push) Waiting to run
doc-engine / coverage (push) Waiting to run
doc-engine / consumer (push) Waiting to run
email / gates (push) Waiting to run
email / email-domain (push) Waiting to run
email / nigig-email (push) Waiting to run
nigig-build (CAD) / cad-module (push) Waiting to run
nigig-build (CAD) / full-crate-check (push) Waiting to run
nigig-build (CAD) / doc-workspace-coverage (push) Waiting to run
nigig-build (CAD) / cad-widget-coverage (push) Waiting to run
email / supply-chain (push) Waiting to run
nigig-build (CAD) / supply-chain (push) Waiting to run
nigig-build (CAD) / cad-engine-coverage (push) Waiting to run
nigig-map / test (push) Waiting to run
sms / android (push) Waiting to run
sms / nigig-sms (push) Waiting to run
sms / supply-chain (push) Waiting to run
sms / gates (push) Waiting to run
sms / robius-sms (push) Waiting to run
spreadsheet / engine-coverage (push) Waiting to run
spreadsheet / ui-controller-coverage (push) Waiting to run
traffic / gates (push) Waiting to run
traffic / nigig-traffic (push) Waiting to run
traffic / supply-chain (push) Waiting to run
nigig-site / Owned paths and honest test contracts (push) Has been cancelled
nigig-site / Cargo check-all-targets (push) Has been cancelled
nigig-site / Cargo clippy-site-owned (push) Has been cancelled
nigig-site / Cargo contained-media-export-fixtures (push) Has been cancelled
nigig-site / Cargo containment-storage-crypto (push) Has been cancelled
nigig-site / Cargo integration-non-live (push) Has been cancelled
nigig-site / Cargo production-dependency-containment (push) Has been cancelled
nigig-site / Cargo unit (push) Has been cancelled
nigig-site / Runtime UI (explicitly skipped until enabled) (push) Has been cancelled
nigig-site / Migration and recovery (explicitly skipped until enabled) (push) Has been cancelled
nigig-site / Media limits (explicitly skipped until enabled) (push) Has been cancelled
nigig-site / Real server interoperability (explicitly skipped until enabled) (push) Has been cancelled
nigig-site / Security and supply-chain baseline (push) Has been cancelled
nigig-site / Release capability gate (push) Has been cancelled
repo hygiene / hygiene (push) Has been cancelled
feat(nigig-site): enforce SITE-01 fail-closed containment
Contain production capabilities, remove the production sync surface, and keep legacy media/export/transport implementations test-only.

Require authenticated existing-key storage with preservation-first recovery and sticky write disablement, backed by deterministic fault and concurrency tests plus dependency and workflow contracts.
2026-09-12 15:46:30 +00:00

481 lines
21 KiB
YAML

name: nigig-site
# Truthful Site CI. Optional capabilities are visibly skipped until their
# dedicated harness/server exists; a release-gate invocation fails instead of
# treating missing infrastructure as a pass.
on:
push:
paths:
- 'crates/apps/nigig-site/**'
- 'crates/nimanyatta/**'
- 'crates/nigig-core/**'
- 'crates/nigig-uikit/**'
- 'crates/matrix_client/**'
- 'crates/robius-notification/**'
- 'crates/apps/doc/**'
- 'crates/apps/pdf/**'
- 'crates/apps/nigig_doc_scanner/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- '.forgejo/workflows/nigig-site.yml'
pull_request:
paths:
- 'crates/apps/nigig-site/**'
- 'crates/nimanyatta/**'
- 'crates/nigig-core/**'
- 'crates/nigig-uikit/**'
- 'crates/matrix_client/**'
- 'crates/robius-notification/**'
- 'crates/apps/doc/**'
- 'crates/apps/pdf/**'
- 'crates/apps/nigig_doc_scanner/**'
- 'Cargo.toml'
- 'Cargo.lock'
- 'rust-toolchain.toml'
- '.forgejo/workflows/nigig-site.yml'
workflow_dispatch:
inputs:
enforce_release_gates:
description: 'Fail unless runtime, migration, media, and real-server gates are enabled'
required: true
type: boolean
default: false
permissions:
contents: read
env:
CARGO_BUILD_JOBS: '1'
CARGO_INCREMENTAL: '0'
CARGO_PROFILE_DEV_DEBUG: '0'
CARGO_PROFILE_TEST_DEBUG: '0'
CARGO_TERM_COLOR: always
RUST_BACKTRACE: '1'
NIGIG_SITE_CI_TIMEOUT_SECONDS: '3300'
jobs:
ownership-and-contracts:
name: Owned paths and honest test contracts
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Every declared path filter must match repository files
run: |
set -euo pipefail
python3 - <<'PY'
from collections import Counter
from pathlib import Path
import glob, re
workflow = Path('.forgejo/workflows/nigig-site.yml').read_text()
trigger_block = workflow.split('\npermissions:', 1)[0]
filters = re.findall(r"^ - '([^']+)'$", trigger_block, re.MULTILINE)
counts = Counter(filters)
assert filters, 'workflow path-filter scan matched nothing'
assert all(count == 2 for count in counts.values()), (
'push and pull_request path filters must be identical', counts
)
for pattern in sorted(counts):
matches = [Path(item) for item in glob.glob(pattern, recursive=True)]
files = [item for item in matches if item.is_file()]
assert files, f'path filter matches no repository files: {pattern}'
print(f'{pattern}: {len(files)} file(s)')
PY
- name: Prove Site-owned source paths were scanned
run: |
set -euo pipefail
mapfile -d '' files < <(find crates/apps/nigig-site \
-type f \( -name '*.rs' -o -name 'Cargo.toml' -o -name '*.sh' \) \
-print0)
if [ "${#files[@]}" -lt 10 ]; then
echo "ERROR: Site source scan matched only ${#files[@]} files." >&2
exit 1
fi
printf 'matched %d Site-owned source/manifest/tool files\n' "${#files[@]}"
test -f crates/apps/nigig-site/src/store.rs
test -f crates/apps/nigig-site/tests/sync_e2e.rs
test -f crates/apps/nigig-site/tools/ci-cargo.sh
test -x crates/apps/nigig-site/tools/check-production-deps.sh
test -f .forgejo/workflows/nigig-site.yml
- name: SITE-01 production containment is structural and fail-closed
run: |
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re, tomllib
root = Path('crates/apps/nigig-site')
lib = (root / 'src/lib.rs').read_text()
for module in ('doc_export', 'gif', 'ocr', 'report_pdf', 'video'):
pattern = rf'#\[cfg\(test\)\]\s+pub mod {module};'
assert re.search(pattern, lib), f'{module} must remain test-only'
manifest = tomllib.loads((root / 'Cargo.toml').read_text())
features = set(manifest.get('features', {}))
assert not features, (
'feature configuration could bypass SITE-01 containment', features
)
production_deps = manifest.get('dependencies', {})
for dependency in (
'nigig-core', 'nigig-uikit', 'doc-ui', 'reqwest',
'makepad-ai-hub', 'makepad-system-speech',
'robius-location', 'robius-notification',
'nigig-pdf-cos', 'nigig-pdf-document',
'nigig-pdf-graphics', 'nigig-pdf-makepad',
'nigig_doc_scanner', 'doc-engine', 'image', 'weezl',
'zip', 'quick-xml',
):
assert dependency not in production_deps, (
f'contained dependency leaked into production: {dependency}'
)
for path in (root / 'src/nimanyatta_client.rs', root / 'src/sync.rs'):
assert not path.exists(), f'test-only transport/codec leaked into src: {path}'
for module in ('nimanyatta_client', 'sync'):
assert not re.search(rf'pub mod {module};', lib), (
f'production transport/codec module exported: {module}'
)
assert (root / 'tests/support/nimanyatta_fixture.rs').is_file()
assert (root / 'tests/support/sync_fixture.rs').is_file()
assert 'nigig_uikit::script_mod' not in lib
assert 'pub use nigig_uikit::shared::*' not in lib
assert 'doc_ui::script_mod' not in lib
for module in ('persistence', 'location', 'tile_service'):
assert not re.search(rf'pub mod {module}\s*\{{', lib), (
f'unsafe compatibility re-export restored: {module}'
)
active = '\n'.join(
path.read_text() for path in (
root / 'src/main.rs',
root / 'src/scheduler.rs',
root / 'src/site_frame/screens/chat.rs',
root / 'src/site_frame/screens/meetings.rs',
root / 'src/site_frame/screens/more_hub.rs',
root / 'src/site_frame/screens/report_editor.rs',
root / 'src/site_frame/screens/reports.rs',
root / 'src/site_frame/screens/sites.rs',
root / 'src/site_frame/screens/workers.rs',
)
)
for token in (
'CameraWidget', 'get_latest_location', 'makepad_system_speech',
'robius_notification::', 'photos_to_gif_file',
'photos_to_clip_file', 'request_send_text', 'demo-site',
):
assert token not in active, f'reachable contained capability found: {token}'
main = (root / 'src/main.rs').read_text()
assert re.search(
r'app_shell\s*:=\s*View\s*\{.*?visible:\s*false',
main,
re.DOTALL,
)
assert re.search(
r'recovery_page\s*:=\s*View\s*\{.*?visible:\s*true',
main,
re.DOTALL,
)
assert 'schedule_daily_eod' not in main and 'check_and_fire_due' not in main
scheduler = (root / 'src/scheduler.rs').read_text()
assert 'register_os_schedule' not in scheduler
assert 'check_and_fire_due' not in scheduler
chat = (root / 'src/site_frame/screens/chat.rs').read_text()
assert 'TextInput' not in chat, 'confidential chat input restored'
editor = (root / 'src/site_frame/screens/report_editor.rs').read_text()
assert 'submit_btn' not in editor, 'unreviewed report submission restored'
reports = (root / 'src/site_frame/screens/reports.rs').read_text()
assert '.approve(' not in reports and '.reject(' not in reports
daily_report = (root / 'src/domain/daily_report.rs').read_text()
assert 'refine_with_ai' not in daily_report
store = (root / 'src/store.rs').read_text()
assert 'pub fn save(' not in store and 'pub fn save_async(' not in store
assert 'pub fn load()' not in store
assert 'flush_writer_queue' not in store
crypto = (root / 'src/crypto.rs').read_text()
assert 'set_password' not in crypto and 'load_or_create' not in crypto
print('SITE-01 containment contract passed')
PY
- name: Live E2E must be explicitly ignored, never early-return green
run: |
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('crates/apps/nigig-site/tests/sync_e2e.rs')
s = p.read_text()
assert '#[ignore = ' in s, 'live test must be an explicit ignored test'
assert 'NIMANYATTA_E2E_URL' in s, 'live test must name required config'
assert 'fn live_round_trip()' in s, 'live test entry point missing'
body = s[s.index('fn live_round_trip()'):]
assert 'return;' not in body, 'live test may not early-return as a pass'
print('live E2E is explicit: normal CI reports ignored; dedicated CI runs --ignored')
PY
- name: Third-party actions must be pinned to full commit SHAs
run: |
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
p = Path('.forgejo/workflows/nigig-site.yml')
bad = []
for number, line in enumerate(p.read_text().splitlines(), 1):
m = re.search(r'\buses:\s*([^\s#]+)', line)
if not m:
continue
ref = m.group(1)
if ref.startswith('./'):
continue
if not re.search(r'@[0-9a-f]{40}$', ref):
bad.append((number, ref))
assert not bad, f'unpinned action references: {bad}'
print('all third-party action references are immutable full SHAs')
PY
cargo-gates:
name: Cargo ${{ matrix.label }}
runs-on: ubuntu-latest
timeout-minutes: 65
strategy:
fail-fast: false
matrix:
include:
- label: check-all-targets
artifact: check
command: cargo check --locked -p nigig-site --all-targets
- label: production-dependency-containment
artifact: production-dependencies
command: crates/apps/nigig-site/tools/check-production-deps.sh /tmp/nigig-site-ci/production-tree.txt
- label: unit
artifact: unit
command: cargo test --locked -p nigig-site --lib -- --test-threads=1
- label: integration-non-live
artifact: integration
command: cargo test --locked -p nigig-site --tests -- --test-threads=1
- label: containment-storage-crypto
artifact: containment-storage
command: cargo test --locked -p nigig-site --lib containment::tests -- --test-threads=1 && cargo test --locked -p nigig-site --lib ai_refine::tests -- --test-threads=1 && cargo test --locked -p nigig-site --lib crypto::tests -- --test-threads=1 && cargo test --locked -p nigig-site --lib store::tests -- --test-threads=1
- label: contained-media-export-fixtures
artifact: contained-media
command: cargo test --locked -p nigig-site --lib gif::tests -- --test-threads=1 && cargo test --locked -p nigig-site --lib video::tests -- --test-threads=1 && cargo test --locked -p nigig-site --lib ocr::tests -- --test-threads=1 && cargo test --locked -p nigig-site --lib report_pdf::tests -- --test-threads=1 && cargo test --locked -p nigig-site --lib doc_export::tests -- --test-threads=1
- label: clippy-site-owned
artifact: clippy
command: cargo clippy --locked -p nigig-site --all-targets --no-deps -- -D warnings
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install pinned toolchain and native packages
run: crates/apps/nigig-site/tools/ci-setup-ubuntu.sh
- name: Run ${{ matrix.label }} with timeout and RSS evidence
env:
SITE_GATE_COMMAND: ${{ matrix.command }}
run: |
crates/apps/nigig-site/tools/ci-cargo.sh \
"${{ matrix.artifact }}" bash -lc "$SITE_GATE_COMMAND"
- name: Upload full command log and resource evidence
if: always()
uses: forgejo/upload-artifact@16871d9e8cfcf27ff31822cac382bbb5450f1e1e # v4
with:
name: nigig-site-${{ matrix.artifact }}-${{ github.sha }}
path: /tmp/nigig-site-ci/
if-no-files-found: error
retention-days: 14
runtime-ui:
name: Runtime UI (explicitly skipped until enabled)
if: ${{ vars.NIGIG_SITE_RUNTIME_UI_ENABLED == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 65
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: crates/apps/nigig-site/tools/ci-setup-ubuntu.sh
- name: Execute real runtime assertions
run: |
set -euo pipefail
test -f crates/apps/nigig-site/tests/runtime_ui.rs
crates/apps/nigig-site/tools/ci-cargo.sh runtime-ui \
cargo test --locked -p nigig-site --test runtime_ui -- --test-threads=1
- if: always()
uses: forgejo/upload-artifact@16871d9e8cfcf27ff31822cac382bbb5450f1e1e # v4
with:
name: nigig-site-runtime-ui-${{ github.sha }}
path: /tmp/nigig-site-ci/
if-no-files-found: error
retention-days: 14
migration-recovery:
name: Migration and recovery (explicitly skipped until enabled)
if: ${{ vars.NIGIG_SITE_MIGRATION_GATE_ENABLED == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 65
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: crates/apps/nigig-site/tools/ci-setup-ubuntu.sh
- name: Execute recovery and fault corpus
run: |
set -euo pipefail
test -f crates/apps/nigig-site/tests/storage_recovery.rs
crates/apps/nigig-site/tools/ci-cargo.sh migration-recovery \
cargo test --locked -p nigig-site --test storage_recovery -- --test-threads=1
- if: always()
uses: forgejo/upload-artifact@16871d9e8cfcf27ff31822cac382bbb5450f1e1e # v4
with:
name: nigig-site-migration-${{ github.sha }}
path: /tmp/nigig-site-ci/
if-no-files-found: error
retention-days: 14
media-limits:
name: Media limits (explicitly skipped until enabled)
if: ${{ vars.NIGIG_SITE_MEDIA_LIMITS_ENABLED == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 65
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: crates/apps/nigig-site/tools/ci-setup-ubuntu.sh
- name: Execute adversarial media limits
run: |
set -euo pipefail
test -f crates/apps/nigig-site/tests/media_limits.rs
crates/apps/nigig-site/tools/ci-cargo.sh media-limits \
cargo test --locked -p nigig-site --test media_limits -- --test-threads=1
- if: always()
uses: forgejo/upload-artifact@16871d9e8cfcf27ff31822cac382bbb5450f1e1e # v4
with:
name: nigig-site-media-limits-${{ github.sha }}
path: /tmp/nigig-site-ci/
if-no-files-found: error
retention-days: 14
sync-interoperability:
name: Real server interoperability (explicitly skipped until enabled)
if: ${{ vars.NIGIG_SITE_SYNC_E2E_ENABLED == 'true' }}
runs-on: ubuntu-latest
timeout-minutes: 65
env:
NIMANYATTA_E2E_URL: ${{ secrets.NIMANYATTA_E2E_URL }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- run: crates/apps/nigig-site/tools/ci-setup-ubuntu.sh
- name: Require pinned server source and execute ignored live test
run: |
set -euo pipefail
test -n "$NIMANYATTA_E2E_URL"
test -d crates/nimanyatta/src
crates/apps/nigig-site/tools/ci-cargo.sh sync-e2e \
cargo test --locked -p nigig-site --test sync_e2e -- \
--ignored --exact live_round_trip --test-threads=1
- if: always()
uses: forgejo/upload-artifact@16871d9e8cfcf27ff31822cac382bbb5450f1e1e # v4
with:
name: nigig-site-sync-e2e-${{ github.sha }}
path: /tmp/nigig-site-ci/
if-no-files-found: error
retention-days: 14
security-supply-chain:
name: Security and supply-chain baseline
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Lockfile resolves without mutation
run: |
set -euo pipefail
test -f Cargo.lock
cargo metadata --locked --format-version 1 >/tmp/nigig-site-metadata.json
git diff --exit-code -- Cargo.lock
- name: Every live git dependency has a full immutable revision
run: |
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import json, re, tomllib
failures = []
def walk(value, where):
if isinstance(value, dict):
if 'git' in value:
rev = value.get('rev')
if not isinstance(rev, str) or not re.fullmatch(r'[0-9a-f]{40}', rev):
failures.append((where, value.get('git'), rev))
for key, child in value.items():
walk(child, f'{where}.{key}')
elif isinstance(value, list):
for index, child in enumerate(value):
walk(child, f'{where}[{index}]')
metadata = json.loads(Path('/tmp/nigig-site-metadata.json').read_text())
packages = {package['id']: package for package in metadata['packages']}
manifests = {Path('Cargo.toml').resolve()}
manifests.update(
Path(packages[member]['manifest_path'])
for member in metadata['workspace_members']
)
assert manifests, 'live workspace manifest scan matched nothing'
for manifest in sorted(manifests):
with manifest.open('rb') as fh:
data = tomllib.load(fh)
walk(data, str(manifest))
assert not failures, f'unpinned git dependencies: {failures}'
print(f'checked {len(manifests)} live manifests; all git dependencies use full revs')
PY
- name: No plaintext sync/store exception may be hidden in workflow shell
run: |
set -euo pipefail
if grep -nE 'cargo (check|test|clippy).*(\|\| true|; true)' \
.forgejo/workflows/nigig-site.yml; then
echo 'ERROR: Cargo failure suppression found in Site workflow.' >&2
exit 1
fi
echo 'no Cargo failure suppression found'
release-capability-gate:
name: Release capability gate
runs-on: ubuntu-latest
timeout-minutes: 10
env:
ENFORCE_RELEASE_GATES: ${{ inputs.enforce_release_gates }}
RUNTIME_ENABLED: ${{ vars.NIGIG_SITE_RUNTIME_UI_ENABLED }}
MIGRATION_ENABLED: ${{ vars.NIGIG_SITE_MIGRATION_GATE_ENABLED }}
MEDIA_ENABLED: ${{ vars.NIGIG_SITE_MEDIA_LIMITS_ENABLED }}
SYNC_ENABLED: ${{ vars.NIGIG_SITE_SYNC_E2E_ENABLED }}
NIMANYATTA_E2E_URL: ${{ secrets.NIMANYATTA_E2E_URL }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Development status or hard release gate
run: |
set -euo pipefail
release=false
case "${GITHUB_REF:-}" in refs/tags/*) release=true ;; esac
if [ "${ENFORCE_RELEASE_GATES:-false}" = true ]; then release=true; fi
if [ "$release" != true ]; then
echo 'Development CI capability status:'
echo " runtime-ui=${RUNTIME_ENABLED:-false} (disabled jobs are shown as skipped)"
echo " migration=${MIGRATION_ENABLED:-false} (disabled jobs are shown as skipped)"
echo " media-limits=${MEDIA_ENABLED:-false} (disabled jobs are shown as skipped)"
echo " sync-e2e=${SYNC_ENABLED:-false} (disabled jobs are shown as skipped)"
exit 0
fi
test "${RUNTIME_ENABLED:-false}" = true
test "${MIGRATION_ENABLED:-false}" = true
test "${MEDIA_ENABLED:-false}" = true
test "${SYNC_ENABLED:-false}" = true
test -n "$NIMANYATTA_E2E_URL"
test -f crates/apps/nigig-site/tests/runtime_ui.rs
test -f crates/apps/nigig-site/tests/storage_recovery.rs
test -f crates/apps/nigig-site/tests/media_limits.rs
test -d crates/nimanyatta/src
echo 'all required release capabilities are configured; their jobs still decide pass/fail'