# Thunderbolt Release Process: How to Publish New Versions Automatically

> Automate Thunderbolt releases with GitHub Actions. Our single workflow trigger updates versions, creates signed tags, builds binaries, and publishes to GitHub, TestFlight, and Google Play.

- Repository: [Thunderbird/thunderbolt](https://github.com/thunderbird/thunderbolt)
- Tags: how-to-guide
- Published: 2026-04-19

---

**Thunderbolt’s release process is fully automated through GitHub Actions, where a single workflow trigger updates version files, creates signed tags, builds cross-platform binaries, and publishes distributable artefacts to GitHub, TestFlight, and the Google Play Store.**

The Thunderbird Thunderbolt repository uses a modern CI/CD pipeline to manage its multi-platform desktop and mobile releases. Understanding the release process for publishing new versions of Thunderbolt helps contributors and maintainers ship updates safely without manual build steps or version drift across platform-specific configuration files.

## Overview of the Automated Release Pipeline

Thunderbolt’s release orchestration lives entirely in GitHub Actions. The pipeline guarantees that version numbers remain consistent across JavaScript, Rust, and mobile-native manifests while producing signed, notarized binaries for every supported platform.

Three entry points control the release flow:

- **[`.github/workflows/release.yml`](https://github.com/thunderbird/thunderbolt/blob/main/.github/workflows/release.yml)** – The primary production pipeline that executes the full build and publish cycle.
- **[`.github/workflows/version-bump.yml`](https://github.com/thunderbird/thunderbolt/blob/main/.github/workflows/version-bump.yml)** – A helper workflow that mutates [`package.json`](https://github.com/thunderbird/thunderbolt/blob/main/package.json), [`Cargo.toml`](https://github.com/thunderbird/thunderbolt/blob/main/Cargo.toml), and [`tauri.conf.json`](https://github.com/thunderbird/thunderbolt/blob/main/tauri.conf.json) before tagging.
- **[`scripts/create-release.ts`](https://github.com/thunderbird/thunderbolt/blob/main/scripts/create-release.ts)** – A local TypeScript CLI for dry-running version bumps or pushing tags manually.

## Step-by-Step Release Workflow

The Thunderbolt release process proceeds through six deterministic phases, each enforced by the [`release.yml`](https://github.com/thunderbird/thunderbolt/blob/main/release.yml) workflow.

### 1. Version Bump and File Synchronization

The pipeline first updates the canonical version identifier across the codebase. In [`scripts/create-release.ts`](https://github.com/thunderbird/thunderbolt/blob/main/scripts/create-release.ts) (lines 38‑41), the script writes the new semver string to:

- [`package.json`](https://github.com/thunderbird/thunderbolt/blob/main/package.json) (Node.js frontend)
- [`src-tauri/Cargo.toml`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/Cargo.toml) (Rust core)
- [`src-tauri/tauri.conf.json`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/tauri.conf.json) (Tauri configuration)

This ensures that desktop, iOS, and Android builds reference identical version metadata.

### 2. Commit and Signed Tag Creation

After file mutations, the workflow commits the changes and creates an annotated Git tag. The implementation in [`scripts/create-release.ts`](https://github.com/thunderbird/thunderbolt/blob/main/scripts/create-release.ts) (lines 40‑44) generates a tag in the format `v{major}.{minor}.{patch}` and pushes it to the remote repository. This tag acts as the immutable trigger for downstream build jobs.

### 3. Draft GitHub Release Generation

Upon tag detection, [`release.yml`](https://github.com/thunderbird/thunderbolt/blob/main/release.yml) (lines 24‑27) initializes a draft GitHub release pre-populated with an auto-generated changelog. The draft state allows maintainers to review release notes before public visibility while the build matrix executes.

### 4. Parallel Cross-Platform Builds

The workflow fans out into platform-specific jobs that execute concurrently:

- **Desktop** – Tauri builds Linux (AppImage), macOS (Intel and Apple Silicon universal binaries), and Windows (x64 and ARM64 installers) with automatic code signing and notarization.
- **iOS** – The pipeline updates [`src-tauri/gen/apple/project.yml`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/gen/apple/project.yml), compiles a `.ipa`, and uploads to TestFlight via the App Store Connect API.
- **Android** – The build computes a monotonic `versionCode` as `BASE_OFFSET + git rev-list --count HEAD`, constructs an `.aab` (Android App Bundle), and publishes to the Google Play Store Internal track.

Platform-specific version identifiers are injected at build time without committing transient changes to the repository.

### 5. Artefact Publication and Store Distribution

After successful builds, [`release.yml`](https://github.com/thunderbird/thunderbolt/blob/main/release.yml) (lines 44‑47) attaches all desktop binaries to the GitHub release assets. Simultaneously, the iOS and Android workflows dispatch the signed mobile packages to their respective distribution channels, completing the end-to-end delivery pipeline.

### 6. Post-Release Validation

A final validation job in [`release.yml`](https://github.com/thunderbird/thunderbolt/blob/main/release.yml) (lines 22‑26) asserts that the Git tag version matches the contents of [`package.json`](https://github.com/thunderbird/thunderbolt/blob/main/package.json), [`Cargo.toml`](https://github.com/thunderbird/thunderbolt/blob/main/Cargo.toml), and [`tauri.conf.json`](https://github.com/thunderbird/thunderbolt/blob/main/tauri.conf.json). If any mismatch is detected, the workflow fails fast to prevent shipping inconsistent version metadata.

## Triggering a Thunderbolt Release

Maintainers can initiate the release process through three methods, ordered by recommendation.

### GitHub Actions (Recommended)

Use the GitHub CLI to trigger the workflow with explicit parameters:

```bash

# Auto-detect version bump from commit history

gh workflow run release.yml -f version_type=auto

# Explicit semver

gh workflow run release.yml -f version=1.2.3

# Bump specific segment (major, minor, or patch)

gh workflow run release.yml -f version_type=minor

```

### Local Helper Script

For testing or offline validation, the TypeScript CLI in [`scripts/create-release.ts`](https://github.com/thunderbird/thunderbolt/blob/main/scripts/create-release.ts) supports dry-runs and local execution:

```bash

# Preview changes without modifying git state

bun run scripts/create-release.ts --dry-run

# Execute bump and push tag to trigger CI

bun run scripts/create-release.ts --push

# Force specific version locally

bun run scripts/create-release.ts --version 1.2.3 --push

```

### Manual Method

While not recommended, maintainers can manually edit the four version files ([`package.json`](https://github.com/thunderbird/thunderbolt/blob/main/package.json), [`src-tauri/Cargo.toml`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/Cargo.toml), [`src-tauri/tauri.conf.json`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/tauri.conf.json)), commit the changes, and push a tag matching the pattern `v*.*.*`. The [`release.yml`](https://github.com/thunderbird/thunderbolt/blob/main/release.yml) workflow will still execute automatically upon tag detection.

## Platform-Specific Version Management

Thunderbolt maintains platform-specific versioning semantics to satisfy store requirements while preserving a unified source of truth.

- **Desktop (Tauri)** – The version string in [`src-tauri/tauri.conf.json`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/tauri.conf.json) acts as the canonical reference. The Tauri CLI propagates this value to [`Cargo.toml`](https://github.com/thunderbird/thunderbolt/blob/main/Cargo.toml) and the Node environment during the build.
- **iOS** – The [`project.yml`](https://github.com/thunderbird/thunderbolt/blob/main/project.yml) file in `src-tauri/gen/apple/` receives the version string during the iOS build job. This transient modification is not committed to the repository.
- **Android** – The `versionCode` is computed dynamically as `BASE_OFFSET + git rev-list --count HEAD` to ensure monotonicity for the Google Play Store. The `tauri.properties` file in `src-tauri/gen/android/app/` is auto-generated by the Tauri CLI and should never be edited manually.

## Key Files in the Thunderbolt Release Process

| Path | Role |
|------|------|
| [`.github/workflows/release.yml`](https://github.com/thunderbird/thunderbolt/blob/main/.github/workflows/release.yml) | Orchestrates the full multi-platform release, including builds, signing, and store uploads. |
| [`.github/workflows/version-bump.yml`](https://github.com/thunderbird/thunderbolt/blob/main/.github/workflows/version-bump.yml) | Helper workflow that mutates version files and creates annotated Git tags. |
| [`scripts/create-release.ts`](https://github.com/thunderbird/thunderbolt/blob/main/scripts/create-release.ts) | TypeScript CLI for local version bumping, dry-runs, and optional git push. |
| [`package.json`](https://github.com/thunderbird/thunderbolt/blob/main/package.json) | Node.js frontend manifest; receives the new semver during the bump phase. |
| [`src-tauri/Cargo.toml`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/Cargo.toml) | Rust backend manifest; synchronized with the canonical version. |
| [`src-tauri/tauri.conf.json`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/tauri.conf.json) | **Source of truth** for application version and Android `versionCode` base. |
| [`src-tauri/gen/apple/project.yml`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/gen/apple/project.yml) | iOS project configuration; updated transiently during the iOS build job. |
| `src-tauri/gen/android/app/tauri.properties` | Auto-generated Android properties; updated by Tauri CLI during builds. |
| [`RELEASE.md`](https://github.com/thunderbird/thunderbolt/blob/main/RELEASE.md) | Human-readable documentation describing the release process and emergency procedures. |

## Summary

- **Thunderbolt’s release process** is fully automated via GitHub Actions, requiring only a single trigger to generate cross-platform binaries and mobile store uploads.
- **Three entry points** control the workflow: [`.github/workflows/release.yml`](https://github.com/thunderbird/thunderbolt/blob/main/.github/workflows/release.yml) for production releases, [`.github/workflows/version-bump.yml`](https://github.com/thunderbird/thunderbolt/blob/main/.github/workflows/version-bump.yml) for file mutations, and [`scripts/create-release.ts`](https://github.com/thunderbird/thunderbolt/blob/main/scripts/create-release.ts) for local testing.
- **Version consistency** is enforced by a post-release validation job that confirms the Git tag matches [`package.json`](https://github.com/thunderbird/thunderbolt/blob/main/package.json), [`Cargo.toml`](https://github.com/thunderbird/thunderbolt/blob/main/Cargo.toml), and [`tauri.conf.json`](https://github.com/thunderbird/thunderbolt/blob/main/tauri.conf.json).
- **Mobile platform versioning** uses transient modifications to [`project.yml`](https://github.com/thunderbird/thunderbolt/blob/main/project.yml) (iOS) and dynamic `versionCode` calculation (Android) to satisfy store requirements without polluting the git history.

## Frequently Asked Questions

### How do I trigger a Thunderbolt release from the command line?

Use the GitHub CLI to dispatch the [`release.yml`](https://github.com/thunderbird/thunderbolt/blob/main/release.yml) workflow with the appropriate version parameter. For automatic versioning based on commit history, run `gh workflow run release.yml -f version_type=auto`. To specify an exact version, use `gh workflow run release.yml -f version=1.2.3`. Alternatively, run `bun run scripts/create-release.ts --push` locally to bump files and push a tag that triggers the same pipeline.

### What files does the version bump update?

The version bump modifies three canonical files to maintain consistency across the Node.js frontend, Rust backend, and Tauri configuration: [`package.json`](https://github.com/thunderbird/thunderbolt/blob/main/package.json) (Node.js), [`src-tauri/Cargo.toml`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/Cargo.toml) (Rust), and [`src-tauri/tauri.conf.json`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/tauri.conf.json) (Tauri source of truth). The workflow does not commit transient platform-specific files like [`src-tauri/gen/apple/project.yml`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/gen/apple/project.yml) or `src-tauri/gen/android/app/tauri.properties`, as these are generated during the build process.

### Can I test the release process without creating a real tag?

Yes. Run the local helper script with the `--dry-run` flag: `bun run scripts/create-release.ts --dry-run`. This executes the version bump logic and displays the proposed changes to [`package.json`](https://github.com/thunderbird/thunderbolt/blob/main/package.json), [`Cargo.toml`](https://github.com/thunderbird/thunderbolt/blob/main/Cargo.toml), and [`tauri.conf.json`](https://github.com/thunderbird/thunderbolt/blob/main/tauri.conf.json) without committing to git or pushing a tag. You can also review the workflow outputs in the GitHub Actions tab after pushing to a feature branch, as the [`release.yml`](https://github.com/thunderbird/thunderbolt/blob/main/release.yml) logic only executes fully on tag pushes.

### How does Thunderbolt handle version numbers for mobile platforms?

Thunderbolt uses platform-specific strategies to satisfy store requirements while keeping a single source of truth in [`tauri.conf.json`](https://github.com/thunderbird/thunderbolt/blob/main/tauri.conf.json). For iOS, the build pipeline transiently updates [`src-tauri/gen/apple/project.yml`](https://github.com/thunderbird/thunderbolt/blob/main/src-tauri/gen/apple/project.yml) during the workflow without committing the change. For Android, the pipeline calculates a monotonic `versionCode` using the formula `BASE_OFFSET + git rev-list --count HEAD`, ensuring the Google Play Store receives incrementing version codes even when the semantic version jumps. The `tauri.properties` file is auto-generated by the Tauri CLI during the Android build and should never be edited manually.