From 9f668cedeb2620d8af070559c1e72f84e4168dcf Mon Sep 17 00:00:00 2001 From: "Terry.Mao" Date: Fri, 2 Oct 2026 12:39:11 +0800 Subject: [PATCH 1/5] =?UTF-8?q?ci(workflows):=20=E5=A2=9E=E5=8A=A0=20actio?= =?UTF-8?q?nlint=20=E5=B9=B6=E7=BB=9F=E4=B8=80=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_codex_model_provider_config.py | 14 +++------ .../test_review_workflow_dispatch.py | 6 ++-- .github/tests/test_ci_workflow.py | 15 ++++++++++ .github/workflows/ci.yml | 30 +++++++++++++++++++ .../create-implementation-from-issue.yml | 8 ++--- .github/workflows/create-spec-from-issue.yml | 6 ++-- .github/workflows/product-change-report.yml | 6 ++-- .github/workflows/product-docs-sync.yml | 6 ++-- .github/workflows/product-wiki-compile.yml | 6 ++-- .github/workflows/respond-to-pr-comment.yml | 8 ++--- .github/workflows/review-pr.yml | 8 ++--- .github/workflows/triage-issue.yml | 6 ++-- .github/workflows/update-dedupe.yml | 6 ++-- .github/workflows/update-pr-review.yml | 6 ++-- .github/workflows/update-triage.yml | 6 ++-- README.md | 10 +++---- docs/github-collaboration-flow.md | 10 +++---- docs/product/raw/implementation-workflow.md | 2 +- .../raw/pr-comment-response-workflow.md | 2 +- .../automated-implementation-workflow.md | 2 +- .../wiki/summaries/implementation-workflow.md | 2 +- .../summaries/pr-comment-response-workflow.md | 2 +- 22 files changed, 101 insertions(+), 66 deletions(-) diff --git a/.github/aicodingflow-tests/test_codex_model_provider_config.py b/.github/aicodingflow-tests/test_codex_model_provider_config.py index 5ecd751..b692ff4 100644 --- a/.github/aicodingflow-tests/test_codex_model_provider_config.py +++ b/.github/aicodingflow-tests/test_codex_model_provider_config.py @@ -26,28 +26,22 @@ def test_all_codex_workflows_use_shared_provider_configuration(self) -> None: with self.subTest(path=path): contents = (ROOT / path).read_text(encoding="utf-8") self.assertEqual(contents.count("uses: openai/codex-action@v1"), 1) - self.assertEqual( - contents.count("openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }}"), - 1, - ) + self.assertEqual(contents.count("openai-api-key: ${{ secrets.CODEX_API_KEY }}"), 1) self.assertEqual( contents.count("model: ${{ vars.CODEX_MODEL }}"), 1, ) - self.assertEqual( - contents.count("CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }}"), - 1, - ) + self.assertEqual(contents.count("CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }}"), 1) self.assertIn('endpoint="${CODEX_API_ENDPOINT%/}"', contents) self.assertIn("*/responses", contents) self.assertIn("$endpoint/responses", contents) - self.assertIn("CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set", contents) + self.assertIn("CODEX_API_ENDPOINT is not set", contents) def test_workflows_do_not_read_the_legacy_endpoint_environment_directly(self) -> None: for path in CODEX_WORKFLOWS: with self.subTest(path=path): contents = (ROOT / path).read_text(encoding="utf-8") - self.assertNotIn('endpoint="${OPENAI_API_ENDPOINT%/}"', contents) + self.assertNotIn("OPENAI_", contents) if __name__ == "__main__": diff --git a/.github/aicodingflow-tests/test_review_workflow_dispatch.py b/.github/aicodingflow-tests/test_review_workflow_dispatch.py index 0fb2e5b..e54eac7 100644 --- a/.github/aicodingflow-tests/test_review_workflow_dispatch.py +++ b/.github/aicodingflow-tests/test_review_workflow_dispatch.py @@ -149,7 +149,7 @@ def test_review_workflow_resolves_pr_before_checkout_and_uses_normalized_event(s self.assertIn("cp -R .agents/contracts pr-worktree/.agents/contracts", prepare_step["run"]) ai_step = next(step for step in review_steps if step.get("name") == "Run AI review") - self.assertEqual(ai_step["with"]["allow-bot-users"], "github-actions[bot]") + self.assertEqual(ai_step["with"]["allow-bots"], "github-actions[bot]") self.assertIn("First change directory to pr-worktree", ai_step["with"]["prompt"]) self.assertIn("Read .agents/contracts/review.md", ai_step["with"]["prompt"]) self.assertIn("shared review contract", ai_step["with"]["prompt"]) @@ -254,7 +254,7 @@ def test_create_implementation_workflow_does_not_dispatch_review_after_pr_creati self.assertEqual(app_token["id"], "app-token") self.assertEqual(app_token["uses"], "actions/create-github-app-token@v3") self.assertEqual(app_token["if"], commit["if"] + " && steps.workflow_update.outputs.required == 'true'") - self.assertEqual(app_token["with"]["client-id"], "${{ vars.APP_CLIENT_ID }}") + self.assertEqual(app_token["with"]["app-id"], "${{ vars.APP_ID }}") self.assertEqual(app_token["with"]["private-key"], "${{ secrets.APP_PRIVATE_KEY }}") self.assertEqual(app_token["with"]["permission-contents"], "write") self.assertEqual(app_token["with"]["permission-workflows"], "write") @@ -451,7 +451,7 @@ def test_respond_to_pr_comment_workflow_has_secure_triggers_and_gates(self) -> N self.assertEqual(app_token["id"], "app-token") self.assertEqual(app_token["uses"], "actions/create-github-app-token@v3") self.assertEqual(app_token["if"], commit["if"] + " && steps.workflow_update.outputs.required == 'true'") - self.assertEqual(app_token["with"]["client-id"], "${{ vars.APP_CLIENT_ID }}") + self.assertEqual(app_token["with"]["app-id"], "${{ vars.APP_ID }}") self.assertEqual(app_token["with"]["private-key"], "${{ secrets.APP_PRIVATE_KEY }}") self.assertEqual(app_token["with"]["permission-contents"], "write") self.assertEqual(app_token["with"]["permission-workflows"], "write") diff --git a/.github/tests/test_ci_workflow.py b/.github/tests/test_ci_workflow.py index e99be95..162d428 100644 --- a/.github/tests/test_ci_workflow.py +++ b/.github/tests/test_ci_workflow.py @@ -33,11 +33,26 @@ def test_ci_dispatches_review_only_after_tests_pass(self) -> None: self.assertIn("github.event.pull_request.head.repo.full_name == github.repository", data["jobs"]["ai-review"]["if"]) test_steps = steps(data, "test") + dependencies = next(step for step in test_steps if step.get("name") == "Install Python test dependencies") + self.assertIn('python3 -m venv "$RUNNER_TEMP/aicodingflow-venv"', dependencies["run"]) + self.assertIn('aicodingflow-venv/bin/python" -m pip install', dependencies["run"]) + self.assertIn('"PyYAML==6.0.3"', dependencies["run"]) + project_tests = next(step for step in test_steps if step.get("name") == "Run repository unit tests") delivery_tests = next(step for step in test_steps if step.get("name") == "Run delivered unit tests") self.assertIn("python3 -m unittest discover -s .github/tests", project_tests["run"]) self.assertIn("python3 -m unittest discover -s .github/aicodingflow-tests", delivery_tests["run"]) + install_actionlint = next(step for step in test_steps if step.get("name") == "Install actionlint") + self.assertEqual(install_actionlint["env"]["ACTIONLINT_VERSION"], "1.7.12") + self.assertIn("sha256sum --check", install_actionlint["run"]) + + lint_workflows = next(step for step in test_steps if step.get("name") == "Lint GitHub Actions workflows") + self.assertIn("find .github/workflows", lint_workflows["run"]) + self.assertIn("-name '*.yml'", lint_workflows["run"]) + self.assertIn("-name '*.yaml'", lint_workflows["run"]) + self.assertIn('actionlint "${workflow_files[@]}"', lint_workflows["run"]) + dispatch_step = next(step for step in steps(data, "ai-review") if step.get("name") == "Dispatch AI PR Review") self.assertEqual(dispatch_step["env"]["GH_TOKEN"], "${{ github.token }}") self.assertEqual(dispatch_step["env"]["PR_NUMBER"], "${{ github.event.pull_request.number }}") diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9af93a0..077f159 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,12 +25,42 @@ jobs: with: fetch-depth: 1 + - name: Install Python test dependencies + run: | + python3 -m venv "$RUNNER_TEMP/aicodingflow-venv" + "$RUNNER_TEMP/aicodingflow-venv/bin/python" -m pip install \ + --disable-pip-version-check "PyYAML==6.0.3" + echo "$RUNNER_TEMP/aicodingflow-venv/bin" >> "$GITHUB_PATH" + - name: Run repository unit tests run: python3 -m unittest discover -s .github/tests - name: Run delivered unit tests run: python3 -m unittest discover -s .github/aicodingflow-tests + - name: Install actionlint + env: + ACTIONLINT_VERSION: "1.7.12" + run: | + archive="actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz" + base_url="https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}" + curl --fail --silent --show-error --location "$base_url/$archive" --output "$RUNNER_TEMP/$archive" + curl --fail --silent --show-error --location "$base_url/actionlint_${ACTIONLINT_VERSION}_checksums.txt" \ + --output "$RUNNER_TEMP/actionlint_checksums.txt" + grep " $archive$" "$RUNNER_TEMP/actionlint_checksums.txt" > "$RUNNER_TEMP/actionlint_checksum.txt" + (cd "$RUNNER_TEMP" && sha256sum --check actionlint_checksum.txt) + tar -xzf "$RUNNER_TEMP/$archive" -C "$RUNNER_TEMP" + echo "$RUNNER_TEMP" >> "$GITHUB_PATH" + + - name: Lint GitHub Actions workflows + run: | + mapfile -d '' workflow_files < <(find .github/workflows -type f \( -name '*.yml' -o -name '*.yaml' \) -print0) + if ((${#workflow_files[@]} == 0)); then + echo "No GitHub Actions workflow files found" >&2 + exit 1 + fi + actionlint "${workflow_files[@]}" + - name: Compile Python scripts run: | PYTHONPYCACHEPREFIX=/tmp/aicodingflow-pycache python3 -m py_compile \ diff --git a/.github/workflows/create-implementation-from-issue.yml b/.github/workflows/create-implementation-from-issue.yml index 75806d6..d4db5e5 100644 --- a/.github/workflows/create-implementation-from-issue.yml +++ b/.github/workflows/create-implementation-from-issue.yml @@ -103,11 +103,11 @@ jobs: if: steps.issue.outputs.should_run == 'true' && steps.issue.outputs.should_noop != 'true' id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -156,7 +156,7 @@ jobs: if: steps.issue.outputs.should_run == 'true' && steps.issue.outputs.should_noop != 'true' uses: openai/codex-action@v1 with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} prompt: | @@ -308,7 +308,7 @@ jobs: id: app-token uses: actions/create-github-app-token@v3 with: - client-id: ${{ vars.APP_CLIENT_ID }} + app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} permission-contents: write permission-workflows: write diff --git a/.github/workflows/create-spec-from-issue.yml b/.github/workflows/create-spec-from-issue.yml index 97f76d6..7bf2e4c 100644 --- a/.github/workflows/create-spec-from-issue.yml +++ b/.github/workflows/create-spec-from-issue.yml @@ -91,11 +91,11 @@ jobs: if: steps.issue.outputs.should_run == 'true' id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -108,7 +108,7 @@ jobs: if: steps.issue.outputs.should_run == 'true' uses: openai/codex-action@v1 with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} prompt: | diff --git a/.github/workflows/product-change-report.yml b/.github/workflows/product-change-report.yml index 33ece84..884571c 100644 --- a/.github/workflows/product-change-report.yml +++ b/.github/workflows/product-change-report.yml @@ -77,11 +77,11 @@ jobs: if: steps.context.outputs.reportable_pr_count != '0' id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -94,7 +94,7 @@ jobs: if: steps.context.outputs.reportable_pr_count != '0' uses: openai/codex-action@v1 with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} prompt: | diff --git a/.github/workflows/product-docs-sync.yml b/.github/workflows/product-docs-sync.yml index cf2bf81..7a61a76 100644 --- a/.github/workflows/product-docs-sync.yml +++ b/.github/workflows/product-docs-sync.yml @@ -103,11 +103,11 @@ jobs: if: steps.context.outputs.should_run == 'true' id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -120,7 +120,7 @@ jobs: if: steps.context.outputs.should_run == 'true' uses: openai/codex-action@v1 with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} prompt: | diff --git a/.github/workflows/product-wiki-compile.yml b/.github/workflows/product-wiki-compile.yml index 5ef5931..0cae287 100644 --- a/.github/workflows/product-wiki-compile.yml +++ b/.github/workflows/product-wiki-compile.yml @@ -64,11 +64,11 @@ jobs: - name: Configure Codex API endpoint id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -80,7 +80,7 @@ jobs: - name: Compile product wiki uses: openai/codex-action@v1 with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} prompt: | diff --git a/.github/workflows/respond-to-pr-comment.yml b/.github/workflows/respond-to-pr-comment.yml index c14f687..9e43f89 100644 --- a/.github/workflows/respond-to-pr-comment.yml +++ b/.github/workflows/respond-to-pr-comment.yml @@ -193,11 +193,11 @@ jobs: if: steps.context.outputs.should_run == 'true' && steps.context.outputs.branch_strategy != 'blocked' id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -210,7 +210,7 @@ jobs: if: steps.context.outputs.should_run == 'true' && steps.context.outputs.branch_strategy != 'blocked' uses: openai/codex-action@v1 with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} prompt: | @@ -353,7 +353,7 @@ jobs: id: app-token uses: actions/create-github-app-token@v3 with: - client-id: ${{ vars.APP_CLIENT_ID }} + app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }} permission-contents: write permission-workflows: write diff --git a/.github/workflows/review-pr.yml b/.github/workflows/review-pr.yml index cba27d1..18545b6 100644 --- a/.github/workflows/review-pr.yml +++ b/.github/workflows/review-pr.yml @@ -176,11 +176,11 @@ jobs: - name: Configure Codex API endpoint id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -192,10 +192,10 @@ jobs: - name: Run AI review uses: openai/codex-action@v1 with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} - allow-bot-users: github-actions[bot] + allow-bots: github-actions[bot] prompt: | First change directory to pr-worktree and do all file inspection from there. Read ${{ steps.review_skill.outputs.path }} in pr-worktree and follow it exactly. diff --git a/.github/workflows/triage-issue.yml b/.github/workflows/triage-issue.yml index e7f0190..6a70dc8 100644 --- a/.github/workflows/triage-issue.yml +++ b/.github/workflows/triage-issue.yml @@ -105,11 +105,11 @@ jobs: if: steps.issue.outputs.should_run == 'true' id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -124,7 +124,7 @@ jobs: env: GH_TOKEN: ${{ github.token }} with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} allow-users: "*" diff --git a/.github/workflows/update-dedupe.yml b/.github/workflows/update-dedupe.yml index f70e778..4bc5779 100644 --- a/.github/workflows/update-dedupe.yml +++ b/.github/workflows/update-dedupe.yml @@ -66,11 +66,11 @@ jobs: - name: Configure Codex API endpoint id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -87,7 +87,7 @@ jobs: - name: Update local dedupe guidance uses: openai/codex-action@v1 with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} prompt: | diff --git a/.github/workflows/update-pr-review.yml b/.github/workflows/update-pr-review.yml index ebd4700..b3abd25 100644 --- a/.github/workflows/update-pr-review.yml +++ b/.github/workflows/update-pr-review.yml @@ -80,11 +80,11 @@ jobs: - name: Configure Codex API endpoint id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -101,7 +101,7 @@ jobs: - name: Update local review guidance uses: openai/codex-action@v1 with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} prompt: | diff --git a/.github/workflows/update-triage.yml b/.github/workflows/update-triage.yml index de57e92..a213be1 100644 --- a/.github/workflows/update-triage.yml +++ b/.github/workflows/update-triage.yml @@ -98,11 +98,11 @@ jobs: - name: Configure Codex API endpoint id: codex_endpoint env: - CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT || vars.OPENAI_API_ENDPOINT }} + CODEX_API_ENDPOINT: ${{ vars.CODEX_API_ENDPOINT }} run: | endpoint="${CODEX_API_ENDPOINT%/}" if [ -z "$endpoint" ]; then - echo "CODEX_API_ENDPOINT or OPENAI_API_ENDPOINT is not set" >&2 + echo "CODEX_API_ENDPOINT is not set" >&2 exit 1 fi case "$endpoint" in @@ -119,7 +119,7 @@ jobs: - name: Update local triage guidance uses: openai/codex-action@v1 with: - openai-api-key: ${{ secrets.CODEX_API_KEY || secrets.OPENAI_API_KEY }} + openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} prompt: | diff --git a/README.md b/README.md index 31b6476..f9c972f 100644 --- a/README.md +++ b/README.md @@ -36,14 +36,12 @@ cd AICodingFlow | 名称 | 类型 | 用途 | | --- | --- | --- | -| `CODEX_API_KEY` | Actions secret | Codex action 使用的模型服务 API key;未设置时回退到 `OPENAI_API_KEY`。 | -| `CODEX_API_ENDPOINT` | Actions variable | Responses API endpoint,可以是 base URL 或 `/responses` URL;未设置时回退到 `OPENAI_API_ENDPOINT`。 | +| `CODEX_API_KEY` | Actions secret | Codex action 使用的模型服务 API key。 | +| `CODEX_API_ENDPOINT` | Actions variable | Responses API endpoint,可以是 base URL 或 `/responses` URL。 | | `CODEX_MODEL` | Actions variable | Codex 使用的模型名称,例如网关暴露的 `deepseek-chat`;为空时使用 Codex 默认模型。 | -| `OPENAI_API_KEY` | Actions secret | 兼容旧配置的 API key 名称。 | -| `OPENAI_API_ENDPOINT` | Actions variable | 兼容旧配置的 Responses API endpoint 名称。 | | `AGENT_LOGIN` | Actions variable | GitHub issue / PR comment 中被分配或 mention 的 agent 登录名。 | | `REVIEW_BOT_LOGIN` | Actions variable | 可选。发布 PR review 的 bot 登录名;默认 `github-actions[bot]`。如果 review workflow 改用其他 token / bot 账号发 review,需要设置为实际 review 作者,用于后续 `APPROVE` 清理旧的 bot `REQUEST_CHANGES`。 | -| `APP_CLIENT_ID` | Actions variable | GitHub App client ID;implementation/comment fix 需要更新 workflow 文件时使用。 | +| `APP_ID` | Actions variable | GitHub App ID;implementation/comment fix 需要更新 workflow 文件时使用。 | | `APP_PRIVATE_KEY` | Actions secret | GitHub App private key;App 需要 `Contents: Read and write` 和 `Workflows: Read and write`。 | 如果目标项目首次接入 issue triage 自动化,可以让 Codex 运行: @@ -56,7 +54,7 @@ $bootstrap-issue-config ### 使用其他模型服务 -所有 Codex workflow 共享同一组模型配置。推荐设置 `CODEX_API_KEY`、`CODEX_API_ENDPOINT` 和 `CODEX_MODEL`;已有项目也可以继续使用 `OPENAI_API_KEY` 和 `OPENAI_API_ENDPOINT`,它们会作为回退配置。 +所有 Codex workflow 共享同一组模型配置,必须设置 `CODEX_API_KEY`、`CODEX_API_ENDPOINT` 和 `CODEX_MODEL`。 `openai/codex-action` 通过 Responses API 调用模型,因此 `CODEX_API_ENDPOINT` 必须指向支持 Responses API 的服务。DeepSeek 原生 API 目前使用 Chat Completions 协议,不能直接填入其 `/chat/completions` 地址;请先使用 LiteLLM、OneAPI 或其他协议转换网关,并把网关的 Responses API 地址和网关暴露的模型名称配置到上述变量中。 diff --git a/docs/github-collaboration-flow.md b/docs/github-collaboration-flow.md index 60ce0c9..01bc88e 100644 --- a/docs/github-collaboration-flow.md +++ b/docs/github-collaboration-flow.md @@ -12,19 +12,17 @@ issue -> triage/spec -> implement -> pr -> review -> comments -> merge | 名称 | 类型 | 用途 | | --- | --- | --- | -| `CODEX_API_KEY` | Actions secret | Codex action 使用的模型服务 API key;未设置时回退到 `OPENAI_API_KEY`。 | -| `CODEX_API_ENDPOINT` | Actions variable | Responses API endpoint,可以是 base URL 或 `/responses` URL;未设置时回退到 `OPENAI_API_ENDPOINT`。 | +| `CODEX_API_KEY` | Actions secret | Codex action 使用的模型服务 API key。 | +| `CODEX_API_ENDPOINT` | Actions variable | Responses API endpoint,可以是 base URL 或 `/responses` URL。 | | `CODEX_MODEL` | Actions variable | Codex 使用的模型名称;为空时使用 Codex 默认模型。 | -| `OPENAI_API_KEY` | Actions secret | 兼容旧配置的 API key 名称。 | -| `OPENAI_API_ENDPOINT` | Actions variable | 兼容旧配置的 Responses API endpoint 名称。 | | `AGENT_LOGIN` | Actions variable | issue / PR comment 中被分配或 mention 的 agent 登录名。 | | `REVIEW_BOT_LOGIN` | Actions variable | 可选。发布 PR review 的 bot 登录名;默认 `github-actions[bot]`。如果 `review-pr.yml` 改用其他 token / bot 账号发 review,需要设置为实际 review 作者。 | -| `APP_CLIENT_ID` | Actions variable | GitHub App client ID;需要提交 workflow 文件更新时使用。 | +| `APP_ID` | Actions variable | GitHub App ID;需要提交 workflow 文件更新时使用。 | | `APP_PRIVATE_KEY` | Actions secret | GitHub App private key;App 需要 `Contents: Read and write` 和 `Workflows: Read and write`。 | 目标仓库已有自己的 CI 时,推荐在 CI 成功路径中 dispatch `review-pr.yml`,不要直接改 managed review workflow。这样后续升级 AICodingFlow 时可以覆盖受管 workflow,而不会丢失目标仓库自己的 CI 编排。 -所有 Codex workflow 使用同一组模型配置。`CODEX_*` 配置优先,未设置时回退到对应的 `OPENAI_*` 配置。`CODEX_API_ENDPOINT` 必须提供 Responses API;仅提供 Chat Completions 的模型服务(例如 DeepSeek 原生 endpoint)需要通过 LiteLLM、OneAPI 或其他协议转换网关接入,`CODEX_MODEL` 填写网关支持的模型名称。 +所有 Codex workflow 使用同一组模型配置。`CODEX_API_KEY`、`CODEX_API_ENDPOINT` 和 `CODEX_MODEL` 必须配置。`CODEX_API_ENDPOINT` 必须提供 Responses API;仅提供 Chat Completions 的模型服务(例如 DeepSeek 原生 endpoint)需要通过 LiteLLM、OneAPI 或其他协议转换网关接入,`CODEX_MODEL` 填写网关支持的模型名称。 ## Label 和触发规则 diff --git a/docs/product/raw/implementation-workflow.md b/docs/product/raw/implementation-workflow.md index 772d909..ec48c97 100644 --- a/docs/product/raw/implementation-workflow.md +++ b/docs/product/raw/implementation-workflow.md @@ -77,7 +77,7 @@ implementation PR,并维护 issue progress comment。提交实现分支时, token,并把该 token 作为 `WORKFLOW_UPDATE_TOKEN` 传给提交脚本,用于推送 implementation 分支。普通不修改 GitHub workflow 文件的实现分支继续使用默认 `GITHUB_TOKEN` 推送。 -仓库需要配置 `APP_CLIENT_ID` Actions variable 和 +仓库需要配置 `APP_ID` Actions variable 和 `APP_PRIVATE_KEY` Actions secret。对应 GitHub App 必须安装到目标仓库,并具有 `Contents: Read and write` 与 `Workflows: Read and write` 权限。不要把生成出来的一次性 installation token 存成 secret;该 token 是短期凭据,会过期。 diff --git a/docs/product/raw/pr-comment-response-workflow.md b/docs/product/raw/pr-comment-response-workflow.md index 31eb053..e09de8f 100644 --- a/docs/product/raw/pr-comment-response-workflow.md +++ b/docs/product/raw/pr-comment-response-workflow.md @@ -112,7 +112,7 @@ workflow 会先根据 `pr-metadata.json` 的 `intended_files` 判断是否需要 installation token,并把该 token 作为 `WORKFLOW_UPDATE_TOKEN` 传给提交脚本。普通不修改 GitHub workflow 文件的修复提交继续使用当前 workflow 的默认写入凭据。 -仓库需要配置 `APP_CLIENT_ID` Actions variable 和 +仓库需要配置 `APP_ID` Actions variable 和 `APP_PRIVATE_KEY` Actions secret。对应 GitHub App 必须安装到目标仓库,并具有 `Contents: Read and write` 与 `Workflows: Read and write` 权限。不要把生成出来的一次性 installation token 存成 secret;该 token 是短期凭据,会过期。 diff --git a/docs/product/wiki/concepts/automated-implementation-workflow.md b/docs/product/wiki/concepts/automated-implementation-workflow.md index f2fe359..ad6ec85 100644 --- a/docs/product/wiki/concepts/automated-implementation-workflow.md +++ b/docs/product/wiki/concepts/automated-implementation-workflow.md @@ -30,7 +30,7 @@ sources: - 普通不修改 GitHub workflow 文件的实现分支使用默认 `GITHUB_TOKEN` 推送。 - 实现变更包含 `.github/workflows/` 下的 GitHub workflow 文件时,外层 workflow 会通过 `actions/create-github-app-token` 生成短期 GitHub App installation token,并作为 `WORKFLOW_UPDATE_TOKEN` 传给提交脚本。 -- 仓库需要配置 `APP_CLIENT_ID` Actions variable 和 `APP_PRIVATE_KEY` Actions secret。 +- 仓库需要配置 `APP_ID` Actions variable 和 `APP_PRIVATE_KEY` Actions secret。 - 对应 GitHub App 必须安装到目标仓库,并具有 `Contents: Read and write` 与 `Workflows: Read and write` 权限。 - 生成出来的一次性 installation token 不应存成 secret。 diff --git a/docs/product/wiki/summaries/implementation-workflow.md b/docs/product/wiki/summaries/implementation-workflow.md index 261e02b..501de96 100644 --- a/docs/product/wiki/summaries/implementation-workflow.md +++ b/docs/product/wiki/summaries/implementation-workflow.md @@ -46,7 +46,7 @@ Source: [docs/product/raw/implementation-workflow.md](../../raw/implementation-w - `intended_files` 不得包含 workflow handoff 文件、validation logs、生成缓存文件或未变化文件;实际变更与 `intended_files` 不一致时,workflow 会拒绝提交。 - context、metadata、summary、branch SHA snapshot 与 validation logs 位于 workspace 内 `.codex-runtime/handoff/`,workflow 排除该目录,不依赖仓库根目录 `.gitignore`。 - implementation 变更包含 `.github/workflows/` 下 GitHub workflow 文件时,外层 workflow 会通过 `actions/create-github-app-token` 生成短期 GitHub App installation token,并作为 `WORKFLOW_UPDATE_TOKEN` 传给提交脚本。 -- 仓库需要配置 `APP_CLIENT_ID` Actions variable 和 `APP_PRIVATE_KEY` Actions secret;对应 GitHub App 必须安装到目标仓库,并具有 `Contents: Read and write` 与 `Workflows: Read and write` 权限。 +- 仓库需要配置 `APP_ID` Actions variable 和 `APP_PRIVATE_KEY` Actions secret;对应 GitHub App 必须安装到目标仓库,并具有 `Contents: Read and write` 与 `Workflows: Read and write` 权限。 - 生成出来的一次性 installation token 不应存成 secret。 - 创建或更新 implementation PR 后不会自动触发 AI PR Review,因为 implementation PR 默认保持 draft;需要 review 时,在 open 且非 draft PR 的普通 conversation comment 中发送 `@AGENT_LOGIN /review`。 diff --git a/docs/product/wiki/summaries/pr-comment-response-workflow.md b/docs/product/wiki/summaries/pr-comment-response-workflow.md index 08f1ea9..85f7429 100644 --- a/docs/product/wiki/summaries/pr-comment-response-workflow.md +++ b/docs/product/wiki/summaries/pr-comment-response-workflow.md @@ -49,7 +49,7 @@ Source: [docs/product/raw/pr-comment-response-workflow.md](../../raw/pr-comment- - PR discussion context、diff snapshot、spec context、metadata、summary 和 validation logs 位于 `pr-worktree/.codex-runtime/handoff/`,workflow 排除 `.codex-runtime/`,不污染提交内容。 - `push-head` 成功后不会按 metadata 改写原 PR title/body;fallback PR 会在 PR body 中说明来源 PR 和触发评论。 - resolve review thread 失败只记录 warning,不回滚已完成的 commit、push 或 PR update。 -- 修改 `.github/workflows/` 下 GitHub workflow 文件时,外层 workflow 会通过 GitHub App installation token 设置 `WORKFLOW_UPDATE_TOKEN`;仓库需要配置 `APP_CLIENT_ID` Actions variable 和 `APP_PRIVATE_KEY` Actions secret。 +- 修改 `.github/workflows/` 下 GitHub workflow 文件时,外层 workflow 会通过 GitHub App installation token 设置 `WORKFLOW_UPDATE_TOKEN`;仓库需要配置 `APP_ID` Actions variable 和 `APP_PRIVATE_KEY` Actions secret。 ## 支持的概念 From 612506d278404997e35c2229408bad90e88d8dd3 Mon Sep 17 00:00:00 2001 From: "Terry.Mao" Date: Fri, 2 Oct 2026 13:20:23 +0800 Subject: [PATCH 2/5] =?UTF-8?q?fix(review-pr):=20=E4=BF=AE=E6=AD=A3=20Code?= =?UTF-8?q?x=20bot=20=E7=94=A8=E6=88=B7=E8=BE=93=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/aicodingflow-tests/test_review_workflow_dispatch.py | 2 +- .github/workflows/review-pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/aicodingflow-tests/test_review_workflow_dispatch.py b/.github/aicodingflow-tests/test_review_workflow_dispatch.py index e54eac7..2c21f1d 100644 --- a/.github/aicodingflow-tests/test_review_workflow_dispatch.py +++ b/.github/aicodingflow-tests/test_review_workflow_dispatch.py @@ -149,7 +149,7 @@ def test_review_workflow_resolves_pr_before_checkout_and_uses_normalized_event(s self.assertIn("cp -R .agents/contracts pr-worktree/.agents/contracts", prepare_step["run"]) ai_step = next(step for step in review_steps if step.get("name") == "Run AI review") - self.assertEqual(ai_step["with"]["allow-bots"], "github-actions[bot]") + self.assertEqual(ai_step["with"]["allow-bot-users"], "github-actions[bot]") self.assertIn("First change directory to pr-worktree", ai_step["with"]["prompt"]) self.assertIn("Read .agents/contracts/review.md", ai_step["with"]["prompt"]) self.assertIn("shared review contract", ai_step["with"]["prompt"]) diff --git a/.github/workflows/review-pr.yml b/.github/workflows/review-pr.yml index 18545b6..687de5e 100644 --- a/.github/workflows/review-pr.yml +++ b/.github/workflows/review-pr.yml @@ -195,7 +195,7 @@ jobs: openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} - allow-bots: github-actions[bot] + allow-bot-users: github-actions[bot] prompt: | First change directory to pr-worktree and do all file inspection from there. Read ${{ steps.review_skill.outputs.path }} in pr-worktree and follow it exactly. From 4609943cf285a8e17c0258598d5e44e874f9286f Mon Sep 17 00:00:00 2001 From: "Terry.Mao" Date: Fri, 2 Oct 2026 13:43:18 +0800 Subject: [PATCH 3/5] =?UTF-8?q?fix(review-pr):=20=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=B8=83=E5=B0=94=20bot=20=E6=9D=83=E9=99=90=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/aicodingflow-tests/test_review_workflow_dispatch.py | 2 +- .github/workflows/review-pr.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/aicodingflow-tests/test_review_workflow_dispatch.py b/.github/aicodingflow-tests/test_review_workflow_dispatch.py index 2c21f1d..e6fb6e0 100644 --- a/.github/aicodingflow-tests/test_review_workflow_dispatch.py +++ b/.github/aicodingflow-tests/test_review_workflow_dispatch.py @@ -149,7 +149,7 @@ def test_review_workflow_resolves_pr_before_checkout_and_uses_normalized_event(s self.assertIn("cp -R .agents/contracts pr-worktree/.agents/contracts", prepare_step["run"]) ai_step = next(step for step in review_steps if step.get("name") == "Run AI review") - self.assertEqual(ai_step["with"]["allow-bot-users"], "github-actions[bot]") + self.assertIs(ai_step["with"]["allow-bots"], True) self.assertIn("First change directory to pr-worktree", ai_step["with"]["prompt"]) self.assertIn("Read .agents/contracts/review.md", ai_step["with"]["prompt"]) self.assertIn("shared review contract", ai_step["with"]["prompt"]) diff --git a/.github/workflows/review-pr.yml b/.github/workflows/review-pr.yml index 687de5e..815a859 100644 --- a/.github/workflows/review-pr.yml +++ b/.github/workflows/review-pr.yml @@ -195,7 +195,7 @@ jobs: openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} - allow-bot-users: github-actions[bot] + allow-bots: true prompt: | First change directory to pr-worktree and do all file inspection from there. Read ${{ steps.review_skill.outputs.path }} in pr-worktree and follow it exactly. From 75f9c917502f2b16f17251fc6d0eb7ce8d845618 Mon Sep 17 00:00:00 2001 From: "Terry.Mao" Date: Fri, 2 Oct 2026 13:52:06 +0800 Subject: [PATCH 4/5] =?UTF-8?q?docs:=20=E8=A1=A5=E5=85=85=20actionlint=20?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AGENTS.md b/AGENTS.md index c320668..94343c7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,6 +30,7 @@ PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s .github/aicodingflow-t PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s .github/aicodingflow-tests -p 'test_.py' PYTHONPYCACHEPREFIX=/tmp/aicodingflow-pycache python3 -m py_compile git diff --check +mapfile -d '' workflow_files < <(find .github/workflows -type f \( -name '*.yml' -o -name '*.yaml' \) -print0) && actionlint "${workflow_files[@]}" ``` ## Repository Conventions From 11d5205157abfbd54ba4cce1b96383c690c6f64f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 2 Oct 2026 06:01:53 +0000 Subject: [PATCH 5/5] =?UTF-8?q?docs:=20=E5=AF=B9=E9=BD=90=20CODEX=5FMODEL?= =?UTF-8?q?=20=E5=8F=AF=E9=80=89=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- docs/github-collaboration-flow.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f9c972f..7d963e7 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ $bootstrap-issue-config ### 使用其他模型服务 -所有 Codex workflow 共享同一组模型配置,必须设置 `CODEX_API_KEY`、`CODEX_API_ENDPOINT` 和 `CODEX_MODEL`。 +所有 Codex workflow 共享同一组模型配置,必须设置 `CODEX_API_KEY` 和 `CODEX_API_ENDPOINT`;`CODEX_MODEL` 可选,为空时使用 Codex 默认模型。 `openai/codex-action` 通过 Responses API 调用模型,因此 `CODEX_API_ENDPOINT` 必须指向支持 Responses API 的服务。DeepSeek 原生 API 目前使用 Chat Completions 协议,不能直接填入其 `/chat/completions` 地址;请先使用 LiteLLM、OneAPI 或其他协议转换网关,并把网关的 Responses API 地址和网关暴露的模型名称配置到上述变量中。 diff --git a/docs/github-collaboration-flow.md b/docs/github-collaboration-flow.md index 01bc88e..9745728 100644 --- a/docs/github-collaboration-flow.md +++ b/docs/github-collaboration-flow.md @@ -22,7 +22,7 @@ issue -> triage/spec -> implement -> pr -> review -> comments -> merge 目标仓库已有自己的 CI 时,推荐在 CI 成功路径中 dispatch `review-pr.yml`,不要直接改 managed review workflow。这样后续升级 AICodingFlow 时可以覆盖受管 workflow,而不会丢失目标仓库自己的 CI 编排。 -所有 Codex workflow 使用同一组模型配置。`CODEX_API_KEY`、`CODEX_API_ENDPOINT` 和 `CODEX_MODEL` 必须配置。`CODEX_API_ENDPOINT` 必须提供 Responses API;仅提供 Chat Completions 的模型服务(例如 DeepSeek 原生 endpoint)需要通过 LiteLLM、OneAPI 或其他协议转换网关接入,`CODEX_MODEL` 填写网关支持的模型名称。 +所有 Codex workflow 使用同一组模型配置。`CODEX_API_KEY` 和 `CODEX_API_ENDPOINT` 必须配置;`CODEX_MODEL` 可选,为空时使用 Codex 默认模型。`CODEX_API_ENDPOINT` 必须提供 Responses API;仅提供 Chat Completions 的模型服务(例如 DeepSeek 原生 endpoint)需要通过 LiteLLM、OneAPI 或其他协议转换网关接入,`CODEX_MODEL` 填写网关支持的模型名称。 ## Label 和触发规则