name: Test
on:
push:
branches:
- main
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
id-token: write
pull-requests: write
jobs:
format:
name: Code Formatting
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.14
- name: Install dependencies
run: uv sync
- name: Check code formatting
run: uv run ruff format --check
lint:
name: Code Linting
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.14
- name: Install dependencies
run: uv sync
- name: Lint code
run: uv run ruff check --output-format=github
continue-on-error: false
typecheck:
name: Type Checking
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.14
- name: Install dependencies
run: uv sync
- name: Type check with mypy
run: uv run mypy packages/ --show-error-codes
continue-on-error: false
basedpyright:
name: Type Checking (basedpyright)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.14
- name: Install dependencies
run: uv sync
- name: Type check with basedpyright
run: uv run basedpyright packages/
continue-on-error: false
lint-extra:
name: Extra Lints (Markdown, Prettier, Shell)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.14
- name: Install dependencies
run: uv sync
- name: Lint Markdown
run: npx -y markdownlint-cli2
- name: Check Formatting (Prettier)
run: npx -y prettier --check "**/*.{md,json,yaml,yml}" --ignore-path .gitignore
- name: ShellCheck
run: |
if [ -d "scripts" ]; then
find scripts -name "*.sh" | xargs -r shellcheck
fi
- name: shfmt
run: |
if [ -d "scripts" ]; then
find scripts -name "*.sh" | xargs -r npx -y shfmt -d
fi
validate-manifest:
name: Validate MCPB Manifest
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Validate manifest
run: npx -y @anthropic-ai/mcpb validate manifest.json
test:
name: Tests (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12", "3.13", "3.14"]
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync
- name: Run tests with coverage
run: uv run pytest --cov=packages/mcp_json_yaml_toml --cov-report=xml --cov-report=term
- name: Upload coverage XML
uses: actions/upload-artifact@v6
if: matrix.python-version == '3.14'
with:
name: coverage-xml
path: coverage.xml
retention-days: 30
coverage-summary:
name: Coverage Summary
runs-on: ubuntu-latest
needs: test
if: github.event_name == 'pull_request'
steps:
- name: Download coverage artifacts
uses: actions/download-artifact@v7
with:
name: coverage-xml
path: coverage-reports
- name: Comment PR with coverage
uses: actions/github-script@v8
with:
script: |
const fs = require('fs');
const xml = fs.readFileSync('coverage-reports/coverage.xml', 'utf8');
const match = xml.match(/line-rate="([^"]+)"/);
const coverage = match ? (parseFloat(match[1]) * 100).toFixed(2) + '%' : 'N/A';
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.data.find(c =>
c.user.type === 'Bot' && c.body.includes('š Test Coverage')
);
const body = `## š Test Coverage Report\n\n**Coverage:** ${coverage}\n\nš„ Coverage XML available as artifact: \`coverage-xml\``;
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}
release:
name: Tag and Release
needs:
[
format,
lint,
typecheck,
basedpyright,
lint-extra,
test,
validate-manifest,
]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Bump version and push tag
id: tag_version
uses: mathieudutour/github-tag-action@v6.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
default_bump: false
create_annotated_tag: true
- name: Create a GitHub release
if: steps.tag_version.outputs.new_tag
uses: ncipollo/release-action@v1
with:
tag: ${{ steps.tag_version.outputs.new_tag }}
name: Release ${{ steps.tag_version.outputs.new_tag }}
body: ${{ steps.tag_version.outputs.changelog }}
token: ${{ secrets.RELEASE_TOKEN }}