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
3 changes: 3 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
# Full history and tags so setuptools-scm can derive the version.
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-python@v5
with:
Expand Down
86 changes: 86 additions & 0 deletions .github/workflows/release-slack.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Release Announcement

on:
push:
tags:
- "libby-[0-9]*"
workflow_dispatch:
inputs:
tag:
description: "Existing release tag to announce, e.g. libby-0.1.0"
required: true
type: string

permissions:
contents: read

jobs:
announce:
name: Post release notes to Slack
runs-on: ubuntu-latest

env:
TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref_name }}

steps:
- name: Check out tag
uses: actions/checkout@v4
with:
ref: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || github.ref }}
fetch-depth: 0

# actions/checkout can leave the tag as a lightweight ref with no
# message, so fetch the annotated tag object explicitly.
- name: Fetch annotated tag
run: |
case "${TAG}" in
libby-[0-9]*) ;;
*) echo "::error::${TAG} does not look like libby-X.Y.Z"; exit 1 ;;
esac
git fetch --force origin "refs/tags/${TAG}:refs/tags/${TAG}"
if [ "$(git cat-file -t "refs/tags/${TAG}")" != "tag" ]; then
echo "::error::${TAG} is not an annotated tag; nothing to announce"
exit 1
fi
echo "VERSION=${TAG#libby-}" >> "${GITHUB_ENV}"

# The package version is derived from the tag by setuptools-scm, so make
# sure it reads this tag the way we expect before announcing it.
- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Check package version matches tag
run: |
pip install --quiet "setuptools-scm>=8"
scm_version="$(python -m setuptools_scm)"
echo "setuptools-scm version: ${scm_version}"
if [ "${scm_version}" != "${VERSION}" ]; then
echo "::error::Tag ${TAG} gives package version ${scm_version}, expected ${VERSION}"
exit 1
fi

# The release notes are the tag message itself. Slack treats &, < and >
# as markup, so escape them in the notes.
- name: Compose Slack message
run: |
git for-each-ref "refs/tags/${TAG}" --format='%(contents)' > release-notes.txt
jq -n --rawfile notes release-notes.txt \
--arg version "${VERSION}" \
--arg tag "${TAG}" \
--arg url "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/tree/${TAG}" \
'{ text: ( "*Libby \($version) released*\n\n"
+ ($notes | sub("\n+$"; "") | gsub("&"; "&amp;") | gsub("<"; "&lt;") | gsub(">"; "&gt;"))
+ "\n\n<\($url)|View \($tag) on GitHub>" ) }' > slack-payload.json
cat slack-payload.json

- name: Post to Slack
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
run: |
if [ -z "${SLACK_WEBHOOK_URL}" ]; then
echo "::error::SLACK_WEBHOOK_URL secret is not set"
exit 1
fi
curl -fsS -X POST -H 'Content-type: application/json' \
--data @slack-payload.json "${SLACK_WEBHOOK_URL}"
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
# Full history and tags so setuptools-scm can derive the version.
- uses: actions/checkout@v4
with:
fetch-depth: 0

- uses: actions/setup-python@v5
with:
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
try:
release = _pkg_version("libby")
except PackageNotFoundError:
release = "0.1.0"
release = "unknown"
version = release

# -- General configuration ----------------------------------------------------
Expand Down
1 change: 1 addition & 0 deletions docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ cli
daemon
keygrabber
telemetry
releasing
api/index
```

Expand Down
54 changes: 54 additions & 0 deletions docs/source/releasing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Releasing

A libby release is an annotated git tag named `libby-MAJOR.MINOR.PATCH`, for
example `libby-0.2.0`. Pushing the tag does two things:

- The package version becomes `0.2.0`. libby has no version number written in
the source: [setuptools-scm](https://setuptools-scm.readthedocs.io/) reads it
from the latest `libby-*` tag. A commit after a tag installs as a dev version
such as `0.2.1.dev3+g1a2b3c4`.
- The **Release Announcement** GitHub Actions workflow posts the tag message to
Slack as the release notes.

## Cutting a release

```bash
git switch main && git pull
git log --oneline libby-0.1.0..HEAD # what changed since the last release
git tag -a libby-0.2.0 # your editor opens; write the release notes
git push origin libby-0.2.0
```

The tag message is posted to Slack word for word, under the heading
*Libby 0.2.0 released*. Slack formatting works in it: `*bold*`, `_italic_`,
`` `code` `` and `•` bullets.

Example tag message:

```text
• Add optional OpenTelemetry tracing and request metrics (#44)
• Add the telemetry backend: pinned releases, configs and unit templates (#48)
• Remove the libby argument from LibbyDaemon (#49)
```

## Rules

- Always use an annotated tag (`git tag -a`). A lightweight tag has no
message, so the workflow fails and nothing is posted.
- Use exactly three numbers after `libby-`. Before posting, the workflow checks
that setuptools-scm reads the tag as the same version, and it fails if not.
- Never move or reuse a tag once it is pushed. If a release is wrong, tag a new
patch version.

## Re-posting an announcement

If the Slack post failed, for example because the webhook secret was missing, go
to **Actions → Release Announcement → Run workflow** and enter the existing tag
(e.g. `libby-0.2.0`).

## One-time setup

The workflow posts through a Slack
[incoming webhook](https://api.slack.com/messaging/webhooks). The webhook URL
must be stored as the repository secret `SLACK_WEBHOOK_URL` (Settings → Secrets
and variables → Actions).
13 changes: 11 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[build-system]
requires = ["setuptools>=68", "wheel"]
requires = ["setuptools>=68", "setuptools-scm>=8", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "libby"
version = "0.1.0"
dynamic = ["version"]
description = "Libby: a tiny messaging library which uses bamboo with a ZMQ transport"
readme = "README.md"
requires-python = ">=3.9"
Expand Down Expand Up @@ -43,6 +43,15 @@ Homepage = "https://caltechopticalobservatories.github.io/libby/"
Repository = "https://github.com/CaltechOpticalObservatories/libby"
"Bug Tracker" = "https://github.com/CaltechOpticalObservatories/libby/issues"

# The version comes from the most recent libby-X.Y.Z git tag (see
# docs/source/releasing.md); untagged commits get X.Y.(Z+1).devN+g<sha>.
[tool.setuptools_scm]
tag_regex = "^libby-(?P<version>\\d+\\.\\d+\\.\\d+.*)$"
fallback_version = "0.0.0+unknown"

[tool.setuptools_scm.scm.git]
describe_command = "git describe --dirty --tags --long --match 'libby-*'"

[tool.setuptools.packages.find]
where = ["."]
include = ["libby", "libby.*"]
Expand Down
Loading