Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c9a00d5
Merge pull request #2 from thatsjet/next
thatsjet Feb 11, 2026
5c5d11a
Merge pull request #4 from thatsjet/next
thatsjet Feb 11, 2026
bec8e6e
Merge pull request #6 from thatsjet/next
thatsjet Feb 11, 2026
9a86173
Merge pull request #9 from thatsjet/next
thatsjet Feb 12, 2026
27b3c42
Initial plan
Copilot Feb 12, 2026
9756c4c
Add GitHub Actions release workflow, smoke test script, and release docs
Copilot Feb 12, 2026
8d679e1
Address code review: fix step numbering, add workflow_dispatch to rel…
Copilot Feb 12, 2026
49241a6
Merge pull request #10 from thatsjet/copilot/fix-issue-7
thatsjet Feb 12, 2026
49d80d2
Merge pull request #12 from thatsjet/next
thatsjet Feb 13, 2026
810b202
Merge pull request #13 from thatsjet/next
thatsjet Feb 13, 2026
c0cc99c
added code signing for mac build
thatsjet Feb 13, 2026
014b297
Merge pull request #18 from thatsjet/automate
thatsjet Feb 13, 2026
092ebd6
Fix release smoke test for bash 4- compatibility
thatsjet Feb 14, 2026
5390692
Merge pull request #19 from thatsjet/automate
thatsjet Feb 14, 2026
d3fd36d
Fix GitHub Releases requiring a tag on manual workflow_dispatch
claude Feb 14, 2026
5c4f4ad
Merge pull request #20 from thatsjet/claude/fix-github-releases-tag-z…
thatsjet Feb 14, 2026
a901b3d
Merge pull request #17 from thatsjet/next
thatsjet Feb 18, 2026
f8a3818
Merge pull request #21 from thatsjet/next
thatsjet Feb 18, 2026
fc23b1c
chore: bump version to 0.1.2a
thatsjet Feb 18, 2026
a3a2bc7
Merge pull request #22 from thatsjet/release/0.1.2a
thatsjet Feb 18, 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
270 changes: 270 additions & 0 deletions .github/workflows/release-electron.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
name: Release Electron App

on:
push:
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to release (e.g. v0.2.0)'
required: true
type: string
draft:
description: 'Create as draft release'
required: false
default: 'true'
type: choice
options:
- 'true'
- 'false'

permissions:
contents: write

env:
NODE_VERSION: '20'

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
platform: mac
- os: ubuntu-latest
platform: linux
- os: windows-latest
platform: win

runs-on: ${{ matrix.os }}

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

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- name: Install dependencies
run: npm ci

- name: Build Vite renderer
run: npm run build:vite

- name: Build Electron main process
run: npm run build:electron

# --- macOS signing & notarization ---
- name: Build Electron app (macOS)
if: matrix.platform == 'mac'
run: npx electron-builder --mac --publish never
env:
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}

# --- Windows signing ---
- name: Build Electron app (Windows)
if: matrix.platform == 'win'
run: npx electron-builder --win --publish never
env:
CSC_LINK: ${{ secrets.WINDOWS_CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.WINDOWS_CSC_KEY_PASSWORD }}

# --- Linux build ---
- name: Build Electron app (Linux)
if: matrix.platform == 'linux'
run: npx electron-builder --linux --publish never

# --- Smoke test ---
- name: Smoke test (macOS)
if: matrix.platform == 'mac'
run: |
APP_PATH=$(find release -name "*.app" -maxdepth 2 | head -1)
if [ -z "$APP_PATH" ]; then
echo "ERROR: No .app bundle found in release/"
exit 1
fi
echo "Found app bundle: $APP_PATH"
# Verify the binary exists and is executable
BINARY="$APP_PATH/Contents/MacOS/Nudge"
if [ ! -f "$BINARY" ]; then
echo "ERROR: Binary not found at $BINARY"
exit 1
fi
echo "Binary found at $BINARY"
echo "macOS smoke test passed"

- name: Smoke test (Linux)
if: matrix.platform == 'linux'
run: |
APPIMAGE=$(find release -name "*.AppImage" -maxdepth 1 | head -1)
if [ -z "$APPIMAGE" ]; then
echo "ERROR: No AppImage found in release/"
exit 1
fi
chmod +x "$APPIMAGE"
echo "Found AppImage: $APPIMAGE"
# Verify the AppImage is a valid ELF binary
file "$APPIMAGE" | grep -q "ELF" && echo "Valid ELF binary" || { echo "ERROR: Not a valid ELF binary"; exit 1; }
echo "Linux smoke test passed"

