Resources
1Install
npx skillscat add hitoshura25/claude-devtools/skills-pypi-publishing Install via the SkillsCat registry.
PyPI Publishing Setup Skill
Set up automated PyPI publishing with GitHub Actions for Python projects.
Purpose
Configure a complete PyPI publishing workflow that uses PyPI Trusted Publishers (no API tokens needed), automatic versioning with setuptools_scm, and TestPyPI for PR testing.
What Gets Created
This skill generates 6 files:
.github/workflows/release.yml- Manual releases via GitHub UI.github/workflows/test-pr.yml- Automatic TestPyPI publishing on PRs.github/workflows/_reusable-test-build.yml- Shared test/build logicscripts/calculate_version.sh- Version calculation scriptpyproject.toml- Build system configuration (if not exists)setup.py- Package metadata (if not exists)
Prerequisites
- Python project with source code
- GitHub repository
- Git initialized with at least one commit
Process
1. Gather Project Information
Extract or ask for the following:
From existing files (if they exist):
- Read
pyproject.tomlfor project name, description - Read
setup.pyfor package metadata - Detect Python version from
.python-version,pyproject.toml, or default to3.11
Ask user if not found:
- Package name: What should the PyPI package be called?
- If user has git configured, suggest:
{github-username}-{project-name} - Example:
hitoshura25-my-package
- If user has git configured, suggest:
- Author name: From
git config user.nameor ask - Author email: From
git config user.emailor ask - Description: Short one-line description
- URL: GitHub repository URL
- Command name (if CLI tool): What command should install the package?
- Example:
my-package→ users runmy-packageafter install - Can be same as package name or different
- Example:
Derive automatically:
- Import name: Package name with hyphens replaced by underscores
- Example:
hitoshura25-my-package→hitoshura25_my_package
- Example:
Configuration options:
- Python version: Default
3.11(or detect from project) - Test path: Default
.(current directory) or ask if tests/ exists - Verbose publish: Default
false
2. Create Project Files (If Needed)
If pyproject.toml doesn't exist, create it:
Read template: ./templates/pyproject.toml
Write to: pyproject.toml (project root)
No substitutions needed - this file is static.
If setup.py doesn't exist, create it:
Read template: ./templates/setup.py
Substitute variables:
{{PACKAGE_NAME}}→ Full package name (e.g.,hitoshura25-my-package){{AUTHOR}}→ Author name{{AUTHOR_EMAIL}}→ Author email{{DESCRIPTION}}→ Package description{{URL}}→ Project URL (GitHub){{COMMAND_NAME}}→ CLI command name (if applicable){{IMPORT_NAME}}→ Import name with underscores
Write to: setup.py (project root)
Note: If user doesn't want a CLI command, remove the entry_points section.
Create package directory and __init__.py:
mkdir -p {{IMPORT_NAME}}Create {{IMPORT_NAME}}/__init__.py:
"""{{PACKAGE_NAME}} package."""
__version__ = "0.1.0"If CLI command specified, create {{IMPORT_NAME}}/main.py:
"""Main module."""
def main():
"""Main entry point."""
print("Hello from {{PACKAGE_NAME}}!")
if __name__ == "__main__":
main()3. Create GitHub Workflows
Create .github/workflows/ directory:
mkdir -p .github/workflowsCreate release workflow:
Read template: ./templates/release.yml
Substitute variables:
{{PYTHON_VERSION}}→ Python version (e.g.,3.11){{TEST_PATH}}→ Test path (e.g.,.ortests/){{VERBOSE_PUBLISH}}→trueorfalse
Write to: .github/workflows/release.yml
Create PR test workflow:
Read template: ./templates/test-pr.yml
Substitute variables:
{{PYTHON_VERSION}}→ Python version{{TEST_PATH}}→ Test path{{VERBOSE_PUBLISH}}→trueorfalse
Write to: .github/workflows/test-pr.yml
Create reusable workflow:
Read template: ./templates/_reusable-test-build.yml
Substitute variables:
{{PYTHON_VERSION}}→ Python version{{TEST_PATH}}→ Test path
Write to: .github/workflows/_reusable-test-build.yml
4. Create Version Script
Create scripts/ directory:
mkdir -p scriptsRead template: ./templates/calculate_version.sh
No substitutions needed - this file is static.
Write to: scripts/calculate_version.sh
Make executable:
chmod +x scripts/calculate_version.sh5. User Instructions
After generating all files, provide instructions:
✓ PyPI publishing workflow created!
Files generated:
- .github/workflows/release.yml
- .github/workflows/test-pr.yml
- .github/workflows/_reusable-test-build.yml
- scripts/calculate_version.sh
- pyproject.toml (if new)
- setup.py (if new)
- {{IMPORT_NAME}}/__init__.py (if new)
Next steps:
1. Configure PyPI Trusted Publishers:
a) For PyPI (production):
- Go to: https://pypi.org/manage/account/publishing/
- Add publisher:
* Owner: {{GITHUB_USERNAME}}
* Repository: {{REPO_NAME}}
* Workflow: release.yml
* Environment: leave blank
b) For TestPyPI (PR testing):
- Go to: https://test.pypi.org/manage/account/publishing/
- Add publisher:
* Owner: {{GITHUB_USERNAME}}
* Repository: {{REPO_NAME}}
* Workflow: test-pr.yml
* Environment: leave blank
2. Commit and push the files:
git add .
git commit -m "Add PyPI publishing workflow"
git push
3. Create your first release:
- Go to: Actions → "Release to PyPI"
- Click "Run workflow"
- Select version bump (patch/minor/major)
- Click "Run workflow"
The workflow will:
- ✓ Run tests
- ✓ Build package
- ✓ Publish to PyPI
- ✓ Create git tag
- ✓ Create GitHub Release
4. Test PR workflow (optional):
- Create a PR
- Workflow will automatically publish to TestPyPI
- Version format: 1.0.0.dev{PR#}{RUN#}Validation
Before completing, verify:
# Check files exist
ls .github/workflows/release.yml
ls .github/workflows/test-pr.yml
ls .github/workflows/_reusable-test-build.yml
ls scripts/calculate_version.sh
ls pyproject.toml
ls setup.py
# Check script is executable
test -x scripts/calculate_version.sh && echo "✓ Script is executable"
# Check package directory
ls {{IMPORT_NAME}}/__init__.pyHow It Works
Version Management
Uses setuptools_scm for git-based versioning:
- Versions are calculated from git tags
- No need to manually update version numbers
- Format:
v1.2.3(tags) →1.2.3(PyPI package)
Release Workflow
Trigger: Manual via GitHub UI
Process:
- Calculate next version (patch/minor/major bump)
- Check tag doesn't already exist
- Run tests with calculated version
- Build package with version override
- Publish to PyPI (critical operation first)
- Create and push git tag (only after successful publish)
- Create GitHub Release
Fail-safe design: Tags only created after successful PyPI publish.
PR Testing Workflow
Trigger: Automatic on pull requests
Process:
- Calculate development version (1.2.3.dev{PR#}{RUN#})
- Run tests
- Build package
- Publish to TestPyPI
Benefits: Test publishing without affecting production PyPI.
Shared Test/Build Logic
The _reusable-test-build.yml workflow contains common logic:
- Checkout code
- Setup Python
- Install dependencies
- Run tests
- Build package
- Upload artifacts
Called by both release and PR workflows (DRY principle).
Troubleshooting
Tests not found:
- Check
test_pathis correct - Ensure tests exist in that directory
- Default
.runs tests from root
Version calculation fails:
- Ensure git has at least one commit
- Script defaults to
v0.0.0if no tags exist - Check script has execute permissions
setuptools_scm not detecting version:
- Ensure
.gitdirectory exists - Ensure
pyproject.tomlhas setuptools_scm config - Workflows use
SETUPTOOLS_SCM_PRETEND_VERSIONto override
PyPI publish fails:
- Check Trusted Publisher is configured correctly
- Workflow name must match exactly:
release.ymlortest-pr.yml - Environment field must be blank (not used)
Package name conflicts:
- PyPI has flat namespace - names must be globally unique
- Use prefix (like
username-package) to avoid conflicts - Check availability: https://pypi.org/project/{package-name}/
Configuration Options
All substitution variables:
{{PACKAGE_NAME}} - Full PyPI package name (with hyphens)
{{IMPORT_NAME}} - Python import name (with underscores)
{{AUTHOR}} - Author full name
{{AUTHOR_EMAIL}} - Author email
{{DESCRIPTION}} - One-line package description
{{URL}} - Project URL (usually GitHub)
{{COMMAND_NAME}} - CLI command name (optional)
{{PYTHON_VERSION}} - Python version (default: 3.11)
{{TEST_PATH}} - Path to tests (default: .)
{{VERBOSE_PUBLISH}} - true or false (default: false)
{{GITHUB_USERNAME}} - GitHub user or org name
{{REPO_NAME}} - Repository nameOutput Confirmation
✓ PyPI publishing configured
✓ 3 workflow files created
✓ 1 script file created
✓ Project files created/verified
✓ All files validated
✓ Instructions provided to userNext Steps
After setup:
- User configures Trusted Publishers
- User commits and pushes files
- User triggers first release
- Package is published to PyPI