Skip to content

Commit df83e48

Browse files
committed
feat: Add PyPI deployment via GitHub Actions and uvx support
🚀 Major improvements: - Add GitHub Actions workflows for CI/CD and PyPI deployment with Trusted Publisher - Configure package for uvx execution (run with `uvx youtrack-rocket-mcp`) - Update version to 0.4.1 for PyPI release ✨ Code quality improvements: - Fix all ruff linting warnings and errors - Fix all mypy type checking issues - Update type annotations throughout codebase - Fix unused variables and imports 📚 Documentation updates: - Add comprehensive uvx usage instructions - Update all documentation to use uv/uvx commands - Fix directory structure in ARCHITECTURE.md - Update DEVELOPMENT.md with correct uv sync usage - Add configuration examples for Claude Desktop, Cursor IDE, and Claude Code CLI 🔧 Technical improvements: - Fix Project.leader attribute (was incorrectly named 'lead') - Add proper type annotations for execute_command - Document YouTrack API command response behavior - Ensure all tests pass (38/38 passing)
1 parent b41c116 commit df83e48

24 files changed

Lines changed: 1706 additions & 327 deletions

.github/workflows/README.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# GitHub Actions Workflows
2+
3+
This repository uses GitHub Actions for CI/CD with PyPI Trusted Publisher (OIDC) authentication.
4+
5+
## Workflows
6+
7+
### 1. CI (`ci.yml`)
8+
- **Trigger**: Push to main/develop, pull requests
9+
- **Purpose**: Run tests, linting, and build checks
10+
- **Jobs**:
11+
- Lint with Ruff and MyPy
12+
- Test on Python 3.9-3.12
13+
- Build and check distribution
14+
15+
### 2. Deploy to PyPI (`deploy.yml`)
16+
- **Trigger**: Git tags matching `v*` (e.g., v0.4.1)
17+
- **Purpose**: Deploy releases to PyPI
18+
- **Uses**: OIDC Trusted Publisher (no API token needed!)
19+
- **Environment**: `pypi` (configured in PyPI settings)
20+
21+
### 3. Test Deploy (`test-deploy.yml`)
22+
- **Trigger**: Push to main/develop, manual
23+
- **Purpose**: Test deployment to TestPyPI
24+
- **Environment**: `testpypi`
25+
26+
### 4. Release Pipeline (`release.yml`)
27+
- **Trigger**: GitHub release creation
28+
- **Purpose**: Full release pipeline with tests
29+
- **Steps**:
30+
1. Run comprehensive tests
31+
2. Build and validate package
32+
3. Deploy to PyPI with attestations
33+
4. Upload artifacts to GitHub release
34+
5. Verify deployment
35+
36+
## Setup Requirements
37+
38+
### PyPI Trusted Publisher Configuration
39+
40+
Already configured on PyPI.org:
41+
- **Project**: youtrack-rocket-mcp
42+
- **Repository**: ivolnistov/youtrack-rocket-mcp
43+
- **Workflow**: deploy.yml
44+
- **Environment**: pypi
45+
46+
### GitHub Repository Settings
47+
48+
1. **Environments**: Create `pypi` environment
49+
- Go to Settings → Environments → New environment
50+
- Name: `pypi`
51+
- Optional: Add protection rules (e.g., required reviewers)
52+
53+
2. **Tags Protection** (optional):
54+
- Settings → Tags → Protection rules
55+
- Pattern: `v*`
56+
- Only allow users with write access
57+
58+
## How to Release
59+
60+
### Option 1: Using Git Tags
61+
```bash
62+
# Update version in src/youtrack_rocket_mcp/version.py
63+
git add .
64+
git commit -m "Release v0.4.1"
65+
git tag v0.4.1
66+
git push origin main --tags
67+
```
68+
69+
### Option 2: Using GitHub Release UI
70+
1. Go to Releases → Create new release
71+
2. Choose a tag (e.g., v0.4.1)
72+
3. Fill in release notes
73+
4. Click "Publish release"
74+
75+
The workflow will automatically:
76+
- Build the package
77+
- Upload to PyPI using Trusted Publisher
78+
- Generate Sigstore attestations
79+
- No API tokens needed!
80+
81+
## Security Benefits
82+
83+
Using Trusted Publisher provides:
84+
- **No long-lived tokens**: Uses short-lived OIDC tokens
85+
- **Sigstore attestations**: Cryptographic proof of build provenance
86+
- **GitHub identity**: Links package to specific repo/workflow
87+
- **Automatic token rotation**: Tokens expire after each use
88+
89+
## Troubleshooting
90+
91+
### If deployment fails:
92+
1. Check PyPI trusted publisher settings match exactly:
93+
- Repository name (case-sensitive)
94+
- Workflow filename
95+
- Environment name
96+
97+
2. Ensure `id-token: write` permission is set
98+
99+
3. Verify environment `pypi` exists in GitHub settings
100+
101+
### Manual deployment (emergency):
102+
```bash
103+
python -m pip install build twine
104+
python -m build
105+
twine upload dist/* # Will need API token
106+
```