- name: Smoke test (Windows)
if: matrix.platform == 'win'
shell: pwsh
run: |
$exe = Get-ChildItem -Path release -Filter "*.exe" -Recurse | Select-Object -First 1
if (-not $exe) {
Write-Error "ERROR: No .exe found in release/"
exit 1
}
Write-Output "Found installer: $($exe.FullName)"
# Verify file size is reasonable (> 50MB for an Electron app)
$sizeMB = [math]::Round($exe.Length / 1MB, 2)
Write-Output "Installer size: ${sizeMB} MB"
if ($exe.Length -lt 50MB) {
Write-Error "ERROR: Installer seems too small (${sizeMB} MB)"
exit 1
}
Write-Output "Windows smoke test passed"

# --- Vault preservation test ---
- name: Vault preservation test (macOS/Linux)
if: matrix.platform != 'win'
run: bash scripts/release-smoke-test.sh
env:
PLATFORM: ${{ matrix.platform }}

- name: Vault preservation test (Windows)
if: matrix.platform == 'win'
shell: pwsh
run: |
# Simulate an existing vault directory with user data
$vaultDir = Join-Path $env:USERPROFILE "Nudge"
New-Item -ItemType Directory -Force -Path $vaultDir | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $vaultDir "ideas") | Out-Null
New-Item -ItemType Directory -Force -Path (Join-Path $vaultDir "daily") | Out-Null
Set-Content -Path (Join-Path $vaultDir "tasks.md") -Value "# My Tasks`nImportant user data"
Set-Content -Path (Join-Path $vaultDir "config.md") -Value "# My Config`nCustom settings"
Set-Content -Path (Join-Path $vaultDir "ideas" "my-idea.md") -Value "# My Idea`nDo not delete"

# Record file hashes before
$beforeHashes = @{}
Get-ChildItem -Path $vaultDir -Recurse -File | ForEach-Object {
$rel = $_.FullName.Substring($vaultDir.Length + 1)
$beforeHashes[$rel] = (Get-FileHash $_.FullName -Algorithm SHA256).Hash
}

# Verify the vault directory still has all files with identical content
$allPresent = $true
foreach ($entry in $beforeHashes.GetEnumerator()) {
$fullPath = Join-Path $vaultDir $entry.Key
if (-not (Test-Path $fullPath)) {
Write-Error "FAIL: File missing after install: $($entry.Key)"
$allPresent = $false
continue
}
$afterHash = (Get-FileHash $fullPath -Algorithm SHA256).Hash
if ($afterHash -ne $entry.Value) {
Write-Error "FAIL: File content changed: $($entry.Key)"
$allPresent = $false
}
}

if ($allPresent) {
Write-Output "PASS: Vault preservation test passed - all user files intact"
} else {
Write-Error "FAIL: Vault preservation test failed"
exit 1
}

# --- Generate checksums ---
- name: Generate SHA256 checksums (macOS/Linux)
if: matrix.platform != 'win'
run: |
cd release
find . -maxdepth 1 -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.snap" -o -name "*.tar.gz" \) -exec sha256sum {} \; > SHA256SUMS-${{ matrix.platform }}.txt
cat SHA256SUMS-${{ matrix.platform }}.txt

- name: Generate SHA256 checksums (Windows)
if: matrix.platform == 'win'
shell: pwsh
run: |
cd release
$files = Get-ChildItem -File | Where-Object { $_.Extension -in '.exe', '.msi', '.nupkg' }
$checksums = @()
foreach ($f in $files) {
$hash = (Get-FileHash $f.FullName -Algorithm SHA256).Hash.ToLower()
$checksums += "$hash ./$($f.Name)"
}
$checksums | Set-Content -Path "SHA256SUMS-win.txt"
Get-Content "SHA256SUMS-win.txt"

# --- Upload artifacts ---
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: release-${{ matrix.platform }}
path: |
release/*.dmg
release/*.zip
release/*.exe
release/*.AppImage
release/*.deb
release/*.snap
release/*.tar.gz
release/SHA256SUMS-*.txt
if-no-files-found: warn

release:
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v') || github.event_name == 'workflow_dispatch'

steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts
merge-multiple: true

- name: Merge checksums
run: |
cd artifacts
cat SHA256SUMS-*.txt > SHA256SUMS.txt 2>/dev/null || true
echo "=== Combined checksums ==="
cat SHA256SUMS.txt

- name: Determine if draft
id: draft
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
echo "is_draft=${{ github.event.inputs.draft }}" >> "$GITHUB_OUTPUT"
else
echo "is_draft=false" >> "$GITHUB_OUTPUT"
fi

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
draft: ${{ steps.draft.outputs.is_draft == 'true' }}
generate_release_notes: true
files: |
artifacts/*.dmg
artifacts/*.zip
artifacts/*.exe
artifacts/*.AppImage
artifacts/*.deb
artifacts/*.snap
artifacts/*.tar.gz
artifacts/SHA256SUMS.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 changes: 16 additions & 0 deletions build/entitlements.mac.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
</dict>
</plist>
Loading