Skip to content

chore(release): bump version to 0.2.0 #8

chore(release): bump version to 0.2.0

chore(release): bump version to 0.2.0 #8

Workflow file for this run

name: Release
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-*'
# Each build job is independent so all three platforms compile in parallel.
# The final job collects all artifacts and creates a draft GitHub Release.
jobs:
# ---------------------------------------------------------------------------
# macOS — .app bundle + .pkg installer.
#
# The job runs in three modes depending on which repository secrets are set:
#
# 1. No signing secrets:
# Produces UNSIGNED .app + UNSIGNED .pkg (current baseline behavior).
#
# 2. Code-signing secrets set (APPLE_DEVELOPER_ID_APPLICATION,
# APPLE_CERTIFICATES_P12_BASE64, APPLE_CERTIFICATES_P12_PASSWORD):
# Signs the .app with Hardened Runtime + entitlements; signs the .pkg
# if APPLE_DEVELOPER_ID_INSTALLER is also set.
#
# 3. Above PLUS notarization secrets set (APPLE_ID,
# APPLE_APP_SPECIFIC_PASSWORD, APPLE_TEAM_ID):
# Submits to Apple notary service, waits, and staples.
#
# Required repository secrets for full signed+notarized distribution:
#
# APPLE_DEVELOPER_ID_APPLICATION Certificate common name, e.g.
# "Developer ID Application: NAME (TEAMID)"
# APPLE_DEVELOPER_ID_INSTALLER Installer cert name, e.g.
# "Developer ID Installer: NAME (TEAMID)"
# APPLE_CERTIFICATES_P12_BASE64 Base64-encoded P12 containing both certs
# and their private keys
# APPLE_CERTIFICATES_P12_PASSWORD Password protecting the P12
# APPLE_ID Apple ID email for notarytool
# APPLE_APP_SPECIFIC_PASSWORD App-specific password (appleid.apple.com)
# APPLE_TEAM_ID 10-character Apple Developer Team ID
#
# See macos/PACKAGING.md for how-to-obtain instructions.
# ---------------------------------------------------------------------------
build-macos:
name: Build macOS
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Select Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
- name: Detect signing secrets
id: signing
env:
P12_B64: ${{ secrets.APPLE_CERTIFICATES_P12_BASE64 }}
run: |
if [[ -n "${P12_B64:-}" ]]; then
echo "have_certs=true" >> "$GITHUB_OUTPUT"
else
echo "have_certs=false" >> "$GITHUB_OUTPUT"
fi
- name: Import code-signing certificates
if: steps.signing.outputs.have_certs == 'true'
uses: apple-actions/import-codesign-certs@v3
with:
p12-file-base64: ${{ secrets.APPLE_CERTIFICATES_P12_BASE64 }}
p12-password: ${{ secrets.APPLE_CERTIFICATES_P12_PASSWORD }}
- name: Build .app bundle (sign + notarize when secrets present)
env:
APPLE_DEVELOPER_ID_APPLICATION: ${{ secrets.APPLE_DEVELOPER_ID_APPLICATION }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: bash macos/scripts/build-app.sh
- name: Build .pkg installer (signed when installer cert present)
env:
APPLE_DEVELOPER_ID_INSTALLER: ${{ secrets.APPLE_DEVELOPER_ID_INSTALLER }}
run: bash macos/scripts/build-pkg.sh
- name: Zip .app bundle
run: |
cd macos/build
zip -r --symlinks "InterlinedSync-macOS-${{ github.ref_name }}.zip" InterlinedSync.app
- name: Rename .pkg for release
run: |
cd macos/build
PKG_SRC="$(ls InterlinedSync-*.pkg | grep -v "macOS-${{ github.ref_name }}" | head -n 1 || true)"
if [[ -n "${PKG_SRC}" ]]; then
mv "${PKG_SRC}" "InterlinedSync-macOS-${{ github.ref_name }}.pkg"
fi
- name: Upload macOS artifacts
uses: actions/upload-artifact@v4
with:
name: macos-release
path: |
macos/build/InterlinedSync-macOS-${{ github.ref_name }}.zip
macos/build/InterlinedSync-macOS-${{ github.ref_name }}.pkg
if-no-files-found: error
retention-days: 1
# ---------------------------------------------------------------------------
# Windows — framework-dependent publish (requires .NET 9 Desktop Runtime)
# PLUS an Inno Setup installer (InterlinedListSync-Setup-<tag>.exe). Both
# are uploaded; the installer is the recommended distribution channel and
# the .zip remains for users who prefer xcopy deploy. The installer is
# signed when WINDOWS_CERT_PFX_BASE64 + WINDOWS_CERT_PASSWORD secrets
# exist; otherwise it ships UNSIGNED (SmartScreen will warn on first
# install). See windows/PACKAGING.md for the full signing checklist.
# MSIX is deferred — see comment further down.
# ---------------------------------------------------------------------------
build-windows:
name: Build Windows
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET 9
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v2
- name: Restore packages
working-directory: windows
run: dotnet restore
- name: Publish
working-directory: windows
run: >
dotnet publish InterlinedSync/InterlinedSync.csproj
--configuration Release
--no-restore
--runtime win-x64
--self-contained false
--output publish
- name: Zip publish output
shell: pwsh
run: |
Compress-Archive `
-Path windows/publish/* `
-DestinationPath "InterlinedSync-Windows-${{ github.ref_name }}.zip"
- name: Install Inno Setup
shell: pwsh
run: choco install innosetup -y --no-progress
- name: Compute installer version
id: insver
shell: pwsh
run: |
# Strip a leading 'v' to match Inno's VersionInfoVersion expectations
# (Inno tolerates the suffix but the leading 'v' confuses some
# Windows VersionInfo consumers).
$tag = "${{ github.ref_name }}"
$ver = $tag -replace '^v',''
"version=$ver" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
- name: Detect Windows signing secrets
id: winsign
shell: pwsh
env:
PFX_B64: ${{ secrets.WINDOWS_CERT_PFX_BASE64 }}
run: |
if ($env:PFX_B64) {
"have_cert=true" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
} else {
"have_cert=false" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
}
- name: Decode signing certificate
if: steps.winsign.outputs.have_cert == 'true'
shell: pwsh
env:
PFX_B64: ${{ secrets.WINDOWS_CERT_PFX_BASE64 }}
run: |
$bytes = [Convert]::FromBase64String($env:PFX_B64)
[IO.File]::WriteAllBytes("$env:RUNNER_TEMP\codesign.pfx", $bytes)
- name: Compile Inno Setup installer (unsigned)
if: steps.winsign.outputs.have_cert != 'true'
shell: pwsh
run: |
& "C:\Program Files (x86)\Inno Setup 6\iscc.exe" `
/Qp `
/DAppVersion=${{ steps.insver.outputs.version }} `
windows/installer/InterlinedSync.iss
- name: Compile Inno Setup installer (signed)
if: steps.winsign.outputs.have_cert == 'true'
shell: pwsh
env:
PFX_PASSWORD: ${{ secrets.WINDOWS_CERT_PASSWORD }}
run: |
# Locate signtool (newest Windows SDK build).
$signtool = Get-ChildItem `
-Path "C:\Program Files (x86)\Windows Kits\10\bin" `
-Recurse -Filter signtool.exe `
| Where-Object { $_.FullName -like "*\x64\signtool.exe" } `
| Sort-Object FullName -Descending `
| Select-Object -First 1
if (-not $signtool) {
throw "signtool.exe not found on runner."
}
$pfx = "$env:RUNNER_TEMP\codesign.pfx"
# Inno invokes the named SignTool template once per file ($f).
$signCmd = "`"$($signtool.FullName)`" sign /fd sha256 /tr http://timestamp.digicert.com /td sha256 /f `"$pfx`" /p `"$env:PFX_PASSWORD`" `$f"
& "C:\Program Files (x86)\Inno Setup 6\iscc.exe" `
/Qp `
/DAppVersion=${{ steps.insver.outputs.version }} `
/DSignToolConfigured=1 `
"/Ssigntool=$signCmd" `
windows/installer/InterlinedSync.iss
- name: Stage installer for upload
shell: pwsh
run: |
# The .iss outputs to windows/installer/Output/. Copy to a flat
# location so the upload-artifact glob below picks it up reliably.
Copy-Item `
-Path "windows/installer/Output/InterlinedListSync-Setup-${{ steps.insver.outputs.version }}.exe" `
-Destination "InterlinedListSync-Setup-${{ github.ref_name }}.exe"
# -------------------------------------------------------------------
# MSIX packaging deferred from CI: the windows-latest runner's
# Windows SDK does not include UAP.props for our target version
# (APPX3217 across multiple version values). Build MSIX on a
# maintainer machine via Visual Studio for now; revisit once we
# standardize on a runner with the right SDK pre-installed.
# See windows/PACKAGING.md.
# -------------------------------------------------------------------
- name: Upload Windows artifacts
uses: actions/upload-artifact@v4
with:
name: windows-release
path: |
InterlinedSync-Windows-${{ github.ref_name }}.zip
InterlinedListSync-Setup-${{ github.ref_name }}.exe
if-no-files-found: error
retention-days: 1
# ---------------------------------------------------------------------------
# Linux — .deb package (via cargo-deb) + standalone binary tarball + .snap
# Snap build uses snapcraft with --destructive-mode (no LXD in GitHub runners).
# Publishing to the Snap Store is a separate manual step — see PACKAGING.md.
# ---------------------------------------------------------------------------
build-linux:
name: Build Linux
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
pkg-config \
libdbus-1-dev \
libsqlite3-dev \
libsecret-1-dev
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
with:
workspaces: linux-ubuntu
- name: Build release binary
working-directory: linux-ubuntu
run: cargo build --release
- name: Install cargo-deb
run: cargo install cargo-deb --version "2.4.0"
- name: Build .deb package
working-directory: linux-ubuntu
run: cargo deb -p interlinedlist-sync --no-build
- name: Create binary tarball
run: |
tar -czf "linux-ubuntu/InterlinedSync-Linux-${{ github.ref_name }}.tar.gz" \
-C linux-ubuntu/target/release interlinedlist-sync
# -------------------------------------------------------------------
# Snap packaging — M7
# Deferred from CI for now: snap-in-snap install of core22 fails
# inside GitHub-hosted runners (snapd restrictions in destructive
# mode). Reach parity by building the snap on a maintainer machine
# or via snapcraft remote-build, then uploading manually. See
# linux-ubuntu/PACKAGING.md and snap/snapcraft.yaml.
# -------------------------------------------------------------------
- name: Upload Linux artifacts
uses: actions/upload-artifact@v4
with:
name: linux-release
path: |
linux-ubuntu/target/debian/*.deb
linux-ubuntu/InterlinedSync-Linux-${{ github.ref_name }}.tar.gz
linux-ubuntu/*.snap
if-no-files-found: error
retention-days: 1
# ---------------------------------------------------------------------------
# Create a draft GitHub Release and attach all platform artifacts.
# Publish the release manually from the GitHub UI after reviewing the files.
# ---------------------------------------------------------------------------
create-release:
name: Create GitHub Release
needs: [build-macos, build-windows, build-linux]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download all release artifacts
uses: actions/download-artifact@v4
with:
path: release-artifacts
merge-multiple: true
- name: List artifacts
run: ls -lh release-artifacts/
- name: Write release notes
run: |
TAG="${{ github.ref_name }}"
cat > release-notes.md << EOF
## InterlinedList Sync ${TAG}
### Downloads
| Platform | File | Notes |
|----------|------|-------|
| macOS 13 Ventura or later | \`InterlinedSync-macOS-${TAG}.zip\` | Unzip and move to \`/Applications\`. Unsigned — right-click → Open to bypass Gatekeeper on first launch. |
| Windows 10 / 11 (x64) — installer | \`InterlinedListSync-Setup-${TAG}.exe\` | Double-click and follow the wizard. Adds a Start Menu entry and optional autostart. Requires [.NET 9 Desktop Runtime](https://dotnet.microsoft.com/download/dotnet/9.0). |
| Windows 10 / 11 (x64) — portable | \`InterlinedSync-Windows-${TAG}.zip\` | Unzip and run \`InterlinedSync.exe\` for users who prefer no installer. Same runtime prerequisite. |
| Ubuntu / Debian (amd64) | \`interlinedlist-sync_*.deb\` | \`sudo dpkg -i interlinedlist-sync_*.deb\` |
| Linux standalone binary | \`InterlinedSync-Linux-${TAG}.tar.gz\` | Extract and place \`interlinedlist-sync\` in your \`\$PATH\`. |
### First-time setup
1. Edit \`~/.config/interlinedlist-sync/config.toml\` — set \`sync.watched_dirs\` and \`network.api_base_url\`.
2. Store credentials once: \`interlinedlist-sync --login --username you@example.com --password …\`
3. Start the daemon: \`interlinedlist-sync --daemon\` (Linux: managed by the installed systemd user unit)
### What changed
See the commit history for details.
EOF
- name: Create draft GitHub Release
env:
GH_TOKEN: ${{ github.token }}
run: |
PRERELEASE_FLAG=""
if [[ "${{ github.ref_name }}" == *-* ]]; then
PRERELEASE_FLAG="--prerelease"
fi
# Use find -type f to enumerate only files; the upload-artifact
# action preserves the source directory structure inside each
# artifact zip (e.g. target/debian/*.deb -> release-artifacts/target/),
# and gh release create refuses to upload directories.
gh release create "${{ github.ref_name }}" \
--title "InterlinedList Sync ${{ github.ref_name }}" \
--notes-file release-notes.md \
--draft \
$PRERELEASE_FLAG \
$(find release-artifacts -type f)