Skip to content
Open
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
52 changes: 52 additions & 0 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
name: release
description: Cut a versioned release and publish everos to PyPI via the tag-triggered workflow
---

# /release

Publish a new version of `everos` to PyPI. Publishing is automated: pushing a
`vX.Y.Z` tag triggers [.github/workflows/release.yml](../../../.github/workflows/release.yml),
which builds, smoke-tests, and uploads via PyPI **Trusted Publishing** (OIDC —
no stored token) behind the `release` environment's manual-approval gate.

## Preconditions

- On `main`, up to date, with green CI (the tag builds from `main`'s tree).
- Decide the version per SemVer: patch = fixes, minor = back-compatible
features, major = breaking changes.

## Steps

```
1. Bump the version → pyproject.toml [project] version = "X.Y.Z"
(single source; everos.__version__ reads installed package metadata)
2. Update CHANGELOG.md → move the Unreleased entries under a new
## [X.Y.Z] - <date> heading
3. Commit → git commit -m "chore(release): vX.Y.Z"
4. Open a PR, merge to main after green CI
5. Tag main + push → git tag -a vX.Y.Z -m "vX.Y.Z" && git push origin vX.Y.Z
6. Approve → the release.yml run pauses on the `release`
environment; a reviewer approves in the Actions run
7. Verify → https://pypi.org/project/everos/X.Y.Z/
```

The tag must equal the `pyproject.toml` version — the workflow refuses to
publish on a mismatch.

## Pre-releases

PEP 440 pre-release tags publish too (PyPI accepts them; `pip install everos`
ignores them unless `--pre`): `vX.Y.ZrcN`, `vX.Y.ZaN`, `vX.Y.ZbN`. Set the same
suffix in `pyproject.toml` version before tagging.

## One-time setup (project owner, not doable from CI)

1. **PyPI trusted publisher** — PyPI → project `everos` → Settings →
Publishing → add: owner `EverMind-AI`, repo `EverOS`, workflow
`release.yml`, environment `release`.
2. **GitHub environment** — repo Settings → Environments → create `release`
with required reviewers, so every publish needs a manual approval.

No PyPI API token is ever stored; the workflow mints a short-lived OIDC token
at publish time.
56 changes: 56 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Publishes to PyPI via Trusted Publishing (OIDC) — no stored token.
# Fires on a version tag (vX.Y.Z, plus PEP 440 pre-releases vX.Y.ZrcN / aN / bN).
# The `release` environment gates the upload behind manual approval; configure
# required reviewers under Settings → Environments → release.
#
# One-time setup on PyPI (project owner, cannot be done from CI):
# PyPI → project `everos` → Settings → Publishing → add a GitHub trusted
# publisher: owner=EverMind-AI, repo=EverOS, workflow=release.yml,
# environment=release.
name: Release

on:
push:
tags:
# vX.Y.Z plus PEP 440 pre-release/dev suffixes (rc1 / a1 / b1 / .dev1).
- "v[0-9]+.[0-9]+.[0-9]+*"

permissions:
contents: read

jobs:
publish:
name: build + publish to PyPI
runs-on: ubuntu-latest
environment: release
permissions:
id-token: write # OIDC token for PyPI Trusted Publishing
contents: read
steps:
- uses: actions/checkout@v6

- name: Install uv
uses: astral-sh/setup-uv@v8.2.0
with:
enable-cache: true
cache-dependency-glob: uv.lock

- name: Set up Python
run: uv python install 3.12

# Guard: the tag must match the package version, so a mistyped tag can't
# publish the wrong release.
- name: Verify tag matches pyproject version
run: |
tag="${GITHUB_REF_NAME#v}"
pkg="$(grep -m1 -E '^version = ' pyproject.toml | sed -E 's/^version = "(.+)"/\1/')"
if [ "$tag" != "$pkg" ]; then
echo "::error::tag v$tag != pyproject version $pkg"
exit 1
fi

- name: Build + smoke-test the package
run: make package # builds sdist+wheel into dist/ and import-smokes it

- name: Publish to PyPI (Trusted Publishing)
uses: pypa/gh-action-pypi-publish@release/v1
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Detailed: [docs/architecture.md](docs/architecture.md).
## Engineering practices

- **Coding rules** auto-loaded from [.claude/rules/](.claude/rules/) (10 rules; the three always-loaded ones cover architecture / code-style / language-policy, the rest are path-scoped and load when Claude Code opens a matching file)
- **Workflows** as slash commands in [.claude/skills/](.claude/skills/) — `/commit`, `/new-branch`, `/pr`
- **Workflows** as slash commands in [.claude/skills/](.claude/skills/) — `/commit`, `/new-branch`, `/pr`, `/release`
- **Project-level decisions** in [docs/](docs/) (low-frequency, human-judgment-required)
- **Language policy**: the project targets a global audience — docs and code are English; CJK appears only in test fixtures and locale-suffixed mirrors. Scanned by `make check-cjk`.
- **Datetime discipline**: never call `datetime.now()` / `time.time()` directly — use `everos.component.utils.datetime`. Enforced by `make check-datetime`.
Expand Down