Skip to content

feat: implement offline preview feature and update UI components #3

feat: implement offline preview feature and update UI components

feat: implement offline preview feature and update UI components #3

# Ship portable binaries (Windows .exe portable, macOS .dmg, Linux AppImage) on each push to main.
# Release version is the next MINOR (MAJOR.(MINOR+1).0) from the latest Release tag or from package.json as baseline.
name: Release Electron builds
on:
push:
branches:
- main
permissions:
contents: write
concurrency:
group: electron-release-${{ github.repository }}
cancel-in-progress: false
env:
# Desktop app renders via Electron/Chromium at runtime; skip huge Playwright browser download in CI install
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'
jobs:
resolve-version:
runs-on: ubuntu-latest
outputs:
release_version: ${{ steps.calc.outputs.release_version }}
tag_v: ${{ steps.calc.outputs.tag_v }}
steps:
- uses: actions/checkout@v4
- name: Compute next MINOR version
id: calc
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
REPO="${{ github.repository }}"
# Latest published release (REST /releases/latest ignores drafts/prereleases in many cases — see GitHub API docs)
LATEST_TAG=""
RESP=$(gh api "repos/${REPO}/releases/latest" 2>/dev/null || true)
if printf '%s' "$RESP" | jq -e .tag_name >/dev/null 2>&1; then
LATEST_TAG="$(printf '%s' "$RESP" | jq -r .tag_name)"
fi
if [[ -n "$LATEST_TAG" && "$LATEST_TAG" != "null" ]]; then
VERSION="${LATEST_TAG#v}"
else
VERSION="$(jq -r .version package.json)"
fi
# bash read splits on IFS=. — MINOR +1 and PATCH resets to 0 (e.g. 2.3.8 → 2.4.0)
old_IFS="$IFS"
IFS=.
read -r MAJOR MINOR PATCH <<<"${VERSION}"
IFS="$old_IFS"
NEXT="${MAJOR}.$((10#${MINOR} + 1)).0"
printf 'release_version=%s\n' "$NEXT" >> "$GITHUB_OUTPUT"
printf 'tag_v=v%s\n' "$NEXT" >> "$GITHUB_OUTPUT"
electron-build:
needs: resolve-version
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
artifact: dist-win
eb_flag: --win
- os: macos-latest
artifact: dist-mac
eb_flag: --mac
- os: ubuntu-latest
artifact: dist-linux
eb_flag: --linux
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- name: Linux dependencies for AppImage (FUSE)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libfuse2
- run: npm ci
- name: Align package.json version with release
shell: bash
run: |
node -e "
const fs=require('fs');
const p=JSON.parse(fs.readFileSync('package.json','utf8'));
p.version='${{ needs.resolve-version.outputs.release_version }}';
fs.writeFileSync('package.json', JSON.stringify(p,null,2)+'\n');
"
- name: Package frontend + Electron (${{ matrix.artifact }})
run: npx vite build && npx electron-builder ${{ matrix.eb_flag }} --publish never
- name: Stage shipping binaries only (skip unpacked dirs)
shell: bash
env:
TARGET: ${{ matrix.eb_flag }}
run: |
set -euo pipefail
mkdir -p upload
case "${TARGET}" in
--win)
find dist-electron -maxdepth 1 -type f \( -iname '*.exe' \) -exec cp {} upload/ \;
;;
--mac)
find dist-electron -maxdepth 1 -type f \( -iname '*.dmg' \) -exec cp {} upload/ \;
;;
--linux)
find dist-electron -maxdepth 1 -type f \( -iname '*.AppImage' \) -exec cp {} upload/ \;
;;
*)
echo "Unknown TARGET=${TARGET}"
exit 1
;;
esac
if [[ -z "$(ls -A upload)" ]]; then
echo "No shipping binaries found under dist-electron"
ls -la dist-electron || true
exit 1
fi
ls -la upload
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: upload/
release:
needs:
- resolve-version
- electron-build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.sha }}
- name: Download desktop artifacts
uses: actions/download-artifact@v4
with:
pattern: dist-*
merge-multiple: true
path: release-assets
- name: Add source archive (tracked files at this commit)
run: |
set -euo pipefail
VER="${{ needs.resolve-version.outputs.release_version }}"
OUT="release-assets/AnyDownload-${VER}-source.zip"
git archive --format=zip --output="$OUT" HEAD
ls -la release-assets
- uses: softprops/action-gh-release@v2
with:
tag_name: ${{ needs.resolve-version.outputs.tag_v }}
target_commitish: ${{ github.sha }}
files: release-assets/*
fail_on_unmatched_files: true
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}