diff --git a/.github/workflows/test-coverage-reporter.lock.yml b/.github/workflows/test-coverage-reporter.lock.yml index 74fa2620..0e41f6c2 100644 --- a/.github/workflows/test-coverage-reporter.lock.yml +++ b/.github/workflows/test-coverage-reporter.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"0d6606811df703678fdac546c05cd622688772a9521152843eb9e2908b088069","body_hash":"ffe40b74ed50e8a68d91160bf69847451f095709d8955ed5071195fc8b097bd4","compiler_version":"v0.79.4","agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"70c2efd4a30202c25a5e437c256991392d2cb3df434e317775e377a2247acfa6","body_hash":"3a6fc0c9f156ba0097f1f4f7470adf217d22e60cbfe1ad58a8b6239511884bf4","compiler_version":"v0.79.4","agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"d059700c6a8ec3b5fd798b9ea60f5d048447b918","version":"v0.79.4"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.0"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.0"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.0"},{"image":"ghcr.io/github/gh-aw-mcpg:latest"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]} # ___ _ _ # / _ \ | | (_) @@ -127,7 +127,7 @@ jobs: GH_AW_INFO_EXPERIMENTAL: "false" GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" GH_AW_INFO_STAGED: "false" - GH_AW_INFO_ALLOWED_DOMAINS: '["node","github"]' + GH_AW_INFO_ALLOWED_DOMAINS: '["github"]' GH_AW_INFO_FIREWALL_ENABLED: "true" GH_AW_INFO_AWF_VERSION: "v0.27.0" GH_AW_INFO_AWMG_VERSION: "latest" @@ -201,9 +201,11 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl + GH_AW_EXPR_14C2CEC4: ${{ steps.recent-changes.outputs.RECENT_FILES }} GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} GH_AW_EXPR_38DC592D: ${{ steps.critical-gaps.outputs.CRITICAL_GAPS }} GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_6760DA50: ${{ steps.func-audit.outputs.FUNC_AUDIT }} GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} GH_AW_EXPR_D05879D3: ${{ steps.coverage-table.outputs.COVERAGE_TABLE }} GH_AW_EXPR_E085E874: ${{ steps.coverage-json.outputs.COVERAGE_JSON }} @@ -276,6 +278,8 @@ jobs: GH_AW_EXPR_E085E874: ${{ steps.coverage-json.outputs.COVERAGE_JSON }} GH_AW_EXPR_D05879D3: ${{ steps.coverage-table.outputs.COVERAGE_TABLE }} GH_AW_EXPR_38DC592D: ${{ steps.critical-gaps.outputs.CRITICAL_GAPS }} + GH_AW_EXPR_6760DA50: ${{ steps.func-audit.outputs.FUNC_AUDIT }} + GH_AW_EXPR_14C2CEC4: ${{ steps.recent-changes.outputs.RECENT_FILES }} with: script: | const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); @@ -286,9 +290,11 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_EXPR_14C2CEC4: ${{ steps.recent-changes.outputs.RECENT_FILES }} GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} GH_AW_EXPR_38DC592D: ${{ steps.critical-gaps.outputs.CRITICAL_GAPS }} GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} + GH_AW_EXPR_6760DA50: ${{ steps.func-audit.outputs.FUNC_AUDIT }} GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }} GH_AW_EXPR_D05879D3: ${{ steps.coverage-table.outputs.COVERAGE_TABLE }} GH_AW_EXPR_E085E874: ${{ steps.coverage-json.outputs.COVERAGE_JSON }} @@ -311,9 +317,11 @@ jobs: return await substitutePlaceholders({ file: process.env.GH_AW_PROMPT, substitutions: { + GH_AW_EXPR_14C2CEC4: process.env.GH_AW_EXPR_14C2CEC4, GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A, GH_AW_EXPR_38DC592D: process.env.GH_AW_EXPR_38DC592D, GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A, + GH_AW_EXPR_6760DA50: process.env.GH_AW_EXPR_6760DA50, GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A, GH_AW_EXPR_D05879D3: process.env.GH_AW_EXPR_D05879D3, GH_AW_EXPR_E085E874: process.env.GH_AW_EXPR_E085E874, @@ -437,13 +445,19 @@ jobs: run: set -o pipefail && npm run test:coverage 2>&1 | tail -20 - id: coverage-json name: Capture coverage summary JSON - run: "{\n echo \"COVERAGE_JSON</dev/null || echo \"{}\"\n echo \"EOF\"\n} >> \"$GITHUB_OUTPUT\"\n" + run: "{\n echo \"COVERAGE_JSON<\n k === 'total' || (v.statements && v.statements.pct < 80)\n )\n );\n console.log(JSON.stringify(filtered, null, 2));\n \" 2>/dev/null || echo \"{}\"\n echo \"EOF\"\n} >> \"$GITHUB_OUTPUT\"\n" - id: coverage-table name: Compute per-file coverage table run: "{\n echo \"COVERAGE_TABLE< k !== 'total')\n .map(([k, v]) => ({\n file: k.replace(process.cwd() + '/', ''),\n stmts: v.statements.pct,\n branch: v.branches.pct,\n funcs: v.functions.pct,\n lines: v.lines.pct,\n }))\n .sort((a, b) => a.stmts - b.stmts);\n console.log('| File | Stmts | Branch | Funcs | Lines | Status |');\n console.log('|------|------:|-------:|------:|------:|--------|');\n rows.forEach(r => {\n const status = r.stmts >= 80 ? '✅' : r.stmts >= 50 ? '⚠️' : '❌';\n console.log(\\`| \\${r.file} | \\${r.stmts}% | \\${r.branch}% | \\${r.funcs}% | \\${r.lines}% | \\${status} |\\`);\n });\n const t = d.total;\n if (t) {\n console.log(\\`| **TOTAL** | **\\${t.statements.pct}%** | **\\${t.branches.pct}%** | **\\${t.functions.pct}%** | **\\${t.lines.pct}%** | |\\`);\n }\n \" 2>/dev/null || echo \"Coverage data not available\"\n echo \"EOF\"\n} >> \"$GITHUB_OUTPUT\"\n" - id: critical-gaps name: Identify critical paths with low coverage run: "{\n echo \"CRITICAL_GAPS< {\n const key = Object.keys(d).find(k => k.includes(p.replace('src/', '')));\n if (key && d[key]) {\n const v = d[key];\n const label = v.statements.pct < 50 ? '🔴 CRITICAL' : v.statements.pct < 80 ? '🟡 LOW' : '🟢 OK';\n console.log(\\`\\${label} \\${p}: stmts=\\${v.statements.pct}% branch=\\${v.branches.pct}% funcs=\\${v.functions.pct}%\\`);\n } else {\n console.log(\\`⬜ NOT FOUND: \\${p}\\`);\n }\n });\n \" 2>/dev/null || echo \"Coverage data not available\"\n echo \"EOF\"\n} >> \"$GITHUB_OUTPUT\"\n" + - id: func-audit + name: Identify exported functions in security-critical files + run: "{\n echo \"FUNC_AUDIT</dev/null | head -20\n echo \"=== squid-config.ts ===\"\n grep -nE \"^export[[:space:]]+(async[[:space:]]+)?function|^export[[:space:]]+const\" src/squid-config.ts 2>/dev/null | head -20\n echo \"=== domain-patterns.ts branches ===\"\n grep -nE \"\\bif[[:space:]]*\\(|\\bswitch[[:space:]]*\\(|\\?.*:\" src/domain-patterns.ts 2>/dev/null | head -20\n echo \"Test files: $(find src -name '*.test.ts' | wc -l) / Source files: $(find src -name '*.ts' ! -name '*.test.ts' | wc -l)\"\n echo \"EOF\"\n} >> \"$GITHUB_OUTPUT\"\n" + - id: recent-changes + name: Identify recently changed source files + run: "{\n echo \"RECENT_FILES</dev/null | grep -E \"^\\+.*export.*function|^\\+.*export.*const.*=.*\\(\" | head -20\n echo \"EOF\"\n} >> \"$GITHUB_OUTPUT\"\n" - name: Configure Git credentials env: @@ -744,7 +758,7 @@ jobs: mkdir -p /home/runner/.copilot GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node) - cat << GH_AW_MCP_CONFIG_c17d5c34bb741810_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" + cat << GH_AW_MCP_CONFIG_dec0f3c248d4cfe0_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs" { "mcpServers": { "github": { @@ -754,7 +768,7 @@ jobs: "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", - "GITHUB_TOOLSETS": "repos,actions,discussions" + "GITHUB_TOOLSETS": "repos,discussions" }, "guard-policies": { "allow-only": { @@ -785,7 +799,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_c17d5c34bb741810_EOF + GH_AW_MCP_CONFIG_dec0f3c248d4cfe0_EOF - name: Mount MCP servers as CLIs id: mount-mcp-clis continue-on-error: true @@ -823,7 +837,7 @@ jobs: export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK" (umask 177 && touch /tmp/gh-aw/agent-stdio.log) GH_AW_MAX_AI_CREDITS="${{ vars.GH_AW_DEFAULT_MAX_AI_CREDITS || '1000' }}" - printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.0/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"api.npms.io\",\"bun.sh\",\"cdn.jsdelivr.net\",\"codeload.github.com\",\"deb.nodesource.com\",\"deno.land\",\"docs.github.com\",\"esm.sh\",\"get.pnpm.io\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"googleapis.deno.dev\",\"googlechromelabs.github.io\",\"host.docker.internal\",\"jsr.io\",\"lfs.github.com\",\"nodejs.org\",\"npm.pkg.github.com\",\"npmjs.com\",\"npmjs.org\",\"objects.githubusercontent.com\",\"patch-diff.githubusercontent.com\",\"raw.githubusercontent.com\",\"registry.bower.io\",\"registry.npmjs.com\",\"registry.npmjs.org\",\"registry.yarnpkg.com\",\"repo.yarnpkg.com\",\"skimdb.npmjs.com\",\"storage.googleapis.com\",\"telemetry.enterprise.githubcopilot.com\",\"telemetry.vercel.com\",\"www.npmjs.com\",\"www.npmjs.org\",\"yarnpkg.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.0\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" + printf '%s\n' "{\"\$schema\":\"https://github.com/github/gh-aw-firewall/releases/download/v0.27.0/awf-config.schema.json\",\"network\":{\"allowDomains\":[\"*.githubusercontent.com\",\"api.business.githubcopilot.com\",\"api.enterprise.githubcopilot.com\",\"api.github.com\",\"api.githubcopilot.com\",\"api.individual.githubcopilot.com\",\"codeload.github.com\",\"docs.github.com\",\"github-cloud.githubusercontent.com\",\"github-cloud.s3.amazonaws.com\",\"github.blog\",\"github.com\",\"github.githubassets.com\",\"host.docker.internal\",\"lfs.github.com\",\"objects.githubusercontent.com\",\"patch-diff.githubusercontent.com\",\"raw.githubusercontent.com\",\"telemetry.enterprise.githubcopilot.com\"]},\"apiProxy\":{\"enabled\":true,\"enableTokenSteering\":true,\"maxRuns\":500,\"maxAiCredits\":${GH_AW_MAX_AI_CREDITS},\"models\":{\"agent\":[\"sonnet-6x\",\"gpt-5.4\",\"gpt-5.3\",\"gemini-pro\",\"any\"],\"antigravity\":[\"copilot/antigravity*\",\"google/antigravity*\",\"gemini/antigravity*\"],\"any\":[\"copilot/*\",\"anthropic/*\",\"openai/*\",\"google/*\",\"gemini/*\"],\"claude\":[\"agent\"],\"codex\":[\"agent\"],\"coding\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\",\"gpt-5-codex\"],\"computer-use\":[\"copilot/*computer-use*\",\"google/*computer-use*\",\"gemini/*computer-use*\",\"openai/*computer-use*\"],\"copilot\":[\"agent\"],\"deep-research\":[\"copilot/deep-research*\",\"copilot/o3-deep-research*\",\"copilot/o4-mini-deep-research*\",\"google/deep-research*\",\"gemini/deep-research*\",\"openai/o3-deep-research*\",\"openai/o4-mini-deep-research*\"],\"gemini\":[\"agent\"],\"gemini-3-flash\":[\"copilot/gemini-3*flash*\",\"google/gemini-3*flash*\",\"gemini/gemini-3*flash*\"],\"gemini-3-pro\":[\"copilot/gemini-3*pro*\",\"google/gemini-3*pro*\",\"google/nano-banana*\",\"gemini/gemini-3*pro*\"],\"gemini-3.1-flash\":[\"copilot/gemini-3.1*flash*\",\"google/gemini-3.1*flash*\",\"gemini/gemini-3.1*flash*\"],\"gemini-3.1-pro\":[\"copilot/gemini-3.1*pro*\",\"google/gemini-3.1*pro*\",\"gemini/gemini-3.1*pro*\"],\"gemini-3.5-flash\":[\"copilot/gemini-3.5*flash*\",\"google/gemini-3.5*flash*\",\"gemini/gemini-3.5*flash*\"],\"gemini-flash\":[\"copilot/gemini-*flash*\",\"google/gemini-*flash*\",\"gemini/gemini-*flash*\"],\"gemini-flash-lite\":[\"copilot/gemini-*flash*lite*\",\"google/gemini-*flash*lite*\",\"gemini/gemini-*flash*lite*\"],\"gemini-pro\":[\"copilot/gemini-*pro*\",\"google/gemini-*pro*\",\"gemini/gemini-*pro*\"],\"gemma\":[\"copilot/gemma*\",\"google/gemma*\",\"gemini/gemma*\"],\"gpt-5\":[\"copilot/gpt-5*\",\"openai/gpt-5*\"],\"gpt-5-codex\":[\"copilot/gpt-5*codex*\",\"openai/gpt-5*codex*\"],\"gpt-5-mini\":[\"copilot/gpt-5*mini*\",\"openai/gpt-5*mini*\"],\"gpt-5-nano\":[\"copilot/gpt-5*nano*\",\"openai/gpt-5*nano*\"],\"gpt-5-pro\":[\"copilot/gpt-5*pro*\",\"openai/gpt-5*pro*\"],\"gpt-5.2\":[\"copilot/gpt-5.2*\",\"openai/gpt-5.2*\"],\"gpt-5.3\":[\"copilot/gpt-5.3*\",\"openai/gpt-5.3*\"],\"gpt-5.4\":[\"copilot/gpt-5.4*\",\"openai/gpt-5.4*\"],\"gpt-5.5\":[\"copilot/gpt-5.5*\",\"openai/gpt-5.5*\"],\"haiku\":[\"copilot/*haiku*\",\"anthropic/*haiku*\"],\"large\":[\"sonnet\",\"gpt-5-pro\",\"gpt-5\",\"gemini-pro\"],\"mai-code\":[\"copilot/MAI-Code*\",\"copilot/mai-code*\",\"openai/MAI-Code*\"],\"mini\":[\"haiku\",\"gpt-5-mini\",\"gpt-5-nano\",\"gemini-flash-lite\"],\"nano-banana\":[\"copilot/nano-banana*\",\"google/nano-banana*\",\"gemini/nano-banana*\"],\"opus\":[\"copilot/*opus*\",\"anthropic/*opus*\"],\"opusplan\":[\"opus?effort=high\"],\"reasoning\":[\"copilot/o1*\",\"copilot/o3*\",\"copilot/o4*\",\"openai/o1*\",\"openai/o3*\",\"openai/o4*\"],\"robotics\":[\"copilot/*robotics*\",\"google/*robotics*\",\"gemini/*robotics*\"],\"small\":[\"mini\"],\"small-agent\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash\"],\"sonnet\":[\"copilot/*sonnet*\",\"anthropic/*sonnet*\"],\"sonnet-6x\":[\"copilot/*sonnet-4.5*\",\"copilot/*sonnet-4.6*\",\"copilot/*sonnet-4-5-*\",\"anthropic/*sonnet-4-5-*\",\"copilot/*sonnet-4-6*\",\"anthropic/*sonnet-4-6*\"],\"summarization\":[\"haiku\",\"gpt-5-mini\",\"gemini-flash-lite\",\"mini\"],\"vision\":[\"copilot/gemini-*image*\",\"gemini/gemini-*image*\",\"copilot/gemini-*flash*\",\"gemini/gemini-*flash*\"]}},\"container\":{\"imageTag\":\"0.27.0\"}}" > "${RUNNER_TEMP}/gh-aw/awf-config.json" GH_AW_MODEL_MULTIPLIERS_PATH="/tmp/gh-aw/model_multipliers.json" node "${RUNNER_TEMP}/gh-aw/actions/merge_awf_model_multipliers.cjs" cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json export GH_AW_MODELS_JSON_PATH="/tmp/gh-aw/models.json" @@ -942,7 +956,7 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,bun.sh,cdn.jsdelivr.net,codeload.github.com,deb.nodesource.com,deno.land,docs.github.com,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,jsr.io,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,patch-diff.githubusercontent.com,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,patch-diff.githubusercontent.com,raw.githubusercontent.com,telemetry.enterprise.githubcopilot.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -1326,7 +1340,7 @@ jobs: env: GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }} GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,bun.sh,cdn.jsdelivr.net,codeload.github.com,deb.nodesource.com,deno.land,docs.github.com,esm.sh,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,googleapis.deno.dev,googlechromelabs.github.io,host.docker.internal,jsr.io,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,patch-diff.githubusercontent.com,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,skimdb.npmjs.com,storage.googleapis.com,telemetry.enterprise.githubcopilot.com,telemetry.vercel.com,www.npmjs.com,www.npmjs.org,yarnpkg.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,patch-diff.githubusercontent.com,raw.githubusercontent.com,telemetry.enterprise.githubcopilot.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"general\",\"expires\":168,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[Coverage Report] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"report_incomplete\":{}}" diff --git a/.github/workflows/test-coverage-reporter.md b/.github/workflows/test-coverage-reporter.md index 05572922..f624dcaa 100644 --- a/.github/workflows/test-coverage-reporter.md +++ b/.github/workflows/test-coverage-reporter.md @@ -26,12 +26,11 @@ sandbox: strict: false network: allowed: - - node - github tools: github: - toolsets: [repos, actions, discussions] + toolsets: [repos, discussions] bash: true safe-outputs: @@ -59,7 +58,17 @@ steps: run: | { echo "COVERAGE_JSON</dev/null || echo "{}" + node -e " + const fs = require('fs'); + const raw = fs.readFileSync('coverage/coverage-summary.json', 'utf8'); + const d = JSON.parse(raw); + const filtered = Object.fromEntries( + Object.entries(d).filter(([k, v]) => + k === 'total' || (v.statements && v.statements.pct < 80) + ) + ); + console.log(JSON.stringify(filtered, null, 2)); + " 2>/dev/null || echo "{}" echo "EOF" } >> "$GITHUB_OUTPUT" @@ -125,6 +134,35 @@ steps: " 2>/dev/null || echo "Coverage data not available" echo "EOF" } >> "$GITHUB_OUTPUT" + + - name: Identify exported functions in security-critical files + id: func-audit + run: | + { + echo "FUNC_AUDIT</dev/null | head -20 + echo "=== squid-config.ts ===" + grep -nE "^export[[:space:]]+(async[[:space:]]+)?function|^export[[:space:]]+const" src/squid-config.ts 2>/dev/null | head -20 + echo "=== domain-patterns.ts branches ===" + grep -nE "\\bif[[:space:]]*\\(|\\bswitch[[:space:]]*\\(|\\?.*:" src/domain-patterns.ts 2>/dev/null | head -20 + echo "Test files: $(find src -name '*.test.ts' | wc -l) / Source files: $(find src -name '*.ts' ! -name '*.test.ts' | wc -l)" + echo "EOF" + } >> "$GITHUB_OUTPUT" + + - name: Identify recently changed source files + id: recent-changes + run: | + { + echo "RECENT_FILES</dev/null | grep -E "^\\+.*export.*function|^\\+.*export.*const.*=.*\\(" | head -20 + echo "EOF" + } >> "$GITHUB_OUTPUT" --- # Test Coverage Reporter @@ -163,35 +201,17 @@ Identify: ### Phase 2: Analyze Coverage Gaps in Security-Critical Paths -Use bash to look deeper at the specific uncovered areas in security-critical files: - -```bash -# Check which functions in host-iptables.ts are covered -grep -n "^export\s\+function\|^export\s\+async\s\+function\|^export\s\+const" src/host-iptables.ts 2>/dev/null | head -20 - -# Check which functions in squid-config.ts are covered -grep -n "^export\s\+function\|^export\s\+const" src/squid-config.ts 2>/dev/null | head -20 - -# Look for uncovered branches in domain-patterns.ts -grep -n "if\s*(\|switch\s*(\|?\s*:" src/domain-patterns.ts 2>/dev/null | head -20 - -# Check overall test file health -find src -name "*.test.ts" | xargs wc -l 2>/dev/null | sort -rn | head -10 -echo "Total test files: $(find src -name '*.test.ts' | wc -l)" -echo "Total source files: $(find src -name '*.ts' ! -name '*.test.ts' | wc -l)" -``` +Use the pre-computed function audit from `${{ steps.func-audit.outputs.FUNC_AUDIT }}` to identify which exported functions and branches exist in security-critical files. This includes: +- Exported functions/constants in `src/host-iptables.ts` +- Exported functions/constants in `src/squid-config.ts` +- Branch points (`if`/`switch`/ternary) in `src/domain-patterns.ts` +- Test file count vs source file count ### Phase 3: Check Recent Coverage Changes -Look at recent commits to identify new code paths that may lack tests: - -```bash -# Files changed in the last 7 days -git log --since="7 days ago" --name-only --format="" | grep -E "^src/.*\.ts$" | sort -u | head -20 - -# New functions added recently (rough heuristic) -git log --since="7 days ago" -p -- "src/*.ts" 2>/dev/null | grep "^+.*export.*function\|^+.*export.*const.*=.*(" | head -20 -``` +Use the pre-computed recent changes from `${{ steps.recent-changes.outputs.RECENT_FILES }}` to identify new code paths that may lack tests. This includes: +- TypeScript files under `src/` changed in the last 7 days +- Newly exported functions or constants added recently ### Phase 4: Post Coverage Report Discussion