Release #22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 }} |