Skip to content
Merged
Changes from all commits
Commits
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
171 changes: 171 additions & 0 deletions .github/workflows/build-desktop-platforms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -411,3 +411,174 @@ jobs:
if-no-files-found: error
retention-days: 30
compression-level: 0

release:
name: Draft release with all installers
needs: [build-windows, build-macos, build-linux]
runs-on: ubuntu-latest
permissions:
contents: write

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

- name: Read project version
id: version
run: |
set -euo pipefail
VERSION=$(grep 'projectVersionName' gradle/libs.versions.toml | head -1 | sed 's/.*= *"\(.*\)"/\1/')
if [ -z "$VERSION" ]; then
echo "ERROR: could not read projectVersionName from gradle/libs.versions.toml"
exit 1
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
echo "Resolved release tag: v$VERSION"
shell: bash

- name: Download all build artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Stage release files (rename arch / compat collisions)
run: |
set -euo pipefail
mkdir -p release-files

# stage() returns 0 on a successful copy, 1 if the source is missing.
# Callers use the exit status to increment per-group counters so the
# completeness guard at the end can detect missing groups.
stage() {
local src="$1"
local target_name="$2"
if [ -f "$src" ]; then
cp "$src" "release-files/$target_name"
echo "Staged: $target_name"
return 0
fi
return 1
}

windows_count=0
macos_x64_count=0
macos_arm64_count=0
linux_modern_count=0
linux_debian12_count=0
linux_appimage_count=0
linux_arch_count=0

# Windows — names already unique (.exe, .msi)
for f in artifacts/windows-installers/*.exe artifacts/windows-installers/*.msi; do
[ -f "$f" ] || continue
stage "$f" "$(basename "$f")" && windows_count=$((windows_count + 1)) || true
done

# macOS — disambiguate x64 vs arm64 (Compose outputs identical filenames per arch)
for f in artifacts/macos-installers-x64/*.dmg artifacts/macos-installers-x64/*.pkg; do
[ -f "$f" ] || continue
base="$(basename "$f")"
ext="${base##*.}"
stem="${base%.*}"
stage "$f" "${stem}-x64.${ext}" && macos_x64_count=$((macos_x64_count + 1)) || true
done
for f in artifacts/macos-installers-arm64/*.dmg artifacts/macos-installers-arm64/*.pkg; do
[ -f "$f" ] || continue
base="$(basename "$f")"
ext="${base##*.}"
stem="${base%.*}"
stage "$f" "${stem}-arm64.${ext}" && macos_arm64_count=$((macos_arm64_count + 1)) || true
done

# Linux modern — default Debian/RPM (unprefixed)
for f in artifacts/linux-installers-modern/*.deb artifacts/linux-installers-modern/*.rpm; do
[ -f "$f" ] || continue
stage "$f" "$(basename "$f")" && linux_modern_count=$((linux_modern_count + 1)) || true
done

# Linux debian12-compat — suffix to avoid collision with modern
for f in artifacts/linux-installers-debian12-compat/*.deb artifacts/linux-installers-debian12-compat/*.rpm; do
[ -f "$f" ] || continue
base="$(basename "$f")"
ext="${base##*.}"
stem="${base%.*}"
stage "$f" "${stem}-debian12.${ext}" && linux_debian12_count=$((linux_debian12_count + 1)) || true
done

# Linux AppImage + zsync (filenames already include -x86_64)
for f in artifacts/linux-appimage/*.AppImage artifacts/linux-appimage/*.AppImage.zsync; do
[ -f "$f" ] || continue
stage "$f" "$(basename "$f")" && linux_appimage_count=$((linux_appimage_count + 1)) || true
done

# Linux Arch (.pkg.tar.zst already has version + arch in filename)
for f in artifacts/linux-arch/*.pkg.tar.zst; do
[ -f "$f" ] || continue
stage "$f" "$(basename "$f")" && linux_arch_count=$((linux_arch_count + 1)) || true
done

echo
echo "Final staged files:"
ls -la release-files/
echo
echo "Per-group counts: windows=$windows_count macos-x64=$macos_x64_count macos-arm64=$macos_arm64_count linux-modern=$linux_modern_count linux-debian12=$linux_debian12_count linux-appimage=$linux_appimage_count linux-arch=$linux_arch_count"

# Completeness guard: refuse to ship an incomplete release. Each
# group must produce >= 1 staged file. Without this guard, a build
# regression (e.g. AppImage step silently producing nothing) would
# ship a draft release missing the affected group and we'd discover
# it only when users complained.
missing=()
[ "$windows_count" -eq 0 ] && missing+=("Windows installers (.exe/.msi)")
[ "$macos_x64_count" -eq 0 ] && missing+=("macOS x64 (.dmg/.pkg)")
[ "$macos_arm64_count" -eq 0 ] && missing+=("macOS arm64 (.dmg/.pkg)")
[ "$linux_modern_count" -eq 0 ] && missing+=("Linux modern (.deb/.rpm)")
[ "$linux_debian12_count" -eq 0 ] && missing+=("Linux debian12-compat (.deb/.rpm)")
[ "$linux_appimage_count" -eq 0 ] && missing+=("Linux AppImage (.AppImage/.zsync)")
[ "$linux_arch_count" -eq 0 ] && missing+=("Linux Arch (.pkg.tar.zst)")

if [ ${#missing[@]} -gt 0 ]; then
echo
echo "ERROR: missing artifacts for the following groups:"
printf " - %s\n" "${missing[@]}"
echo
echo "Refusing to publish an incomplete release."
exit 1
fi
shell: bash
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Create or update draft release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
VERSION: ${{ steps.version.outputs.version }}
run: |
set -euo pipefail
if gh release view "$TAG" >/dev/null 2>&1; then
# Release with this tag exists. Only clobber assets if it's still
# a DRAFT — we never silently overwrite assets on a published
# release. Operator must bump projectVersionName in
# gradle/libs.versions.toml to roll a new tag.
is_draft=$(gh release view "$TAG" --json isDraft -q '.isDraft')
if [ "$is_draft" = "true" ]; then
echo "Draft release $TAG already exists — replacing assets via --clobber..."
gh release upload "$TAG" release-files/* --clobber
else
echo "ERROR: release $TAG is already PUBLISHED."
echo "Refusing to clobber published assets."
echo
echo "If you intended to ship a new version, bump"
echo "projectVersionName in gradle/libs.versions.toml first,"
echo "then re-push to generate-installers."
exit 1
fi
else
echo "Creating new draft release $TAG..."
gh release create "$TAG" \
--draft \
--title "$VERSION" \
--generate-notes \
release-files/*
fi
shell: bash
Comment thread
coderabbitai[bot] marked this conversation as resolved.