Skip to content

Publish Release

Publish Release #8

Workflow file for this run

name: Publish Release
on:
workflow_dispatch:
inputs:
version:
description: 'Version to verify or publish, for example 0.6.0'
required: true
publish_pypi:
description: 'Publish the Python package to PyPI using Trusted Publisher'
required: true
type: boolean
default: false
publish_npm:
description: 'Publish the npm wrapper to npm using Trusted Publishing'
required: true
type: boolean
default: false
permissions:
contents: read
jobs:
verify:
name: Verify release package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
enable-cache: true
- name: Set up Python
run: uv python install 3.11
- name: Validate version metadata
run: |
python3 - <<'PY'
import json
import sys
import tomllib
from pathlib import Path
expected = "${{ inputs.version }}"
pyproject = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
python_version = pyproject["project"]["version"]
init_version = None
for line in Path("src/magicmd/__init__.py").read_text(encoding="utf-8").splitlines():
if line.startswith("__version__"):
init_version = line.split("=", 1)[1].strip().strip('"')
break
npm_version = json.loads(Path("npm/magicmd/package.json").read_text(encoding="utf-8"))["version"]
versions = {
"workflow input": expected,
"pyproject.toml": python_version,
"src/magicmd/__init__.py": init_version,
"npm/magicmd/package.json": npm_version,
}
mismatches = {source: version for source, version in versions.items() if version != expected}
if mismatches:
print("Version metadata mismatch:")
for source, version in versions.items():
print(f"- {source}: {version}")
sys.exit(1)
print(f"Version metadata verified: {expected}")
PY
- name: Install dependencies
run: uv sync --extra dev
- name: Run tests
run: uv run pytest -q
- name: Run ruff
run: uv run ruff check .
- name: Build Python package
run: uv build
- name: Check Python distributions
run: uvx twine check dist/*
- name: Check npm wrapper package
working-directory: npm/magicmd
run: npm pack --dry-run
- name: Upload Python distributions
uses: actions/upload-artifact@v4
with:
name: python-dist-${{ inputs.version }}
path: dist/*
if-no-files-found: error
publish-pypi:
name: Publish Python package to PyPI
if: ${{ inputs.publish_pypi }}
needs: verify
runs-on: ubuntu-latest
environment: pypi
permissions:
contents: read
id-token: write
steps:
- name: Download Python distributions
uses: actions/download-artifact@v4
with:
name: python-dist-${{ inputs.version }}
path: dist
- name: Publish Python package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
publish-npm:
name: Publish npm wrapper to npm
if: ${{ always() && inputs.publish_npm && needs.verify.result == 'success' && (needs.publish-pypi.result == 'success' || needs.publish-pypi.result == 'skipped') }}
needs:
- verify
- publish-pypi
runs-on: ubuntu-latest
environment: npm
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v6
with:
node-version: 24
registry-url: https://registry.npmjs.org
package-manager-cache: false
- name: Check npm wrapper package
working-directory: npm/magicmd
run: npm pack --dry-run
- name: Publish npm wrapper
working-directory: npm/magicmd
run: npm publish --registry=https://registry.npmjs.org --provenance