TNTStack
Deployment

CI / CD Pipeline

GitHub Actions workflows, Release Please automation, and Conventional Commits.

GitHub Actions for validation, Release Please for versioning, manual dispatch for builds. 7 workflow files total.

Workflow Overview

WorkflowTriggerPurpose
ci.ymlPR to masterLint, type check, build validation
release.ymlPush to masterRelease Please automation
build-apps.ymlManual dispatchOrchestrates desktop + Android builds
build-desktop.ymlCalled by build-appsDesktop builds (Windows, macOS, Linux)
build-universal-apk.ymlCalled by build-appsUniversal Android APK + AAB
build-split-apk.ymlCalled by build-appsPer-architecture Android APKs
publish-cli.ymlRelease published (@tntstack/create-app@v*)Publish CLI to npm

CI Workflow

Runs on every pull request to validate code quality:

.github/workflows/ci.yml
name: CI
on:
  pull_request:
    branches: [master]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: "pnpm"
      - run: pnpm install --frozen-lockfile
      - run: pnpm format:check
      - run: pnpm lint
      - run: pnpm typecheck
      - run: pnpm build

Four checks must pass: formatting, linting, type checking, and production build.

Release Please

Release Please automates versioning based on Conventional Commits.

How It Works

  1. Push commits to master using conventional commit format
  2. Release Please opens a Release PR with version bumps and changelog
  3. Merge the Release PR to create a GitHub Release
  4. Manually trigger build-apps.yml with the release tag to build platform binaries

Builds are not automatic. After a release is created, you go to Actions > Build Apps > Run workflow and enter the tag name (e.g. v1.2.0). This gives you control over which platforms to build for each release.

Conventional Commits

PrefixSemVer BumpChangelog Section
feat:MinorFeatures
fix:PatchBug Fixes
perf:PatchPerformance Improvements
docs:PatchDocumentation
refactor:PatchCode Refactoring
deps:PatchDependencies
revert:PatchReverts
style:Styles
test:Tests
build:Build System
ci:Continuous Integration
chore:Hidden (no changelog entry)

Adding BREAKING CHANGE: in the commit footer or ! after the type (e.g., feat!:) triggers a major version bump.

Unified Versioning

All packages share a single version managed from the root. The root "." package uses extra-files to update version fields across the monorepo:

release-please-config.json (excerpt)
{
  "sequential-calls": true,
  "separate-pull-requests": true,
  "packages": {
    ".": {
      "release-type": "node",
      "extra-files": [
        { "type": "json", "path": "apps/native/package.json", "jsonpath": "$.version" },
        { "type": "json", "path": "apps/web/package.json", "jsonpath": "$.version" },
        { "type": "json", "path": "packages/ui/package.json", "jsonpath": "$.version" },
        { "type": "json", "path": "packages/i18n/package.json", "jsonpath": "$.version" },
        { "type": "json", "path": "packages/core/package.json", "jsonpath": "$.version" },
        { "type": "json", "path": "apps/native/src-tauri/tauri.conf.json", "jsonpath": "$.version" },
        { "type": "toml", "path": "apps/native/src-tauri/Cargo.toml", "jsonpath": "$.package.version" }
      ]
    },
    "packages/cli": {
      "release-type": "node",
      "include-component-in-tag": true,
      "component": "@tntstack/create-app"
    }
  }
}

When a release is created, all packages bump to the same version, including package.json, tauri.conf.json, and Cargo.toml. The CLI (@tntstack/create-app) is versioned independently with separate-pull-requests: true, so it gets its own Release PR.

Version Files Updated

FileField
package.json (root)version
apps/native/package.jsonversion
apps/web/package.jsonversion
packages/ui/package.jsonversion
packages/i18n/package.jsonversion
packages/core/package.jsonversion
apps/native/src-tauri/tauri.conf.jsonversion
apps/native/src-tauri/Cargo.tomlpackage.version

Only the root package generates a CHANGELOG.md. The CLI package has its own changelog.

Release Workflow

.github/workflows/release.yml
name: Release
on:
  workflow_dispatch:
  push:
    branches: [master]

permissions:
  contents: write
  pull-requests: write
  issues: write

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - name: Release Please
        id: release
        uses: googleapis/release-please-action@v4
        with:
          token: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
          config-file: release-please-config.json
          manifest-file: .release-please-manifest.json

      - name: Set correct latest release
        if: ${{ steps.release.outputs.releases_created == 'true' }}
        env:
          GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN || secrets.GITHUB_TOKEN }}
        run: |
          LATEST_TAG=$(gh release list --repo ${{ github.repository }} --limit 10 --json tagName -q ".[].tagName" | grep -E '^v[0-9]' | head -n 1)
          if [ -n "$LATEST_TAG" ]; then
            gh release edit "$LATEST_TAG" --repo ${{ github.repository }} --latest
          fi

Uses RELEASE_PLEASE_TOKEN if available, falls back to GITHUB_TOKEN. The "Set correct latest release" step ensures the main monorepo release (not the CLI release) is marked as latest on GitHub.

Build Workflows

Builds are triggered manually via build-apps.yml. You select a tag and toggle which platforms to build:

InputTypeDefault
tag_namestring (required)
build_windowsbooleantrue
build_macosbooleantrue
build_linuxbooleantrue
build_android_splitbooleantrue
build_android_universalbooleantrue

build-apps.yml delegates to three reusable workflows:

Calls build-desktop.yml. Dynamically constructs a build matrix based on the boolean toggles you provide. See Desktop Builds for details on caching, architectures, and the generated assets.

Calls build-universal-apk.yml. Builds a single APK containing all architectures, plus an AAB for the Play Store. Requires Android NDK r27d.

Calls build-split-apk.yml. Builds per-architecture APKs (arm64, arm, x86, x86_64) for smaller download sizes.

Android workflows automatically check for keystore secrets. If found, they produce signed release builds. If not, they safely fall back to unsigned debug builds. See the Android Builds page for keystore and GitHub Secrets setup.

CLI Publishing

publish-cli.yml triggers automatically when a release tagged @tntstack/create-app@v* is published. It builds the CLI with pnpm --filter @tntstack/create-app build and publishes to npm with --provenance for supply chain security.

Requires NPM_TOKEN in repository secrets.

On this page