Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Publish to PyPI

on:
release:
types: [published]
workflow_dispatch: # Allow manual triggering
inputs:
target:
description: 'Target repository'
required: true
default: 'testpypi'
type: choice
options:
- testpypi
- pypi

jobs:
publish:
name: Publish to PyPI
runs-on: ubuntu-latest
environment:
name: ${{ github.event.inputs.target || 'pypi' }}
url: ${{ github.event.inputs.target == 'testpypi' && 'https://test.pypi.org/project/toady-cli/' || 'https://pypi.org/project/toady-cli/' }}

permissions:
id-token: write # For trusted publishing
contents: read

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true

- name: Install dependencies
run: |
uv sync --all-extras

- name: Run tests
run: |
make test-fast

- name: Build package
run: |
make build

- name: Check package
run: |
uv run twine check dist/*

- name: Publish to TestPyPI
if: github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'testpypi'
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
password: ${{ secrets.TEST_PYPI_API_TOKEN }}

- name: Publish to PyPI
if: github.event_name == 'release' && github.event.action == 'published'
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}

- name: Create release comment
if: github.event_name == 'release'
uses: actions/github-script@v7
with:
script: |
const { data: release } = await github.rest.repos.getReleaseByTag({
owner: context.repo.owner,
repo: context.repo.repo,
tag: context.ref.replace('refs/tags/', '')
});

await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: release.id,
body: `🎉 Package published to PyPI!\n\n📦 Install: \`pip install toady-cli\`\n🔗 View: https://pypi.org/project/toady-cli/`
});
42 changes: 42 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Include license and documentation
include LICENSE
include README.md
include CHANGELOG.md

# Include configuration for packaging
include pyproject.toml

# Include type hints marker
include src/toady/py.typed

# Exclude development and configuration files
exclude .env.example
exclude .pre-commit-config.yaml
exclude Makefile
exclude mypy.ini
exclude pytest.ini
exclude requirements.txt
exclude uv.lock

# Exclude development directories
recursive-exclude .cursor *
recursive-exclude .roo *
recursive-exclude .taskmaster *
recursive-exclude tests *
recursive-exclude scripts *
recursive-exclude docs *
recursive-exclude htmlcov *
recursive-exclude build *
recursive-exclude dist *

# Exclude cache and temporary files
global-exclude *.pyc
global-exclude *.pyo
global-exclude *.orig
global-exclude *.rej
global-exclude *~
global-exclude __pycache__
global-exclude .pytest_cache
global-exclude .coverage
global-exclude coverage.xml
global-exclude coverage.json
62 changes: 62 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.PHONY: help install install-dev test test-fast test-integration test-performance test-analysis
.PHONY: lint format format-check type-check pre-commit check check-fast fix-check clean build
.PHONY: sync lock update add remove deps-check shell run
.PHONY: publish-test publish check-publish setup-publish

# Single source of truth: ALL commands use uv
export PATH := $(HOME)/.local/bin:$(PATH)
Expand Down Expand Up @@ -44,6 +45,12 @@ help:
@echo " make shell Open shell with project dependencies loaded"
@echo " make run ARGS='...' Run toady CLI in development mode"
@echo ""
@echo "📦 Publishing:"
@echo " make setup-publish Set up PyPI credentials (one-time setup)"
@echo " make check-publish Check package for PyPI compliance"
@echo " make publish-test Publish to TestPyPI for testing"
@echo " make publish Publish to production PyPI"
@echo ""
@echo "🧹 Maintenance:"
@echo " make clean Remove build artifacts and cache files"
@echo " make build Build distribution packages"
Expand Down Expand Up @@ -180,3 +187,58 @@ clean:
build: clean
@echo "📦 Building distribution packages..."
uv build

## Publishing (PyPI Distribution)
setup-publish:
@echo "🔐 Setting up PyPI publishing credentials..."
@echo ""
@echo "1. Register accounts (if you haven't already):"
@echo " • TestPyPI: https://test.pypi.org/account/register/"
@echo " • PyPI: https://pypi.org/account/register/"
@echo ""
@echo "2. Create API tokens:"
@echo " • TestPyPI: https://test.pypi.org/manage/account/token/"
@echo " • PyPI: https://pypi.org/manage/account/token/"
@echo ""
@echo "3. Configure credentials in ~/.pypirc:"
@echo " [distutils]"
@echo " index-servers = pypi testpypi"
@echo ""
@echo " [pypi]"
@echo " username = __token__"
@echo " password = pypi-YOUR_PRODUCTION_TOKEN_HERE"
@echo ""
@echo " [testpypi]"
@echo " repository = https://test.pypi.org/legacy/"
@echo " username = __token__"
@echo " password = pypi-YOUR_TEST_TOKEN_HERE"
@echo ""
@echo "4. Alternatively, set environment variables:"
@echo " export TWINE_USERNAME=__token__"
@echo " export TWINE_PASSWORD=pypi-YOUR_TOKEN_HERE"

check-publish: build
@echo "🔍 Checking package for PyPI compliance..."
uv run twine check dist/*
@echo "✅ Package passed PyPI compliance checks!"

publish-test: check-publish
@echo "🧪 Publishing to TestPyPI..."
@echo "⚠️ This will upload version $(shell grep '^version = ' pyproject.toml | cut -d'"' -f2) to TestPyPI"
@read -p "Continue? (y/N) " confirm && [ "$$confirm" = "y" ] || exit 1
uv run twine upload --repository testpypi dist/*
@echo ""
@echo "✅ Published to TestPyPI!"
@echo "🔗 View at: https://test.pypi.org/project/toady-cli/"
@echo "📦 Test install: pip install --index-url https://test.pypi.org/simple/ toady-cli"

publish: check-publish
@echo "🚀 Publishing to production PyPI..."
@echo "⚠️ WARNING: This will publish version $(shell grep '^version = ' pyproject.toml | cut -d'"' -f2) to PRODUCTION PyPI"
@echo "⚠️ This action CANNOT be undone!"
@read -p "Are you absolutely sure? (y/N) " confirm && [ "$$confirm" = "y" ] || exit 1
uv run twine upload dist/*
@echo ""
@echo "🎉 Published to PyPI!"
@echo "🔗 View at: https://pypi.org/project/toady-cli/"
@echo "📦 Install: pip install toady-cli"
Loading
Loading