Skip to content

feat(site): interactive 3D blueprint viewer for hardware diagrams #398

feat(site): interactive 3D blueprint viewer for hardware diagrams

feat(site): interactive 3D blueprint viewer for hardware diagrams #398

Workflow file for this run

---
name: Pull Request Validation
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches: [main]
workflow_dispatch:
permissions:
contents: write
pull-requests: write
issues: write
concurrency:
group: pr-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: true
env:
DOCKER_BUILDKIT: 1
COMPOSE_DOCKER_CLI_BUILD: 1
jobs:
# Fork guard: block fork PRs from running on self-hosted runners with write perms
fork-guard:
name: Fork PR Guard
runs-on: ubuntu-latest
if: >-
github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository
steps:
- run: echo "Not a fork PR - proceeding"
# -- CI Stages (containerized) -------------------------------------------
ci:
name: OASIS_OS CI
needs: fork-guard
runs-on: self-hosted
timeout-minutes: 60
steps:
- name: Pre-checkout cleanup
run: |
for item in outputs target psp_output_file.log .git/index.lock; do
if [ -d "$item" ] || [ -f "$item" ]; then
docker run --rm -v "$(pwd):/workspace" busybox:1.36.1 sh -c \
"rm -rf /workspace/$item" 2>/dev/null || \
sudo rm -rf "$item" 2>/dev/null || true
fi
done
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
clean: true
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.head_ref }}
- name: Set UID/GID
run: |
echo "USER_ID=$(id -u)" >> $GITHUB_ENV
echo "GROUP_ID=$(id -g)" >> $GITHUB_ENV
- name: Build CI Docker image
run: docker compose --profile ci build rust-ci
# -- Formatting -------------------------------------------------------
- name: Format check
timeout-minutes: 5
run: docker compose --profile ci run --rm rust-ci cargo fmt --all -- --check
# -- Linting -----------------------------------------------------------
- name: Clippy
timeout-minutes: 10
run: docker compose --profile ci run --rm rust-ci cargo clippy --workspace -- -D warnings
# -- Nightly Warnings Check (advisory) -----------------------------------
- name: Nightly warnings check
timeout-minutes: 10
continue-on-error: true
run: >
docker compose --profile ci run --rm rust-ci
cargo +nightly clippy --workspace -- -D warnings
-W unused-comparisons
# -- Documentation ------------------------------------------------------
- name: Doc build
timeout-minutes: 10
run: >
docker compose --profile ci run --rm
-e RUSTDOCFLAGS="-D warnings"
rust-ci cargo doc --workspace --no-deps
# -- Markdown Link Check ------------------------------------------------
- name: Markdown link check
timeout-minutes: 5
run: |
set -o pipefail
FAIL=0
# Check top-level markdown and docs/ (skip vendored target/node_modules)
for target in *.md docs/ scripts/psp-scenarios.md; do
[ -e "$target" ] || continue
md-link-checker "$target" --internal-only 2>&1 \
| tee -a link_check_output.txt || FAIL=1
done
if [ $FAIL -ne 0 ]; then
echo "### Markdown Link Check" >> $GITHUB_STEP_SUMMARY
grep -E '(broken link|->.*not found)' link_check_output.txt \
>> $GITHUB_STEP_SUMMARY 2>/dev/null || true
fi
exit $FAIL
# -- Tests -------------------------------------------------------------
- name: Test
timeout-minutes: 15
run: |
set -o pipefail
docker compose --profile ci run --rm rust-ci \
cargo test --workspace 2>&1 | tee test_output.txt
# -- Build -------------------------------------------------------------
- name: Build (release)
timeout-minutes: 15
run: docker compose --profile ci run --rm rust-ci cargo build --workspace --release
# -- Screenshot Regression Tests ----------------------------------------
- name: Screenshot tests (generate)
timeout-minutes: 5
run: |
docker compose --profile ci run --rm \
-e SDL_VIDEODRIVER=dummy -e SDL_RENDER_DRIVER=software \
rust-ci cargo run -p oasis-app --bin screenshot-tests --release
- name: Upload screenshot report
if: always()
uses: actions/upload-artifact@v4
with:
name: screenshot-report
path: screenshots/tests/
retention-days: 14
# -- License / Advisory ------------------------------------------------
- name: cargo-deny
run: docker compose --profile ci run --rm rust-ci cargo deny check
# -- Benchmarks (manual only via workflow_dispatch) -------------------------
- name: Download main branch baseline
if: github.event_name == 'workflow_dispatch'
continue-on-error: true
uses: dawidd6/action-download-artifact@v6
with:
workflow: main-ci.yml
branch: main
name: criterion-baseline
path: baseline-criterion/
if_no_artifact_found: warn
- name: Benchmarks
if: github.event_name == 'workflow_dispatch'
run: |
set -o pipefail
docker compose --profile ci run --rm rust-ci \
cargo bench --workspace 2>&1 | tee bench_results.txt
echo "::group::Benchmark Results"
grep -E '(time:|bench)' bench_results.txt || true
echo "::endgroup::"
- name: Benchmark regression check
if: github.event_name == 'workflow_dispatch'
run: |
bash scripts/bench-compare.sh \
baseline-criterion/ \
target/criterion/ \
10 \
>> $GITHUB_STEP_SUMMARY
- name: Upload benchmark results
if: github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v4
with:
name: benchmark-results
path: bench_results.txt
if-no-files-found: ignore
retention-days: 30
# -- Test Metrics -------------------------------------------------------
- name: Test metrics summary
if: always()
run: |
if [ ! -f test_output.txt ]; then
echo "No test output captured (test step may have been skipped)."
echo "### Test Summary" >> $GITHUB_STEP_SUMMARY
echo "No test output available." >> $GITHUB_STEP_SUMMARY
exit 0
fi
PASS=$(grep -c '\.\.\. *ok$' test_output.txt 2>/dev/null) || PASS=0
FAIL=$(grep -c 'FAILED$' test_output.txt 2>/dev/null) || FAIL=0
TOTAL=$((PASS + FAIL))
echo "### Test Summary" >> $GITHUB_STEP_SUMMARY
echo "| Metric | Count |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Passed | $PASS |" >> $GITHUB_STEP_SUMMARY
echo "| Failed | $FAIL |" >> $GITHUB_STEP_SUMMARY
echo "| Total | $TOTAL |" >> $GITHUB_STEP_SUMMARY
# -- PSP Backend Build -------------------------------------------------
- name: Setup PSP SDK
uses: ./.github/actions/setup-psp
- name: Build PSP EBOOT
run: |
cd crates/oasis-backend-psp
RUST_PSP_BUILD_STD=1 cargo +nightly psp --release
# -- PSP Emulator Test -------------------------------------------------
- name: Run OASIS_OS EBOOT in PPSSPPHeadless
run: |
docker compose --profile psp run --rm -e PPSSPP_HEADLESS=1 ppsspp \
/roms/release/EBOOT.PBP --timeout=30 2>/dev/null || true
if [ -f psp_output_file.log ]; then
cat psp_output_file.log
else
echo "No output log -- headless exited without crash (TIMEOUT ok)"
fi
- name: Fix Docker file ownership
if: always()
run: |
for dir in target outputs; do
if [ -d "$dir" ]; then
docker run --rm -v "$(pwd)/$dir:/workspace" busybox:1.36.1 \
chown -Rh "$(id -u):$(id -g)" /workspace 2>/dev/null || true
fi
done
# -- Gemini AI Code Review -----------------------------------------------
gemini-review:
name: Gemini AI Code Review
needs: fork-guard
if: >-
github.event_name == 'pull_request' &&
!github.event.pull_request.draft
runs-on: self-hosted
timeout-minutes: 30
outputs:
status: ${{ steps.review.outputs.status }}
steps:
- name: Pre-checkout cleanup
run: |
for item in outputs target psp_output_file.log .git/index.lock; do
if [ -d "$item" ] || [ -f "$item" ]; then
docker run --rm -v "$(pwd):/workspace" busybox:1.36.1 sh -c \
"rm -rf /workspace/$item" 2>/dev/null || \
sudo rm -rf "$item" 2>/dev/null || true
fi
done
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
clean: true
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.head_ref }}
- name: Log commit info
id: check-agent
run: |
LAST_AUTHOR=$(git log -1 --format='%an')
echo "Last commit author: $LAST_AUTHOR"
IS_AGENT="false"
if [[ "$LAST_AUTHOR" == "AI Review Agent" ]] || \
[[ "$LAST_AUTHOR" == "AI Pipeline Agent" ]] || \
[[ "$LAST_AUTHOR" == "AI Agent Bot" ]]; then
IS_AGENT="true"
fi
echo "is_agent_commit=$IS_AGENT" >> $GITHUB_OUTPUT
- name: Run Gemini review
id: review
env:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
if ! command -v github-agents &>/dev/null; then
echo "::warning::github-agents not found on PATH - skipping Gemini review"
echo "status=skipped" >> $GITHUB_OUTPUT
exit 0
fi
set +e
OUTPUT=$(github-agents pr-review "$PR_NUMBER" --editor 2>&1)
EXIT_CODE=$?
set -e
echo "$OUTPUT"
echo "$OUTPUT" > gemini-review.md
if [ $EXIT_CODE -ne 0 ]; then
if echo "$OUTPUT" | grep -qiE '429|rate.?limit|quota|resource.?exhausted'; then
echo "::warning::Gemini API rate limit hit - skipping review"
echo "status=rate_limited" >> $GITHUB_OUTPUT
exit 0
elif echo "$OUTPUT" | grep -qiE '503|502|service.?unavailable|ECONNREFUSED|ETIMEDOUT'; then
echo "::warning::Gemini API unavailable - skipping review"
echo "status=unavailable" >> $GITHUB_OUTPUT
exit 0
elif echo "$OUTPUT" | grep -qiE 'panicked at|thread.*panic'; then
echo "::warning::Gemini review tool crashed - skipping review"
echo "status=tool_crash" >> $GITHUB_OUTPUT
exit 0
else
echo "status=failure" >> $GITHUB_OUTPUT
exit $EXIT_CODE
fi
fi
echo "status=success" >> $GITHUB_OUTPUT
- name: Upload review artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: gemini-review-${{ github.run_id }}-${{ github.run_attempt }}
path: gemini-review.md
retention-days: 7
if-no-files-found: ignore
# -- Codex AI Code Review (DISABLED) --------------------------------------
# DISABLED: OpenAI Codex is being phased out due to security concerns.
# OpenAI has entered partnerships with governments that enable mass surveillance
# and autonomous weapons decision-making. This poses unacceptable risk.
# We strongly recommend Anthropic (Claude) models instead.
codex-review:
name: Codex AI Code Review (DISABLED)
needs: [fork-guard, gemini-review]
if: contains(github.event.pull_request.labels.*.name, 'enable-codex') # Permanently disabled -- see security notice above
runs-on: self-hosted
timeout-minutes: 15
continue-on-error: true
outputs:
status: ${{ steps.review.outputs.status }}
steps:
- name: Pre-checkout cleanup
run: |
for item in outputs target psp_output_file.log .git/index.lock; do
if [ -d "$item" ] || [ -f "$item" ]; then
docker run --rm -v "$(pwd):/workspace" busybox:1.36.1 sh -c \
"rm -rf /workspace/$item" 2>/dev/null || \
sudo rm -rf "$item" 2>/dev/null || true
fi
done
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
clean: true
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.head_ref }}
- name: Run Codex review
id: review
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
if ! command -v github-agents &>/dev/null; then
echo "::warning::github-agents not found on PATH - skipping Codex review"
echo "status=skipped" >> $GITHUB_OUTPUT
exit 0
fi
set +e
OUTPUT=$(github-agents pr-review "$PR_NUMBER" --agent codex 2>&1)
EXIT_CODE=$?
set -e
echo "$OUTPUT"
echo "$OUTPUT" > codex-review.md
if [ $EXIT_CODE -ne 0 ]; then
if echo "$OUTPUT" | grep -qiE '429|rate.?limit|quota|resource.?exhausted'; then
echo "::warning::Codex API rate limit hit"
echo "status=rate_limited" >> $GITHUB_OUTPUT
exit 0
elif echo "$OUTPUT" | grep -qiE '503|502|service.?unavailable|ECONNREFUSED|ETIMEDOUT'; then
echo "::warning::Codex API unavailable"
echo "status=unavailable" >> $GITHUB_OUTPUT
exit 0
elif echo "$OUTPUT" | grep -qiE 'panicked at|thread.*panic'; then
echo "::warning::Codex review tool crashed - skipping review"
echo "status=tool_crash" >> $GITHUB_OUTPUT
exit 0
else
echo "status=failure" >> $GITHUB_OUTPUT
exit $EXIT_CODE
fi
fi
echo "status=success" >> $GITHUB_OUTPUT
- name: Upload review artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: codex-review-${{ github.run_id }}-${{ github.run_attempt }}
path: codex-review.md
retention-days: 7
if-no-files-found: ignore
# -- Agent Review Response (responds to Gemini/Codex feedback) -----------
agent-review-response:
name: Agent Review Response
needs: [ci, gemini-review, codex-review]
if: |
always() &&
!cancelled() &&
github.event_name == 'pull_request' &&
!github.event.pull_request.draft &&
!contains(github.event.pull_request.labels.*.name, 'no-auto-fix')
runs-on: self-hosted
timeout-minutes: 30
outputs:
made_changes: ${{ steps.agent-fix.outputs.made_changes }}
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ secrets.AGENT_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
steps:
- name: Pre-checkout cleanup
run: |
for item in outputs target psp_output_file.log .git/index.lock; do
if [ -d "$item" ] || [ -f "$item" ]; then
docker run --rm -v "$(pwd):/workspace" busybox:1.36.1 sh -c \
"rm -rf /workspace/$item" 2>/dev/null || \
sudo rm -rf "$item" 2>/dev/null || true
fi
done
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
clean: false
token: ${{ secrets.AGENT_TOKEN }}
ref: ${{ github.head_ref }}
- name: Ensure clean working directory
run: |
git checkout -- . 2>/dev/null || true
git clean -fd 2>/dev/null || true
- name: Check iteration count
id: iteration
uses: ./.github/actions/agent-iteration-check
with:
pr_number: ${{ github.event.pull_request.number }}
max_iterations: '5'
agent_type: 'review-fix'
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Skip if max iterations reached
if: steps.iteration.outputs.exceeded_max == 'true'
run: |
echo "Maximum iterations (5) reached for review-fix agent. Manual intervention required."
echo "made_changes=false" >> $GITHUB_OUTPUT
exit 0
- name: Download review artifacts
if: steps.iteration.outputs.should_skip != 'true'
uses: actions/download-artifact@v4
continue-on-error: true
with:
pattern: '*-review-${{ github.run_id }}-${{ github.run_attempt }}'
merge-multiple: true
path: .
- name: Run agent review response
id: agent-fix
if: steps.iteration.outputs.should_skip != 'true'
env:
GEMINI_REVIEW_PATH: gemini-review.md
CODEX_REVIEW_PATH: "" # Codex disabled -- see security notice
BRANCH_NAME: ${{ github.head_ref }}
ITERATION_COUNT: ${{ steps.iteration.outputs.iteration_count }}
run: |
if ! command -v automation-cli &>/dev/null; then
echo "::warning::automation-cli not found on PATH - skipping review response"
echo "made_changes=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "Running agent review response..."
automation-cli review respond \
"$PR_NUMBER" \
"$BRANCH_NAME" \
"$ITERATION_COUNT" \
"5"
# -- Agent Failure Handler -----------------------------------------------
agent-failure-handler:
name: Agent Failure Handler
needs: [ci, gemini-review, codex-review, agent-review-response]
if: |
failure() &&
github.event_name == 'pull_request' &&
!github.event.pull_request.draft &&
!contains(github.event.pull_request.labels.*.name, 'no-auto-fix') &&
needs.ci.result == 'failure'
runs-on: self-hosted
timeout-minutes: 30
outputs:
made_changes: ${{ steps.agent-fix.outputs.made_changes }}
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ secrets.AGENT_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
steps:
- name: Pre-checkout cleanup
run: |
for item in outputs target psp_output_file.log .git/index.lock; do
if [ -d "$item" ] || [ -f "$item" ]; then
docker run --rm -v "$(pwd):/workspace" busybox:1.36.1 sh -c \
"rm -rf /workspace/$item" 2>/dev/null || \
sudo rm -rf "$item" 2>/dev/null || true
fi
done
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
clean: false
token: ${{ secrets.AGENT_TOKEN }}
ref: ${{ github.head_ref }}
- name: Ensure clean working directory
run: |
git checkout -- . 2>/dev/null || true
git clean -fd 2>/dev/null || true
- name: Check iteration count
id: iteration
uses: ./.github/actions/agent-iteration-check
with:
pr_number: ${{ github.event.pull_request.number }}
max_iterations: '5'
agent_type: 'failure-fix'
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Skip if max iterations reached
if: steps.iteration.outputs.exceeded_max == 'true'
run: |
echo "Maximum iterations (5) reached for failure-fix agent. Manual intervention required."
echo "made_changes=false" >> $GITHUB_OUTPUT
exit 0
- name: Run agent failure handler
id: agent-fix
if: steps.iteration.outputs.exceeded_max != 'true'
env:
BRANCH_NAME: ${{ github.head_ref }}
ITERATION_COUNT: ${{ steps.iteration.outputs.iteration_count }}
run: |
if ! command -v automation-cli &>/dev/null; then
echo "::warning::automation-cli not found on PATH - skipping failure handler"
echo "made_changes=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "Running agent failure handler..."
automation-cli review failure \
"$PR_NUMBER" \
"$BRANCH_NAME" \
"$ITERATION_COUNT" \
"5" \
"format,lint,test"
# -- PR Status Summary ---------------------------------------------------
pr-status:
name: PR Status Summary
needs: [ci, gemini-review, codex-review, agent-review-response, agent-failure-handler]
if: always()
runs-on: self-hosted
steps:
- name: Generate status summary
run: |
echo "## PR Validation Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Check | Status |" >> $GITHUB_STEP_SUMMARY
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| CI | ${{ needs.ci.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Gemini Review | ${{ needs.gemini-review.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Codex Review | disabled (security) |" >> $GITHUB_STEP_SUMMARY
echo "| Review Response | ${{ needs.agent-review-response.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Failure Handler | ${{ needs.agent-failure-handler.result }} |" >> $GITHUB_STEP_SUMMARY
# CI failure is blocking; reviews are advisory
if [[ "${{ needs.ci.result }}" == "failure" ]]; then
echo "" >> $GITHUB_STEP_SUMMARY
echo "CI failed - please review the logs" >> $GITHUB_STEP_SUMMARY
exit 1
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "PR validation completed successfully" >> $GITHUB_STEP_SUMMARY