From e523db921d5b1150c5b4920de8866acb27526461 Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Fri, 23 Oct 2026 18:16:04 +0800 Subject: [PATCH 1/3] =?UTF-8?q?refactor(ci):=20=E5=88=A0=E9=99=A4=E6=9C=AA?= =?UTF-8?q?=E6=B6=88=E8=B4=B9=E6=9F=A5=E8=AF=A2=E5=92=8C=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 4 ++-- scripts/automation/preview_delivery.sh | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 12cbab6..8f1c38a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,8 +101,8 @@ jobs: if: github.event_name == 'pull_request' run: pdm run check:migration-history "origin/${{ github.base_ref }}" - - name: Run pre-commit contract - run: pdm run pre-commit run --all-files + - name: Validate GitHub Actions definitions + run: pdm run pre-commit run actionlint --all-files artifact: name: Portable peer database runtime diff --git a/scripts/automation/preview_delivery.sh b/scripts/automation/preview_delivery.sh index 3a5ee94..0a74ea7 100644 --- a/scripts/automation/preview_delivery.sh +++ b/scripts/automation/preview_delivery.sh @@ -173,12 +173,9 @@ case "${1:-}" in test "$stage" = review fi done - deployed_release="$(heroku releases --app "$app_name" --json | jq -r \ - '[.[] | select(.status == "succeeded" and (.description | startswith("Deployed web")))] | first | .version // empty')" postgrest_release="$(heroku releases --app "$postgrest_app_name" --json | jq -r \ '[.[] | select(.status == "succeeded" and (.description | startswith("Deployed web")))] | first | .version // empty')" emit_output app_name "$app_name" - emit_output has_deployed_release "$([ -n "$deployed_release" ] && echo true || echo false)" emit_output has_postgrest_release "$([ -n "$postgrest_release" ] && echo true || echo false)" emit_output postgrest_app_name "$postgrest_app_name" emit_output postgrest_url "$(jq -r '.app.web_url' <<<"$postgrest_json")" From b711b646d47acd456e823f8667d80aa8694edaae Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Fri, 23 Oct 2026 18:20:50 +0800 Subject: [PATCH 2/3] =?UTF-8?q?ci(preview):=20=E8=B7=B3=E8=BF=87=E7=BA=AF?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=20PR=20=E7=9A=84=E5=BA=94=E7=94=A8=E4=BA=A4?= =?UTF-8?q?=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/preview-deploy.yml | 28 ++++++++++- docs/40-deployment/heroku.md | 9 ++++ scripts/automation/preview_workflow.sh | 39 +++++++++++++- scripts/tests/test_preview_delivery.py | 70 ++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 3 deletions(-) diff --git a/.github/workflows/preview-deploy.yml b/.github/workflows/preview-deploy.yml index 2490b88..0668506 100644 --- a/.github/workflows/preview-deploy.yml +++ b/.github/workflows/preview-deploy.yml @@ -18,11 +18,35 @@ concurrency: cancel-in-progress: true jobs: - deploy: + scope: + name: impl-py preview checks if: >- github.event.action != 'closed' && github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest + outputs: + required: ${{ steps.scope.outputs.required }} + steps: + - name: Checkout trusted preview scope controller + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 + with: + ref: ${{ github.workflow_sha }} + persist-credentials: false + + - name: Check the complete exact-head pull request diff + id: scope + env: + GH_TOKEN: ${{ github.token }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: bash scripts/automation/preview_workflow.sh application-required + + deploy: + name: impl-py preview + needs: scope + if: needs.scope.outputs.required == 'true' + runs-on: ubuntu-latest environment: preview steps: - name: Checkout trusted delivery implementation @@ -128,7 +152,7 @@ jobs: run: bash .delivery/scripts/automation/preview_workflow.sh summarize cleanup: - name: Retire preview resources + name: impl-py preview cleanup if: >- github.event.action == 'closed' && github.event.pull_request.head.repo.full_name == github.repository diff --git a/docs/40-deployment/heroku.md b/docs/40-deployment/heroku.md index 00b5212..3b1989c 100644 --- a/docs/40-deployment/heroku.md +++ b/docs/40-deployment/heroku.md @@ -40,6 +40,15 @@ Trusted same-repository pull requests currently use: - matching seven-day Neon branch `preview/core-py/pr-`; - no Heroku addon. +应用 Preview 在读取完整 PR 文件清单后,仅对全部变更都位于根目录 `tests/` 的候选跳过 wheel、 +镜像、Registry 和 Heroku 交付。重命名同时检查新旧路径;文件清单不完整、读取失败或查询前后 +head/base 变化时,继续原交付流程。不将此规则扩展到 `scripts/tests/`,因为镜像包含整个 +`scripts/`。判定来自受信 controller,不执行候选代码。 + +Neon 分支仍由原必需检查创建或复用;它的成功状态继续代表实际供给,不表示一次无资源的跳过。 +打开的 PR 已有现场不会因后续只剩测试改动而删除;关闭时的 Registry、Heroku 和 Neon 清理 +独立于路径判定,始终按原身份边界执行。此策略减少应用交付,不承诺纯测试 PR 零资源成本。 + The branch workflow owns only exact isolated branch creation/deletion. After the repository, portable-runtime, and branch checks pass for the exact PR SHA, preview delivery: diff --git a/scripts/automation/preview_workflow.sh b/scripts/automation/preview_workflow.sh index f6845c1..680e163 100644 --- a/scripts/automation/preview_workflow.sh +++ b/scripts/automation/preview_workflow.sh @@ -10,6 +10,43 @@ validate_preview_identity() { } case "${1:-}" in + application-required) + for name in GH_TOKEN GITHUB_REPOSITORY PR_NUMBER HEAD_SHA BASE_SHA; do require_env "$name"; done + [[ "$PR_NUMBER" =~ ^[0-9]+$ ]] + [[ "$HEAD_SHA" =~ ^[a-f0-9]{40}$ && "$BASE_SHA" =~ ^[a-f0-9]{40}$ ]] + required=true + # Only a complete, stable PR diff may suppress delivery; uncertainty keeps the existing path. + endpoint="repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER" + if before="$(gh api "$endpoint")" && + files="$(gh api --paginate --slurp "$endpoint/files?per_page=100" | + jq '[.[][] | {filename, previous_filename}]')" && + after="$(gh api "$endpoint")" && + printf '%s\n' "$before" "$files" "$after" | + jq -se --arg head "$HEAD_SHA" --arg base "$BASE_SHA" --arg repo "$GITHUB_REPOSITORY" ' + .[0] as $before | .[1] as $files | .[2] as $after | + all([$before, $after][]; + .state == "open" and .head.sha == $head and .base.sha == $base and + .head.repo.full_name == $repo and .base.repo.full_name == $repo and + .base.ref == "main") and + $before.changed_files > 0 and + $before.changed_files == $after.changed_files and + ($files | length) == $before.changed_files and + all($files[]; + (.filename | startswith("tests/")) and + ((.previous_filename // .filename) | startswith("tests/"))) + ' >/dev/null; then + required=false + fi + emit_output required "$required" + if [ "$required" = false ]; then + printf '%s\n' '### 无需应用 Preview' '' \ + '本次完整 PR 差异仅涉及根 tests/;跳过 wheel、镜像、Registry 和 Heroku 交付。' \ + 'Neon 必需检查照常运行;已有现场不删除,PR 关闭清理不受影响。' | append_summary + else + printf '%s\n' '### 继续应用 Preview' '' \ + '差异涉及运行产物,或无法确认完整的纯 tests/ 差异;保留既有交付与必需检查。' | append_summary + fi + ;; build-extensions) validate_preview_identity preview_inputs="$GITHUB_WORKSPACE/.extension-preview-inputs" @@ -61,5 +98,5 @@ EOF fi done ;; - *) echo "usage: $0 build-extensions|build-images|summarize|validate-identity|cleanup-apps" >&2; exit 2 ;; + *) echo "usage: $0 application-required|build-extensions|build-images|summarize|validate-identity|cleanup-apps" >&2; exit 2 ;; esac diff --git a/scripts/tests/test_preview_delivery.py b/scripts/tests/test_preview_delivery.py index be7ec0c..c5c2dd8 100644 --- a/scripts/tests/test_preview_delivery.py +++ b/scripts/tests/test_preview_delivery.py @@ -5,6 +5,8 @@ from pathlib import Path import subprocess +import pytest + ROOT = Path(__file__).resolve().parents[2] @@ -96,3 +98,71 @@ def test_failed_migration_keeps_observed_heads_and_reports_partial_delivery(tmp_ assert '"version":7' in summary assert "https://snapshot.pages.dev" in summary assert "secret-not-for-report" not in summary + failed.stdout + failed.stderr + + +@pytest.mark.parametrize( + "paths,changed_files,scenario,required", + [ + (["tests/a.py", "tests/b.py"], 2, None, "false"), + (["tests/a.py", "app/main.py"], 2, None, "true"), + (["tests/a.py"], 1, "rename-from-app", "true"), + (["scripts/tests/a.py"], 1, None, "true"), + (["tests/a.py"], 3001, None, "true"), + (["tests/a.py"], 1, "head", "true"), + (["tests/a.py"], 1, "base", "true"), + (["tests/a.py"], 1, "api-failure", "true"), + ([], 0, None, "true"), + ], +) +def test_preview_skips_only_a_complete_stable_tests_only_diff( + tmp_path, paths, changed_files, scenario, required +): + pull = { + "state": "open", + "head": {"sha": "a" * 40, "repo": {"full_name": "InKCre/impl-py"}}, + "base": { + "sha": "b" * 40, + "ref": "main", + "repo": {"full_name": "InKCre/impl-py"}, + }, + "changed_files": changed_files, + } + (tmp_path / "before.json").write_text(json.dumps(pull)) + if scenario in ("head", "base"): + pull[scenario]["sha"] = "c" * 40 + (tmp_path / "after.json").write_text(json.dumps(pull)) + pages = [[{"filename": path}] for path in paths] + if scenario == "rename-from-app": + pages[0][0]["previous_filename"] = "app/main.py" + (tmp_path / "files.json").write_text(json.dumps(pages)) + binary = tmp_path / "gh" + binary.write_text( + "#!/bin/sh\n" + 'cd "$GH_SCOPE_FIXTURE" || exit 1\n' + 'case "$*" in\n' + ' *"/files?"*) [ "$GH_SCOPE_FAILURE" = false ] || exit 17; cat files.json ;;\n' + " *) if [ -e called ]; then cat after.json; " + "else touch called; cat before.json; fi ;;\n" + "esac\n" + ) + binary.chmod(0o700) + output = tmp_path / "output" + result = _run( + "preview_workflow.sh", + "application-required", + { + **os.environ, + "PATH": f"{tmp_path}:{os.environ['PATH']}", + "GH_SCOPE_FIXTURE": str(tmp_path), + "GH_SCOPE_FAILURE": str(scenario == "api-failure").lower(), + "GH_TOKEN": "fixture-only", + "GITHUB_REPOSITORY": "InKCre/impl-py", + "GITHUB_OUTPUT": str(output), + "GITHUB_STEP_SUMMARY": str(tmp_path / "summary"), + "HEAD_SHA": "a" * 40, + "BASE_SHA": "b" * 40, + "PR_NUMBER": "123", + }, + ) + assert result.returncode == 0, result.stderr + assert output.read_text() == f"required={required}\n" From 1eef9e3f2b8614b014ec82b6767dd58ebaef717a Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Fri, 23 Oct 2026 20:14:46 +0800 Subject: [PATCH 3/3] =?UTF-8?q?ci:=20=E8=BF=81=E7=A7=BB=20Python=20?= =?UTF-8?q?=E5=BF=85=E9=9C=80=E6=A3=80=E6=9F=A5=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 4 ++-- scripts/automation/github_delivery.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f1c38a..a177a5c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: run: bash scripts/automation/cli_publication.sh check-build repository: - name: Hermetic repository contract + name: impl-py checks runs-on: ubuntu-latest services: postgres: @@ -105,7 +105,7 @@ jobs: run: pdm run pre-commit run actionlint --all-files artifact: - name: Portable peer database runtime + name: impl-py e2e tests runs-on: ubuntu-latest services: postgres: diff --git a/scripts/automation/github_delivery.sh b/scripts/automation/github_delivery.sh index 3f40191..451d8d4 100644 --- a/scripts/automation/github_delivery.sh +++ b/scripts/automation/github_delivery.sh @@ -27,8 +27,8 @@ case "${1:-}" in test "$(jq -r '.head.sha' <<<"$pr_json")" = "$HEAD_SHA" test "$(jq -r '.state' <<<"$pr_json")" = "open" required_checks=( - "Hermetic repository contract" - "Portable peer database runtime" + "impl-py checks" + "impl-py e2e tests" "Provision isolated branch" ) ;;