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
47 changes: 47 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Releasing OpenYXDB

Standard procedure for cutting an OpenYXDB release and publishing to PyPI.

## Steps

1. **Bump the version.** Update `version` in `pyproject.toml`.
2. **Update the changelog.** Move the `[Unreleased]` notes in
[CHANGELOG.md](CHANGELOG.md) under a new `## [X.Y.Z] - YYYY-MM-DD`
heading.
3. **Commit.** `release: vX.Y.Z` (see `git log` for the established style).
Push the commit to `main` (directly or via PR) and let CI run on it.
4. **Confirm CI is green before tagging.** `publish.yml` triggers directly
on `push: tags: ["v*"]` and has no way to depend on `ci.yml` passing -
GitHub Actions can't `needs:` a job defined in another workflow file.
So this has to be checked by hand, before the tag exists, not after:

```sh
scripts/check-release-ready.sh main
```

This resolves the ref to a commit and checks that the most recent
`ci.yml` run for that commit completed successfully. It refuses (exit 1)
if CI hasn't run yet or didn't pass. Run it against the exact commit
you're about to tag.

5. **Tag.** Once the check passes, create an annotated tag on that commit
and push it:

```sh
git tag -a vX.Y.Z -m "vX.Y.Z"
git push origin vX.Y.Z
```

The tag push triggers `publish.yml`, which builds the sdist and wheels
(Linux, macOS, Windows) and publishes to PyPI via trusted publishing.
6. **Verify.** Watch the run (`gh run watch` or the Actions tab) and check
the new version shows up on [PyPI](https://pypi.org/project/openyxdb/).

## Notes

- Not every version bump has to be tagged/published immediately; a
version can be bumped and committed to `main` without a corresponding
tag if the release itself isn't ready to ship yet. Only a pushed `vX.Y.Z`
tag triggers a publish.
- This repo has no `audit.yml`, so `check-release-ready.sh` only checks
`ci.yml`.
59 changes: 59 additions & 0 deletions scripts/check-release-ready.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# Refuse to say a commit is release-ready unless the most recent ci.yml run
# for that commit completed successfully.
#
# publish.yml triggers directly on `push: tags: ["v*"]` and GitHub Actions
# has no way for one workflow file to `needs:` a job defined in a separate
# workflow file, so this check has to run before the tag is created (see
# RELEASING.md and https://github.com/Sigilweaver/OpenYXDB/issues/1).
#
# This repo has no audit.yml, so only ci.yml is checked.
#
# Usage: scripts/check-release-ready.sh [ref]
# ref defaults to HEAD.

set -euo pipefail

ref="${1:-HEAD}"
# `^{commit}` peels annotated tags to the commit they point at; git rev-parse
# on a bare tag name otherwise returns the tag object's own SHA, which never
# has a CI run against it.
sha="$(git rev-parse "${ref}^{commit}")"

echo "Checking release readiness for $ref ($sha)..."

check_workflow() {
local workflow="$1"
local runs
runs="$(gh run list -w "$workflow" -c "$sha" --json status,conclusion,url -L 1)"

if [[ "$(echo "$runs" | jq 'length')" -eq 0 ]]; then
echo "FAIL: no run of $workflow found for commit $sha" >&2
return 1
fi

local status conclusion url
status="$(echo "$runs" | jq -r '.[0].status')"
conclusion="$(echo "$runs" | jq -r '.[0].conclusion')"
url="$(echo "$runs" | jq -r '.[0].url')"

if [[ "$status" != "completed" ]]; then
echo "FAIL: latest $workflow run for $sha has not completed (status=$status) - $url" >&2
return 1
fi

if [[ "$conclusion" != "success" ]]; then
echo "FAIL: latest $workflow run for $sha did not succeed (conclusion=$conclusion) - $url" >&2
return 1
fi

echo "OK: $workflow succeeded for $sha - $url"
return 0
}

if check_workflow "ci.yml"; then
echo "Release ready: ci.yml is green for $sha"
exit 0
fi

exit 1
Loading