diff --git a/docs/evals/uat-kit/outreach.md b/docs/evals/uat-kit/outreach.md index d3b9524..d826443 100644 --- a/docs/evals/uat-kit/outreach.md +++ b/docs/evals/uat-kit/outreach.md @@ -6,7 +6,7 @@ Copy/adapt. Do **not** include maintainer walkthroughs or internal architecture Subject: 60–90 min CodeDecay usability session (published npm package) -Hi , +Hi <Name>, We're running independent acceptance testing for CodeDecay's published AI workflow before closing our AI-native milestone. Looking for people who did diff --git a/judge-lab/package-lock.json b/judge-lab/package-lock.json index b25d95a..f25025a 100644 --- a/judge-lab/package-lock.json +++ b/judge-lab/package-lock.json @@ -23,11 +23,11 @@ "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "@vitejs/plugin-react": "6.0.2", - "@vitejs/plugin-rsc": "0.5.26", + "@vitejs/plugin-rsc": "0.5.34", "react-server-dom-webpack": "19.2.8", "tailwindcss": "4.2.1", "typescript": "5.9.3", - "vinext": "0.0.50", + "vinext": "1.0.0-beta.8", "vite": "8.1.5", "wrangler": "4.114.0" }, @@ -2528,6 +2528,16 @@ "node": ">=16" } }, + "node_modules/@vinext/types": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/@vinext/types/-/types-1.0.0-beta.2.tgz", + "integrity": "sha512-kbKY/H1RWlf7yox4G6Vwk1JeM6x0Tv1EAv28f3tDYkK3brq+89vp5eZHbdw3L/pZMbAhG4oFyhT9C976y+jkhQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, "node_modules/@vitejs/plugin-react": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", @@ -2555,17 +2565,17 @@ } }, "node_modules/@vitejs/plugin-rsc": { - "version": "0.5.26", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-rsc/-/plugin-rsc-0.5.26.tgz", - "integrity": "sha512-T8W8ODEutblw9qXQB512LDPyv1tAbJRD/Gf0QEGsAoydl4nxEtIrghnhoI9oLY9R+7aw+cLk1ZEltxWHWf4aHw==", + "version": "0.5.34", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-rsc/-/plugin-rsc-0.5.34.tgz", + "integrity": "sha512-95V6fyGQklQMYIWTr5qwwNmpDYxsHkTunuzq8i/keIcgdckL9zb6nyEgTnb1CEl1IPJWVxGgORMkTFHrct7XJg==", "dev": true, "license": "MIT", "dependencies": { - "@rolldown/pluginutils": "1.0.0-rc.18", - "es-module-lexer": "^2.1.0", + "@rolldown/pluginutils": "^1.0.1", + "es-module-lexer": "^2.3.1", "estree-walker": "^3.0.3", "magic-string": "^0.30.21", - "srvx": "^0.11.15", + "srvx": "^0.12.4", "strip-literal": "^3.1.0", "turbo-stream": "^3.2.0", "vitefu": "^1.1.3" @@ -2582,13 +2592,6 @@ } } }, - "node_modules/@vitejs/plugin-rsc/node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.18", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz", - "integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==", - "dev": true, - "license": "MIT" - }, "node_modules/@webassemblyjs/ast": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", @@ -3090,24 +3093,6 @@ "dev": true, "license": "MIT" }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -3352,13 +3337,6 @@ "license": "BSD-2-Clause", "peer": true }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -3390,19 +3368,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/image-size": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", - "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", - "dev": true, - "license": "MIT", - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=16.x" - } - }, "node_modules/ipaddr.js": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz", @@ -3872,17 +3837,10 @@ "node": ">=22.0.0" } }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/nanoid": { - "version": "3.3.16", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", - "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "funding": [ { "type": "github", @@ -4463,9 +4421,9 @@ } }, "node_modules/srvx": { - "version": "0.11.15", - "resolved": "https://registry.npmjs.org/srvx/-/srvx-0.11.15.tgz", - "integrity": "sha512-iXsux0UcOjdvs0LCMa2Ws3WwcDUozA3JN3BquNXkaFPP7TpRqgunKdEgoZ/uwb1J6xaYHfxtz9Twlh6yzwM6Tg==", + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/srvx/-/srvx-0.12.7.tgz", + "integrity": "sha512-PIaq1pDGg3EU2OGSN3pzRfYVu9MJDzLdojlGN6E3lvhAdxqn/lNMJFMA1xGA38jYkBb0V9xw02QEVVMDb0PExA==", "dev": true, "license": "MIT", "bin": { @@ -4696,27 +4654,6 @@ "node": ">=8.0" } }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "dev": true, - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -4822,19 +4759,18 @@ } }, "node_modules/vinext": { - "version": "0.0.50", - "resolved": "https://registry.npmjs.org/vinext/-/vinext-0.0.50.tgz", - "integrity": "sha512-uo72YNnq94NtogETWnhMdFSrkMLwWgeXh5PS6qh8ksajuvAaZX50bXYJ4a6dERQ/AnnXAlNByAGHCjjNxQrvig==", + "version": "1.0.0-beta.8", + "resolved": "https://registry.npmjs.org/vinext/-/vinext-1.0.0-beta.8.tgz", + "integrity": "sha512-JXiyi0V13PkcIHWUiql3wO/UyNlI1R325RBwBx4CIDa8dd4lgnI90LN5bD2V1J6T+EU8eYCYnKDmtxWjRdL0cg==", "dev": true, "license": "MIT", "dependencies": { "@unpic/react": "^1.0.2", "@vercel/og": "^0.8.6", - "image-size": "2.0.2", + "@vinext/types": "^1.0.0-beta.2", "ipaddr.js": "^2.1.0", "magic-string": "^0.30.21", "vite-plugin-commonjs": "^0.10.4", - "vite-tsconfig-paths": "^6.1.1", "web-vitals": "^4.2.4" }, "bin": { @@ -4846,11 +4782,11 @@ "peerDependencies": { "@mdx-js/rollup": "^3.0.0", "@vitejs/plugin-react": "^5.1.4 || ^6.0.0", - "@vitejs/plugin-rsc": "^0.5.23", + "@vitejs/plugin-rsc": "^0.5.34", "react": "^19.2.6", "react-dom": "^19.2.6", "react-server-dom-webpack": "^19.2.6", - "vite": "^7.0.0 || ^8.0.0" + "vite": "^8.0.0" }, "peerDependenciesMeta": { "@mdx-js/rollup": { @@ -5004,21 +4940,6 @@ "node": ">= 6" } }, - "node_modules/vite-tsconfig-paths": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-6.1.1.tgz", - "integrity": "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - } - }, "node_modules/vite/node_modules/lightningcss": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", diff --git a/judge-lab/package.json b/judge-lab/package.json index 2bc069f..d7dba4f 100644 --- a/judge-lab/package.json +++ b/judge-lab/package.json @@ -30,11 +30,11 @@ "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "@vitejs/plugin-react": "6.0.2", - "@vitejs/plugin-rsc": "0.5.26", + "@vitejs/plugin-rsc": "0.5.34", "react-server-dom-webpack": "19.2.8", "tailwindcss": "4.2.1", "typescript": "5.9.3", - "vinext": "0.0.50", + "vinext": "1.0.0-beta.8", "vite": "8.1.5", "wrangler": "4.114.0" }, @@ -42,7 +42,8 @@ "postcss": "8.5.23", "sharp": "0.35.3", "fast-uri": "3.1.5", - "undici": "7.29.0" + "undici": "7.29.0", + "nanoid": "3.3.18" }, "type": "module" } diff --git a/packages/github-action/action.yml b/packages/github-action/action.yml index ab4c0de..c791cca 100644 --- a/packages/github-action/action.yml +++ b/packages/github-action/action.yml @@ -103,12 +103,20 @@ runs: - name: "Write CodeDecay summary" shell: "bash" + env: + CODEDECAY_INPUT_MODE: "${{ inputs.mode }}" + CODEDECAY_INPUT_CWD: "${{ inputs.cwd }}" + CODEDECAY_INPUT_BASE: "${{ inputs.base }}" + CODEDECAY_INPUT_HEAD: "${{ inputs.head }}" + CODEDECAY_INPUT_TASK: "${{ inputs.task }}" + CODEDECAY_INPUT_REQUIREMENTS: "${{ inputs.requirements }}" + CODEDECAY_INPUT_PROFILE: "${{ inputs.profile }}" run: | set -euo pipefail ACTION_ROOT="$(cd "$GITHUB_ACTION_PATH/../.." && pwd)" CLI_PATH="$ACTION_ROOT/packages/cli/dist/index.js" SUMMARY_PATH="$RUNNER_TEMP/codedecay-summary.md" - MODE="${{ inputs.mode }}" + MODE="$CODEDECAY_INPUT_MODE" if [[ "$MODE" == "product" ]]; then exit 0 @@ -123,22 +131,22 @@ runs: ;; esac - args=("$MODE" --cwd "${{ inputs.cwd }}" --format markdown) - if [[ -n "${{ inputs.base }}" ]]; then - args+=(--base "${{ inputs.base }}") + args=("$MODE" --cwd "$CODEDECAY_INPUT_CWD" --format markdown) + if [[ -n "$CODEDECAY_INPUT_BASE" ]]; then + args+=(--base "$CODEDECAY_INPUT_BASE") fi - if [[ -n "${{ inputs.head }}" ]]; then - args+=(--head "${{ inputs.head }}") + if [[ -n "$CODEDECAY_INPUT_HEAD" ]]; then + args+=(--head "$CODEDECAY_INPUT_HEAD") fi - if [[ -n "${{ inputs.task }}" ]]; then - args+=(--task "${{ inputs.task }}") + if [[ -n "$CODEDECAY_INPUT_TASK" ]]; then + args+=(--task "$CODEDECAY_INPUT_TASK") fi - if [[ -n "${{ inputs.requirements }}" ]]; then - args+=(--requirements "${{ inputs.requirements }}") + if [[ -n "$CODEDECAY_INPUT_REQUIREMENTS" ]]; then + args+=(--requirements "$CODEDECAY_INPUT_REQUIREMENTS") fi if [[ "$MODE" == "agent" || "$MODE" == "ai" ]]; then - if [[ -n "${{ inputs.profile }}" ]]; then - args+=(--profile "${{ inputs.profile }}") + if [[ -n "$CODEDECAY_INPUT_PROFILE" ]]; then + args+=(--profile "$CODEDECAY_INPUT_PROFILE") fi fi @@ -150,6 +158,9 @@ runs: shell: "bash" env: CODEDECAY_GITHUB_TOKEN: "${{ inputs.github-token }}" + CODEDECAY_INPUT_CWD: "${{ inputs.cwd }}" + CODEDECAY_INPUT_BASE: "${{ inputs.base }}" + CODEDECAY_INPUT_HEAD: "${{ inputs.head }}" run: | set -uo pipefail ACTION_ROOT="$(cd "$GITHUB_ACTION_PATH/../.." && pwd)" @@ -183,12 +194,12 @@ runs: # shellcheck disable=SC1090 source "$PR_META" - args=("analyze" --cwd "${{ inputs.cwd }}" --format pr-comment) - if [[ -n "${{ inputs.base }}" ]]; then - args+=(--base "${{ inputs.base }}") + args=("analyze" --cwd "$CODEDECAY_INPUT_CWD" --format pr-comment) + if [[ -n "$CODEDECAY_INPUT_BASE" ]]; then + args+=(--base "$CODEDECAY_INPUT_BASE") fi - if [[ -n "${{ inputs.head }}" ]]; then - args+=(--head "${{ inputs.head }}") + if [[ -n "$CODEDECAY_INPUT_HEAD" ]]; then + args+=(--head "$CODEDECAY_INPUT_HEAD") fi cd "$GITHUB_WORKSPACE" @@ -223,12 +234,33 @@ runs: - name: "Run CodeDecay" shell: "bash" + env: + CODEDECAY_INPUT_MODE: "${{ inputs.mode }}" + CODEDECAY_INPUT_FORMAT: "${{ inputs.format }}" + CODEDECAY_INPUT_CWD: "${{ inputs.cwd }}" + CODEDECAY_INPUT_BASE: "${{ inputs.base }}" + CODEDECAY_INPUT_HEAD: "${{ inputs.head }}" + CODEDECAY_INPUT_OUTPUT: "${{ inputs.output }}" + CODEDECAY_INPUT_TASK: "${{ inputs.task }}" + CODEDECAY_INPUT_REQUIREMENTS: "${{ inputs.requirements }}" + CODEDECAY_INPUT_PROFILE: "${{ inputs.profile }}" + CODEDECAY_INPUT_WITH_CHECKS: "${{ inputs.with-checks }}" + CODEDECAY_INPUT_FAIL_ON_REQUIREMENTS: "${{ inputs.fail-on-requirements }}" + CODEDECAY_INPUT_FAIL_ON: "${{ inputs.fail-on }}" + CODEDECAY_INPUT_TARGET: "${{ inputs.target }}" + CODEDECAY_INPUT_PREVIEW_URL: "${{ inputs.preview-url }}" + CODEDECAY_INPUT_PRODUCT_EXPLORE: "${{ inputs.product-explore }}" + CODEDECAY_INPUT_PRODUCT_GENERATE_TESTS: "${{ inputs.product-generate-tests }}" + CODEDECAY_INPUT_PRODUCT_RUN_GENERATED_TESTS: "${{ inputs.product-run-generated-tests }}" + CODEDECAY_INPUT_PRODUCT_GENERATE_API_TESTS: "${{ inputs.product-generate-api-tests }}" + CODEDECAY_INPUT_PRODUCT_RUN_GENERATED_API_TESTS: "${{ inputs.product-run-generated-api-tests }}" + CODEDECAY_INPUT_PRODUCT_FAIL_ON_CLASSIFICATION: "${{ inputs.product-fail-on-classification }}" run: | set -euo pipefail ACTION_ROOT="$(cd "$GITHUB_ACTION_PATH/../.." && pwd)" CLI_PATH="$ACTION_ROOT/packages/cli/dist/index.js" - MODE="${{ inputs.mode }}" - FORMAT="${{ inputs.format }}" + MODE="$CODEDECAY_INPUT_MODE" + FORMAT="$CODEDECAY_INPUT_FORMAT" SUMMARY_PATH="$RUNNER_TEMP/codedecay-product-summary.md" case "$MODE" in @@ -253,80 +285,80 @@ runs: fi if [[ "$MODE" == "product" ]]; then - args=("product" --cwd "${{ inputs.cwd }}" --format "$FORMAT") - if [[ -n "${{ inputs.target }}" ]]; then - args+=(--target "${{ inputs.target }}") + args=("product" --cwd "$CODEDECAY_INPUT_CWD" --format "$FORMAT") + if [[ -n "$CODEDECAY_INPUT_TARGET" ]]; then + args+=(--target "$CODEDECAY_INPUT_TARGET") fi - if [[ -n "${{ inputs.preview-url }}" ]]; then - export CODEDECAY_PRODUCT_PREVIEW_URL="${{ inputs.preview-url }}" + if [[ -n "$CODEDECAY_INPUT_PREVIEW_URL" ]]; then + export CODEDECAY_PRODUCT_PREVIEW_URL="$CODEDECAY_INPUT_PREVIEW_URL" fi - if [[ "${{ inputs.product-explore }}" == "true" ]]; then + if [[ "$CODEDECAY_INPUT_PRODUCT_EXPLORE" == "true" ]]; then args+=(--explore) fi - if [[ "${{ inputs.product-generate-tests }}" == "true" ]]; then + if [[ "$CODEDECAY_INPUT_PRODUCT_GENERATE_TESTS" == "true" ]]; then args+=(--generate-tests) fi - if [[ "${{ inputs.product-run-generated-tests }}" == "true" ]]; then + if [[ "$CODEDECAY_INPUT_PRODUCT_RUN_GENERATED_TESTS" == "true" ]]; then args+=(--run-generated-tests) fi - if [[ "${{ inputs.product-generate-api-tests }}" == "true" ]]; then + if [[ "$CODEDECAY_INPUT_PRODUCT_GENERATE_API_TESTS" == "true" ]]; then args+=(--generate-api-tests) fi - if [[ "${{ inputs.product-run-generated-api-tests }}" == "true" ]]; then + if [[ "$CODEDECAY_INPUT_PRODUCT_RUN_GENERATED_API_TESTS" == "true" ]]; then args+=(--run-generated-api-tests) fi - if [[ -n "${{ inputs.product-fail-on-classification }}" ]]; then - args+=(--fail-on-classification "${{ inputs.product-fail-on-classification }}") + if [[ -n "$CODEDECAY_INPUT_PRODUCT_FAIL_ON_CLASSIFICATION" ]]; then + args+=(--fail-on-classification "$CODEDECAY_INPUT_PRODUCT_FAIL_ON_CLASSIFICATION") fi - if [[ -n "${{ inputs.output }}" ]]; then - args+=(--output "${{ inputs.output }}") + if [[ -n "$CODEDECAY_INPUT_OUTPUT" ]]; then + args+=(--output "$CODEDECAY_INPUT_OUTPUT") fi else - args=("$MODE" --cwd "${{ inputs.cwd }}" --format "$FORMAT") - if [[ -n "${{ inputs.base }}" ]]; then - args+=(--base "${{ inputs.base }}") + args=("$MODE" --cwd "$CODEDECAY_INPUT_CWD" --format "$FORMAT") + if [[ -n "$CODEDECAY_INPUT_BASE" ]]; then + args+=(--base "$CODEDECAY_INPUT_BASE") fi - if [[ -n "${{ inputs.head }}" ]]; then - args+=(--head "${{ inputs.head }}") + if [[ -n "$CODEDECAY_INPUT_HEAD" ]]; then + args+=(--head "$CODEDECAY_INPUT_HEAD") fi - if [[ -n "${{ inputs.output }}" ]]; then - args+=(--output "${{ inputs.output }}") + if [[ -n "$CODEDECAY_INPUT_OUTPUT" ]]; then + args+=(--output "$CODEDECAY_INPUT_OUTPUT") fi - if [[ -n "${{ inputs.task }}" ]]; then - args+=(--task "${{ inputs.task }}") + if [[ -n "$CODEDECAY_INPUT_TASK" ]]; then + args+=(--task "$CODEDECAY_INPUT_TASK") fi - if [[ -n "${{ inputs.requirements }}" ]]; then - args+=(--requirements "${{ inputs.requirements }}") + if [[ -n "$CODEDECAY_INPUT_REQUIREMENTS" ]]; then + args+=(--requirements "$CODEDECAY_INPUT_REQUIREMENTS") fi if [[ "$MODE" == "agent" || "$MODE" == "ai" ]]; then - if [[ -n "${{ inputs.profile }}" ]]; then - args+=(--profile "${{ inputs.profile }}") + if [[ -n "$CODEDECAY_INPUT_PROFILE" ]]; then + args+=(--profile "$CODEDECAY_INPUT_PROFILE") fi fi if [[ "$MODE" == "redteam" || "$MODE" == "ai" ]]; then - if [[ "${{ inputs.with-checks }}" == "true" ]]; then + if [[ "$CODEDECAY_INPUT_WITH_CHECKS" == "true" ]]; then args+=(--with-checks) fi fi - if [[ "$MODE" != "agent" && "${{ inputs.fail-on-requirements }}" == "true" ]]; then + if [[ "$MODE" != "agent" && "$CODEDECAY_INPUT_FAIL_ON_REQUIREMENTS" == "true" ]]; then args+=(--fail-on-requirements) fi - if [[ "$MODE" != "agent" && -n "${{ inputs.fail-on }}" ]]; then - args+=(--fail-on "${{ inputs.fail-on }}") + if [[ "$MODE" != "agent" && -n "$CODEDECAY_INPUT_FAIL_ON" ]]; then + args+=(--fail-on "$CODEDECAY_INPUT_FAIL_ON") fi fi cd "$GITHUB_WORKSPACE" if [[ "$MODE" == "product" && "$FORMAT" == "markdown" ]]; then set +e - if [[ -n "${{ inputs.output }}" ]]; then + if [[ -n "$CODEDECAY_INPUT_OUTPUT" ]]; then node "$CLI_PATH" "${args[@]}" status=$? - output_path="${{ inputs.output }}" + output_path="$CODEDECAY_INPUT_OUTPUT" if [[ "$output_path" = /* ]]; then summary_source="$output_path" else - summary_source="$GITHUB_WORKSPACE/${{ inputs.cwd }}/$output_path" + summary_source="$GITHUB_WORKSPACE/$CODEDECAY_INPUT_CWD/$output_path" fi if [[ -f "$summary_source" ]]; then cat "$summary_source" >> "$GITHUB_STEP_SUMMARY" diff --git a/packages/github-action/test/action-runtime.test.ts b/packages/github-action/test/action-runtime.test.ts index aa86f41..a83b18f 100644 --- a/packages/github-action/test/action-runtime.test.ts +++ b/packages/github-action/test/action-runtime.test.ts @@ -202,6 +202,35 @@ describe("GitHub Action shell runtime", () => { expect(invalidFormat.stderr).toContain("CodeDecay mode 'ai' does not support SARIF output."); expect(existsSync(recordPath)).toBe(false); }); + + it("forwards shell-like input literally without evaluating it", () => { + const result = runMainStep({ + mode: "ai", + task: "$(touch should-not-exist)\"; echo unsafe", + requirements: "requirements;$(echo still-data).yml" + }); + + expect(result).toMatchObject({ status: 0, stderr: "", timedOut: false }); + expect(existsSync(join(workspace, "should-not-exist"))).toBe(false); + expect(readRecords()).toEqual([ + { + argv: [ + "ai", + "--cwd", + ".", + "--format", + "markdown", + "--task", + "$(touch should-not-exist)\"; echo unsafe", + "--requirements", + "requirements;$(echo still-data).yml" + ], + cwd: realpathSync(workspace), + githubToken: null, + previewUrl: null + } + ]); + }); }); function runMainStep(inputs: Record) { diff --git a/packages/github-action/test/action-yml.test.ts b/packages/github-action/test/action-yml.test.ts index 4f83d14..54d28d6 100644 --- a/packages/github-action/test/action-yml.test.ts +++ b/packages/github-action/test/action-yml.test.ts @@ -34,19 +34,19 @@ describe("GitHub Action metadata", () => { expect(action.inputs["github-token"].default).toBe("${{ github.token }}"); }); - it("forwards cwd to every CLI invocation", () => { + it("passes action inputs through environment variables before constructing CLI arguments", () => { const actionYaml = readFileSync("packages/github-action/action.yml", "utf8"); - const invocations = actionYaml.match(/args=\("\$MODE"[^\n]+/g) ?? []; - expect(invocations).toHaveLength(2); - expect(invocations.every((line) => line.includes("--cwd \"${{ inputs.cwd }}\""))).toBe(true); + expect(actionYaml).toContain('CODEDECAY_INPUT_CWD: "${{ inputs.cwd }}"'); + expect(actionYaml).toContain('args=("$MODE" --cwd "$CODEDECAY_INPUT_CWD" --format "$FORMAT")'); + expect(actionYaml).not.toContain('--cwd "${{ inputs.cwd }}"'); }); it("supports report modes and explicit product verification only", () => { const actionYaml = readFileSync("packages/github-action/action.yml", "utf8"); expect(actionYaml).toContain("analyze|redteam|agent|ai|product"); - expect(actionYaml).toContain('args=("product" --cwd "${{ inputs.cwd }}" --format "$FORMAT")'); + expect(actionYaml).toContain('args=("product" --cwd "$CODEDECAY_INPUT_CWD" --format "$FORMAT")'); expect(actionYaml).toContain("Unsupported CodeDecay mode"); expect(actionYaml).toContain("does not support SARIF output"); expect(actionYaml).not.toContain("analyze|redteam|agent|ai|product|execute"); @@ -77,28 +77,28 @@ describe("GitHub Action metadata", () => { it("wires product verification inputs without arbitrary command passthrough", () => { const actionYaml = readFileSync("packages/github-action/action.yml", "utf8"); - expect(actionYaml).toContain('export CODEDECAY_PRODUCT_PREVIEW_URL="${{ inputs.preview-url }}"'); - expect(actionYaml).toContain('args+=(--target "${{ inputs.target }}")'); + expect(actionYaml).toContain('export CODEDECAY_PRODUCT_PREVIEW_URL="$CODEDECAY_INPUT_PREVIEW_URL"'); + expect(actionYaml).toContain('args+=(--target "$CODEDECAY_INPUT_TARGET")'); expect(actionYaml).toContain("args+=(--explore)"); expect(actionYaml).toContain("args+=(--generate-tests)"); expect(actionYaml).toContain("args+=(--run-generated-tests)"); expect(actionYaml).toContain("args+=(--generate-api-tests)"); expect(actionYaml).toContain("args+=(--run-generated-api-tests)"); - expect(actionYaml).toContain('args+=(--fail-on-classification "${{ inputs.product-fail-on-classification }}")'); + expect(actionYaml).toContain('args+=(--fail-on-classification "$CODEDECAY_INPUT_PRODUCT_FAIL_ON_CLASSIFICATION")'); expect(actionYaml).not.toContain("product-extra-args"); }); it("does not forward fail-on to agent mode", () => { const actionYaml = readFileSync("packages/github-action/action.yml", "utf8"); - expect(actionYaml).toContain('if [[ "$MODE" != "agent" && -n "${{ inputs.fail-on }}" ]]; then'); + expect(actionYaml).toContain('if [[ "$MODE" != "agent" && -n "$CODEDECAY_INPUT_FAIL_ON" ]]; then'); }); it("wires the AI profile and explicit checks without arbitrary argument passthrough", () => { const actionYaml = readFileSync("packages/github-action/action.yml", "utf8"); expect(actionYaml).toContain('if [[ "$MODE" == "agent" || "$MODE" == "ai" ]]; then'); - expect(actionYaml).toContain('args+=(--profile "${{ inputs.profile }}")'); + expect(actionYaml).toContain('args+=(--profile "$CODEDECAY_INPUT_PROFILE")'); expect(actionYaml).toContain('if [[ "$MODE" == "redteam" || "$MODE" == "ai" ]]; then'); expect(actionYaml).toContain("args+=(--with-checks)"); expect(actionYaml).not.toContain("agent-extra-args"); @@ -108,9 +108,9 @@ describe("GitHub Action metadata", () => { it("forwards explicit structured requirement inputs and the opt-in CI gate", () => { const actionYaml = readFileSync("packages/github-action/action.yml", "utf8"); - expect(actionYaml).toContain('if [[ -n "${{ inputs.task }}" ]]; then'); - expect(actionYaml).toContain('args+=(--task "${{ inputs.task }}")'); - expect(actionYaml).toContain('args+=(--requirements "${{ inputs.requirements }}")'); + expect(actionYaml).toContain('if [[ -n "$CODEDECAY_INPUT_TASK" ]]; then'); + expect(actionYaml).toContain('args+=(--task "$CODEDECAY_INPUT_TASK")'); + expect(actionYaml).toContain('args+=(--requirements "$CODEDECAY_INPUT_REQUIREMENTS")'); expect(actionYaml).toContain('args+=(--fail-on-requirements)'); expect(actionYaml).not.toContain("pull_request.body"); }); @@ -122,6 +122,16 @@ describe("GitHub Action metadata", () => { expect(actionYaml).not.toContain("pnpm --filter codedecay build"); }); + it("does not interpolate action inputs directly into Bash commands", () => { + const actionYaml = readFileSync("packages/github-action/action.yml", "utf8"); + const runBlocks = [...actionYaml.matchAll(/run: \|\n([\s\S]*?)(?=\n - name:|$)/g)].map((match) => match[1]); + + expect(runBlocks).not.toHaveLength(0); + for (const run of runBlocks) { + expect(run).not.toMatch(/\$\{\{\s*inputs\./); + } + }); + it("documents only supported action inputs in examples", () => { const action = parse(readFileSync("packages/github-action/action.yml", "utf8")); const supportedInputs = new Set(Object.keys(action.inputs));