Skip to content

DataConnect v0.7.33 #114

DataConnect v0.7.33

DataConnect v0.7.33 #114

Workflow file for this run

name: Release
on:
release:
types: [created]
permissions:
contents: write
env:
CARGO_TERM_COLOR: always
jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
target: aarch64-apple-darwin
pkg_target: node22-macos-arm64
binary_name: playwright-runner
ps_binary_name: personal-server
- platform: macos-latest
target: x86_64-apple-darwin
pkg_target: node22-macos-x64
binary_name: playwright-runner
ps_binary_name: personal-server
- platform: ubuntu-22.04
target: x86_64-unknown-linux-gnu
pkg_target: node22-linux-x64
binary_name: playwright-runner
ps_binary_name: personal-server
- platform: windows-latest
target: x86_64-pc-windows-msvc
pkg_target: node22-win-x64
binary_name: playwright-runner.exe
ps_binary_name: personal-server.exe
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Verify version matches release tag
shell: bash
run: |
tag="${{ github.ref_name }}"
conf_version=$(grep '"version"' src-tauri/tauri.conf.json | head -1 | sed 's/.*"\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)".*/\1/')
tag_version="${tag#v}"
if [ "$conf_version" != "$tag_version" ]; then
echo "::error::Version mismatch: tauri.conf.json has '$conf_version' but release tag is '$tag' ($tag_version)."
echo "::error::Run 'npm run release:github -- --version $tag_version' instead of creating a release manually."
exit 1
fi
echo "Version check passed: $conf_version matches tag $tag"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'npm'
- name: Setup Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install dependencies (Ubuntu)
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: Install Apple certificate and API key
if: matrix.platform == 'macos-latest'
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_BUILD_CERTIFICATE_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_BUILD_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.APPLE_BUILD_CERTIFICATE_PASSWORD }}
APPLE_API_KEY: ${{ secrets.APPLE_ASC_API_KEY_KEY_BASE64 }}
APPLE_API_KEY_ID: ${{ secrets.APPLE_ASC_API_KEY_ID }}
run: |
# Create variables
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
API_KEY_PATH=$RUNNER_TEMP/AuthKey_${APPLE_API_KEY_ID}.p8
# Decode certificate
echo -n "$APPLE_CERTIFICATE" | base64 --decode -o $CERTIFICATE_PATH
# Decode API key for notarization
echo -n "$APPLE_API_KEY" | base64 --decode -o $API_KEY_PATH
echo "APPLE_API_KEY_PATH=$API_KEY_PATH" >> $GITHUB_ENV
# Create temporary keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
# Import certificate to keychain
security import $CERTIFICATE_PATH -P "$APPLE_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
security list-keychain -d user -s $KEYCHAIN_PATH
- name: Install frontend dependencies
run: npm ci
- name: Install playwright-runner dependencies
working-directory: playwright-runner
run: npm ci
- name: Build playwright-runner binary
working-directory: playwright-runner
run: |
npx pkg index.cjs -t ${{ matrix.pkg_target }} -o dist/${{ matrix.binary_name }} --no-bytecode --public-packages '*' --public
- name: Sign playwright-runner binary (macOS)
if: matrix.platform == 'macos-latest'
run: |
codesign --force --options runtime --timestamp \
--entitlements playwright-runner/entitlements.plist \
--sign "Developer ID Application: Corsali, Inc (${{ secrets.APPLE_TEAM_ID }})" \
playwright-runner/dist/${{ matrix.binary_name }}
- name: Install personal-server dependencies
working-directory: personal-server
run: npm ci
- name: Build personal-server binary
working-directory: personal-server
run: npm run build
- name: Sign personal-server binary (macOS)
if: matrix.platform == 'macos-latest'
run: |
codesign --force --options runtime --timestamp \
--entitlements personal-server/entitlements.plist \
--sign "Developer ID Application: Corsali, Inc (${{ secrets.APPLE_TEAM_ID }})" \
personal-server/dist/${{ matrix.ps_binary_name }}
- name: Sign personal-server native modules (macOS)
if: matrix.platform == 'macos-latest'
run: |
echo "=== Signing native .node binaries in personal-server/dist/node_modules ==="
find personal-server/dist/node_modules -name "*.node" -type f | while read node_binary; do
echo "Signing: $node_binary"
codesign --force --options runtime --timestamp \
--sign "Developer ID Application: Corsali, Inc (${{ secrets.APPLE_TEAM_ID }})" \
"$node_binary"
done
- 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 }}
VITE_GATEWAY_URL: ${{ secrets.VITE_GATEWAY_URL }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_BUILD_CERTIFICATE_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_BUILD_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: "Developer ID Application: Corsali, Inc (${{ secrets.APPLE_TEAM_ID }})"
APPLE_API_KEY_PATH: ${{ env.APPLE_API_KEY_PATH }}
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
run: |
echo "=== Disk usage before cleanup ==="
df -h / || true
# Remove Rust build artifacts (no longer needed after Tauri build)
rm -rf src-tauri/target/${{ matrix.target }}/release/deps
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
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
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -x # Enable verbose debugging
# Copy node_modules into macOS .app bundles (preserving directory structure)
if [ "${{ matrix.platform }}" = "macos-latest" ]; then
# Debug: Show what's in personal-server/dist
echo "=== Contents of personal-server/dist ==="
ls -la personal-server/dist/ || echo "dist not found"
echo "=== Contents of personal-server/dist/node_modules ==="
ls -la personal-server/dist/node_modules/ || echo "node_modules not found in dist"
# Sign all native .node binaries BEFORE copying to app bundle
# This is required for Apple notarization to succeed
echo "=== Signing native .node binaries ==="
find personal-server/dist/node_modules -name "*.node" -type f | while read node_binary; do
echo "Signing: $node_binary"
codesign --force --options runtime --timestamp \
--sign "Developer ID Application: Corsali, Inc (${{ secrets.APPLE_TEAM_ID }})" \
"$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
# 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
codesign --force --options runtime --timestamp \
--entitlements personal-server/entitlements.plist \
--sign "Developer ID Application: Corsali, Inc (${{ secrets.APPLE_TEAM_ID }})" \
"$ps_bin"
echo "Signed personal-server binary with JIT entitlements"
fi
# Sign native .node addons inside the app bundle
find "$app/Contents/Resources/personal-server/dist/node_modules" -name "*.node" -type f | while read node_binary; do
codesign --force --options runtime --timestamp \
--sign "Developer ID Application: Corsali, Inc (${{ secrets.APPLE_TEAM_ID }})" \
"$node_binary"
done
# Sign the outer .app (without --deep to preserve nested entitlements)
codesign --force --options runtime --timestamp \
--sign "Developer ID Application: Corsali, Inc (${{ secrets.APPLE_TEAM_ID }})" \
"$app"
echo "Re-signed $app"
done
# Recreate DMG with updated .app (including Applications symlink)
for app in src-tauri/target/${{ matrix.target }}/release/bundle/macos/*.app; do
[ -e "$app" ] || continue
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"
dmg_path="src-tauri/target/${{ matrix.target }}/release/bundle/dmg/${dmg_name}"
# Create temp folder with app and Applications symlink
dmg_temp="dmg_temp_${arch}"
rm -rf "$dmg_temp"
mkdir -p "$dmg_temp"
cp -R "$app" "$dmg_temp/"
ln -s /Applications "$dmg_temp/Applications"
# Verify node_modules is in the temp folder before creating DMG
echo "=== Verifying temp folder contents ==="
ls -la "$dmg_temp/"
ls -la "$dmg_temp/$(basename $app)/Contents/Resources/personal-server/dist/" || echo "WARNING: personal-server dist not found in temp"
ls -la "$dmg_temp/$(basename $app)/Contents/Resources/personal-server/dist/node_modules/" || { echo "ERROR: node_modules NOT in temp folder app!"; exit 1; }
# Create DMG (two-step: writable image with padding, then convert to compressed)
# Single-step -srcfolder -format UDZO can underestimate temp volume size
rm -f "$dmg_path"
folder_size_mb=$(du -sm "$dmg_temp" | cut -f1)
image_size_mb=$(( folder_size_mb + 50 ))
echo "Source folder: ${folder_size_mb}MB, image size: ${image_size_mb}MB"
hdiutil create -volname "$app_name" -srcfolder "$dmg_temp" -ov -format UDRW -size "${image_size_mb}m" "${dmg_path}.rw.dmg"
rm -rf "$dmg_temp"
hdiutil convert "${dmg_path}.rw.dmg" -format UDZO -o "$dmg_path"
rm -f "${dmg_path}.rw.dmg"
echo "Created $dmg_path"
# Verify DMG contents by mounting and checking
echo "=== Verifying DMG contents ==="
mount_point="/tmp/verify_dmg_${arch}"
mkdir -p "$mount_point"
hdiutil attach "$dmg_path" -mountpoint "$mount_point" -nobrowse -quiet
ls -la "$mount_point/"
ls -la "$mount_point/DataConnect.app/Contents/Resources/personal-server/dist/" || echo "WARNING: dist not in mounted DMG"
ls -la "$mount_point/DataConnect.app/Contents/Resources/personal-server/dist/node_modules/" || { hdiutil detach "$mount_point" -quiet; echo "ERROR: node_modules NOT in final DMG!"; exit 1; }
hdiutil detach "$mount_point" -quiet
echo "=== DMG verification passed ==="
# 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-id "${{ secrets.APPLE_ASC_API_KEY_ID }}" \
--issuer "${{ secrets.APPLE_ASC_API_KEY_ISSUER_UUID }}" \
--wait 2>&1 | tee /tmp/notarize_${arch}.log; then
# Staple the notarization ticket
xcrun stapler staple "$dmg_path" && echo "Notarized and stapled $dmg_path"
else
echo "::error::Notarization FAILED for $dmg_path"
cat /tmp/notarize_${arch}.log || true
# Get detailed log from Apple
submission_id=$(grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' /tmp/notarize_${arch}.log | head -1)
if [ -n "$submission_id" ]; then
echo "=== Fetching notarization log for $submission_id ==="
xcrun notarytool log "$submission_id" \
--key "${{ env.APPLE_API_KEY_PATH }}" \
--key-id "${{ secrets.APPLE_ASC_API_KEY_ID }}" \
--issuer "${{ secrets.APPLE_ASC_API_KEY_ISSUER_UUID }}" || true
fi
exit 1
fi
done
fi
# Copy node_modules for Linux AppImage (extract, modify, repack)
if [ "${{ matrix.platform }}" = "ubuntu-22.04" ]; then
for appimage in src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage; do
[ -e "$appimage" ] || continue
# Extract AppImage
chmod +x "$appimage"
"$appimage" --appimage-extract
# Copy node_modules
dest="squashfs-root/usr/lib/personal-server/dist/node_modules"
mkdir -p "$dest"
cp -R personal-server/dist/node_modules/* "$dest/"
# Repack (requires appimagetool)
if command -v appimagetool &> /dev/null; then
rm "$appimage"
ARCH=x86_64 appimagetool squashfs-root "$appimage"
fi
rm -rf squashfs-root
echo "Updated AppImage with node_modules"
done
fi
# Copy node_modules for Windows (into the installation directory structure)
if [ "${{ matrix.platform }}" = "windows-latest" ]; then
# For NSIS, we need to modify the installer or include in resources
# The simpler approach is to ensure node_modules is included in the bundle
echo "Windows: node_modules should be handled by resource bundling"
fi
shell: bash
- name: Upload release artifacts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Upload macOS DMG
if [ "${{ matrix.platform }}" = "macos-latest" ]; then
for f in src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg; do
if [ -f "$f" ]; then
gh release upload "${{ github.ref_name }}" "$f" --clobber
echo "Uploaded $(basename "$f")"
fi
done
fi
# Upload Linux artifacts
if [ "${{ matrix.platform }}" = "ubuntu-22.04" ]; then
for f in src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb \
src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage; do
if [ -f "$f" ]; then
gh release upload "${{ github.ref_name }}" "$f" --clobber
echo "Uploaded $(basename "$f")"
fi
done
fi
# Upload Windows artifacts
if [ "${{ matrix.platform }}" = "windows-latest" ]; then
for f in src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*.exe \
src-tauri/target/${{ matrix.target }}/release/bundle/msi/*.msi; do
if [ -f "$f" ]; then
gh release upload "${{ github.ref_name }}" "$f" --clobber
echo "Uploaded $(basename "$f")"
fi
done
fi
shell: bash
- name: Cleanup keychain
if: matrix.platform == 'macos-latest' && always()
run: |
security delete-keychain $RUNNER_TEMP/app-signing.keychain-db || true