From a98615b996b0635a1696339027eeeca036c36485 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 13 Jul 2026 20:12:22 +0900 Subject: [PATCH] fix(opencode): reuse locked CodeGraph runtime --- .github/workflows/opencode-review.yml | 14 ++++++++++--- scripts/ci/test_strix_quick_gate.sh | 29 +++++++++++++++++++++++++-- tests/test_opencode_agent_contract.py | 11 +++++++++- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 613ee5c56..614b66926 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -1771,6 +1771,7 @@ jobs: - name: Initialize CodeGraph index for OpenCode env: + CODEGRAPH_NO_DOWNLOAD: "1" CODEGRAPH_TRUSTED_ROOT: ${{ runner.temp }}/trusted-codegraph NPM_CONFIG_IGNORE_SCRIPTS: "true" OPENCODE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-pr-head @@ -1781,7 +1782,10 @@ jobs: cp scripts/ci/codegraph-package/package.json \ scripts/ci/codegraph-package/package-lock.json \ "$CODEGRAPH_TRUSTED_ROOT"/ - npm ci --ignore-scripts --omit=dev --prefix "$CODEGRAPH_TRUSTED_ROOT" + ( + cd "$CODEGRAPH_TRUSTED_ROOT" + npm ci --ignore-scripts --omit=dev --no-audit --no-fund + ) CODEGRAPH_BIN="${CODEGRAPH_TRUSTED_ROOT}/node_modules/.bin/codegraph" test -x "$CODEGRAPH_BIN" cd "$OPENCODE_SOURCE_WORKDIR" @@ -2418,6 +2422,8 @@ jobs: - name: Prepare isolated OpenCode review workspace env: + CODEGRAPH_BIN: ${{ runner.temp }}/trusted-codegraph/node_modules/.bin/codegraph + CODEGRAPH_NO_DOWNLOAD: "1" OPENCODE_REVIEW_WORKDIR: ${{ runner.temp }}/opencode-review-project OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md OPENCODE_FAILED_CHECK_EVIDENCE_FILE: ${{ runner.temp }}/opencode-failed-check-evidence.md @@ -2788,7 +2794,9 @@ jobs: cp "$GITHUB_WORKSPACE/scripts/ci/sandboxed_web_e2e.py" "${OPENCODE_REVIEW_WORKDIR}/scripts/ci/sandboxed_web_e2e.py" cp "$GITHUB_WORKSPACE/scripts/ci/review_execution_contracts.py" "${OPENCODE_REVIEW_WORKDIR}/scripts/ci/review_execution_contracts.py" - jq -n --arg workspace "$OPENCODE_SOURCE_WORKDIR" '{ + jq -n \ + --arg workspace "$OPENCODE_SOURCE_WORKDIR" \ + --arg codegraph_bin "$CODEGRAPH_BIN" '{ "$schema": "https://opencode.ai/config.json", "model": "github-models/deepseek/deepseek-r1-0528", "small_model": "github-models/deepseek/deepseek-v3-0324", @@ -2800,7 +2808,7 @@ jobs: "command": [ "bash", "-lc", - ("cd " + ($workspace | @sh) + " && NPM_CONFIG_IGNORE_SCRIPTS=true npx -y @colbymchenry/codegraph@0.9.9 serve --mcp") + ("cd " + ($workspace | @sh) + " && CODEGRAPH_NO_DOWNLOAD=1 exec " + ($codegraph_bin | @sh) + " serve --mcp") ], "enabled": true }, diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 4acb8edfd..373f4f9d6 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -498,12 +498,20 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() { assert_file_contains "$workflow_file" 'GITHUB_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }}' "opencode review gives the provider the same model token source" assert_file_matches "$workflow_file" 'uses:[[:space:]]+actions/checkout@[0-9a-fA-F]{40}([[:space:]]|$)' "opencode review workflow pins checkout to a full commit SHA" assert_workflow_uses_are_sha_pinned "$workflow_file" "opencode review workflow" - assert_file_contains "$workflow_file" "@colbymchenry/codegraph@0.9.9" "opencode review workflow pins the CodeGraph package" + assert_file_contains "$workflow_file" "scripts/ci/codegraph-package/package-lock.json" "opencode review workflow installs CodeGraph from the committed lockfile" + if ! jq -e ' + .packages["node_modules/@colbymchenry/codegraph"] + | .version == "0.9.9" and (.integrity | startswith("sha512-")) + ' "$REPO_ROOT/scripts/ci/codegraph-package/package-lock.json" >/dev/null; then + record_failure "opencode review CodeGraph lockfile pins version 0.9.9 with integrity" + fi + assert_file_contains "$workflow_file" "CODEGRAPH_NO_DOWNLOAD=1 exec" "opencode review reuses the integrity-pinned CodeGraph binary for MCP" + assert_file_not_contains "$workflow_file" "@colbymchenry/codegraph@0.9.9 serve --mcp" "opencode review must not fetch CodeGraph again for MCP" assert_file_contains "$workflow_file" "https://mcp.deepwiki.com/mcp" "opencode review workflow configures the DeepWiki remote MCP server" assert_file_contains "$workflow_file" "@upstash/context7-mcp@3.1.0" "opencode review workflow pins the Context7 MCP package" assert_file_contains "$workflow_file" "@guhcostan/web-search-mcp@1.0.5" "opencode review workflow pins a web search MCP package" assert_file_contains "$workflow_file" "NPM_CONFIG_LOGLEVEL" "opencode review workflow suppresses npm warning output for local MCP package fetches" - assert_file_contains "$workflow_file" 'NPM_CONFIG_IGNORE_SCRIPTS: "true"' "opencode review workflow disables npm lifecycle scripts for CodeGraph npx" + assert_file_contains "$workflow_file" 'NPM_CONFIG_IGNORE_SCRIPTS: "true"' "opencode review workflow disables npm lifecycle scripts for local MCP packages" assert_file_contains "$workflow_file" "init -i" "opencode review workflow builds the CodeGraph index" assert_file_contains "$workflow_file" "CodeGraph MCP tools" "opencode review prompt requires CodeGraph-backed review evidence" assert_file_contains "$workflow_file" "general-purpose and meticulous" "opencode review prompt requires a general-purpose meticulous review" @@ -2871,6 +2879,8 @@ run_gate_case() { local raw_llm_api_base="https://example.invalid/generateContent" if [ "$raw_llm_api_base_override" != "__DEFAULT__" ]; then raw_llm_api_base="$raw_llm_api_base_override" + elif [ "$default_provider" = "openai" ]; then + raw_llm_api_base="" fi local transient_retry_per_model="${12-0}" local min_fail_severity="${13-CRITICAL}" @@ -2890,6 +2900,10 @@ run_gate_case() { local gemini_fallback_models="${27-__SAME_AS_FALLBACK_MODELS__}" local generic_fallback_models="${28-}" local fail_on_provider_signal="${29-1}" + if [ "$default_provider" = "openai" ] && [ -z "$generic_fallback_models" ] && [ -n "$fallback_models" ]; then + generic_fallback_models="$fallback_models" + fallback_models="" + fi if [ -n "${STRIX_TEST_CASE_FILTER:-}" ] && [ "$scenario" != "$STRIX_TEST_CASE_FILTER" ]; then return @@ -5406,6 +5420,17 @@ run_filtered_gate_case_if_requested() { "vertex_ai/missing-primary|vertex_ai/fallback-one" \ "|" ;; + openai-primary-quota-fallback-success) + run_gate_case_allow_provider_signal "openai-primary-quota-fallback-success" \ + "openai/quota-primary" \ + "openai/fallback-one openai/fallback-two" \ + "0" \ + "REGEX:Strix quick scan succeeded with fallback model 'openai/fallback-one' in [0-9]+s\\." \ + "2" \ + "openai/quota-primary|openai/fallback-one" \ + "|" \ + "openai" + ;; pr-critical-changed-json-target) run_gate_case "pr-critical-changed-json-target" \ "vertex_ai/gemini-2.5-pro" \ diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index d3bf9b546..257ec8b04 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -1067,13 +1067,22 @@ def test_opencode_privileged_review_security_boundaries_are_fail_closed(): "\n - name:", 1 )[0] assert "CODEGRAPH_TRUSTED_ROOT" in codegraph_step + assert 'CODEGRAPH_NO_DOWNLOAD: "1"' in codegraph_step assert "cp scripts/ci/codegraph-package/package.json" in codegraph_step assert "scripts/ci/codegraph-package/package-lock.json" in codegraph_step - assert 'npm ci --ignore-scripts --omit=dev --prefix "$CODEGRAPH_TRUSTED_ROOT"' in codegraph_step + assert 'cd "$CODEGRAPH_TRUSTED_ROOT"' in codegraph_step + assert "npm ci --ignore-scripts --omit=dev --no-audit --no-fund" in codegraph_step + assert '--prefix "$CODEGRAPH_TRUSTED_ROOT"' not in codegraph_step assert '"$CODEGRAPH_BIN" init -i' in codegraph_step assert '"$CODEGRAPH_BIN" status' in codegraph_step assert "npm install --ignore-scripts --no-save" not in codegraph_step assert 'npx -y "$CODEGRAPH_PACKAGE" init -i' not in codegraph_step + isolated_step = target_job.split( + " - name: Prepare isolated OpenCode review workspace", 1 + )[1].split("\n - name:", 1)[0] + assert "CODEGRAPH_BIN: ${{ runner.temp }}/trusted-codegraph/node_modules/.bin/codegraph" in isolated_step + assert "CODEGRAPH_NO_DOWNLOAD=1 exec " in isolated_step + assert "@colbymchenry/codegraph@0.9.9 serve --mcp" not in isolated_step package_lock = json.loads( Path("scripts/ci/codegraph-package/package-lock.json").read_text(encoding="utf-8") )