Skip to content

Release

Release #22

Workflow file for this run

name: Release
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Existing git tag to release (e.g. v0.5.0)"
required: true
type: string
permissions:
contents: write
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: windows-latest
args: "--bundles nsis,msi"
- platform: macos-latest
args: "--target universal-apple-darwin --bundles app,dmg,updater"
- platform: ubuntu-latest
args: "--bundles appimage,deb,rpm"
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve release tag
id: release_tag
shell: bash
run: |
if [ "${{ github.event_name }}" = "push" ]; then
TAG="${GITHUB_REF_NAME}"
else
TAG="${{ inputs.tag }}"
fi
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$ ]]; then
echo "Invalid tag format: $TAG"
echo "Expected format like v0.5.0"
exit 1
fi
git fetch --tags --force
if ! git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then
echo "Tag not found: $TAG"
echo "Create/push the tag first, then re-run release."
exit 1
fi
echo "version=$TAG" >> "$GITHUB_OUTPUT"
# ── macOS: universal binary needs both targets ──────────────────────────
- name: Add macOS targets
if: matrix.platform == 'macos-latest'
run: |
rustup target add aarch64-apple-darwin
rustup target add x86_64-apple-darwin
# ── Node.js ─────────────────────────────────────────────────────────────
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install frontend dependencies
run: npm ci
- name: Validate version consistency
shell: bash
run: |
TAG_VERSION="${{ steps.release_tag.outputs.version }}"
TAG_VERSION="${TAG_VERSION#v}"
PKG_VERSION=$(node -p "require('./package.json').version")
TAURI_VERSION=$(node -p "require('./src-tauri/tauri.conf.json').version")
echo "Release tag version: $TAG_VERSION"
echo "package.json version: $PKG_VERSION"
echo "tauri.conf.json version: $TAURI_VERSION"
if [ "$TAG_VERSION" != "$PKG_VERSION" ] || [ "$TAG_VERSION" != "$TAURI_VERSION" ]; then
echo "Version mismatch detected."
echo "Tag version must match package.json and src-tauri/tauri.conf.json."
exit 1
fi
- name: Extract release notes from CHANGELOG.md
id: changelog
shell: bash
run: |
VERSION_NO_V="${{ steps.release_tag.outputs.version }}"
VERSION_NO_V="${VERSION_NO_V#v}"
SECTION=$(awk -v version="$VERSION_NO_V" '
$0 ~ "^## \\[" version "\\]" { in_section=1; next }
in_section && $0 ~ /^## \[/ { exit }
in_section { print }
' CHANGELOG.md)
MACOS_NOTE="\n\n---\n\n### 🚩 macOS 无签名运行说明\n\n**首次打开如遇“无法验证开发者”或“已损坏”提示,请右键 App → 打开,或在终端执行:**\n\n\n xattr -dr com.apple.quarantine /Applications/TimeLens.app\n\n**如仍无法运行,请在“系统设置 → 隐私与安全性”中允许此 App。**\n"
if [ -z "$SECTION" ]; then
echo "notes=No changelog entry found for version ${VERSION_NO_V}.${MACOS_NOTE}" >> "$GITHUB_OUTPUT"
else
{
echo "notes<<EOF"
printf '%s\n' "$SECTION"
printf '%b' "$MACOS_NOTE"
echo "EOF"
} >> "$GITHUB_OUTPUT"
fi
# ── Rust ────────────────────────────────────────────────────────────────
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
# ── Linux system libs (required by glib-sys, gtk, webkit2gtk) ─────────────
- name: Install Linux dependencies
if: matrix.platform == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y \
libwebkit2gtk-4.1-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
libglib2.0-dev \
pkg-config
# ── Build & publish ──────────────────────────────────────────────────────
- name: Build and release
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
with:
tagName: ${{ steps.release_tag.outputs.version }}
releaseName: "TimeLens ${{ steps.release_tag.outputs.version }}"
releaseBody: ${{ steps.changelog.outputs.notes }}
releaseDraft: false
prerelease: false
args: ${{ matrix.args }}
build-msix:
name: Build and upload MSIX
runs-on: windows-latest
needs: build
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve release tag
id: release_tag
shell: pwsh
run: |
if ("${{ github.event_name }}" -eq "push") {
$tag = "${{ github.ref_name }}"
} else {
$tag = "${{ inputs.tag }}"
}
if ($tag -notmatch '^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$') {
Write-Error "Invalid tag format: $tag"
}
git fetch --tags --force
git rev-parse -q --verify "refs/tags/$tag" | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Error "Tag not found: $tag"
}
$versionNoV = $tag.TrimStart('v')
$msixVersion = "$versionNoV.0"
"tag=$tag" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"version=$versionNoV" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"msix_version=$msixVersion" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install frontend dependencies
run: npm ci
- name: Validate version consistency
shell: pwsh
run: |
$tagVersion = "${{ steps.release_tag.outputs.version }}"
$pkgVersion = (node -p "require('./package.json').version").Trim()
$tauriVersion = (node -p "require('./src-tauri/tauri.conf.json').version").Trim()
Write-Host "Release tag version: $tagVersion"
Write-Host "package.json version: $pkgVersion"
Write-Host "tauri.conf.json version: $tauriVersion"
if ($tagVersion -ne $pkgVersion -or $tagVersion -ne $tauriVersion) {
Write-Error "Version mismatch detected. Tag version must match package.json and src-tauri/tauri.conf.json."
}
- name: Build MSIX package
shell: pwsh
run: |
./scripts/build-msix.ps1 -Version "${{ steps.release_tag.outputs.msix_version }}"
- name: Upload MSIX to release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.release_tag.outputs.tag }}
files: src-tauri/windows/out/TimeLens-${{ steps.release_tag.outputs.msix_version }}.msix
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build-windows-with-webview2:
name: Build and upload Windows installers (WithWebView2)
runs-on: windows-latest
needs: build
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve release tag
id: release_tag
shell: pwsh
run: |
if ("${{ github.event_name }}" -eq "push") {
$tag = "${{ github.ref_name }}"
} else {
$tag = "${{ inputs.tag }}"
}
if ($tag -notmatch '^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?$') {
Write-Error "Invalid tag format: $tag"
}
git fetch --tags --force
git rev-parse -q --verify "refs/tags/$tag" | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Error "Tag not found: $tag"
}
$versionNoV = $tag.TrimStart('v')
"tag=$tag" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"version=$versionNoV" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- name: Install frontend dependencies
run: npm ci
- name: Validate version consistency
shell: pwsh
run: |
$tagVersion = "${{ steps.release_tag.outputs.version }}"
$pkgVersion = (node -p "require('./package.json').version").Trim()
$tauriVersion = (node -p "require('./src-tauri/tauri.conf.json').version").Trim()
Write-Host "Release tag version: $tagVersion"
Write-Host "package.json version: $pkgVersion"
Write-Host "tauri.conf.json version: $tauriVersion"
if ($tagVersion -ne $pkgVersion -or $tagVersion -ne $tauriVersion) {
Write-Error "Version mismatch detected. Tag version must match package.json and src-tauri/tauri.conf.json."
}
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Build NSIS/MSI with bundled WebView2 runtime
shell: pwsh
run: |
$overridePath = "src-tauri/tauri.webview2.override.json"
$override = @{
bundle = @{
windows = @{
webviewInstallMode = @{
type = "offlineInstaller"
silent = $true
}
}
}
}
$override | ConvertTo-Json -Depth 16 | Set-Content -Path $overridePath -Encoding UTF8
npx tauri build --bundles nsis,msi --config $overridePath
- name: Prepare renamed WithWebView2 artifacts
id: artifacts
shell: pwsh
run: |
$bundleRoot = "src-tauri/target/release/bundle"
$nsis = Get-ChildItem -Path (Join-Path $bundleRoot "nsis") -Filter "*.exe" -File | Sort-Object LastWriteTime -Descending | Select-Object -First 1
$msi = Get-ChildItem -Path (Join-Path $bundleRoot "msi") -Filter "*.msi" -File | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if (-not $nsis) { throw "NSIS installer not found." }
if (-not $msi) { throw "MSI installer not found." }
$version = "${{ steps.release_tag.outputs.version }}"
$outDir = Join-Path $bundleRoot "with-webview2"
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
$nsisOut = Join-Path $outDir "TimeLens-$version-WithWebView2-Setup.exe"
$msiOut = Join-Path $outDir "TimeLens-$version-WithWebView2.msi"
Copy-Item -Path $nsis.FullName -Destination $nsisOut -Force
Copy-Item -Path $msi.FullName -Destination $msiOut -Force
"nsis_file=$nsisOut" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"msi_file=$msiOut" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
- name: Upload WithWebView2 installers to release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.release_tag.outputs.tag }}
files: |
${{ steps.artifacts.outputs.nsis_file }}
${{ steps.artifacts.outputs.msi_file }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}