vet

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit c2a31b81cdf6935a9a7ff75199a89ea2cc29f66d
parent 8c3f52e91a837a91917070421dbf6238139b08c7
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date:   Tue, 17 Feb 2026 06:47:49 +0000

Updated action to unpin version / make setup easier. (#93)

* Updated action

* Refactoring

* Refactored

* build local version in vet repo

* Updated messaging

* Remove useless information

* send keys differently

* Added anthropic key messaging

---------

Co-authored-by: Andrew Laack <andrew@laack.co>
Diffstat:
M.github/workflows/vet-agentic.yml | 41+++++------------------------------------
M.github/workflows/vet.yml | 37++++---------------------------------
MDEVELOPMENT.md | 22+++++++---------------
MREADME.md | 43+++++--------------------------------------
Aaction.yml | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aaction/run.sh | 44++++++++++++++++++++++++++++++++++++++++++++
Muv.lock | 2+-
7 files changed, 177 insertions(+), 123 deletions(-)

diff --git a/.github/workflows/vet-agentic.yml b/.github/workflows/vet-agentic.yml @@ -12,45 +12,14 @@ jobs: vet: if: github.event.pull_request.draft == false runs-on: ubuntu-latest + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: ./ with: - python-version: "3.11" - - uses: actions/setup-node@v4 - with: - node-version: "20" - - run: npm install -g @anthropic-ai/claude-code - # we want to run the current version of vet against the PR, not necessarily the deployed / main version. - - run: pip install . - - name: Run vet (agentic) - if: github.event.pull_request.head.repo.full_name == github.repository - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BASE_REF: ${{ github.event.pull_request.base.ref }} - VET_GOAL: | - ${{ github.event.pull_request.title }} - - Additional context (not necessarily part of the goal): - ${{ github.event.pull_request.body }} - run: | - set +e - MERGE_BASE=$(git merge-base "origin/$BASE_REF" "${{ github.event.pull_request.head.sha }}") - vet "$VET_GOAL" --quiet --output-format github \ - --agentic --base-commit "$MERGE_BASE" \ - > "$RUNNER_TEMP/review.json" - status=$? - if [ "$status" -ne 0 ] && [ "$status" -ne 10 ]; then exit "$status"; fi - - jq --arg sha "${{ github.event.pull_request.head.sha }}" \ - '. + {commit_id: $sha}' "$RUNNER_TEMP/review.json" > "$RUNNER_TEMP/review-final.json" - - gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" \ - --method POST --input "$RUNNER_TEMP/review-final.json" > /dev/null || \ - gh pr comment "${{ github.event.pull_request.number }}" \ - --body "$(jq -r '[.body] + [.comments[] | "**\(.path):\(.line)**\n\n\(.body)"] | join("\n\n---\n\n")' "$RUNNER_TEMP/review-final.json")" - exit 0 + agentic: true + build-from-source: true diff --git a/.github/workflows/vet.yml b/.github/workflows/vet.yml @@ -12,42 +12,13 @@ jobs: vet: if: github.event.pull_request.draft == false runs-on: ubuntu-latest + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: ./ with: - python-version: "3.11" - # we want to run the current version of vet against the PR, not necessarily the deployed / main version. - # this is the only difference between this vet.yml and the recommended one in the readme. - - run: pip install . - - name: Run vet - if: github.event.pull_request.head.repo.full_name == github.repository - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BASE_REF: ${{ github.event.pull_request.base.ref }} - VET_GOAL: | - ${{ github.event.pull_request.title }} - - Additional context (not necessarily part of the goal): - ${{ github.event.pull_request.body }} - run: | - set +e - MERGE_BASE=$(git merge-base "origin/$BASE_REF" "${{ github.event.pull_request.head.sha }}") - vet "$VET_GOAL" --quiet --output-format github \ - --base-commit "$MERGE_BASE" \ - > "$RUNNER_TEMP/review.json" - status=$? - if [ "$status" -ne 0 ] && [ "$status" -ne 10 ]; then exit "$status"; fi - - jq --arg sha "${{ github.event.pull_request.head.sha }}" \ - '. + {commit_id: $sha}' "$RUNNER_TEMP/review.json" > "$RUNNER_TEMP/review-final.json" - - gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" \ - --method POST --input "$RUNNER_TEMP/review-final.json" > /dev/null || \ - gh pr comment "${{ github.event.pull_request.number }}" \ - --body "$(jq -r '[.body] + [.comments[] | "**\(.path):\(.line)**\n\n\(.body)"] | join("\n\n---\n\n")' "$RUNNER_TEMP/review-final.json")" - exit 0 + build-from-source: true diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md @@ -63,8 +63,8 @@ Current workflows: - `test-unit.yml` (`Test / Unit`, job: `unit`) — pytest suite (lint + unit tests) - `test-pkgbuild.yml` (`Test / PKGBUILD`, job: `pkgbuild`) — Arch Linux package build + smoke test -- `vet.yml` (`Vet`, job: `vet`) — Self-review via vet on PRs -- `vet-agentic.yml` (`Vet (Agentic)`, job: `vet`) — Agentic self-review via vet on PRs +- `vet.yml` (`Vet`, job: `vet`) — Self-review via vet on PRs (uses the reusable action via `uses: ./`) +- `vet-agentic.yml` (`Vet (Agentic)`, job: `vet`) — Agentic self-review via vet on PRs (uses the reusable action via `uses: ./`) - `publish-pypi.yml` (`Publish / PyPI`, job: `pypi`) — Build and publish to PyPI on tag push - `publish-github-release.yml` (`Publish / GitHub Release`, job: `github-release`) — Create a GitHub Release on tag push @@ -77,23 +77,15 @@ Vet is published to PyPI via the `publish-pypi.yml` GitHub Actions workflow. Dep 1. Create and checkout a branch to bump the version 2. Update the version in `pyproject.toml` 3. Update `pkgver` in `pkg/arch/PKGBUILD` -4. Update the recommended GitHub action pinned version in the `README.md` - ```yaml - - run: pip install verify-everything==0.2.0 - ``` -5. Commit and push the changes -6. Tag the commit and push the tag: +4. Commit and push the changes +5. Tag the commit and push the tag: ```bash git tag v0.2.0 -m "v0.2.0: Updated XYZ" git push origin v0.2.0 ``` -7. Create a PR for the new branch -8. The `Publish / PyPI` workflow will automatically build and publish the package -9. Merge PR into main. - -### Why pin the version in the README? - -The README contains a sample GitHub Actions workflow that users copy into their repos. The `pip install verify-everything==X.Y.Z` line in that sample should always reference the latest stable release so that new adopters get a known-good version. Vet is very much experimental. While we give guarantees about it working in a barebones sense, it is not guaranteed that breaking changes won't be made over time so pinning a version for CI makes sense for most use cases. +6. Create a PR for the new branch +7. The `Publish / PyPI` workflow will automatically build and publish the package +8. Merge PR into main. ## Development Notes diff --git a/README.md b/README.md @@ -81,7 +81,7 @@ The `--history-loader` option executes the specified shell command as the curren ## GitHub PRs (Actions) -Vet can run on pull requests. +Vet can run on pull requests using the reusable GitHub Action. Create `.github/workflows/vet.yml`: @@ -100,50 +100,17 @@ jobs: vet: if: github.event.pull_request.draft == false runs-on: ubuntu-latest + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 - - uses: actions/setup-python@v5 - with: - python-version: "3.11" - - run: pip install verify-everything==0.1.7 - - name: Run vet - if: github.event.pull_request.head.repo.full_name == github.repository - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - BASE_REF: ${{ github.event.pull_request.base.ref }} - VET_GOAL: | - ${{ github.event.pull_request.title }} - - Additional context (not necessarily part of the goal): - ${{ github.event.pull_request.body }} - run: | - set +e - MERGE_BASE=$(git merge-base "origin/$BASE_REF" "${{ github.event.pull_request.head.sha }}") - vet "$VET_GOAL" --quiet --output-format github \ - --base-commit "$MERGE_BASE" \ - > "$RUNNER_TEMP/review.json" - status=$? - if [ "$status" -ne 0 ] && [ "$status" -ne 10 ]; then exit "$status"; fi - - jq --arg sha "${{ github.event.pull_request.head.sha }}" \ - '. + {commit_id: $sha}' "$RUNNER_TEMP/review.json" > "$RUNNER_TEMP/review-final.json" - - gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews" \ - --method POST --input "$RUNNER_TEMP/review-final.json" > /dev/null || \ - gh pr comment "${{ github.event.pull_request.number }}" \ - --body "$(jq -r '[.body] + [.comments[] | "**\(.path):\(.line)**\n\n\(.body)"] | join("\n\n---\n\n")' "$RUNNER_TEMP/review-final.json")" - exit 0 + - uses: imbue-ai/vet@main ``` -NOTE: This will not fail in CI if Vet finds an issue. - -#### Environment variables - -- `ANTHROPIC_API_KEY` is required for the default model configuration. +The action handles Python setup, vet installation, merge base computation, and posting the review to the PR. `ANTHROPIC_API_KEY` must be set as a repository secret when using Anthropic models (the default). See [`action.yml`](https://github.com/imbue-ai/vet/blob/main/action.yml) for all available inputs. ## How it works diff --git a/action.yml b/action.yml @@ -0,0 +1,111 @@ +name: "Vet: Verify Everything" +description: "LLM-based code review that finds issues tests and linters miss" +author: "Imbue" + +branding: + icon: "check-circle" + color: "blue" + +inputs: + agentic: + description: "Enable agentic mode (requires Claude Code; Node.js will be installed automatically)" + required: false + default: "false" + model: + description: "LLM model to use" + required: false + default: "" + confidence-threshold: + description: "Minimum confidence for reported issues (0.0-1.0)" + required: false + default: "" + max-workers: + description: "Maximum number of parallel workers" + required: false + default: "" + max-spend: + description: "Maximum API spend in dollars" + required: false + default: "" + temperature: + description: "Model temperature (0.0-2.0)" + required: false + default: "" + enabled-issue-codes: + description: "Space-separated list of issue codes to enable" + required: false + default: "" + disabled-issue-codes: + description: "Space-separated list of issue codes to disable" + required: false + default: "" + config: + description: "Named config preset from .vet/configs.toml" + required: false + default: "" + extra-context: + description: "Space-separated paths to extra context files" + required: false + default: "" + fail-on-issues: + description: "Fail the workflow when vet finds issues" + required: false + default: "false" + build-from-source: + description: "Build and install vet from the checked-out source tree instead of PyPI" + required: false + default: "false" + +runs: + using: "composite" + steps: + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Setup Node.js + if: inputs.agentic == 'true' + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install Claude Code + if: inputs.agentic == 'true' + shell: bash + run: npm install -g @anthropic-ai/claude-code + + - name: Install vet + shell: bash + run: | + if [[ "${{ inputs.build-from-source }}" == "true" ]]; then + pip install . + else + pip install verify-everything + fi + + - name: Run vet + if: github.event.pull_request.head.repo.full_name == github.repository + shell: bash + env: + GH_TOKEN: ${{ github.token }} + INPUT_AGENTIC: ${{ inputs.agentic }} + INPUT_MODEL: ${{ inputs.model }} + INPUT_CONFIDENCE_THRESHOLD: ${{ inputs.confidence-threshold }} + INPUT_MAX_WORKERS: ${{ inputs.max-workers }} + INPUT_MAX_SPEND: ${{ inputs.max-spend }} + INPUT_TEMPERATURE: ${{ inputs.temperature }} + INPUT_ENABLED_ISSUE_CODES: ${{ inputs.enabled-issue-codes }} + INPUT_DISABLED_ISSUE_CODES: ${{ inputs.disabled-issue-codes }} + INPUT_CONFIG: ${{ inputs.config }} + INPUT_EXTRA_CONTEXT: ${{ inputs.extra-context }} + INPUT_FAIL_ON_ISSUES: ${{ inputs.fail-on-issues }} + INPUT_BASE_REF: ${{ github.event.pull_request.base.ref }} + INPUT_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + INPUT_PR_NUMBER: ${{ github.event.pull_request.number }} + INPUT_GOAL: | + ${{ github.event.pull_request.title }} + + Additional context (not necessarily part of the goal): + ${{ github.event.pull_request.body }} + run: ${{ github.action_path }}/action/run.sh diff --git a/action/run.sh b/action/run.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +set +e + +MERGE_BASE=$(git merge-base "origin/${INPUT_BASE_REF}" "${INPUT_HEAD_SHA}") +if [ $? -ne 0 ]; then + echo "::error::Failed to compute merge base between origin/${INPUT_BASE_REF} and ${INPUT_HEAD_SHA}" + exit 1 +fi + +ARGS=("${INPUT_GOAL}" --quiet --output-format github --base-commit "${MERGE_BASE}") + +[[ "${INPUT_AGENTIC}" == "true" ]] && ARGS+=(--agentic) +[[ -n "${INPUT_MODEL}" ]] && ARGS+=(--model "${INPUT_MODEL}") +[[ -n "${INPUT_CONFIDENCE_THRESHOLD}" ]] && ARGS+=(--confidence-threshold "${INPUT_CONFIDENCE_THRESHOLD}") +[[ -n "${INPUT_MAX_WORKERS}" ]] && ARGS+=(--max-workers "${INPUT_MAX_WORKERS}") +[[ -n "${INPUT_MAX_SPEND}" ]] && ARGS+=(--max-spend "${INPUT_MAX_SPEND}") +[[ -n "${INPUT_TEMPERATURE}" ]] && ARGS+=(--temperature "${INPUT_TEMPERATURE}") +[[ -n "${INPUT_CONFIG}" ]] && ARGS+=(--config "${INPUT_CONFIG}") + +[[ -n "${INPUT_ENABLED_ISSUE_CODES}" ]] && ARGS+=(--enabled-issue-codes ${INPUT_ENABLED_ISSUE_CODES}) +[[ -n "${INPUT_DISABLED_ISSUE_CODES}" ]] && ARGS+=(--disabled-issue-codes ${INPUT_DISABLED_ISSUE_CODES}) +[[ -n "${INPUT_EXTRA_CONTEXT}" ]] && ARGS+=(--extra-context ${INPUT_EXTRA_CONTEXT}) + +vet "${ARGS[@]}" > "${RUNNER_TEMP}/review.json" +status=$? + +if [ "$status" -ne 0 ] && [ "$status" -ne 10 ]; then + echo "::error::Vet failed with exit code ${status}" + exit "$status" +fi + +jq --arg sha "${INPUT_HEAD_SHA}" \ + '. + {commit_id: $sha}' "${RUNNER_TEMP}/review.json" > "${RUNNER_TEMP}/review-final.json" + +gh api "repos/${GITHUB_REPOSITORY}/pulls/${INPUT_PR_NUMBER}/reviews" \ + --method POST --input "${RUNNER_TEMP}/review-final.json" > /dev/null || \ + gh pr comment "${INPUT_PR_NUMBER}" \ + --body "$(jq -r '[.body] + [.comments[] | "**\(.path):\(.line)**\n\n\(.body)"] | join("\n\n---\n\n")' "${RUNNER_TEMP}/review-final.json")" + +if [[ "${INPUT_FAIL_ON_ISSUES}" == "true" ]] && [ "$status" -eq 10 ]; then + exit 1 +fi + +exit 0 diff --git a/uv.lock b/uv.lock @@ -1511,7 +1511,7 @@ wheels = [ [[package]] name = "verify-everything" -version = "0.1.6" +version = "0.1.7" source = { editable = "." } dependencies = [ { name = "anthropic" },