Spaces:
Build error
Description
Implement unified CI/CD release automation with semantic versioning across all three packages:
- Python (headroom-ai) β pip package on PyPI
- TypeScript SDK (headroom-ai) β npm package on npmjs.org
- OpenClaw plugin (headroom-openclaw) β npm package on npmjs.org and GitHub Package Registry
Currently the three packages are independently versioned (0.5.25 / 0.1.0 / 0.1.0). This PR introduces a single-source-of-truth version in pyproject.toml that propagates to all packages on every release, driven by conventional commit messages.
Fixes #(issue number)
Type of Change
- Bug fix (non-breaking change that fixes an issue)
- New feature (non-breaking change that adds functionality)
- Breaking change (fix or feature that would cause existing functionality to change)
- Documentation update
- Performance improvement
- Code refactoring (no functional changes)
Changes Made
New Files
Scripts:
scripts/version-sync.pyβ Reads version frompyproject.toml, updates all 4 version files. Supports--version X.Y.Zand--bump {major,minor,patch}.scripts/changelog-gen.pyβ Parses conventional commits since last tag, groups by type, generates markdown changelog with breaking change detection.scripts/verify-versions.pyβ Pre-release sanity check that all 4 version files are in sync.scripts/tests/test_version_sync.pyβ 5 tests for version-sync.pyscripts/tests/test_changelog_gen.pyβ 23 tests for changelog-gen.py
Workflows:
.github/workflows/release.ymlβ Unified release pipeline: detect β build β publish-pypi β publish-npm β publish-github-packages β create-release.commitlintrc.jsonβ Conventional commit enforcement via@commitlint/config-conventional
Local Testing (act):
.actrcβ Defaultactflags (Ubuntu runner, reuse, quiet).github/act/dry-run.jsonβactevent file for dry-run testing.github/act/push-feat.jsonβactevent file for simulating a feat commit.actrc.local.exampleβ Local override template foract.env.act.exampleβ Secrets documentation template foractlocal testing
Documentation:
docs/content/docs/releases.mdxβ Full documentation for the release pipeline, testing guide, and configuration reference
Modified Files
.github/workflows/ci.ymlβ Addedcommitlintjob to enforce conventional commits.github/workflows/publish.ymlβ Changed fromreleasetrigger toworkflow_dispatchonly (superseded byrelease.yml).github/workflows/release.ymlβ Rewritten with canonical+commit-height algorithm (no more commit loop).gitignoreβ Added!scripts/version-sync.py,!scripts/changelog-gen.py,!scripts/verify-versions.py,!scripts/tests/,.env.act,.actrc.local
Testing
- Unit tests pass (
pytest)scripts/tests/test_version_sync.pyβ 5/5 passingscripts/tests/test_changelog_gen.pyβ 23/23 passing
- Linting passes (
ruff check .) - Type checking passes (
mypy headroom) β pre-existing issue inheadroom/cli/wrap.py:487(unrelated) - New tests added for new functionality
- Workflow tested with
act(dry-run passes all jobs through build step β no infinite loop)
Algorithm Validation
The canonical+commit-height algorithm was validated with test cases:
- Canonical
0.5.25, no prior tag,feat:commit β git tagv0.6.0.0, npm0.6.0β - Canonical
0.5.25, tagv0.5.25.2,fix:commit β git tagv0.5.25.3, npm0.5.26β - Canonical
0.5.25, no prior tag,fix:commit β git tagv0.5.25.0, npm0.5.25β - Manual override
1.2.3β git tagv1.2.3, npm1.2.3β
Test Output
scripts/tests/test_version_sync.py .....
scripts/tests/test_changelog_gen.py .......................
Checklist
- My code follows the project's style guidelines
- I have performed a self-review of my code
- I have commented my code, particularly in hard-to-understand areas
- My changes generate no new warnings
- I have added tests that prove my fix is effective or that my feature works
- New and existing unit tests pass locally with my changes
- I have made corresponding changes to the documentation
- I have updated the CHANGELOG.md if applicable
Additional Notes
Version Bump Logic
Canonical + Commit Height Algorithm β The workflow NEVER commits back to the repo. pyproject.toml is the canonical source of truth, updated manually before merging.
| Commit | Bump | Git Tag | npm Version |
|---|---|---|---|
fix:, ci:, chore:, perf:, refactor: |
patch | v0.5.25.3 |
0.5.26 |
feat: |
minor | v0.6.0.0 |
0.6.0 |
feat!: or feat: + BREAKING CHANGE body |
major | v1.0.0.0 |
1.0.0 |
The git tag uses v{canonical}.{height} (e.g., v0.5.25.3 = 3 commits since canonical 0.5.25). npm versions use 3-part semver, bumped from canonical.
Package Publishing Targets
| Package | Target | Status |
|---|---|---|
headroom-ai (Python) |
PyPI | β
via pypa/gh-action-pypi-publish |
headroom-ai (TypeScript SDK) |
npmjs.org | β
via npm publish |
headroom-openclaw |
npmjs.org | β
via npm publish |
headroom-openclaw |
GitHub Package Registry | β
via npm publish --registry npm.pkg.github.com |
Safety Gates
Each publish job requires both dry_run != 'true' and the corresponding skip variable not set:
| Variable | Effect |
|---|---|
PYPI_SKIP=true |
Skip PyPI publish |
NPM_SKIP=true |
Skip both npm publishes |
GH_PACKAGES_SKIP=true |
Skip GitHub Package Registry publish |
Set in: GitHub repo β Settings β Variables β Actions Variables.
Workflow Triggers
- Auto: On push to
mainβ analyzes latest commit, bumps version, builds, publishes, creates GitHub Release - Manual:
workflow_dispatchwith optionalversionoverride anddry_runflag - Paths ignore: Skips runs when only
docs/,.github/workflows/ci.yml,.github/workflows/publish.yml,scripts/,.commitlintrc.json,.actrc,.github/act/, or.env.act.examplechange
Local Testing
# Install act
winget install act
# Dry-run (no publishes)
act -W .github/workflows/release.yml -e .github/act/dry-run.json
# Test feat: commit (minor bump)
act -W .github/workflows/release.yml -e .github/act/push-feat.json
Required GitHub Secrets
| Secret | Purpose |
|---|---|
NPM_TOKEN |
Publishing to npmjs.org |
GITHUB_TOKEN |
GitHub Package Registry (auto-provided by GitHub Actions) |
PyPI uses trusted publisher OIDC β no secret required, only the pypi GitHub Environment must be configured.
First Release Note
The TypeScript packages are currently at 0.1.0 while Python is at 0.5.25. The first release will align all three to the same version. Update pyproject.toml to the desired canonical version before merging, then use workflow_dispatch with a manual version input to set the target explicitly.
After each release, update pyproject.toml to match the published version to keep the canonical current and ensure unique git tags.
Parameterized Configuration
All package names and registries are top-level env constants in release.yml:
env:
PYPI_PACKAGE: headroom-ai
PYPI_ENVIRONMENT: pypi
NPM_REGISTRY_URL: https://registry.npmjs.org
NPM_SDK_PACKAGE: headroom-ai
NPM_OPENCLAW_PACKAGE: headroom-openclaw
GITHUB_PACKAGES_REGISTRY_URL: https://npm.pkg.github.com