Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
version: 2

# Modeled on the ZeroPath monorepo (cunningham) conventions: monthly cadence,
# grouped updates, cooldowns to keep churn low, and a dedicated group for
# security updates so those can be reviewed/merged quickly.
updates:
- package-ecosystem: "pip" # covers this uv/pyproject project (reads uv.lock)
directory: "/"
schedule:
interval: "monthly"
time: "07:00"
timezone: "America/Los_Angeles"
cooldown:
default-days: 14
semver-major-days: 30
semver-minor-days: 21
semver-patch-days: 14
open-pull-requests-limit: 2
groups:
python-dependencies:
patterns:
- "*"
python-security:
applies-to: "security-updates"
patterns:
- "*"

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
time: "08:00"
timezone: "America/Los_Angeles"
open-pull-requests-limit: 1
groups:
github-actions:
patterns:
- "*"
github-actions-security:
applies-to: "security-updates"
patterns:
- "*"
132 changes: 132 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
name: CI

on:
push:
branches: [main]
tags: ["v*"]
pull_request:

# Cancel superseded runs on the same ref (e.g. rapid pushes to a PR).
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

env:
# Pinned tool versions so local runs match CI. Keep in sync with pyproject.
RUFF_VERSION: "0.15.1"
PYRIGHT_VERSION: "1.1.411"

jobs:
lint:
name: ruff (lint + format)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Install uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true

- name: Ruff lint
run: uvx ruff@${{ env.RUFF_VERSION }} check --output-format=github .

- name: Ruff format check
run: uvx ruff@${{ env.RUFF_VERSION }} format --check --diff .

typecheck:
name: pyright
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Install uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true

# Resolve project deps (--frozen: fail if uv.lock is stale) so pyright
# can see mcp/requests types, then add pyright to the ephemeral env.
- name: Run pyright
run: uv run --frozen --python 3.12 --with pyright==${{ env.PYRIGHT_VERSION }} pyright

test:
name: pytest (py${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.12", "3.13"]
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Install uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true

- name: Run tests
run: uv run --frozen --python ${{ matrix.python-version }} pytest -v --tb=short

build:
name: build + verify package
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4

- name: Install uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
enable-cache: true

- name: Build sdist + wheel
run: uv build

# On a version tag, the built version must match the tag (v0.2.0 -> 0.2.0).
- name: Verify tag matches package version
if: startsWith(github.ref, 'refs/tags/v')
run: |
tag="${GITHUB_REF_NAME#v}"
pkg="$(uv run --frozen python -c 'import zeropath_mcp_server as z; print(z.__version__)')"
echo "tag=$tag package=$pkg"
if [ "$tag" != "$pkg" ]; then
echo "::error::Release tag ($tag) does not match package version ($pkg)"
exit 1
fi

- name: Validate distribution metadata
run: uvx twine check dist/*

- name: Smoke-test the built wheel
run: |
uv run --isolated --no-project --with dist/*.whl \
python -c "import zeropath_mcp_server as z; print('import OK, version', z.__version__)"

- name: Upload build artifacts
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: dist
path: dist/*
if-no-files-found: error

# Single required status: green only when every matrix leg + job above passed.
ci-ok:
name: CI passed
if: always()
needs: [lint, typecheck, test, build]
runs-on: ubuntu-latest
steps:
- name: Verify all jobs succeeded
run: |
if [ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" = "true" ]; then
echo "One or more CI jobs failed."
exit 1
fi
echo "All CI jobs passed."
39 changes: 39 additions & 0 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Claude Code Review

on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]

# Requires the ANTHROPIC_API_KEY repository secret. Until it is set the review
# step is skipped (the job still succeeds), so the workflow is inert rather
# than red on repos/forks without the secret configured.
jobs:
claude-review:
# Skip PRs from forks: secrets are not available to fork PRs anyway.
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}

steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 1

- name: Run Claude Code Review
id: claude-review
if: ${{ env.ANTHROPIC_API_KEY != '' }}
uses: anthropics/claude-code-action@ff9acae5886d41a99ed4ec14b7dc147d55834722 # v1
with:
anthropic_api_key: ${{ env.ANTHROPIC_API_KEY }}
allowed_bots: "cursor"
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review --comment ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
claude_args: '--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"'
45 changes: 45 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Claude Code

on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]

# Responds to @claude mentions in issues / PR comments / reviews.
# Requires the ANTHROPIC_API_KEY repository secret; inert (skipped) without it.
jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
issues: write
id-token: write
actions: read # Required for Claude to read CI results on PRs
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 1

- name: Run Claude Code
id: claude
if: ${{ env.ANTHROPIC_API_KEY != '' }}
uses: anthropics/claude-code-action@ff9acae5886d41a99ed4ec14b7dc147d55834722 # v1
with:
anthropic_api_key: ${{ env.ANTHROPIC_API_KEY }}
# Allows Claude to read CI results on PRs.
additional_permissions: |
actions: read
10 changes: 10 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]

# Linting/formatting is enforced in CI with ruff (pinned in .github/workflows/ci.yml).
[tool.ruff]
line-length = 120
target-version = "py312"

[tool.pyright]
# Type-checked in CI with pyright against the uv-resolved environment.
include = ["src", "tests"]
pythonVersion = "3.12"

[dependency-groups]
dev = [
"pytest==9.0.2",
Expand Down
Loading