CI/CD
Automate releases with GitHub Actions.
This guide provides a complete GitHub Actions workflow for building, signing, and uploading LightShell releases on every tag push. The workflow produces binaries for macOS (arm64 and x64) and Linux (x64), signs them with your Ed25519 key, and uploads them to your release server.
Prerequisites
Section titled “Prerequisites”Before setting up CI/CD, make sure you have:
- A release server running (see Release Server) or a GitHub Releases setup (see GitHub Releases)
- An Ed25519 signing key pair (see Signing Keys)
- A GitHub repository with your LightShell app
Secrets
Section titled “Secrets”Add these secrets to your GitHub repository (Settings > Secrets and variables > Actions):
| Secret | Description |
|---|---|
SIGNING_PRIVATE_KEY | Contents of ~/.lightshell/signing-key.pem. The full PEM file contents. |
RELEASE_SERVER | URL of your release server (e.g., https://releases.example.com). |
RELEASE_TOKEN | Your LIGHTSHELL_API_KEY value for authenticating uploads. |
To add a secret:
# Copy your signing key to clipboardcat ~/.lightshell/signing-key.pem | pbcopy # macOScat ~/.lightshell/signing-key.pem | xclip # Linux
# Then paste it into the GitHub secret formComplete Workflow
Section titled “Complete Workflow”Create .github/workflows/release.yml in your repository:
name: Release
on: push: tags: - 'v*'
permissions: contents: write
jobs: build: strategy: matrix: include: - os: macos-latest platform: darwin-arm64 target_arch: arm64 - os: macos-13 platform: darwin-x64 target_arch: x64 - os: ubuntu-latest platform: linux-x64 target_arch: x64
runs-on: ${{ matrix.os }}
steps: - name: Checkout uses: actions/checkout@v4
- name: Install LightShell run: npm install -g lightshell
- name: Install Linux dependencies if: runner.os == 'Linux' run: | sudo apt-get update sudo apt-get install -y libwebkit2gtk-4.1-dev libgtk-3-dev
- name: Build run: lightshell build
- name: Package archive run: | cd dist tar -czf ../my-app-${{ matrix.platform }}.tar.gz . cd ..
- name: Upload artifact uses: actions/upload-artifact@v4 with: name: build-${{ matrix.platform }} path: my-app-${{ matrix.platform }}.tar.gz
sign-and-release: needs: build runs-on: ubuntu-latest
steps: - name: Checkout uses: actions/checkout@v4
- name: Install LightShell run: npm install -g lightshell
- name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts merge-multiple: true
- name: Sign and upload releases env: LIGHTSHELL_SIGNING_KEY: ${{ secrets.SIGNING_PRIVATE_KEY }} LIGHTSHELL_RELEASE_SERVER: ${{ secrets.RELEASE_SERVER }} LIGHTSHELL_RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | VERSION=${GITHUB_REF#refs/tags/v}
for archive in artifacts/*.tar.gz; do PLATFORM=$(echo "$archive" | sed 's/.*my-app-\(.*\)\.tar\.gz/\1/')
lightshell release \ --server "$LIGHTSHELL_RELEASE_SERVER" \ --archive "$archive" \ --platform "$PLATFORM" \ --version "$VERSION" \ --sign \ --notes "Release v${VERSION}" doneHow It Works
Section titled “How It Works”The workflow runs in two stages:
Stage 1: Build (parallel)
Section titled “Stage 1: Build (parallel)”Three jobs run in parallel, one for each target platform:
- macOS arm64 on
macos-latest(Apple Silicon runners) - macOS x64 on
macos-13(Intel runners) - Linux x64 on
ubuntu-latest
Each job:
- Checks out the code
- Installs the LightShell CLI
- Installs platform-specific dependencies (Linux only: WebKitGTK and GTK)
- Runs
lightshell buildto produce the binary - Packages the output into a
.tar.gzarchive - Uploads the archive as a GitHub Actions artifact
Stage 2: Sign and Release (sequential)
Section titled “Stage 2: Sign and Release (sequential)”After all builds complete:
- Downloads all build artifacts
- For each archive, runs
lightshell releasewhich:- Computes the SHA256 hash
- Signs the archive with the Ed25519 private key
- Uploads the archive and metadata to the release server
Triggering a Release
Section titled “Triggering a Release”Create a git tag and push it:
# Update version in lightshell.json first# Then tag and pushgit tag v1.2.0git push origin v1.2.0The workflow triggers on any tag matching v*. The version number is extracted from the tag name (stripping the v prefix).
Release Notes
Section titled “Release Notes”The workflow uses a generic release note (Release v1.2.0). For richer release notes, you have two options:
From a CHANGELOG
Section titled “From a CHANGELOG”- name: Extract release notes id: notes run: | # Extract notes for this version from CHANGELOG.md VERSION=${GITHUB_REF#refs/tags/v} NOTES=$(sed -n "/^## ${VERSION}/,/^## /p" CHANGELOG.md | head -n -1) echo "notes<<EOF" >> $GITHUB_OUTPUT echo "$NOTES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT
- name: Sign and upload run: | lightshell release \ --notes "${{ steps.notes.outputs.notes }}" \ # ... rest of argsFrom the Git Tag Message
Section titled “From the Git Tag Message”# Create an annotated tag with release notesgit tag -a v1.2.0 -m "Bug fixes and performance improvements
- Fixed crash on startup with large files- Improved search performance by 3x- Updated dependencies"
git push origin v1.2.0- name: Get tag message id: tag run: | NOTES=$(git tag -l --format='%(contents)' ${GITHUB_REF#refs/tags/}) echo "notes<<EOF" >> $GITHUB_OUTPUT echo "$NOTES" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUTCustomizing the Workflow
Section titled “Customizing the Workflow”Adding macOS Code Signing
Section titled “Adding macOS Code Signing”If you have an Apple Developer ID, add code signing to the macOS build step:
- name: Build (signed) if: runner.os == 'macOS' env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} APPLE_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }} run: | # Import certificate echo "$APPLE_CERTIFICATE" | base64 -d > cert.p12 security create-keychain -p "" build.keychain security import cert.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain security default-keychain -s build.keychain
# Build with signing lightshell build --signCustom Build Arguments
Section titled “Custom Build Arguments”Pass additional build arguments for specific platforms:
- name: Build run: | if [ "${{ matrix.platform }}" = "linux-x64" ]; then lightshell build --target appimage else lightshell build fiVerifying the Pipeline
Section titled “Verifying the Pipeline”After your first automated release, verify everything is correct:
- Check your release server dashboard for the new version
- Run
lightshell updater.check()in a dev build of the previous version - Confirm the update downloads and installs successfully
- Check the SHA256 hash matches:
shasum -a 256 downloaded-archive.tar.gz