mirror of
https://github.com/priyanshujain/margin-calendar.git
synced 2026-10-02 11:07:04 +00:00
Build and sign releases in CI, and install locally with one command
Ported from margin's pipeline, with the platform list this app actually claims. Release is manual: it bumps tauri.conf.json, package.json and Cargo.toml together, tags, and then builds the tag rather than whatever main has drifted to by the time the runners pick it up. Nothing publishes until every platform lands. The last job downloads latest.json and refuses to take the release out of draft unless darwin-aarch64, darwin-x86_64 and linux-x86_64 are all present, because a half-populated manifest is worse than no release at all: the updater would offer an update to the platforms that made it and error on the ones that did not. Linux builds on Ubuntu 22.04 rather than latest. The bundle will not run on anything older than the glibc it was linked against, and 22.04 is the baseline docs/setup.md commits to. Windows is not built, matching the bundle targets and the README; adding it is a matrix entry, msi and nsis in the targets, and windows-x86_64 in the publish gate. The updater had a plugin, a capability and a menu item but no keypair and no endpoint, so releases would have produced artifacts nothing could verify. The public half is now in tauri.release.conf.json and the private half is a repository secret, alongside the Google OAuth client that build.rs embeds. Without that secret the build falls back to the example credentials and warns rather than failing, which yields an app that runs and then says Google Calendar is not set up. CI enforces the gate setup.md already names, the two test suites, and nothing more. cargo fmt --check and cargo clippy -D warnings both fail on the tree as it stands, and adopting either is a cleanup pass to decide on rather than something to bolt onto a new pipeline. justfile is the local equivalent of all this. `just install` builds for the machine it is run on and installs it, and is the same command whether or not the app is already there, so it doubles as the update. On macOS it asks a running copy to quit first, because replacing a bundle under a live process leaves it half old and half new.
This commit is contained in:
1 parent
d4c3a304b5
commit
590506eb92
6 files changed
+458
-5
No files matched your search
@@ -0,0 +1,70 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 26
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
# `pnpm build` is tsc then vite, so this is the typecheck and the bundle in one step.
|
||||
- run: pnpm build
|
||||
|
||||
- run: pnpm test
|
||||
|
||||
rust:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
libwebkit2gtk-4.1-dev \
|
||||
libgtk-3-dev \
|
||||
libayatana-appindicator3-dev \
|
||||
librsvg2-dev \
|
||||
patchelf \
|
||||
libxdo-dev \
|
||||
libssl-dev \
|
||||
build-essential
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- uses: swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: src-tauri -> target
|
||||
|
||||
# tauri_build::build() wants a frontendDist that exists, and build.rs wants credentials to
|
||||
# embed. The example file is what a fresh clone compiles against, so that is what CI uses.
|
||||
- name: Stub the build inputs
|
||||
run: |
|
||||
mkdir -p dist && touch dist/index.html
|
||||
cp google-credentials.example.json google-credentials.json
|
||||
|
||||
# The gate docs/setup.md names is the test suites, and that is all this enforces. `cargo fmt
|
||||
# --check` and `cargo clippy -D warnings` both fail on the tree as it stands; adopting either
|
||||
# is a cleanup pass to decide on separately, not something to bolt onto CI first.
|
||||
- name: Test
|
||||
working-directory: src-tauri
|
||||
run: cargo test
|
||||
@@ -0,0 +1,181 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Release version, e.g. 0.2.0. Leave empty to bump the patch number."
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
tag: ${{ steps.version.outputs.tag }}
|
||||
release_id: ${{ steps.release.outputs.release_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Determine version
|
||||
id: version
|
||||
run: |
|
||||
if [ -n "${{ inputs.version }}" ]; then
|
||||
VERSION="${{ inputs.version }}"
|
||||
VERSION="${VERSION#v}"
|
||||
else
|
||||
CURRENT=$(jq -r .version src-tauri/tauri.conf.json)
|
||||
IFS=. read -r MAJOR MINOR PATCH <<< "$CURRENT"
|
||||
VERSION="$MAJOR.$MINOR.$((PATCH + 1))"
|
||||
fi
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "Releasing v$VERSION"
|
||||
|
||||
- name: Bump version in manifests
|
||||
env:
|
||||
VERSION: ${{ steps.version.outputs.version }}
|
||||
run: |
|
||||
tmp=$(mktemp)
|
||||
jq --arg v "$VERSION" '.version = $v' src-tauri/tauri.conf.json > "$tmp" && mv "$tmp" src-tauri/tauri.conf.json
|
||||
jq --arg v "$VERSION" '.version = $v' package.json > "$tmp" && mv "$tmp" package.json
|
||||
sed -i "0,/^version = \".*\"/s//version = \"$VERSION\"/" src-tauri/Cargo.toml
|
||||
|
||||
- name: Commit and tag
|
||||
env:
|
||||
TAG: ${{ steps.version.outputs.tag }}
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git add src-tauri/tauri.conf.json package.json src-tauri/Cargo.toml
|
||||
git commit -m "chore(release): $TAG"
|
||||
for attempt in 1 2 3 4 5; do
|
||||
git fetch origin main
|
||||
git rebase origin/main
|
||||
if git push origin HEAD; then
|
||||
break
|
||||
fi
|
||||
if [ "$attempt" = "5" ]; then
|
||||
echo "::error::main kept advancing; could not push release bump after 5 attempts."
|
||||
exit 1
|
||||
fi
|
||||
echo "main advanced during release; rebasing and retrying ($attempt)…"
|
||||
sleep 3
|
||||
done
|
||||
git tag "$TAG"
|
||||
git push origin "$TAG"
|
||||
|
||||
- name: Create draft release
|
||||
id: release
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
TAG: ${{ steps.version.outputs.tag }}
|
||||
run: |
|
||||
gh release create "$TAG" --draft --title "Margin Calendar $TAG" --notes "Release $TAG"
|
||||
ID=$(gh release view "$TAG" --json databaseId --jq .databaseId)
|
||||
echo "release_id=$ID" >> "$GITHUB_OUTPUT"
|
||||
|
||||
build:
|
||||
needs: prepare
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-26
|
||||
args: "--target universal-apple-darwin --config src-tauri/tauri.release.conf.json"
|
||||
rust-targets: "aarch64-apple-darwin,x86_64-apple-darwin"
|
||||
# Ubuntu 22.04 is the glibc baseline docs/setup.md commits to: the bundle will not run on
|
||||
# anything older than the glibc it was linked against, so build on the oldest supported.
|
||||
- os: ubuntu-22.04
|
||||
args: "--config src-tauri/tauri.release.conf.json"
|
||||
rust-targets: ""
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ needs.prepare.outputs.tag }}
|
||||
|
||||
- name: Install Linux dependencies
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
libwebkit2gtk-4.1-dev \
|
||||
libgtk-3-dev \
|
||||
libayatana-appindicator3-dev \
|
||||
librsvg2-dev \
|
||||
patchelf \
|
||||
libxdo-dev \
|
||||
libssl-dev \
|
||||
build-essential \
|
||||
curl \
|
||||
wget \
|
||||
file
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 26
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
with:
|
||||
version: 10
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.rust-targets }}
|
||||
|
||||
- uses: swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: src-tauri -> target
|
||||
|
||||
- name: Install frontend dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Provision Google credentials
|
||||
shell: bash
|
||||
env:
|
||||
GOOGLE_CREDENTIALS: ${{ secrets.GOOGLE_CREDENTIALS }}
|
||||
run: |
|
||||
if [ -n "$GOOGLE_CREDENTIALS" ]; then
|
||||
printf '%s' "$GOOGLE_CREDENTIALS" > google-credentials.json
|
||||
echo "Wrote google-credentials.json from GOOGLE_CREDENTIALS secret."
|
||||
else
|
||||
cp google-credentials.example.json google-credentials.json
|
||||
echo "::warning::GOOGLE_CREDENTIALS secret not set — embedding placeholder credentials; this build cannot connect to Google Calendar."
|
||||
fi
|
||||
|
||||
- name: Build and upload
|
||||
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:
|
||||
releaseId: ${{ needs.prepare.outputs.release_id }}
|
||||
args: ${{ matrix.args }}
|
||||
|
||||
publish:
|
||||
needs: [prepare, build]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Verify manifest is complete, then publish
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
REPO: ${{ github.repository }}
|
||||
TAG: ${{ needs.prepare.outputs.tag }}
|
||||
run: |
|
||||
gh release download "$TAG" --repo "$REPO" --pattern latest.json --output latest.json --clobber
|
||||
echo "Platforms in latest.json:"
|
||||
jq '.platforms | keys' latest.json
|
||||
for key in darwin-aarch64 darwin-x86_64 linux-x86_64; do
|
||||
if ! jq -e ".platforms[\"$key\"].url" latest.json > /dev/null; then
|
||||
echo "::error::latest.json is missing platform '$key' — refusing to publish a partial update manifest. Re-run the release."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
gh release edit "$TAG" --repo "$REPO" --draft=false --latest
|
||||
Reference in new issue
Block a user