From 5e5a4055201343421516053d46d9fc42d2581cfa Mon Sep 17 00:00:00 2001 From: "Terry.Mao" Date: Wed, 4 Nov 2026 12:41:54 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20=E4=BF=AE=E5=A4=8D=20Markdown=20?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=E5=99=A8=E5=AE=89=E8=A3=85=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_prepare_issue_spec_context.py | 20 +++++++++++++++++++ .../test_resolve_pr_event.py | 19 ++++++++++++++++++ .../test_review_workflow_dispatch.py | 16 +++++++++++++++ .github/scripts/prepare_issue_spec_context.py | 4 ++-- .github/scripts/resolve_pr_event.py | 4 ++-- .github/workflows/ci.yml | 5 +++++ .github/workflows/create-spec-from-issue.yml | 7 +++++++ .github/workflows/respond-to-pr-comment.yml | 10 ++++++++++ .github/workflows/review-pr.yml | 14 +++++++++++++ 9 files changed, 95 insertions(+), 4 deletions(-) diff --git a/.github/aicodingflow-tests/test_prepare_issue_spec_context.py b/.github/aicodingflow-tests/test_prepare_issue_spec_context.py index 9ea1720..8c37250 100644 --- a/.github/aicodingflow-tests/test_prepare_issue_spec_context.py +++ b/.github/aicodingflow-tests/test_prepare_issue_spec_context.py @@ -4,6 +4,8 @@ import argparse import json +import subprocess +import sys import tempfile import unittest from pathlib import Path @@ -18,6 +20,24 @@ class PrepareIssueSpecContextTest(unittest.TestCase): + def test_issue_event_runs_without_installed_markdown_parser(self) -> None: + script_dir = Path(__file__).resolve().parents[1] / "scripts" + subprocess.run( + [ + sys.executable, "-S", "-c", + "import argparse, sys; " + "sys.path.insert(0, sys.argv[1]); " + "from prepare_issue_spec_context import should_run; " + "args = argparse.Namespace(event_path='', event_name='workflow_dispatch', force=False, agent_login='codex'); " + "issue = {'labels': [{'name': 'ready-to-spec'}], 'assignees': [{'login': 'codex'}]}; " + "assert should_run(args, issue)[0] is True", + str(script_dir), + ], + check=True, + capture_output=True, + text=True, + ) + def test_spec_paths_use_issue_directory_and_spec_branch(self) -> None: self.assertEqual( prepare_issue_spec_context.spec_paths(32), diff --git a/.github/aicodingflow-tests/test_resolve_pr_event.py b/.github/aicodingflow-tests/test_resolve_pr_event.py index 5c9fe62..0706daa 100644 --- a/.github/aicodingflow-tests/test_resolve_pr_event.py +++ b/.github/aicodingflow-tests/test_resolve_pr_event.py @@ -1,6 +1,8 @@ from __future__ import annotations import json +import subprocess +import sys import tempfile import unittest from pathlib import Path @@ -95,6 +97,23 @@ def test_pull_request_event_reuses_existing_payload(self) -> None: event, ) + def test_pull_request_event_runs_without_installed_markdown_parser(self) -> None: + with tempfile.TemporaryDirectory() as directory: + event_path = Path(directory) / "event.json" + output_path = Path(directory) / "resolved.json" + event_path.write_text(json.dumps({"pull_request": pr_payload()}), encoding="utf-8") + subprocess.run( + [ + sys.executable, "-S", str(Path(__file__).resolve().parents[1] / "scripts/resolve_pr_event.py"), + "--repo", "owner/repo", "--event-name", "pull_request_target", + "--event-path", str(event_path), "--output", str(output_path), + ], + check=True, + capture_output=True, + text=True, + ) + self.assertTrue(output_path.is_file()) + def test_pull_request_target_event_reuses_existing_payload(self) -> None: with tempfile.TemporaryDirectory() as directory: event_path = Path(directory) / "event.json" diff --git a/.github/aicodingflow-tests/test_review_workflow_dispatch.py b/.github/aicodingflow-tests/test_review_workflow_dispatch.py index 2434477..fb2fd71 100644 --- a/.github/aicodingflow-tests/test_review_workflow_dispatch.py +++ b/.github/aicodingflow-tests/test_review_workflow_dispatch.py @@ -30,7 +30,10 @@ def test_pr_comment_jobs_install_markdown_parser_before_context(self) -> None: for job in ("preflight", "respond"): with self.subTest(job=job): job_steps = steps(data, job) + setup = next(step for step in job_steps if step.get("name") == "set-up-python-for-markdown-parser") install = next(step for step in job_steps if step.get("name") == "install-markdown-parser") + self.assertEqual(setup["uses"], "actions/setup-python@v6") + self.assertLess(job_steps.index(setup), job_steps.index(install)) self.assertIn("-r .github/scripts/requirements-spec-context.txt", install["run"]) context_index = next(i for i, step in enumerate(job_steps) if step.get("name") == "prepare-pr-comment-context") self.assertLess(job_steps.index(install), context_index) @@ -40,7 +43,12 @@ def test_pr_review_jobs_install_markdown_parser_before_resolving_event(self) -> for job in ("preflight", "review"): with self.subTest(job=job): job_steps = steps(data, job) + setup = next(step for step in job_steps if step.get("name") == "set-up-python-for-markdown-parser") install = next(step for step in job_steps if step.get("name") == "install-markdown-parser") + self.assertEqual(setup["uses"], "actions/setup-python@v6") + self.assertEqual(setup["if"], "github.event_name == 'issue_comment'") + self.assertEqual(install["if"], "github.event_name == 'issue_comment'") + self.assertLess(job_steps.index(setup), job_steps.index(install)) self.assertIn("-r .github/scripts/requirements-spec-context.txt", install["run"]) resolve_index = next(i for i, step in enumerate(job_steps) if step.get("name") == "resolve-pull-request") self.assertLess(job_steps.index(install), resolve_index) @@ -356,6 +364,11 @@ def test_create_spec_workflow_dispatches_review_after_pr_creation(self) -> None: create_steps = steps(data, "create-spec") parser_install = next(step for step in create_steps if step.get("name") == "install-markdown-parser") + parser_setup = next(step for step in create_steps if step.get("name") == "set-up-python-for-markdown-parser") + self.assertEqual(parser_setup["uses"], "actions/setup-python@v6") + self.assertEqual(parser_setup["if"], "github.event_name == 'issue_comment'") + self.assertEqual(parser_install["if"], "github.event_name == 'issue_comment'") + self.assertLess(create_steps.index(parser_setup), create_steps.index(parser_install)) self.assertIn("-r .github/scripts/requirements-spec-context.txt", parser_install["run"]) self.assertLess( create_steps.index(parser_install), @@ -363,6 +376,9 @@ def test_create_spec_workflow_dispatches_review_after_pr_creation(self) -> None: ) ci_data = workflow(".github/workflows/ci.yml") ci_install = next(step for step in steps(ci_data, "test") if step.get("name") == "install-python-test-dependencies") + ci_setup = next(step for step in steps(ci_data, "test") if step.get("name") == "set-up-python") + self.assertEqual(ci_setup["uses"], "actions/setup-python@v6") + self.assertLess(steps(ci_data, "test").index(ci_setup), steps(ci_data, "test").index(ci_install)) self.assertIn("-r .github/scripts/requirements-spec-context.txt", ci_install["run"]) handoff = next(step for step in create_steps if step.get("name") == "prepare-workflow-handoff-directory") prepare = next(step for step in create_steps if step.get("name") == "prepare-issue-context") diff --git a/.github/scripts/prepare_issue_spec_context.py b/.github/scripts/prepare_issue_spec_context.py index ead0d4e..662d23c 100644 --- a/.github/scripts/prepare_issue_spec_context.py +++ b/.github/scripts/prepare_issue_spec_context.py @@ -10,8 +10,6 @@ from pathlib import Path from typing import Any -from markdown_it import MarkdownIt - from artifact_contracts import load_json, write_github_output, write_json from context_snapshot import flatten_pages, format_issue_comments, remove_triggering_comment from github_api import fetch_default_branch, run_gh_json @@ -127,6 +125,8 @@ def mask(match: re.Match[str]) -> str: def comment_mentions_login(comment: str, login: str) -> bool: if not login: return False + + from markdown_it import MarkdownIt pattern = re.compile(rf"(?