Skip to content

Latest commit

 

History

History
290 lines (205 loc) · 9 KB

File metadata and controls

290 lines (205 loc) · 9 KB

Release Process for lager-cli

This document is for maintainers who publish releases of the lager-cli package to PyPI.

If you are looking to contribute code, see CONTRIBUTING.md.

Prerequisites

  • Python 3.10+
  • build (pip install build)
  • twine (pip install twine)
  • A PyPI API token with upload access to the lager-cli project
  • Push access to lagerdata/lager on GitHub

Remote Setup

Contributors work from personal forks. By convention:

Remote Points to
origin Your fork (e.g. youruser/lager)
upstream Canonical repo (lagerdata/lager)
# One-time setup (after forking on GitHub)
git clone git@github.com:<youruser>/lager.git
cd lager
git remote add upstream git@github.com:lagerdata/lager.git
git fetch upstream

Contributing Code

  1. Create a feature branch from the latest upstream main:
    git fetch upstream
    git checkout -b my-feature upstream/main
  2. Make changes, commit, and push to your fork:
    git push -u origin my-feature
  3. Open a PR against the upstream repo:
    gh pr create --repo lagerdata/lager
  4. After review, the PR is merged into upstream main.

Pre-release Checklist

Before starting a release, verify:

  • All PRs merged to upstream main and CI is green
  • Tests pass against target hardware boxes
  • CHANGELOG.md has entries for all user-facing changes
  • SECURITY.md supported-version table matches the release you are about to cut
  • No open security issues that should block the release

Release Steps

Throughout this guide, replace X.Y.Z with the actual version number (e.g., 0.4.2).

1. Update Version Number

Edit the version in cli/__init__.py:

__version__ = 'X.Y.Z'

2. Update CHANGELOG.md

Add a new section at the top of CHANGELOG.md following the Keep a Changelog format:

## [X.Y.Z] - YYYY-MM-DD

### Added
- New features

### Changed
- Changes to existing functionality

### Fixed
- Bug fixes

### Removed
- Removed features

To see what changed since the last release, review the commit history:

git log upstream/main..HEAD --oneline --no-decorate

3. Create Release Notes File

Create docs/source/release-notes/vX.Y.Z.mdx:

---
title: "Version X.Y.Z"
description: "Month DD, YYYY"
---

## <u>Features</u>

- Feature description

## <u>Bug Fixes</u>

- Bug fix description

## <u>Improvements</u>

- Improvement description

## <u>Installation</u>

To install this version:

\`\`\`bash
pip install lager-cli==X.Y.Z
\`\`\`

To upgrade from a previous version:

\`\`\`bash
pip install --upgrade lager-cli
\`\`\`

## Resources

[View Release on PyPI](https://pypi.org/project/lager-cli/X.Y.Z/)

Categorization guidelines:

  • Features: New functionality, new commands, new device support
  • Bug Fixes: Fixes for issues, crashes, incorrect behavior
  • Improvements: Performance, code cleanup, minor enhancements

If this release drops support for an older minor version, also bump the table in SECURITY.md.

Terminology: Use "Lager Box" (not "gateway"), "Lager Boxes" (not "gateways").

4. Update Navigation

Add the new version to the top of the Release Notes list in docs/docs.json:

{
  "tab": "Release Notes",
  "groups": [
    {
      "group": "Version History",
      "pages": [
        "source/release-notes/vX.Y.Z",
        "source/release-notes/v0.4.2",
        ...
      ]
    }
  ]
}

5. Open a PR Against Upstream Main

The main branch requires all changes to go through a pull request (direct pushes are blocked). Create a release branch, push it to your fork, and open a PR:

git fetch upstream
git checkout main
git reset --hard upstream/main
git checkout -b release/vX.Y.Z
git add cli/__init__.py CHANGELOG.md docs/source/release-notes/vX.Y.Z.mdx docs/docs.json
git commit -m "vX.Y.Z"
git push -u origin release/vX.Y.Z
gh pr create --repo lagerdata/lager --base main --title "vX.Y.Z"

After the PR is reviewed and approved, merge it using Squash and merge or Rebase and merge — the branch requires linear history, so standard merge commits are not allowed.

6. Tag the Release

Create an annotated tag on main:

git tag -a vX.Y.Z -m "vX.Y.Z"
git push upstream vX.Y.Z

7. Wait for Tag Validation, Then Download the Artifact

Pushing the tag triggers two workflows in parallel:

  • Release: Validate Tag (.github/workflows/release-validation.yml) — builds the sdist and wheel from the tagged commit, runs twine check, installs each into a clean venv, asserts the installed lager --version equals the tag, import-walks the installed package against tools/packaging_import_baseline.txt, and uploads the result as a workflow artifact named dist-vX.Y.Z (kept 90 days).

  • Release: Publish Box Image (.github/workflows/box-image-publish.yml) — builds box/lager/docker/box.Dockerfile and pushes box/lager/docker/box.Dockerfile and pushes ghcr.io/lagerdata/lager-box:vX.Y.Z (and :X.Y.Z), labelled with the tag and commit it was built from. lager update --pull --version vX.Y.Z fetches that image by digest instead of building on the box; without --pull (the default while this soaks) nothing consumes it. For a box to pull it anonymously the package must be public — set that once in the GitHub UI after the first successful publish (Packages → lager-box → Package settings).

    The org.opencontainers.image.version label is what the client checks before deploying a pulled image. If this workflow is ever changed such that the label stops matching the tag, every box silently falls back to building — slower, but never wrong.

Wait for Validate Tag to go green, then download the artifact:

gh run list --repo lagerdata/lager --workflow release-validation.yml --limit 1
gh run download <run-id> --repo lagerdata/lager --name dist-vX.Y.Z --dir dist

Do not rebuild locally. The artifact is the set of bytes the validation proved; a local rebuild is a different, unproven build. (If the workflow is red, the tag has a real problem -- fix it before anything reaches PyPI.)

A red box-image publish does not block the PyPI upload — no box depends on that image yet — but fix it before cutting the next release.

8. Upload to PyPI

Upload the downloaded artifact:

twine upload dist/*

When prompted, use __token__ as the username and your PyPI API token as the password.

9. Create GitHub Release

Create a file with the release notes (copy the relevant section from CHANGELOG.md):

gh release create vX.Y.Z --repo lagerdata/lager --title "vX.Y.Z" --notes-file release-notes.md

Alternatively, go to Releases and create a new release manually:

  • Tag: Select the vX.Y.Z tag
  • Title: vX.Y.Z
  • Description: Copy the relevant section from CHANGELOG.md

Pinning: Boxes pin to a release via its taglager update --version vX.Y.Z. The CLI also accepts the bare form X.Y.Z and resolves it to the vX.Y.Z tag. Do not create a per-version branch; tags are the single source of truth for pinned versions.

Post-release

  1. Verify the release is live:
    pip install lager-cli==X.Y.Z
    lager --version
  2. Sync your fork with upstream:
    git fetch upstream
    git checkout main
    git reset --hard upstream/main
    git push origin main

Versioning Policy

This project follows Semantic Versioning:

  • MAJOR (X): Breaking changes to CLI commands or Python API
  • MINOR (Y): New features, new device/instrument support
  • PATCH (Z): Bug fixes, documentation, minor improvements

Note: Releases are identified by tags (vX.Y.Z). Older releases also had a matching X.Y.Z branch used for box pinning; these are deprecated — lager update/lager install now resolve a X.Y.Z pin to the vX.Y.Z tag, so version branches are no longer created. Any remaining X.Y.Z branches can be recreated from the tag if ever needed (git push origin vX.Y.Z^{}:refs/heads/X.Y.Z).

Troubleshooting

python -m build fails: Make sure build is installed (pip install build) and you are in the cli/ directory. (Local builds are only needed for debugging; releases use the CI artifact from step 7.)

twine upload fails: Verify your PyPI API token is valid and the version does not already exist on PyPI. Check that the package name is lager-cli.

Import errors during build: Ensure all dependencies listed in cli/setup.py are available in your environment.

Tag already exists: If you need to re-tag (e.g., after a fix), delete the old tag first: git tag -d vX.Y.Z && git push upstream :refs/tags/vX.Y.Z. Only do this if the release has not been published to PyPI.