.github/workflows/ci.yml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
branches: [main]
8+
workflow_dispatch:
9+
10+
jobs:
11+
lint:
12+
name: Lint and type check
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- name: Set up Python
19+
uses: actions/setup-python@v5
20+
with:
21+
python-version: '3.12'
22+
23+
- name: Install dependencies
24+
run: |
25+
python -m pip install --upgrade pip
26+
pip install ruff mypy
27+
pip install -r requirements.txt
28+
29+
- name: Run Ruff
30+
run: ruff check src/
31+
32+
- name: Run MyPy
33+
run: mypy src/ --ignore-missing-imports
34+
35+
test:
36+
name: Test Python ${{ matrix.python-version }}
37+
runs-on: ubuntu-latest
38+
strategy:
39+
matrix:
40+
python-version: ['3.9', '3.10', '3.11', '3.12']
41+
42+
steps:
43+
- uses: actions/checkout@v4
44+
45+
- name: Set up Python ${{ matrix.python-version }}
46+
uses: actions/setup-python@v5
47+
with:
48+
python-version: ${{ matrix.python-version }}
49+
50+
- name: Install dependencies
51+
run: |
52+
python -m pip install --upgrade pip
53+
pip install -r requirements.txt
54+
pip install pytest pytest-asyncio pytest-cov
55+
56+
- name: Run tests
57+
run: |
58+
pytest tests/ -v --cov=youtrack_rocket_mcp --cov-report=term-missing
59+
continue-on-error: true # Don't fail if tests are missing
60+
61+
build:
62+
name: Build distribution
63+
runs-on: ubuntu-latest
64+
65+
steps:
66+
- uses: actions/checkout@v4
67+
68+
- name: Set up Python
69+
uses: actions/setup-python@v5
70+
with:
71+
python-version: '3.12'
72+
73+
- name: Install build tools
74+
run: |
75+
python -m pip install --upgrade pip
76+
pip install build twine
77+
78+
- name: Build package
79+
run: python -m build
80+
81+
- name: Check distribution
82+
run: |
83+
twine check dist/*
84+
ls -lah dist/
85+
86+
- name: Upload artifacts
87+
uses: actions/upload-artifact@v4
88+
with:
89+
name: dist-${{ github.sha }}
90+
path: dist/
91+
retention-days: 7

.github/workflows/deploy.yml

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Deploy to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*' # Trigger on version tags like v0.4.1, v1.0.0
7+
workflow_dispatch: # Allow manual triggering
8+
9+
jobs:
10+
build:
11+
name: Build distribution
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.12'
21+
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install build
26+
27+
- name: Build package
28+
run: python -m build
29+
30+
- name: Store the distribution packages
31+
uses: actions/upload-artifact@v4
32+
with:
33+
name: python-package-distributions
34+
path: dist/
35+
36+
publish-to-pypi:
37+
name: Publish to PyPI
38+
needs:
39+
- build
40+
runs-on: ubuntu-latest
41+
environment: pypi # MUST match your PyPI trusted publisher configuration
42+
permissions:
43+
id-token: write # MANDATORY for trusted publishing
44+
45+
steps:
46+
- name: Download all the dists
47+
uses: actions/download-artifact@v4
48+
with:
49+
name: python-package-distributions
50+
path: dist/
51+
52+
- name: Publish distribution to PyPI
53+
uses: pypa/gh-action-pypi-publish@release/v1
54+
# No API token needed - uses OIDC trusted publishing!

.github/workflows/release.yml

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
name: Release Pipeline
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch:
7+
inputs:
8+
version:
9+
description: 'Version to release (e.g., 0.4.1)'
10+
required: true
11+
type: string
12+
13+
jobs:
14+
test:
15+
name: Run tests
16+
runs-on: ubuntu-latest
17+
strategy:
18+
matrix:
19+
python-version: ['3.9', '3.10', '3.11', '3.12']
20+
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- name: Set up Python ${{ matrix.python-version }}
25+
uses: actions/setup-python@v5
26+
with:
27+
python-version: ${{ matrix.python-version }}
28+
29+
- name: Install dependencies
30+
run: |
31+
python -m pip install --upgrade pip
32+
pip install -r requirements.txt
33+
pip install pytest pytest-asyncio pytest-cov
34+
35+
- name: Run tests
36+
run: |
37+
pytest tests/ --cov=youtrack_rocket_mcp --cov-report=term-missing
38+
39+
- name: Run linting
40+
run: |
41+
pip install ruff mypy
42+
ruff check src/
43+
mypy src/ --ignore-missing-imports
44+
45+
build:
46+
name: Build distribution
47+
needs: test
48+
runs-on: ubuntu-latest
49+
50+
steps:
51+
- uses: actions/checkout@v4
52+
53+
- name: Set up Python
54+
uses: actions/setup-python@v5
55+
with:
56+
python-version: '3.12'
57+
58+
- name: Install build dependencies
59+
run: |
60+
python -m pip install --upgrade pip
61+
pip install build twine
62+
63+
- name: Build package
64+
run: python -m build
65+
66+
- name: Check package
67+
run: |
68+
twine check dist/*
69+
# Check that the package size is reasonable (< 10MB)
70+
find dist -type f -size +10M -exec echo "Warning: {} is larger than 10MB" \;
71+
72+
- name: Store the distribution packages
73+
uses: actions/upload-artifact@v4
74+
with:
75+
name: python-package-distributions
76+
path: dist/
77+
retention-days: 7
78+
79+
publish-to-pypi:
80+
name: Publish to PyPI
81+
needs: build
82+
runs-on: ubuntu-latest
83+
environment:
84+
name: pypi
85+
url: https://pypi.org/project/youtrack-rocket-mcp/
86+
permissions:
87+
id-token: write # MANDATORY for trusted publishing
88+
contents: write # For creating GitHub release assets
89+
90+
steps:
91+
- uses: actions/checkout@v4
92+
93+
- name: Download all the dists
94+
uses: actions/download-artifact@v4
95+
with:
96+
name: python-package-distributions
97+
path: dist/
98+
99+
- name: Publish distribution to PyPI
100+
uses: pypa/gh-action-pypi-publish@release/v1
101+
with:
102+
attestations: true # Enable sigstore attestations
103+
verbose: true
104+
105+
- name: Upload to GitHub Release
106+
if: github.event_name == 'release'
107+
env:
108+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
109+
run: |
110+
gh release upload ${{ github.event.release.tag_name }} dist/* --clobber
111+
112+
verify:
113+
name: Verify PyPI deployment
114+
needs: publish-to-pypi
115+
runs-on: ubuntu-latest
116+
117+
steps:
118+
- name: Wait for PyPI to update
119+
run: sleep 30
120+
121+
- name: Install from PyPI
122+
run: |
123+
python -m pip install --upgrade pip
124+
pip install youtrack-rocket-mcp
125+
126+
- name: Verify installation
127+
run: |
128+
python -c "import youtrack_rocket_mcp; print(f'Successfully installed version: {youtrack_rocket_mcp.__version__}')"

0 commit comments

Comments
 (0)