Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
524f401
Create 260311-tauri-auto-update-phase2-feasibility.md
callumflack Mar 11, 2026
e868a05
docs: reorganize auto-update feasibility spike result
callumflack Mar 11, 2026
8306c84
build(macos): remove redundant bundled node_modules copy
callumflack Mar 11, 2026
d7f0637
docs(updater): formalize phase 2 track B handoff
callumflack Mar 11, 2026
9019821
build(updater): generate macOS updater artifacts after final signing
callumflack Mar 11, 2026
2e87d60
docs(plan): record macOS updater notarization proof
callumflack Mar 11, 2026
f5f9d69
ci(updater): harden macOS release artifact validation
callumflack Mar 11, 2026
860950f
docs(plan): add macOS updater CI proof run checklist
callumflack Mar 11, 2026
450084d
ci(updater): disable tauri-action release uploads
callumflack Mar 11, 2026
8820c8b
release: v0.7.36
callumflack Mar 24, 2026
b505b2b
release: v0.7.37
callumflack Mar 24, 2026
ece1532
fix(updater): call tauri signer through npm script
callumflack Mar 24, 2026
34158d0
release: v0.7.38
callumflack Mar 24, 2026
74e2b21
fix(updater): preserve tauri cli for macOS artifact signing
callumflack Mar 24, 2026
60f5232
release: v0.7.39
callumflack Mar 24, 2026
db2e707
fix(updater): ignore empty signing key path in CI
callumflack Mar 24, 2026
f65168e
release: v0.7.40
callumflack Mar 24, 2026
1a48116
fix(updater): drop raw tauri macOS tarballs
callumflack Mar 24, 2026
8e54e70
release: v0.7.41
callumflack Mar 24, 2026
8374f4d
fix(release): normalize intel macOS artifact names
callumflack Mar 24, 2026
2f3237e
docs(updater): record proof status and runtime cut
callumflack Mar 24, 2026
6e88566
release: v0.7.42
callumflack Mar 24, 2026
7cbf42a
feat(updater): add macOS staged runtime update flow
callumflack Mar 24, 2026
e82339c
chore(release): cut v0.7.43
callumflack Mar 24, 2026
3f5add1
build(updater): refresh Cargo.lock
callumflack Mar 24, 2026
0820534
docs(updater): add clean-room rebuild handoff docs
callumflack Mar 24, 2026
94011f9
docs(updater): clarify secret sources for handoff
callumflack Mar 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 120 additions & 40 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
node-version: "22"
cache: "npm"

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
Expand Down Expand Up @@ -154,7 +154,6 @@ jobs:
- name: Build Tauri app
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VITE_PRIVY_APP_ID: ${{ secrets.VITE_PRIVY_APP_ID }}
VITE_PRIVY_CLIENT_ID: ${{ secrets.VITE_PRIVY_CLIENT_ID }}
VITE_SESSION_RELAY_URL: ${{ secrets.VITE_SESSION_RELAY_URL }}
Expand All @@ -166,11 +165,6 @@ jobs:
APPLE_API_KEY: ${{ secrets.APPLE_ASC_API_KEY_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_ASC_API_KEY_ISSUER_UUID }}
with:
tagName: ${{ github.ref_name }}
releaseName: 'DataConnect v__VERSION__'
releaseBody: 'See the assets to download this version and install.'
releaseDraft: false
prerelease: false
args: --target ${{ matrix.target }}

- name: Free disk space before finalization
Expand All @@ -182,22 +176,27 @@ jobs:
rm -rf src-tauri/target/${{ matrix.target }}/release/build
rm -rf src-tauri/target/${{ matrix.target }}/release/.fingerprint
rm -rf src-tauri/target/${{ matrix.target }}/release/incremental
# Remove npm caches no longer needed
rm -rf node_modules
# Keep root node_modules until finalization completes because the
# custom macOS updater artifact signer uses the repo-pinned Tauri CLI.
# Removing it here breaks updater tarball signing in Finalize bundles.
rm -rf playwright-runner/node_modules
rm -rf ~/.cargo/registry/cache
echo "=== Disk usage after cleanup ==="
df -h / || true
shell: bash

- name: Copy native modules and finalize bundles
- name: Finalize bundles
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 }}
run: |
set -x # Enable verbose debugging
set -euo pipefail

