Skip to content
Merged
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
20 changes: 13 additions & 7 deletions .claude/rules/pytest.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,10 @@ class TestEnrichmentCheck:
def parse_source():
def _parse(source: str) -> ast.Module:
return ast.parse(source)

return _parse


@pytest.fixture
def git_repo(tmp_path):
subprocess.run(["git", "init"], cwd=tmp_path, check=True, capture_output=True)
Expand All @@ -87,8 +89,8 @@ def git_repo(tmp_path):
```python
@pytest.fixture
def make_source():
def _make(*, has_raise=False, has_yield=False, has_docstring=True):
...
def _make(*, has_raise=False, has_yield=False, has_docstring=True): ...

return _make
```

Expand All @@ -103,11 +105,15 @@ def make_source():

### Parametrizing with IDs
```python
@pytest.mark.parametrize("source, expected_sections", [
(SOURCE_WITH_RAISE, ["Raises"]),
(SOURCE_WITH_YIELD, ["Yields"]),
(SOURCE_WITH_KWARGS, ["Other Parameters"]),
], ids=["raises", "yields", "kwargs"])
@pytest.mark.parametrize(
"source, expected_sections",
[
(SOURCE_WITH_RAISE, ["Raises"]),
(SOURCE_WITH_YIELD, ["Yields"]),
(SOURCE_WITH_KWARGS, ["Other Parameters"]),
],
ids=["raises", "yields", "kwargs"],
)
def test_enrichment_detects_missing_sections(source, expected_sections):
findings = run_enrichment(source)
assert [f.section for f in findings] == expected_sections
Expand Down
1 change: 0 additions & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,3 @@ assignees: ''

## Possible Cause
<!-- Optional: Any ideas on root cause? -->

21 changes: 21 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: 2
updates:
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "chore(deps)"
groups:
python-dependencies:
patterns: ["*"]

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "chore(ci)"
groups:
actions:
patterns: ["*"]
29 changes: 19 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
push:
branches: [main]

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Expand All @@ -15,16 +18,22 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v10.0.1
with:
enable-cache: true
- run: uv python install 3.12
- run: uv lock --check
- run: uv sync --locked --dev
- run: uv run ruff check .
- run: uv run ruff format --check .
- run: uv audit
- run: uv run yamllint -c .yamllint.yaml .
# Pin the version .pre-commit-config.yaml names; the action defaults
# to `latest`, which is exactly the drift the hook line avoids.
- uses: raven-actions/actionlint@v2
with:
version: 1.7.12
- run: uv audit --preview-features audit-command
- run: uv cache prune --ci
if: always()
continue-on-error: true
Expand All @@ -33,8 +42,8 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v10.0.1
with:
enable-cache: true
- run: uv python install 3.12
Expand All @@ -53,8 +62,8 @@ jobs:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.12", "3.13"]
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v10.0.1
with:
enable-cache: true
cache-suffix: ${{ matrix.os }}-py${{ matrix.python-version }}
Expand All @@ -65,7 +74,7 @@ jobs:
- run: uv run pytest --tb=short --cov=docvet --cov-report=term-missing --cov-report=xml --cov-fail-under=85
env:
UV_PYTHON: ${{ matrix.python-version }}
- uses: codecov/codecov-action@v5
- uses: codecov/codecov-action@v7
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
with:
fail_ci_if_error: false
Expand All @@ -79,7 +88,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: Alberto-Codes/docvet@v1
Expand All @@ -89,7 +98,7 @@ jobs:
- name: Update docvet badge
if: always() && github.ref == 'refs/heads/main' && vars.BADGE_GIST_ID != ''
continue-on-error: true
uses: schneegans/dynamic-badges-action@v1.7.0
uses: schneegans/dynamic-badges-action@v1.9.0
with:
auth: ${{ secrets.GIST_SECRET }}
gistID: ${{ vars.BADGE_GIST_ID }}
Expand Down
11 changes: 10 additions & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ name: CodeQL
on:
pull_request:
branches: [main]
push:
branches: [main]
schedule:
- cron: "0 6 * * 1" # Weekly Monday 06:00 UTC

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Expand All @@ -14,10 +19,14 @@ jobs:
analyze:
runs-on: ubuntu-latest
timeout-minutes: 10
# Job-level permissions replace the workflow-level block, so
# contents: read must be restated or checkout fails.
permissions:
actions: read
contents: read
security-events: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- uses: github/codeql-action/init@v4
with:
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@v10.0.1
with:
enable-cache: true
- name: Install Python
run: uv python install 3.12
- name: Install documentation dependencies
run: uv sync --extra docs
run: uv sync --locked --extra docs
- name: Build documentation site
run: uv run mkdocs build --strict
- name: Upload pages artifact
uses: actions/upload-pages-artifact@v4
uses: actions/upload-pages-artifact@v5
with:
path: site
- name: Prune uv cache
Expand All @@ -48,5 +48,5 @@ jobs:
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v5
id: deployment
31 changes: 19 additions & 12 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,16 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- uses: astral-sh/setup-uv@v7
- uses: actions/checkout@v7
- uses: astral-sh/setup-uv@v10.0.1
with:
enable-cache: true

# Pin the interpreter rather than trust the runner image, matching
# ci.yml. The wheel is py3-none-any, so this fixes the build
# environment, not the artifact.
- run: uv python install 3.12

- name: Build package
run: uv build --no-sources

Expand All @@ -43,12 +48,14 @@ jobs:
echo "Wheel size: ${SIZE} bytes — OK"

CONTENTS=$(unzip -l "$WHEEL")
if echo "$CONTENTS" | grep -q "tests/"; then
echo "::error::Wheel contains tests/ directory"
exit 1
fi
if echo "$CONTENTS" | grep -q "_bmad"; then
echo "::error::Wheel contains _bmad files"
for UNWANTED in "tests/" "docs/" "scripts/" "_bmad"; do
if echo "$CONTENTS" | grep -q "$UNWANTED"; then
echo "::error::Wheel contains ${UNWANTED}"
exit 1
fi
done
if ! echo "$CONTENTS" | grep -q "docvet/py.typed"; then
echo "::error::Wheel omits the py.typed marker"
exit 1
fi
echo "Wheel contents clean — OK"
Expand Down Expand Up @@ -104,7 +111,7 @@ jobs:
contents: read
attestations: write
steps:
- uses: astral-sh/setup-uv@v7
- uses: astral-sh/setup-uv@v10.0.1
with:
enable-cache: false
ignore-empty-workdir: true
Expand All @@ -118,7 +125,7 @@ jobs:
run: uv publish --trusted-publishing always --check-url https://pypi.org/simple/

- name: Attest build provenance
uses: astral-sh/attest-action@v0.0.5
uses: astral-sh/attest-action@v0.0.6
with:
paths: dist/*

Expand All @@ -134,7 +141,7 @@ jobs:
id-token: write
contents: read
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Install mcp-publisher
run: |
Expand All @@ -155,7 +162,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- name: Update floating v1 tag
run: |
Expand Down
37 changes: 31 additions & 6 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,66 @@ on:
push:
branches: [main]

# Least-privilege: read-only by default. Each job escalates for itself.
permissions:
contents: write
pull-requests: write
contents: read

jobs:
release-please:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
env:
HAS_PAT: ${{ secrets.RELEASE_PLEASE_TOKEN != '' }}
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
upload_url: ${{ steps.release.outputs.upload_url }}
prs_created: ${{ steps.release.outputs.prs_created }}
pr_branch: ${{ steps.release.outputs.prs_created == 'true' && fromJSON(steps.release.outputs.pr).headBranchName || '' }}
pr_branch: >-
${{ steps.release.outputs.prs_created == 'true'
&& fromJSON(steps.release.outputs.pr).headBranchName || '' }}
steps:
- uses: googleapis/release-please-action@v4
- uses: googleapis/release-please-action@v5
id: release
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
target-branch: main
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

# publish.yml fires on the tag push. A tag pushed with GITHUB_TOKEN
# never triggers another workflow, so a release cut without the PAT
# would silently skip PyPI. Fail rather than report a success that
# is only partly true.
- name: Fail a release cut without the PAT
if: steps.release.outputs.release_created == 'true' && env.HAS_PAT != 'true'
run: |
echo "::error::Released ${{ steps.release.outputs.tag_name }} with \
GITHUB_TOKEN. The tag will not trigger publish.yml. Set \
RELEASE_PLEASE_TOKEN, then push the tag by hand."
exit 1

update-lockfile:
needs: release-please
if: needs.release-please.outputs.prs_created == 'true'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout release-please PR branch
uses: actions/checkout@v6
uses: actions/checkout@v7
with:
token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
ref: ${{ needs.release-please.outputs.pr_branch }}

- name: Set up uv
uses: astral-sh/setup-uv@v7
uses: astral-sh/setup-uv@v10.0.1

# Pin the interpreter rather than trust the runner image, matching
# ci.yml.
- run: uv python install 3.12

- name: Update uv.lock
run: uv lock --upgrade-package docvet
Expand Down
Loading
Loading