Migrate Keboola Component to uv Build System
You are an expert at migrating Keboola Python packages from legacy setup.py + pip to modern pyproject.toml + uv build system. You understand PEP 517/518/639 standards, GitHub Actions workflows, and Keboola's established migration patterns.
When to Use This Skill
Use this skill when:
- •Migrating a Keboola Python package from
setup.pytopyproject.toml - •Modernizing build system to use uv instead of pip
- •Adding deterministic dependency management with
uv.lock - •Updating CI/CD workflows to use uv
- •Following Keboola's python-http-client and python-component patterns
Prerequisites Check
Before starting, verify:
- • Repository uses
setup.pyand/orrequirements.txt - • Package has test suite with pytest
- • Git repository with clean working tree
- • Access to GitHub secrets configuration
- • PyPI and Test PyPI accounts available
Migration Philosophy
Flexible Commit Strategy
Guideline (not dogma): Use 3 logical commits
- •Linting baseline - Fix linting first to avoid noise in migration diffs
- •Package metadata - Migrate to pyproject.toml
- •CI/CD workflows - Update to uv + generate lock file
Why this works:
- •Clean diffs: Linting fixes don't pollute actual migration changes
- •Reviewable: Each commit has clear, focused purpose
- •Flexible: Could be 2 commits (combine lint+pyproject) or 4 (separate workflows)
Key principle: Logical, reviewable chunks that make sense independently
Lint-First Principle
Always fix linting BEFORE migrating metadata:
- •Establishes clean baseline
- •Reveals code quality issues early
- •Prevents attribution confusion (linting vs migration changes)
- •Makes review significantly easier
Version Strategy
Testing phase: Use next minor version
- •Example: Current 1.6.13 → Test as 1.7.0, 1.7.1, 1.7.2
Production release: Use following minor version
- •Example: After testing 1.7.x → Release 1.8.0
Flexibility: Could use 1.7.0a1, 1.7.0a2 instead - pattern matters, not exact format
Step-by-Step Migration Guide
Phase 1: Analysis
- •Check current state:
# What's in setup.py? cat setup.py # What's in requirements.txt? cat requirements.txt # What's the current version? # Check PyPI or setup.py
- •Identify Python version support:
# From setup.py python_requires # Determine: min_version = max(3.8, current_requires_python)
- •Check for docs generation:
# Does push_main.yml exist with pdoc? cat .github/workflows/push_main.yml 2>/dev/null | grep pdoc
Phase 2: Commit 1 - Linting Baseline
Purpose: Establish clean linting baseline
Steps:
- •Rename and update flake8 config:
# Rename to standard name mv flake8.cfg .flake8 # if it exists # Use cookiecutter template standard: cat > .flake8 << 'EOF' [flake8] exclude = __pycache__, .git, .venv, venv, docs ignore = E203,W503 max-line-length = 120 EOF
- •Run flake8 and fix ALL errors:
# Install flake8 uv add --dev flake8 # Run and fix errors flake8 src/ tests/
Common fixes:
- •F403/F405: Replace star imports with explicit imports
- •F841: Remove unused variables
- •E501: Break long lines
- •E231: Add missing whitespace
- •E123: Fix bracket indentation
- •CRLF→LF: Normalize line endings (expected, good cleanup)
- •Commit:
git add .flake8 src/ tests/ git commit -m "flake8 config consistent with cookiecutter template 🍪"
Phase 3: Commit 2 - Package Metadata
Purpose: Migrate to modern pyproject.toml
Steps:
- •Create pyproject.toml (see
templates/pyproject.toml.template):
Key points:
- •
version = "0.0.0"- replaced by git tags in CI - •Extract dependencies from setup.py
install_requires - •Extract dev dependencies from setup.py
setup_requiresandtests_require - •Add
pdoc3to dev if docs workflow exists - •
requires-python = ">=MIN_VERSION"(≥3.8) - •Remove
License :: OSI Approved :: MIT Licenseclassifier (PEP 639) - •Keep
license = "MIT"field - •Add TestPyPI index configuration
- •Delete old files:
git rm setup.py requirements.txt
- •Update LICENSE copyright year:
# Update to current year (2026) sed -i 's/Copyright (c) 20[0-9][0-9]/Copyright (c) 2026/' LICENSE
- •Commit:
git add pyproject.toml LICENSE git commit -m "migrate package configuration to pyproject.toml 📦"
Phase 4: Commit 3 - uv Workflows
Purpose: Update CI/CD to use uv
Steps:
- •Update all 3-4 workflows (see
references/workflow-templates.md):- •
push_dev.yml- Testing on dev branches - •
deploy.yml- Production PyPI deployment - •
deploy_to_test.yml- Test PyPI deployment - •
push_main.yml- Docs generation (OPTIONAL - only if docs exist)
- •
Key changes per workflow:
- •Update action versions:
@v4→@v5,@v6 - •Add uv installation:
uses: astral-sh/setup-uv@v6 - •Add ruff action:
uses: astral-sh/ruff-action@v3(blocking, no continue-on-error) - •Change:
pip install→uv sync --all-groups --frozen - •Change:
flake8 --config=...→uv run flake8 - •Change:
pytest tests→uv run pytest tests - •Add version replacement:
uv version $TAG_VERSION - •Update secrets:
UV_PUBLISH_TOKEN,UV_PUBLISH_TOKEN_TEST_PYPI - •Python matrix:
[MIN_VERSION, "3.13", "3.14"](min + 2 latest)
- •Generate uv.lock:
uv sync --all-groups
- •Verify build works:
uv build # Should create dist/*.tar.gz and dist/*.whl uv version 1.7.0 --dry-run # Should show: package-name 0.0.0 => 1.7.0
- •Commit:
git add .github/workflows/*.yml uv.lock git commit -m "uv 💜"
Phase 5: Testing on Test PyPI
- •Push branch:
git push origin BRANCH_NAME
- •Create test tag:
git tag 1.7.0 git push origin 1.7.0
- •
Manually trigger Test PyPI workflow:
- •Go to GitHub Actions → "Build & Upload Python Package To Test PyPI"
- •Click "Run workflow"
- •Select branch or tag
- •Click "Run workflow"
- •
Verify on Test PyPI:
- •Check: https://test.pypi.org/project/PACKAGE_NAME/
- •Verify version appears
- •
Test installation:
cd /tmp && mkdir test_install && cd test_install
uv init
uv add --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
--index-strategy unsafe-best-match \
PACKAGE_NAME==1.7.0
uv run python -c "import PACKAGE; print('✅ Works!')"
cd .. && rm -rf test_install
Phase 6: Production Release
- •
Create PR (see
templates/pr-description.md.template) - •
Get approval and merge to main
- •
Create production release:
- •Go to: https://github.com/ORG/REPO/releases/new
- •Tag:
1.8.0 - •Target:
main - •Title:
1.8.0 - •Description: Migration summary
- •Click "Publish release"
- •
Workflow auto-triggers → Publishes to PyPI
- •
Verify production:
cd /tmp && mkdir test_prod && cd test_prod
uv init
uv add PACKAGE_NAME==1.8.0
uv run python -c "import PACKAGE; print('✅ Production works!')"
cd .. && rm -rf test_prod
Python Matrix Strategy
Smart matrix logic:
python-version: [ "MIN_SUPPORTED", # max(3.8, current_requires_python) "3.13", # Second-latest stable "3.14" # Latest stable ]
Examples:
- •Package supports ≥3.8 →
["3.8", "3.13", "3.14"] - •Package supports ≥3.10 →
["3.10", "3.13", "3.14"] - •Package supports ≥3.12 →
["3.12", "3.13", "3.14"]
Rationale: Test minimum (compatibility floor) + 2 latest (future-proofing)
Docs Workflow Handling
Detection:
# Check if push_main.yml exists AND contains pdoc
if [ -f .github/workflows/push_main.yml ] && grep -q pdoc .github/workflows/push_main.yml; then
# Include docs workflow in migration
# Add pdoc3 to [dependency-groups] dev
fi
Migration for docs:
- •Old:
pip install --user pdoc3(ad-hoc) - •New: Add
pdoc3to[dependency-groups] dev+ useuv run pdoc - •Improvement: Tracked dependency instead of ad-hoc install
Secret Configuration
Required GitHub secrets:
- •
UV_PUBLISH_TOKEN - Production PyPI token
- •Create at: https://pypi.org/manage/account/token/
- •Scope: Entire account or specific project
- •Add at: GitHub repo → Settings → Secrets → UV_PUBLISH_TOKEN
- •
UV_PUBLISH_TOKEN_TEST_PYPI - Test PyPI token
- •Create at: https://test.pypi.org/manage/account/token/
- •Scope: Entire account
- •Add at: GitHub repo → Settings → Secrets → UV_PUBLISH_TOKEN_TEST_PYPI
Note: uv automatically uses __token__ as username when UV_PUBLISH_TOKEN env var is set
Workflow Trigger Strategy
Test PyPI: on: workflow_dispatch (manual)
- •Allows testing any version without tag naming constraints
- •Full control over when to test
Production PyPI: on: push: tags: ['*'] (automatic)
- •Triggers automatically when tag pushed to main
- •Simpler workflow:
git tag X.Y.Z && git push origin X.Y.Z
Common Issues & Solutions
See references/troubleshooting.md for detailed troubleshooting guide.
Quick fixes:
- •
License classifier conflict
- •Error:
License classifiers have been superseded - •Fix: Remove
License :: OSI Approved :: MIT Licensefrom classifiers
- •Error:
- •
CRLF line endings
- •Symptom: Huge diffs in unchanged files
- •Status: Expected and correct (CRLF → LF normalization)
- •
Test PyPI installation fails
- •Error:
No solution found - •Fix: Add
--index-strategy unsafe-best-match
- •Error:
- •
uv version command not found
- •Fix: Ensure uv ≥ 0.5.0
Modern Tooling Requirements
100% modern uv - ZERO pip mentions:
✅ CORRECT:
- •
uv add PACKAGE- Add production dependency - •
uv add --dev PACKAGE- Add dev dependency - •
uv sync --all-groups --frozen- Install from lock - •
uv run COMMAND- Run command in environment - •
uv build- Build package - •
uv publish- Publish to PyPI
❌ NEVER USE:
- •
pip install- OLD - •
pip- OLD - •
uv pip install- WRONG uv usage
References
- •
references/migration-guide.md- Complete step-by-step guide - •
references/workflow-templates.md- All 4 workflow YAML files - •
references/troubleshooting.md- Common issues and solutions - •
references/examples.md- python-http-client and python-component migrations - •
templates/pyproject.toml.template- Template pyproject.toml - •
templates/flake8.template- Template .flake8 - •
templates/pr-description.md.template- PR description template
Questions to Ask User
Before starting:
- •What's the current latest version on PyPI?
- •Do you have access to configure GitHub secrets?
- •What Python versions should we support? (check setup.py)
- •Does the package generate HTML docs with pdoc?
- •Any custom build requirements or special dependencies?
Success Criteria
Migration complete when:
- •✅ All tests pass with uv locally
- •✅ Package builds successfully (
uv build) - •✅ Test PyPI release installable and functional
- •✅ Production PyPI release installable and functional
- •✅ CI/CD workflows green on main branch
- •✅ Documentation updated (if needed)
- •✅ Team notified
Remember: This is a build system migration, NOT an API change. End users should see no difference except faster installs and more reliable dependency resolution.