# Copy node_modules into macOS .app bundles (preserving directory structure)
# Re-sign the completed macOS app and recreate the DMG from the final app.
# personal-server/dist/node_modules is already bundled by Tauri resources.
if [ "${{ matrix.platform }}" = "macos-latest" ]; then
echo "=== Finalizing macOS bundles for ${{ matrix.target }} ==="
# Debug: Show what's in personal-server/dist
echo "=== Contents of personal-server/dist ==="
ls -la personal-server/dist/ || echo "dist not found"
Expand All @@ -214,34 +213,23 @@ jobs:
"$node_binary"
done

for app in src-tauri/target/${{ matrix.target }}/release/bundle/macos/*.app; do
[ -e "$app" ] || { echo "No .app found at $app"; continue; }
echo "=== Processing $app ==="

# Show current state
echo "Before copy - Resources contents:"
ls -la "$app/Contents/Resources/personal-server/dist/" || echo "personal-server/dist not in Resources"

dest="$app/Contents/Resources/personal-server/dist/node_modules"
mkdir -p "$dest"

# Copy with verbose output
if [ -d "personal-server/dist/node_modules" ]; then
cp -Rv personal-server/dist/node_modules/* "$dest/"
echo "=== After copy - node_modules contents ==="
ls -la "$dest/" || echo "copy failed"
else
echo "ERROR: personal-server/dist/node_modules does not exist!"
exit 1
fi

echo "Copied node_modules to $dest"
done

# Re-sign nested binaries with their entitlements, then re-sign the .app
for app in src-tauri/target/${{ matrix.target }}/release/bundle/macos/*.app; do
[ -e "$app" ] || continue

echo "=== Processing $app ==="
app_name=$(basename "$app" .app)
version=$(grep '"version"' src-tauri/tauri.conf.json | head -1 | sed 's/.*: "\(.*\)".*/\1/')
arch=$(echo "${{ matrix.target }}" | cut -d- -f1)
public_arch="$arch"
if [ "$public_arch" = "x86_64" ]; then
public_arch="x64"
fi
updater_name="${app_name}_${version}_${public_arch}.app.tar.gz"
echo "Bundled resource contents:"
ls -la "$app/Contents/Resources/personal-server/dist/" || echo "personal-server/dist not in Resources"
ls -la "$app/Contents/Resources/personal-server/dist/node_modules/" || { echo "ERROR: node_modules NOT in app bundle!"; exit 1; }

# Sign personal-server binary with JIT entitlements (--deep would strip them)
ps_bin="$app/Contents/Resources/personal-server/dist/${{ matrix.ps_binary_name }}"
if [ -f "$ps_bin" ]; then
Expand All @@ -264,6 +252,44 @@ jobs:
--sign "Developer ID Application: Corsali, Inc (${{ secrets.APPLE_TEAM_ID }})" \
"$app"
echo "Re-signed $app"

# Notarize/staple the finalized .app before packaging the updater tarball.
APPLE_NOTARY_KEY_PATH="$APPLE_API_KEY_PATH" \
APPLE_NOTARY_KEY_ID="${{ secrets.APPLE_ASC_API_KEY_ID }}" \
APPLE_NOTARY_ISSUER="${{ secrets.APPLE_ASC_API_KEY_ISSUER_UUID }}" \
node scripts/notarize-macos-app.mjs \
--app "$app" \
--output-dir "src-tauri/target/${{ matrix.target }}/release/bundle/macos"

# tauri-action still leaves behind a raw archless updater tarball
# for the pre-finalization .app. Remove those artifacts so the
# release only publishes the finalized, versioned updater payloads.
raw_updater_name="$(basename "$app").tar.gz"
raw_updater_sig_name="${raw_updater_name}.sig"
rm -f \
"src-tauri/target/${{ matrix.target }}/release/bundle/macos/$raw_updater_name" \
"src-tauri/target/${{ matrix.target }}/release/bundle/macos/$raw_updater_sig_name"

# Create updater artifacts from the finalized notarized .app, not the pre-finalization Tauri output.
if [ -n "$TAURI_SIGNING_PRIVATE_KEY" ] || [ -n "$TAURI_SIGNING_PRIVATE_KEY_PATH" ]; then
node scripts/build-macos-updater-artifacts.mjs \
--app "$app" \
--output-dir "src-tauri/target/${{ matrix.target }}/release/bundle/macos" \
--artifact-name "$updater_name"

