From a6171746f9b3598227eb0248f4d858e475a4b8ee Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 30 Jun 2026 00:17:57 +0800 Subject: [PATCH] feat(ci): copy onboarding warning-step template --- .../awoooi-onboarding-warning-step.yaml | 33 +++++++ apps/api/src/api/v1/agents.py | 34 +++++++ ...rning_step_template_copy_execution_plan.py | 47 ++++++++-- ...t_p0_cicd_baseline_source_readiness_api.py | 89 +++++++++++++++++++ ...itea-onboarding-warning-step.workflow.yaml | 33 +++++++ 5 files changed, 229 insertions(+), 7 deletions(-) create mode 100644 .gitea/workflows/awoooi-onboarding-warning-step.yaml create mode 100644 docs/operations/templates/awoooi-gitea-onboarding-warning-step.workflow.yaml diff --git a/.gitea/workflows/awoooi-onboarding-warning-step.yaml b/.gitea/workflows/awoooi-onboarding-warning-step.yaml new file mode 100644 index 000000000..59c7aa188 --- /dev/null +++ b/.gitea/workflows/awoooi-onboarding-warning-step.yaml @@ -0,0 +1,33 @@ +# AWOOOI Gitea Onboarding Warning Step template. +# +# Controlled-copy default: +# - no push / pull_request / pull_request_target trigger +# - no generic runner labels +# - job is disabled until a separate controlled gate changes the switch +# - no secrets, host writes, workflow dispatch from Codex, or runtime apply + +name: AWOOOI Onboarding Warning Step + +on: + workflow_dispatch: + +env: + AWOOOI_ONBOARDING_WARNING_STEP_EXECUTION_ENABLED: "0" + +jobs: + warning-step: + if: ${{ env.AWOOOI_ONBOARDING_WARNING_STEP_EXECUTION_ENABLED == '1' }} + runs-on: awoooi-non110-host + timeout-minutes: 3 + steps: + - uses: actions/checkout@v4 + + - name: Validate warning-step boundaries + run: | + set -euo pipefail + python3 ops/runner/guard-gitea-runner-pressure.py --root . + git diff --check + cat <<'SUMMARY' + AWOOOI onboarding warning step template is present. + Runtime execution remains disabled until the next controlled gate. + SUMMARY diff --git a/apps/api/src/api/v1/agents.py b/apps/api/src/api/v1/agents.py index b5cd7c136..3281383d5 100644 --- a/apps/api/src/api/v1/agents.py +++ b/apps/api/src/api/v1/agents.py @@ -319,6 +319,9 @@ from src.services.ai_technology_report_cadence_readback import ( from src.services.awoooi_gitea_onboarding_warning_step_template_copy_apply_gate import ( load_latest_awoooi_gitea_onboarding_warning_step_template_copy_apply_gate, ) +from src.services.awoooi_gitea_onboarding_warning_step_template_copy_execution_plan import ( + load_latest_awoooi_gitea_onboarding_warning_step_template_copy_execution_plan, +) from src.services.awoooi_status_cleanup_dashboard import ( load_latest_awoooi_status_cleanup_dashboard, ) @@ -1128,6 +1131,37 @@ async def get_awoooi_gitea_onboarding_warning_step_template_copy_apply_gate() -> ) from exc +@router.get( + "/awoooi-gitea-onboarding-warning-step-template-copy-execution-plan", + response_model=dict[str, Any], + summary="取得 AWOOOI Gitea onboarding warning-step template copy execution plan", + description=( + "讀取 P0-004 warning-step template copy 的受控 execution plan;" + "此端點只回傳 committed template-copy plan readiness。" + "它不複製 workflow、不觸發 workflow、不建立 repo、不同步 refs、" + "不呼叫 GitHub、不讀 secret、不操作 host / K8s。" + ), +) +async def get_awoooi_gitea_onboarding_warning_step_template_copy_execution_plan() -> dict[ + str, Any +]: + """回傳 P0-004 warning-step template copy execution plan 只讀讀回。""" + try: + payload = await asyncio.to_thread( + load_latest_awoooi_gitea_onboarding_warning_step_template_copy_execution_plan + ) + return redact_public_lan_topology(payload) + except (json.JSONDecodeError, ValueError) as exc: + logger.error( + "awoooi_gitea_onboarding_warning_step_template_copy_execution_plan_invalid", + error=str(exc), + ) + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="P0-004 warning-step template copy execution plan 無效", + ) from exc + + @router.get( "/reboot-auto-recovery-slo-scorecard", response_model=dict[str, Any], diff --git a/apps/api/src/services/awoooi_gitea_onboarding_warning_step_template_copy_execution_plan.py b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_template_copy_execution_plan.py index 555337155..4b26c3976 100644 --- a/apps/api/src/services/awoooi_gitea_onboarding_warning_step_template_copy_execution_plan.py +++ b/apps/api/src/services/awoooi_gitea_onboarding_warning_step_template_copy_execution_plan.py @@ -10,25 +10,58 @@ _SCHEMA_VERSION = "awoooi_gitea_onboarding_warning_step_template_copy_execution_ def load_latest_awoooi_gitea_onboarding_warning_step_template_copy_execution_plan() -> dict[str, Any]: - """Return the dry-run-only template copy plan source.""" + """Return the controlled template copy plan source.""" return source_ready_payload( schema_version=_SCHEMA_VERSION, source_id="warning_step_template_copy_execution_plan_service", - status="dry_run_plan_ready_apply_gate_required", + status="controlled_template_copy_execution_plan_ready", readback={ - "execution_status": "blocked_apply_gate_required", - "dry_run_only": True, + "execution_status": "ready_for_controlled_template_copy", + "dry_run_only": False, + "controlled_apply_scope": "warning_step_template_copy_only", + "source_template_path": ( + "docs/operations/templates/" + "awoooi-gitea-onboarding-warning-step.workflow.yaml" + ), + "destination_workflow_path": ( + ".gitea/workflows/awoooi-onboarding-warning-step.yaml" + ), "command_preview_count": 1, "command_preview": [ - "copy warning-step template after apply gate confirms source readiness" + ( + "cp docs/operations/templates/" + "awoooi-gitea-onboarding-warning-step.workflow.yaml " + ".gitea/workflows/awoooi-onboarding-warning-step.yaml" + ) + ], + "template_runtime_default": "fail_closed_job_disabled", + "forbidden_events": [ + "push", + "pull_request", + "pull_request_target", + ], + "forbidden_runner_labels": [ + "awoooi-host", + "awoooi-ubuntu", + "ubuntu-latest", + "ubuntu-*", + "self-hosted", + ], + "allowed_runner_labels": [ + "awoooi-non110-host", ], "post_copy_validators": [ "python3 ops/runner/guard-gitea-runner-pressure.py --root .", "git diff --check", ], + "rollback_plan": ( + "remove .gitea/workflows/awoooi-onboarding-warning-step.yaml " + "before commit" + ), }, next_actions=[ - "open_template_copy_apply_gate", - "do_not_copy_workflow_until_apply_gate_allows", + "copy_warning_step_template_with_fail_closed_runtime_default", + "run_runner_pressure_guard_and_git_diff_check", + "do_not_trigger_workflow_until_next_controlled_gate", ], ) diff --git a/apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py b/apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py index 48c2d8ab9..52418637a 100644 --- a/apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py +++ b/apps/api/tests/test_p0_cicd_baseline_source_readiness_api.py @@ -42,6 +42,19 @@ _SNAPSHOT_PATH = ( / "operations" / "p0-cicd-baseline-source-readiness.snapshot.json" ) +_WARNING_STEP_TEMPLATE_PATH = ( + _REPO_ROOT + / "docs" + / "operations" + / "templates" + / "awoooi-gitea-onboarding-warning-step.workflow.yaml" +) +_WARNING_STEP_WORKFLOW_PATH = ( + _REPO_ROOT + / ".gitea" + / "workflows" + / "awoooi-onboarding-warning-step.yaml" +) def test_p0_cicd_baseline_source_readiness_loader_reports_ready_sources(): @@ -117,6 +130,34 @@ def test_recreated_onboarding_sources_open_only_controlled_template_copy_gate(): == payloads[1]["readback"]["required_acknowledgement_count"] ) assert payloads[1]["readback"]["ready_for_template_copy_plan"] is True + execution_plan = payloads[2] + assert execution_plan["status"] == "controlled_template_copy_execution_plan_ready" + assert ( + execution_plan["readback"]["execution_status"] + == "ready_for_controlled_template_copy" + ) + assert execution_plan["readback"]["dry_run_only"] is False + assert ( + execution_plan["readback"]["source_template_path"] + == "docs/operations/templates/" + "awoooi-gitea-onboarding-warning-step.workflow.yaml" + ) + assert ( + execution_plan["readback"]["destination_workflow_path"] + == ".gitea/workflows/awoooi-onboarding-warning-step.yaml" + ) + assert execution_plan["readback"]["forbidden_events"] == [ + "push", + "pull_request", + "pull_request_target", + ] + assert execution_plan["readback"]["allowed_runner_labels"] == [ + "awoooi-non110-host" + ] + assert ( + execution_plan["operation_boundaries"]["workflow_modification_allowed"] + is False + ) apply_gate = payloads[3] assert apply_gate["status"] == "controlled_template_copy_apply_gate_ready" assert apply_gate["readback"]["apply_allowed"] is True @@ -163,6 +204,32 @@ def test_p0_cicd_baseline_source_readiness_endpoint_returns_snapshot(): assert data["operation_boundaries"]["workflow_modification_allowed"] is False +def test_template_copy_execution_plan_endpoint_returns_controlled_plan(): + app = FastAPI() + app.include_router(router, prefix="/api/v1") + client = TestClient(app) + + response = client.get( + "/api/v1/agents/" + "awoooi-gitea-onboarding-warning-step-template-copy-execution-plan" + ) + + assert response.status_code == 200 + data = response.json() + assert ( + data["schema_version"] + == "awoooi_gitea_onboarding_warning_step_template_copy_execution_plan_v1" + ) + assert data["status"] == "controlled_template_copy_execution_plan_ready" + assert ( + data["readback"]["execution_status"] + == "ready_for_controlled_template_copy" + ) + assert data["readback"]["dry_run_only"] is False + assert data["readback"]["template_runtime_default"] == "fail_closed_job_disabled" + assert data["operation_boundaries"]["workflow_trigger_allowed"] is False + + def test_template_copy_apply_gate_endpoint_returns_controlled_gate(): app = FastAPI() app.include_router(router, prefix="/api/v1") @@ -185,3 +252,25 @@ def test_template_copy_apply_gate_endpoint_returns_controlled_gate(): assert data["readback"]["workflow_trigger_authorized"] is False assert data["operation_boundaries"]["workflow_modification_allowed"] is True assert data["operation_boundaries"]["workflow_trigger_allowed"] is False + + +def test_warning_step_template_copy_is_fail_closed_and_pressure_guarded(): + template = _WARNING_STEP_TEMPLATE_PATH.read_text(encoding="utf-8") + workflow = _WARNING_STEP_WORKFLOW_PATH.read_text(encoding="utf-8") + + assert workflow == template + for forbidden in [ + "push:", + "pull_request:", + "pull_request_target:", + "awoooi-host", + "awoooi-ubuntu", + "ubuntu-latest", + "self-hosted", + ]: + assert forbidden not in workflow + + assert "workflow_dispatch:" in workflow + assert "runs-on: awoooi-non110-host" in workflow + assert 'AWOOOI_ONBOARDING_WARNING_STEP_EXECUTION_ENABLED: "0"' in workflow + assert "ops/runner/guard-gitea-runner-pressure.py --root ." in workflow diff --git a/docs/operations/templates/awoooi-gitea-onboarding-warning-step.workflow.yaml b/docs/operations/templates/awoooi-gitea-onboarding-warning-step.workflow.yaml new file mode 100644 index 000000000..59c7aa188 --- /dev/null +++ b/docs/operations/templates/awoooi-gitea-onboarding-warning-step.workflow.yaml @@ -0,0 +1,33 @@ +# AWOOOI Gitea Onboarding Warning Step template. +# +# Controlled-copy default: +# - no push / pull_request / pull_request_target trigger +# - no generic runner labels +# - job is disabled until a separate controlled gate changes the switch +# - no secrets, host writes, workflow dispatch from Codex, or runtime apply + +name: AWOOOI Onboarding Warning Step + +on: + workflow_dispatch: + +env: + AWOOOI_ONBOARDING_WARNING_STEP_EXECUTION_ENABLED: "0" + +jobs: + warning-step: + if: ${{ env.AWOOOI_ONBOARDING_WARNING_STEP_EXECUTION_ENABLED == '1' }} + runs-on: awoooi-non110-host + timeout-minutes: 3 + steps: + - uses: actions/checkout@v4 + + - name: Validate warning-step boundaries + run: | + set -euo pipefail + python3 ops/runner/guard-gitea-runner-pressure.py --root . + git diff --check + cat <<'SUMMARY' + AWOOOI onboarding warning step template is present. + Runtime execution remains disabled until the next controlled gate. + SUMMARY