Release Process Skill
Overview
Lichtblick has two release tracks:
- Stable releases — triggered automatically when a merged pull request into
main comes from a release/* or hotfix/* branch. This runs .github/workflows/release.yml, which creates the GitHub Release and then fan-outs into post-release publishing and main→develop sync via GitHub's release: released event.
- Manual pre-release / RC builds — triggered manually through
.github/workflows/prerelease.yml (workflow_dispatch only). This builds the same production artifacts and, when create_release: true, creates a GitHub Pre-release through ncipollo/release-action with prerelease: true. Because GitHub's release event fires prereleased (not released) for pre-releases, this does not trigger post-release.yml or release-sync.yml — RC builds are never auto-published to NPM/GHCR or auto-synced to develop.
The stable and RC flows share the same artifact packaging shape, but only the stable flow performs the in-repo version bump, commit, and tag push to main.
Branch Naming -> Version Bump Mapping
The version bump is computed by regex in the bump_type step of .github/workflows/release.yml.
| Branch prefix pattern |
Bump type |
Example |
hotfix/* |
patch |
hotfix/fix-crash |
release/major or release/major/* |
major |
release/major/v2.0.0 |
release/minor or release/minor/* |
minor |
release/minor/v1.5.0 |
Any other branch prefix causes the workflow to fail with an explicit error.
Stable Release Pipeline (release.yml)
Trigger condition:
on:
pull_request:
types: [closed]
branches:
- main
jobs:
release:
if: |
github.event.pull_request.merged == true &&
(startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/'))
Step order:
- Check out the repository on
main.
- Set up Node.js 24 and enable Yarn via Corepack.
- Install dependencies with
yarn install --immutable.
- Determine the bump type from the source branch name:
hotfix/* -> patch
release/major(/.*)? -> major
release/minor(/.*)? -> minor
- anything else -> fail
- Bump the root version with
yarn version <type>.
- Bump
packages/suite/package.json with yarn version <type>.
- Read the new version from the root
package.json.
- Update
sonar-project.properties so sonar.projectVersion matches the new version.
- Commit the version files and tag
main as v${version}, then push main and tags.
- Build the production desktop and web bundles:
yarn desktop:build:prod
yarn web:build:prod
- Package release binaries for Windows, Linux, and macOS:
yarn package:win
yarn package:linux
yarn package:darwin
- Create the web static tarball at
dist/lichtblick-web.tar.gz.
- Create the GitHub Release with
ncipollo/release-action@v1.
- Trigger
sonarqube.yml on main via gh workflow run.
Release artifacts:
dist/lichtblick-${version}-linux-amd64.deb
dist/lichtblick-${version}-linux-x64.tar.gz
dist/lichtblick-${version}-linux-arm64.deb
dist/lichtblick-${version}-linux-arm64.tar.gz
dist/lichtblick-${version}-mac-universal.dmg
dist/lichtblick-${version}-win.exe
dist/lichtblick-web.tar.gz
dist/latest-linux.yml
dist/latest-mac.yml
dist/latest.yml
Post-Release Publishing (post-release.yml)
Trigger:
release: types: [released] — fires only for full (non-prerelease) GitHub Releases; pre-releases fire GitHub's separate prereleased event, which this workflow does not listen for.
- manual
workflow_dispatch — requires an explicit tag input (for example v1.28.1). Both jobs use github.event.inputs.tag || github.event.release.tag_name for the checkout ref and Docker version tagging, so a manual run publishes the specified tag instead of depending on release-event context.
This workflow fans out into two parallel jobs:
npm
Yarn (per the packageManager field in package.json, currently 4.17.0) via Corepack remains this repo's dependency manager everywhere else; npm publish here is an intentional, pipeline-only exception used solely to publish the built package to the npm registry.
- Check out the release tag (
github.event.release.tag_name).
- Set up Node.js 24 and point npm at
https://registry.npmjs.org.
- Enable Yarn and run
yarn install --immutable.
- Publish
./packages/suite to npm with npm publish ./packages/suite.
docker
- Check out the same release tag.
- Set up QEMU and Docker Buildx.
- Log in to GHCR.
- Strip the leading
v from the release tag for the versioned container tag.
- Build and push a multi-arch image for
linux/amd64,linux/arm64.
- Push both:
ghcr.io/<repo>:latest
ghcr.io/<repo>:<version-without-v-prefix>
Release Sync (release-sync.yml)
Trigger:
release: types: [released] — same caveat as post-release.yml: pre-releases fire prereleased, not released, so this workflow does not run for RC builds.
- manual
workflow_dispatch
This workflow keeps develop descended from main after a release:
- Check out
main with full history.
- Read the released version from
package.json.
- Create
sync/main-to-develop-{version} from origin/develop.
- Attempt
git merge origin/main --no-ff.
Two paths follow:
- Clean merge — push the sync branch, open a PR into
develop, and enable auto-merge with gh pr merge --merge --auto.
- Conflicted merge — commit the conflict markers, push the branch, and open a PR whose body warns that conflicts must be resolved manually.
In both cases, the workflow emphasizes the same rule: merge the sync PR with a MERGE COMMIT only — never squash or rebase — so main remains an ancestor of develop.
Manual Pre-release / RC Flow (prerelease.yml)
This workflow is manual-only (workflow_dispatch).
| Input |
Type / options |
Default |
Purpose |
branch |
choice (fixed dropdown values): develop, release/* |
develop |
Branch to build the pre-release from |
version_type |
choice: prerelease, prepatch, preminor, premajor |
prerelease |
How to compute the next RC version |
create_release |
boolean |
true |
Whether to publish a GitHub Pre-release |
release_notes |
string |
none |
Optional custom release notes body |
Note: release/* is a literal option string in the workflow's choice input, not a wildcard/glob pattern. Selecting it only works if a branch is literally named release/*; to build an RC from an actual release branch (e.g. release/minor/v1.5.0), that exact branch name would need to be added as its own choice option (or the input changed to a free-text string type).
Version computation logic:
- Check out the selected branch with full history.
- Find the latest RC tag matching
v*rc.*.
- Compute the next version:
- If
version_type == "prerelease":
- with no RC tag yet:
semver.inc(baseVersion, "prerelease", "rc")
- with an existing RC tag:
semver.inc(lastRcWithoutLeadingV, "prerelease", "rc")
- Otherwise:
- derive a stable base version from
package.json
- run
semver.inc(stableBase, version_type, "rc")
- Use the computed version for artifact naming and release metadata.
Build and release steps:
- Build production desktop and web bundles.
- Package Windows, Linux, and macOS artifacts using
--config.extraMetadata.version=${version}.
- Create
dist/lichtblick-web.tar.gz.
- If
create_release == true, create a GitHub Pre-release with ncipollo/release-action@v1, prerelease: true, and the same artifact list as the stable release flow.
Note: This flow does not commit a version bump into git history. As covered above, GitHub fires prereleased (not released) for pre-releases, so post-release.yml and release-sync.yml do not run automatically after this workflow creates a GitHub Pre-release — NPM publishing, the GHCR image push, and the develop-sync PR only happen for stable releases.
Workflow Trigger Reference
| Workflow file |
Trigger |
Jobs |
.github/workflows/release.yml |
pull_request closed on main, gated to merged PRs whose head branch starts with release/ or hotfix/ |
release |
.github/workflows/post-release.yml |
release: released (excludes pre-releases) or workflow_dispatch (requires tag input) |
npm, docker |
.github/workflows/prerelease.yml |
workflow_dispatch |
prerelease |
.github/workflows/release-sync.yml |
release: released (excludes pre-releases) or workflow_dispatch |
sync |
Key Files
.github/workflows/release.yml
.github/workflows/post-release.yml
.github/workflows/prerelease.yml
.github/workflows/release-sync.yml
1---2name: release-process3description: Release pipeline knowledge covering the stable release flow (release.yml -> post-release.yml -> release-sync.yml), the manual pre-release/RC flow (prerelease.yml), branch-naming to version-bump mapping, and NPM/GHCR publishing. Use when cutting a release, verifying a completed release, or troubleshooting the release pipeline.4---56# Release Process Skill78## Overview910Lichtblick has two release tracks:11121. **Stable releases** — triggered automatically when a merged pull request into `main` comes from a `release/*` or `hotfix/*` branch. This runs `.github/workflows/release.yml`, which creates the GitHub Release and then fan-outs into post-release publishing and main→develop sync via GitHub's `release: released` event.132. **Manual pre-release / RC builds** — triggered manually through `.github/workflows/prerelease.yml` (`workflow_dispatch` only). This builds the same production artifacts and, when `create_release: true`, creates a GitHub **Pre-release** through `ncipollo/release-action` with `prerelease: true`. Because GitHub's `release` event fires `prereleased` (not `released`) for pre-releases, this does **not** trigger `post-release.yml` or `release-sync.yml` — RC builds are never auto-published to NPM/GHCR or auto-synced to `develop`.1415The stable and RC flows share the same artifact packaging shape, but only the stable flow performs the in-repo version bump, commit, and tag push to `main`.1617## Branch Naming -> Version Bump Mapping1819The version bump is computed by regex in the `bump_type` step of `.github/workflows/release.yml`.2021| Branch prefix pattern | Bump type | Example |22|-----------------------|-----------|---------|23| `hotfix/*` | `patch` | `hotfix/fix-crash` |24| `release/major` or `release/major/*` | `major` | `release/major/v2.0.0` |25| `release/minor` or `release/minor/*` | `minor` | `release/minor/v1.5.0` |2627Any other branch prefix causes the workflow to fail with an explicit error.2829## Stable Release Pipeline (release.yml)3031Trigger condition:3233```yaml34on:35 pull_request:36 types: [closed]37 branches:38 - main3940jobs:41 release:42 if: |43 github.event.pull_request.merged == true &&44 (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/'))45```4647Step order:48491. Check out the repository on `main`.502. Set up Node.js 24 and enable Yarn via Corepack.513. Install dependencies with `yarn install --immutable`.524. Determine the bump type from the source branch name:53 - `hotfix/*` -> `patch`54 - `release/major(/.*)?` -> `major`55 - `release/minor(/.*)?` -> `minor`56 - anything else -> fail575. Bump the root version with `yarn version <type>`.586. Bump `packages/suite/package.json` with `yarn version <type>`.597. Read the new version from the root `package.json`.608. Update `sonar-project.properties` so `sonar.projectVersion` matches the new version.619. Commit the version files and tag `main` as `v${version}`, then push `main` and tags.6210. Build the production desktop and web bundles:63 - `yarn desktop:build:prod`64 - `yarn web:build:prod`6511. Package release binaries for Windows, Linux, and macOS:66 - `yarn package:win`67 - `yarn package:linux`68 - `yarn package:darwin`6912. Create the web static tarball at `dist/lichtblick-web.tar.gz`.7013. Create the GitHub Release with `ncipollo/release-action@v1`.7114. Trigger `sonarqube.yml` on `main` via `gh workflow run`.7273Release artifacts:7475- `dist/lichtblick-${version}-linux-amd64.deb`76- `dist/lichtblick-${version}-linux-x64.tar.gz`77- `dist/lichtblick-${version}-linux-arm64.deb`78- `dist/lichtblick-${version}-linux-arm64.tar.gz`79- `dist/lichtblick-${version}-mac-universal.dmg`80- `dist/lichtblick-${version}-win.exe`81- `dist/lichtblick-web.tar.gz`82- `dist/latest-linux.yml`83- `dist/latest-mac.yml`84- `dist/latest.yml`8586## Post-Release Publishing (post-release.yml)8788Trigger:8990- `release: types: [released]` — fires only for full (non-prerelease) GitHub Releases; pre-releases fire GitHub's separate `prereleased` event, which this workflow does not listen for.91- manual `workflow_dispatch` — requires an explicit `tag` input (for example `v1.28.1`). Both jobs use `github.event.inputs.tag || github.event.release.tag_name` for the checkout ref and Docker version tagging, so a manual run publishes the specified tag instead of depending on release-event context.9293This workflow fans out into two parallel jobs:9495### `npm`9697> Yarn (per the `packageManager` field in `package.json`, currently 4.17.0) via Corepack remains this repo's dependency manager everywhere else; `npm publish` here is an intentional, pipeline-only exception used solely to publish the built package to the npm registry.98991. Check out the release tag (`github.event.release.tag_name`).1002. Set up Node.js 24 and point npm at `https://registry.npmjs.org`.1013. Enable Yarn and run `yarn install --immutable`.1024. Publish `./packages/suite` to npm with `npm publish ./packages/suite`.103104### `docker`1051061. Check out the same release tag.1072. Set up QEMU and Docker Buildx.1083. Log in to GHCR.1094. Strip the leading `v` from the release tag for the versioned container tag.1105. Build and push a multi-arch image for `linux/amd64,linux/arm64`.1116. Push both:112 - `ghcr.io/<repo>:latest`113 - `ghcr.io/<repo>:<version-without-v-prefix>`114115## Release Sync (release-sync.yml)116117Trigger:118119- `release: types: [released]` — same caveat as `post-release.yml`: pre-releases fire `prereleased`, not `released`, so this workflow does not run for RC builds.120- manual `workflow_dispatch`121122This workflow keeps `develop` descended from `main` after a release:1231241. Check out `main` with full history.1252. Read the released version from `package.json`.1263. Create `sync/main-to-develop-{version}` from `origin/develop`.1274. Attempt `git merge origin/main --no-ff`.128129Two paths follow:130131- **Clean merge** — push the sync branch, open a PR into `develop`, and enable auto-merge with `gh pr merge --merge --auto`.132- **Conflicted merge** — commit the conflict markers, push the branch, and open a PR whose body warns that conflicts must be resolved manually.133134In both cases, the workflow emphasizes the same rule: merge the sync PR with a **MERGE COMMIT** only — never squash or rebase — so `main` remains an ancestor of `develop`.135136## Manual Pre-release / RC Flow (prerelease.yml)137138This workflow is manual-only (`workflow_dispatch`).139140| Input | Type / options | Default | Purpose |141|-------|----------------|---------|---------|142| `branch` | choice (fixed dropdown values): `develop`, `release/*` | `develop` | Branch to build the pre-release from |143| `version_type` | choice: `prerelease`, `prepatch`, `preminor`, `premajor` | `prerelease` | How to compute the next RC version |144| `create_release` | boolean | `true` | Whether to publish a GitHub Pre-release |145| `release_notes` | string | none | Optional custom release notes body |146147> **Note:** `release/*` is a literal option string in the workflow's `choice` input, not a wildcard/glob pattern. Selecting it only works if a branch is literally named `release/*`; to build an RC from an actual release branch (e.g. `release/minor/v1.5.0`), that exact branch name would need to be added as its own choice option (or the input changed to a free-text `string` type).148149Version computation logic:1501511. Check out the selected branch with full history.1522. Find the latest RC tag matching `v*rc.*`.1533. Compute the next version:154 - If `version_type == "prerelease"`:155 - with no RC tag yet: `semver.inc(baseVersion, "prerelease", "rc")`156 - with an existing RC tag: `semver.inc(lastRcWithoutLeadingV, "prerelease", "rc")`157 - Otherwise:158 - derive a stable base version from `package.json`159 - run `semver.inc(stableBase, version_type, "rc")`1604. Use the computed version for artifact naming and release metadata.161162Build and release steps:1631641. Build production desktop and web bundles.1652. Package Windows, Linux, and macOS artifacts using `--config.extraMetadata.version=${version}`.1663. Create `dist/lichtblick-web.tar.gz`.1674. If `create_release == true`, create a GitHub Pre-release with `ncipollo/release-action@v1`, `prerelease: true`, and the same artifact list as the stable release flow.168169> **Note:** This flow does not commit a version bump into git history. As covered above, GitHub fires `prereleased` (not `released`) for pre-releases, so `post-release.yml` and `release-sync.yml` do not run automatically after this workflow creates a GitHub Pre-release — NPM publishing, the GHCR image push, and the develop-sync PR only happen for stable releases.170171## Workflow Trigger Reference172173| Workflow file | Trigger | Jobs |174|---------------|---------|------|175| `.github/workflows/release.yml` | `pull_request` closed on `main`, gated to merged PRs whose head branch starts with `release/` or `hotfix/` | `release` |176| `.github/workflows/post-release.yml` | `release: released` (excludes pre-releases) or `workflow_dispatch` (requires `tag` input) | `npm`, `docker` |177| `.github/workflows/prerelease.yml` | `workflow_dispatch` | `prerelease` |178| `.github/workflows/release-sync.yml` | `release: released` (excludes pre-releases) or `workflow_dispatch` | `sync` |179180## Key Files181182- `.github/workflows/release.yml`183- `.github/workflows/post-release.yml`184- `.github/workflows/prerelease.yml`185- `.github/workflows/release-sync.yml`