# Smoke-check the updater payload after tar packaging. This must stay a hard gate.
updater_smoke_dir="/tmp/updater_smoke_${arch}"
rm -rf "$updater_smoke_dir"
mkdir -p "$updater_smoke_dir"
tar -xzf "src-tauri/target/${{ matrix.target }}/release/bundle/macos/$updater_name" -C "$updater_smoke_dir"
extracted_app="$updater_smoke_dir/$(basename "$app")"
xcrun stapler validate "$extracted_app"
spctl --assess -vv "$extracted_app"
codesign --verify --strict "$extracted_app"
rm -rf "$updater_smoke_dir"
else
echo "::notice::Skipping finalized macOS updater artifact generation because no Tauri updater signing key is configured"
fi
done

# Recreate DMG with updated .app (including Applications symlink)
Expand All @@ -272,7 +298,11 @@ jobs:
app_name=$(basename "$app" .app)
version=$(grep '"version"' src-tauri/tauri.conf.json | head -1 | sed 's/.*: "\(.*\)".*/\1/')
arch=$(echo "${{ matrix.target }}" | cut -d- -f1)
dmg_name="${app_name}_${version}_${arch}.dmg"
public_arch="$arch"
if [ "$public_arch" = "x86_64" ]; then
public_arch="x64"
fi
dmg_name="${app_name}_${version}_${public_arch}.dmg"
dmg_path="src-tauri/target/${{ matrix.target }}/release/bundle/dmg/${dmg_name}"

# Create temp folder with app and Applications symlink
Expand Down Expand Up @@ -314,7 +344,7 @@ jobs:
# Notarize the DMG using App Store Connect API key
echo "=== Notarizing $dmg_path ==="
if xcrun notarytool submit "$dmg_path" \
--key "${{ env.APPLE_API_KEY_PATH }}" \
--key "$APPLE_API_KEY_PATH" \
--key-id "${{ secrets.APPLE_ASC_API_KEY_ID }}" \
--issuer "${{ secrets.APPLE_ASC_API_KEY_ISSUER_UUID }}" \
--wait 2>&1 | tee /tmp/notarize_${arch}.log; then
Expand All @@ -328,7 +358,7 @@ jobs:
if [ -n "$submission_id" ]; then
echo "=== Fetching notarization log for $submission_id ==="
xcrun notarytool log "$submission_id" \
--key "${{ env.APPLE_API_KEY_PATH }}" \
--key "$APPLE_API_KEY_PATH" \
--key-id "${{ secrets.APPLE_ASC_API_KEY_ID }}" \
--issuer "${{ secrets.APPLE_ASC_API_KEY_ISSUER_UUID }}" || true
fi
Expand Down Expand Up @@ -378,6 +408,13 @@ jobs:
echo "Uploaded $(basename "$f")"
fi
done
for f in src-tauri/target/${{ matrix.target }}/release/bundle/macos/*.app.tar.gz \
src-tauri/target/${{ matrix.target }}/release/bundle/macos/*.app.tar.gz.sig; do
if [ -f "$f" ]; then
gh release upload "${{ github.ref_name }}" "$f" --clobber
echo "Uploaded $(basename "$f")"
fi
done
fi

# Upload Linux artifacts
Expand Down Expand Up @@ -407,3 +444,46 @@ jobs:
if: matrix.platform == 'macos-latest' && always()
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true

publish_updater_manifest:
needs: build
runs-on: ubuntu-22.04

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"

- name: Build latest.json
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail

manifest_dir="/tmp/dataconnect-updater-manifest"
signatures_dir="$manifest_dir/signatures"
release_json="$manifest_dir/release.json"
output_path="$manifest_dir/latest.json"

rm -rf "$manifest_dir"
mkdir -p "$signatures_dir"

gh api "repos/${{ github.repository }}/releases/tags/${{ github.ref_name }}" > "$release_json"
gh release download "${{ github.ref_name }}" \
--pattern "*.app.tar.gz.sig" \
--dir "$signatures_dir"

node scripts/build-updater-manifest.mjs \
--release-json "$release_json" \
--signature-dir "$signatures_dir" \
--output "$output_path"

- name: Upload latest.json
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload "${{ github.ref_name }}" /tmp/dataconnect-updater-manifest/latest.json --clobber
Loading