Skip to content

Commit 3832dea

Browse files
committed
infrastructure for QC and publishing to PyPI
1 parent c0d3bd2 commit 3832dea

12 files changed

Lines changed: 1581 additions & 488 deletions

File tree

.github/workflows/ci.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Test and Install Package
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
workflow_dispatch:
9+
10+
jobs:
11+
build:
12+
runs-on: ubuntu-latest
13+
strategy:
14+
matrix:
15+
python-version: ["3.11", "3.12"]
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0
21+
22+
- name: Set up Python ${{ matrix.python-version }}
23+
uses: actions/setup-python@v5
24+
with:
25+
python-version: ${{ matrix.python-version }}
26+
27+
- name: Install uv
28+
uses: astral-sh/setup-uv@v4
29+
with:
30+
version: "latest"
31+
32+
- name: Install dependencies
33+
run: uv sync --group dev
34+
35+
- name: Build and test
36+
run: make all

.github/workflows/publish.yml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
name: Build and Publish to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*' # Trigger on version tags (v1.0.0, v0.8.1, etc.)
7+
workflow_dispatch: # Allow manual triggering
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Checkout code
14+
uses: actions/checkout@v4
15+
with:
16+
fetch-depth: 0
17+
18+
- name: Set up Python
19+
uses: actions/setup-python@v5
20+
with:
21+
python-version: '3.11'
22+
23+
- name: Install uv
24+
uses: astral-sh/setup-uv@v4
25+
with:
26+
version: "latest"
27+
28+
- name: Install dependencies
29+
run: uv sync --group dev
30+
31+
- name: Run tests
32+
run: uv run pytest tests/
33+
34+
build-and-publish:
35+
needs: test
36+
runs-on: ubuntu-latest
37+
permissions:
38+
contents: read
39+
40+
steps:
41+
- name: Checkout code
42+
uses: actions/checkout@v4
43+
with:
44+
fetch-depth: 0 # Required for hatch-vcs versioning
45+
46+
- name: Set up Python
47+
uses: actions/setup-python@v5
48+
with:
49+
python-version: '3.11'
50+
51+
- name: Install uv
52+
uses: astral-sh/setup-uv@v4
53+
with:
54+
version: "latest"
55+
56+
- name: Install hatch
57+
run: uv tool install hatch
58+
59+
- name: Build package with hatch
60+
run: uv tool run hatch build
61+
62+
- name: Check package
63+
run: |
64+
uv tool install twine
65+
uv tool run twine check dist/*
66+
67+
- name: Verify version matches tag
68+
run: |
69+
TAG_VERSION=${GITHUB_REF#refs/tags/v}
70+
PACKAGE_VERSION=$(uv tool run hatch version)
71+
echo "Tag version: $TAG_VERSION"
72+
echo "Package version: $PACKAGE_VERSION"
73+
if [ "$TAG_VERSION" != "$PACKAGE_VERSION" ]; then
74+
echo "Version mismatch! Tag: $TAG_VERSION, Package: $PACKAGE_VERSION"
75+
exit 1
76+
fi
77+
78+
- name: Publish to PyPI (using token-based auth)
79+
if: "!contains(github.ref, 'test') && !contains(github.ref, 'dryrun')"
80+
uses: pypa/gh-action-pypi-publish@release/v1
81+
with:
82+
verify-metadata: true
83+
verbose: true
84+
password: ${{ secrets.PYPI_API_TOKEN }}
85+
86+
- name: Publish to TestPyPI (using token-based auth)
87+
if: "contains(github.ref, 'test') && !contains(github.ref, 'dryrun')"
88+
uses: pypa/gh-action-pypi-publish@release/v1
89+
with:
90+
repository-url: https://test.pypi.org/legacy/
91+
verify-metadata: true
92+
verbose: true
93+
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
94+
95+
- name: Dry run (build only)
96+
if: "contains(github.ref, 'dryrun')"
97+
run: |
98+
echo "Dry run mode - would publish these files:"
99+
ls -la dist/
100+
echo "Package contents:"
101+
uv tool run twine check dist/* --verbose
102+
103+
- name: Upload build artifacts
104+
uses: actions/upload-artifact@v4
105+
if: always()
106+
with:
107+
name: build-artifacts-${{ github.ref_name }}
108+
path: dist/

Makefile

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
.PHONY: test-coverage clean install dev format lint all server build upload-test upload release deptry mypy test-mcp test-mcp-extended test-integration
2+
3+
# Default target
4+
all: clean install dev test-coverage format lint mypy deptry build test-mcp test-mcp-extended test-integration
5+
6+
# Install everything for development
7+
dev:
8+
uv sync --group dev
9+
10+
# Install production only
11+
install:
12+
uv sync
13+
14+
# Run tests with coverage
15+
test-coverage:
16+
uv run pytest --cov=ols_mcp --cov-report=html --cov-report=term tests/
17+
18+
# Clean up build artifacts
19+
clean:
20+
rm -rf build/
21+
rm -rf dist/
22+
rm -rf *.egg-info
23+
find . -type d -name __pycache__ -exec rm -rf {} +
24+
find . -type f -name "*.pyc" -delete
25+
rm -rf src/*.egg-info
26+
27+
# Run server mode
28+
server:
29+
uv run python src/ols_mcp/main.py
30+
31+
# Format code with black
32+
format:
33+
uv run black src/ tests/
34+
35+
lint:
36+
uv run ruff check --fix src/ tests/
37+
38+
# Check for unused dependencies
39+
deptry:
40+
uvx deptry .
41+
42+
# Type checking
43+
mypy:
44+
uv run mypy src/
45+
46+
# Build package with hatch
47+
build:
48+
uv run hatch build
49+
50+
# Upload to TestPyPI (using token-based auth)
51+
upload-test:
52+
uv run twine upload --repository testpypi dist/*
53+
54+
# Upload to PyPI (using token-based auth - set TWINE_PASSWORD environment variable first)
55+
upload:
56+
uv run twine upload dist/*
57+
58+
# Complete release workflow
59+
release: clean test-coverage build upload
60+
61+
# Integration Testing
62+
test-integration:
63+
@echo "🔬 Testing OLS integration..."
64+
uv run pytest tests/test_api.py -v
65+
66+
# MCP Server testing
67+
test-mcp:
68+
@echo "Testing MCP protocol handshake..."
69+
echo '{"jsonrpc": "2.0", "method": "initialize", "params": {"protocolVersion": "1.0", "capabilities": {"tools": {}}, "clientInfo": {"name": "test-client", "version": "1.0.0"}}, "id": 1}' | timeout 3 uv run python src/ols_mcp/main.py 2>/dev/null | head -1
70+
71+
test-mcp-extended:
72+
@echo "Testing MCP protocol initialization..."
73+
@(echo '{"jsonrpc": "2.0", "method": "initialize", "params": {"protocolVersion": "2025-03-26", "capabilities": {"tools": {}}, "clientInfo": {"name": "test-client", "version": "1.0.0"}}, "id": 1}'; \
74+
sleep 0.1; \
75+
echo '{"jsonrpc": "2.0", "method": "tools/list", "id": 2}'; \
76+
sleep 0.1; \
77+
echo '{"jsonrpc": "2.0", "method": "tools/call", "params": {"name": "search_ontologies", "arguments": {"term": "cancer", "ontology": "mondo", "n": 2}}, "id": 3}') | \
78+
timeout 5 uv run python src/ols_mcp/main.py 2>/dev/null | head -10
79+
80+
# OLS MCP - Claude Desktop config:
81+
# Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
82+
# {
83+
# "mcpServers": {
84+
# "ols-mcp": {
85+
# "command": "uv",
86+
# "args": ["run", "python", "src/ols_mcp/main.py"],
87+
# "cwd": "/path/to/ols-mcp"
88+
# }
89+
# }
90+
# }
91+
#
92+
# Claude Code MCP setup:
93+
# claude mcp add -s project ols-mcp uv run python src/ols_mcp/main.py
94+
#
95+
# Goose setup:
96+
# goose session --with-extension "uv run python src/ols_mcp/main.py"

0 commit comments

Comments
 (0)