-
-
Notifications
You must be signed in to change notification settings - Fork 0
feat(action): GitHub Marketplace Action — Slices 1-4 of #73 plan #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
b65df6a
feat(audit): --format sarif on `codemap audit` (Slice 1a of #73 plan)
SutuSebastian 7a81b88
feat(cli): --ci aggregate flag on `query` + `audit` (Slice 1b of #73 …
SutuSebastian 9383a95
feat(action): action.yml + scripts/detect-pm.mjs (Slice 2 of #73 plan)
SutuSebastian 3409d27
feat(cli): codemap pr-comment + action.yml integration (Slice 3 of #7…
SutuSebastian 08dec00
ci(action): dogfood action-smoke job + bundle changeset (Slice 4 of #…
SutuSebastian e35f820
chore: slim new comments + sweep docs staleness
SutuSebastian f7762d0
fix(action): empty defaults for github-context inputs (composite acti…
SutuSebastian 33a23b1
fix: address PR #74 CodeRabbit review (6 findings, all real)
SutuSebastian c489066
docs(research): mark § 1.5 boundary-violations as shipped (PR #72)
SutuSebastian 80b575b
docs(plan): consolidate Slice 5 runbook for later pickup
SutuSebastian File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| --- | ||
| "@stainless-code/codemap": minor | ||
| --- | ||
|
|
||
| `codemap audit --format <text|json|sarif>` — emit a SARIF 2.1.0 doc directly from the audit envelope, no JSON→SARIF transform step needed. One rule per delta key (`codemap.audit.files-added`, `codemap.audit.dependencies-added`, `codemap.audit.deprecated-added`); one result per `added` row; severity = `warning` (audit deltas are more actionable than per-recipe `note`). Locations auto-detected via the same `file_path` / `path` / `to_path` / `from_path` priority list that `query --format sarif` uses; line ranges (`line_start` / `line_end`) populate the SARIF `region`. Pure output-formatter addition on top of the existing audit envelope; no schema impact. | ||
|
|
||
| `--json` stays as the shortcut for `--format json` (backward-compatible). `--json` + `--format <other>` rejected as a contradiction. `--summary` is a no-op with `--format sarif` (SARIF results are per-row, not counts) and surfaces a stderr warning. | ||
|
|
||
| `removed` rows are intentionally excluded from SARIF output — SARIF surfaces findings to act on, not cleanups. Location-only rows (e.g. files-added has only `path`) get a "new files: src/foo.ts" message instead of the generic "(no message)" fallback. | ||
|
|
||
| This is the first half of Slice 1 from the [GitHub Marketplace Action plan](../docs/plans/github-marketplace-action.md) — independently useful for any CI consumer running `codemap audit` who wants Code Scanning surface without a translation layer; required for the upcoming Marketplace Action's headline default command. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| --- | ||
| "@stainless-code/codemap": minor | ||
| --- | ||
|
|
||
| GitHub Marketplace Action — Slices 1b-4 of [`docs/plans/github-marketplace-action.md`](../docs/plans/github-marketplace-action.md). v1.0 readiness; `action.yml` is now installable via `- uses: stainless-code/codemap@v1` once the corresponding tag is published. | ||
|
|
||
| **`--ci` aggregate flag (Slice 1b)** on `query` + `audit`. Aliases `--format sarif` + `process.exitCode = 1` on findings/additions + suppresses no-locatable-rows stderr warning. Mutually exclusive with `--json` and `--format <other>`. Parser rejects contradictions with helpful errors. | ||
|
|
||
| **`action.yml` + `scripts/detect-pm.mjs` (Slice 2).** Composite Action wrapping the codemap CLI. ~16 declarative inputs across 3 categories (where to run / what to run / what to do with output); Q1 resolution. Default α command on `pull_request` events: `audit --base ${{ github.base_ref }} --ci`; no-op on other events unless an explicit `command:` input is passed. Package-manager autodetection delegates to [`package-manager-detector`](https://github.com/antfu-collective/package-manager-detector) (antfu/userquin, MIT, 0 transitive deps); CLI invocation resolution via the library's `'execute-local'` / `'execute'` intents. | ||
|
|
||
| **`codemap pr-comment` (Slice 3).** New CLI verb that renders a markdown PR-summary comment from a codemap-audit-JSON envelope or a SARIF doc. Auto-detects input shape; `--shape audit|sarif` overrides. Reads from a file or stdin (`-`). `--json` envelope emits `{ markdown, findings_count, kind }` for action.yml steps. Closes the SARIF→Code-Scanning gap for: private repos without GHAS, repos that haven't enabled Code Scanning, aggregate audit deltas without a single file:line anchor, trend / delta narratives, and bot-context seeding (review bots read PR conversation, not workflow artifacts). v1.0 ships the (b) summary-comment shape per Q4 resolution; (c) inline-review comments deferred to v1.x. | ||
|
|
||
| **Dogfood (Slice 4).** New `action-smoke` job in `.github/workflows/ci.yml` runs `uses: ./` on every PR with `command: --version` to validate the composite-step flow + npm-pulled codemap binary. Non-blocking until v1.0.0 ships (at which point the smoke gates the build). | ||
|
|
||
| **Engine + CLI separation discipline preserved:** `pr-comment-engine.ts` is pure; `cmd-pr-comment.ts` wraps it. Tests cover the engine (12 cases) and the CLI parser (4 audit + 4 query tests for `--ci`). | ||
|
|
||
| **Lockstep agent updates** (per `docs/README.md` Rule 10): `.agents/rules/codemap.md` + `templates/agents/rules/codemap.md` gain rows for `--ci` and `pr-comment` so installed agents and this clone's session view stay in lockstep. | ||
|
|
||
| Slice 5 (Marketplace publish + listing metadata) is post-merge — gated on a v1.0.0 tag. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,269 @@ | ||
| name: "Codemap" | ||
| description: "SQL-queryable structural index of your codebase. Run any predicate as a recipe; CI gating via SARIF → Code Scanning." | ||
| author: "stainless-code" | ||
| branding: | ||
| icon: "database" | ||
| color: "blue" | ||
|
|
||
| inputs: | ||
| # WHERE TO RUN | ||
| working-directory: | ||
| description: "Subdirectory to run codemap in (for monorepos). Defaults to the repository root." | ||
| required: false | ||
| default: "." | ||
| package-manager: | ||
| description: "Override package-manager autodetect. Accepts npm | pnpm | yarn | yarn@berry | bun. Empty = autodetect via package-manager-detector (lockfile + packageManager field + devEngines.packageManager + install-metadata + parent-dir walk)." | ||
| required: false | ||
| default: "" | ||
| version: | ||
| description: "Pin codemap CLI version (e.g. 1.2.3). Empty = use the project's devDependency if present, else fall back to <pm> dlx codemap@latest." | ||
| required: false | ||
| default: "" | ||
| state-dir: | ||
| description: "Override codemap state directory location. Empty = .codemap/ at the working-directory root (codemap default)." | ||
| required: false | ||
| default: "" | ||
|
|
||
| # WHAT TO RUN — high-level (mutually exclusive; precedence: command > mode > defaults) | ||
| mode: | ||
| description: "Run shape: 'audit' | 'recipe' | 'aggregate' | 'command'. Default: 'audit' on pull_request events; ignored on other events (Action no-ops). 'aggregate' is reserved for v1.x — currently rejected." | ||
| required: false | ||
| default: "audit" | ||
| recipe: | ||
| description: "Recipe id (when mode=recipe). Use --recipes-json on the CLI to list known recipes." | ||
| required: false | ||
| default: "" | ||
| params: | ||
| description: "Recipe params for parametrised recipes (when mode=recipe). Multiline `key=value` pairs, one per line." | ||
| required: false | ||
| default: "" | ||
| baseline: | ||
| description: "Saved baseline name to diff against (when mode=recipe + a baseline was previously saved with --save-baseline)." | ||
| required: false | ||
| default: "" | ||
| audit-base: | ||
| description: "Git ref to audit against (when mode=audit). Empty (default) → falls back to `github.base_ref` on `pull_request` events; on other events the action no-ops unless an explicit `command:` is set." | ||
| required: false | ||
| default: "" | ||
| changed-since: | ||
| description: "Filter results to files changed since the given git ref (e.g. 'origin/main')." | ||
| required: false | ||
| default: "" | ||
| group-by: | ||
| description: "Bucket results by 'owner' (CODEOWNERS) | 'directory' | 'package' (workspace package). Empty = no bucketing." | ||
| required: false | ||
| default: "" | ||
| command: | ||
| description: "Raw CLI args to invoke codemap with (escape hatch). When set, overrides mode / recipe / params / baseline / audit-base / changed-since / group-by — those are silently ignored with a warning." | ||
| required: false | ||
| default: "" | ||
|
|
||
| # WHAT TO DO WITH OUTPUT | ||
| format: | ||
| description: "Output format: 'sarif' | 'json' | 'annotations' | 'mermaid' | 'diff' (per-mode availability varies; audit supports text/json/sarif). Default: 'sarif' (SARIF 2.1.0 → Code Scanning)." | ||
| required: false | ||
| default: "sarif" | ||
| output-path: | ||
| description: "Where to write the output file. Used as the artifact-upload source when format=sarif and upload-sarif=true." | ||
| required: false | ||
| default: "codemap.sarif" | ||
| upload-sarif: | ||
| description: "Upload the SARIF artifact to GitHub Code Scanning. Requires GitHub Advanced Security on private repos. Set 'false' if your repo can't use Code Scanning (still produces the artifact for manual download / pr-comment writer)." | ||
| required: false | ||
| default: "true" | ||
| pr-comment: | ||
| description: "Post a markdown summary comment on the PR (Slice 3 — opt-in for v1.0). Set 'true' to enable. Useful when SARIF→Code-Scanning isn't available (private repos without GHAS, or repos that haven't enabled Code Scanning)." | ||
| required: false | ||
| default: "false" | ||
| fail-on: | ||
| description: "Exit-code policy: 'any' | 'error' | 'warning' | 'never'. v1.0 ships only 'any' (fails when any finding) and 'never' (no exit code). 'error' / 'warning' deferred until per-recipe severity overrides ship." | ||
| required: false | ||
| default: "any" | ||
| token: | ||
| description: "GitHub token for SARIF upload + PR comment posting. Empty (default) → falls back to `github.token` automatically. Pass an explicit fine-grained PAT only if you need elevated permissions." | ||
| required: false | ||
| default: "" | ||
|
|
||
| outputs: | ||
| agent: | ||
| description: "Resolved package manager (npm / pnpm / yarn / bun)." | ||
| value: ${{ steps.detect-pm.outputs.agent }} | ||
| exec: | ||
| description: "Shell-ready command used to invoke codemap." | ||
| value: ${{ steps.detect-pm.outputs.exec }} | ||
| install_method: | ||
| description: "How codemap was located: 'project-installed' | 'dlx-pinned' | 'dlx-latest'." | ||
| value: ${{ steps.detect-pm.outputs.install_method }} | ||
| output-file: | ||
| description: "Path to the written output file (echoes inputs.output-path)." | ||
| value: ${{ inputs.output-path }} | ||
|
|
||
| runs: | ||
| using: "composite" | ||
| steps: | ||
| - name: Skip on non-PR events when defaulting to audit | ||
| id: gate | ||
| shell: bash | ||
| env: | ||
| EVENT_NAME: ${{ github.event_name }} | ||
| BASE_REF: ${{ github.base_ref }} | ||
| MODE: ${{ inputs.mode }} | ||
| COMMAND: ${{ inputs.command }} | ||
| AUDIT_BASE_INPUT: ${{ inputs.audit-base }} | ||
| run: | | ||
| if [ -n "$COMMAND" ]; then | ||
| echo "skip=false" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
| if [ "$MODE" != "audit" ]; then | ||
| echo "skip=false" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
| BASE="${AUDIT_BASE_INPUT:-$BASE_REF}" | ||
| if [ -z "$BASE" ]; then | ||
| echo "codemap action: no PR context (event_name=$EVENT_NAME, base_ref empty), skipping. Pass an explicit 'command:' input to run on non-PR events." | ||
| echo "skip=true" >> "$GITHUB_OUTPUT" | ||
| exit 0 | ||
| fi | ||
| echo "skip=false" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Setup Node.js (for the package-manager-detector wrapper) | ||
| if: steps.gate.outputs.skip != 'true' | ||
| uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "20" | ||
|
|
||
| - name: Detect package manager + resolve CLI invocation | ||
| if: steps.gate.outputs.skip != 'true' | ||
| id: detect-pm | ||
| shell: bash | ||
| env: | ||
| PACKAGE_MANAGER: ${{ inputs.package-manager }} | ||
| VERSION: ${{ inputs.version }} | ||
| WORKING_DIRECTORY: ${{ inputs.working-directory }} | ||
| run: | | ||
| # Action runs without its own node_modules; install the detector lazily. | ||
| # Pinned to a known version so consumers get reproducible builds. | ||
| npm install --no-save --prefix "$RUNNER_TEMP/codemap-action" package-manager-detector@1.6.0 >/dev/null | ||
| cp "${{ github.action_path }}/scripts/detect-pm.mjs" "$RUNNER_TEMP/codemap-action/detect-pm.mjs" | ||
| cd "$RUNNER_TEMP/codemap-action" | ||
| node detect-pm.mjs | ||
|
|
||
| - name: Validate inputs (mode + flag interactions) | ||
| if: steps.gate.outputs.skip != 'true' | ||
| shell: bash | ||
| env: | ||
| MODE: ${{ inputs.mode }} | ||
| RECIPE: ${{ inputs.recipe }} | ||
| COMMAND: ${{ inputs.command }} | ||
| run: | | ||
| # Precedence: command > mode. If command is set, mode/recipe are silently | ||
| # ignored but we surface a single warning so consumers notice. | ||
| if [ -n "$COMMAND" ] && [ -n "$RECIPE" ]; then | ||
| echo "::warning::codemap action: 'command' input takes precedence; 'recipe' (and other mode-* inputs) are ignored." | ||
| fi | ||
|
|
||
| case "$MODE" in | ||
| audit | recipe | command) ;; | ||
| aggregate) | ||
| echo "::error::codemap action: mode='aggregate' is reserved for v1.x and not yet implemented. Use mode='audit' or pass a 'command:' input." | ||
| exit 1 | ||
| ;; | ||
| *) | ||
| echo "::error::codemap action: unknown mode '$MODE'. Expected: audit | recipe | aggregate | command." | ||
| exit 1 | ||
| ;; | ||
| esac | ||
|
|
||
| if [ "$MODE" = "recipe" ] && [ -z "$RECIPE" ] && [ -z "$COMMAND" ]; then | ||
| echo "::error::codemap action: mode='recipe' requires the 'recipe' input (a recipe id). Run codemap query --recipes-json to list known recipes." | ||
| exit 1 | ||
| fi | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| if [ "$MODE" = "command" ] && [ -z "$COMMAND" ]; then | ||
| echo "::error::codemap action: mode='command' requires the 'command' input (raw CLI args)." | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Run codemap | ||
| if: steps.gate.outputs.skip != 'true' | ||
| id: run | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| env: | ||
| EXEC: ${{ steps.detect-pm.outputs.exec }} | ||
| MODE: ${{ inputs.mode }} | ||
| RECIPE: ${{ inputs.recipe }} | ||
| PARAMS: ${{ inputs.params }} | ||
| BASELINE: ${{ inputs.baseline }} | ||
| AUDIT_BASE: ${{ inputs.audit-base }} | ||
| CHANGED_SINCE: ${{ inputs.changed-since }} | ||
| GROUP_BY: ${{ inputs.group-by }} | ||
| COMMAND: ${{ inputs.command }} | ||
| FORMAT: ${{ inputs.format }} | ||
| OUTPUT_PATH: ${{ inputs.output-path }} | ||
| FAIL_ON: ${{ inputs.fail-on }} | ||
| STATE_DIR: ${{ inputs.state-dir }} | ||
| BASE_REF: ${{ github.base_ref }} | ||
| run: | | ||
| set +e | ||
|
|
||
| if [ -n "$COMMAND" ]; then | ||
| ARGS="$COMMAND" | ||
| elif [ "$MODE" = "audit" ]; then | ||
| BASE="${AUDIT_BASE:-$BASE_REF}" | ||
| ARGS="audit --base $BASE --format $FORMAT" | ||
| elif [ "$MODE" = "recipe" ]; then | ||
| ARGS="query --recipe $RECIPE --format $FORMAT" | ||
| [ -n "$PARAMS" ] && while IFS= read -r line; do | ||
| [ -n "$line" ] && ARGS="$ARGS --params $line" | ||
| done <<< "$PARAMS" | ||
| [ -n "$BASELINE" ] && ARGS="$ARGS --baseline $BASELINE" | ||
| fi | ||
|
|
||
| [ -n "$CHANGED_SINCE" ] && ARGS="$ARGS --changed-since $CHANGED_SINCE" | ||
| [ -n "$GROUP_BY" ] && ARGS="$ARGS --group-by $GROUP_BY" | ||
| [ -n "$STATE_DIR" ] && ARGS="--state-dir $STATE_DIR $ARGS" | ||
|
|
||
| echo "+ $EXEC $ARGS" | ||
| $EXEC $ARGS > "$OUTPUT_PATH" | ||
| EXIT=$? | ||
|
|
||
| echo "exit_code=$EXIT" >> "$GITHUB_OUTPUT" | ||
|
|
||
| # `fail-on` policy. v1.0 supports 'any' (default) and 'never'. Other | ||
| # values fall back to passing the underlying exit code through. | ||
| case "$FAIL_ON" in | ||
| never) exit 0 ;; | ||
| any | *) exit "$EXIT" ;; | ||
| esac | ||
|
|
||
| - name: Upload SARIF to Code Scanning | ||
| if: steps.gate.outputs.skip != 'true' && inputs.upload-sarif == 'true' && inputs.format == 'sarif' && always() | ||
| uses: github/codeql-action/upload-sarif@v3 | ||
| with: | ||
| sarif_file: ${{ inputs.working-directory }}/${{ inputs.output-path }} | ||
| token: ${{ inputs.token != '' && inputs.token || github.token }} | ||
|
|
||
| - name: Post PR summary comment | ||
| if: steps.gate.outputs.skip != 'true' && inputs.pr-comment == 'true' && github.event_name == 'pull_request' && always() | ||
| shell: bash | ||
| working-directory: ${{ inputs.working-directory }} | ||
| env: | ||
| EXEC: ${{ steps.detect-pm.outputs.exec }} | ||
| OUTPUT_PATH: ${{ inputs.output-path }} | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| GH_TOKEN: ${{ inputs.token != '' && inputs.token || github.token }} | ||
| run: | | ||
| # Render the markdown body via `codemap pr-comment`, then post via | ||
| # `gh pr comment`. The same binary that produced the SARIF / JSON | ||
| # output renders the comment — keeps the version stream coherent. | ||
| BODY=$($EXEC pr-comment "$OUTPUT_PATH" 2>/dev/null) || { | ||
| echo "::warning::codemap action: pr-comment renderer failed; skipping PR comment." | ||
| exit 0 | ||
| } | ||
| if [ -z "$BODY" ]; then | ||
| echo "::notice::codemap action: pr-comment produced empty body; skipping." | ||
| exit 0 | ||
| fi | ||
| echo "$BODY" | gh pr comment "$PR_NUMBER" -F - | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.