feat: add CI release workflow for installers, AppImage and Docker
Gitea Actions workflow triggered on tag push or manual dispatch: builds the Windows NSIS/portable installers and Linux AppImage, builds+pushes the Docker image, then creates/updates the Gitea release with a changelog-compare link as the body and all artifacts attached. Flatpak stays local-only (build-release.ps1) since flatpak-builder's sandbox isn't reliable on a containerized Actions runner. Also fixes build-release.ps1: the win.signExecutable=false override only applied when cross-building via the WSL path, not to a genuine native-Linux build of the "win" target (the CI's case). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
name: Release
|
||||
|
||||
# Builds the Docker image, the Windows installers (NSIS + portable) and the Linux
|
||||
# AppImage, then publishes all of it: the image goes to the Gitea container
|
||||
# registry as `latest` + the tag, the installers are attached to a Gitea release
|
||||
# for that tag. Flatpak is intentionally not built here — flatpak-builder's
|
||||
# sandbox (bwrap) needs working user namespaces, which a containerized Actions
|
||||
# runner is not guaranteed to have; it stays a local-only step via
|
||||
# scripts/publish.ps1.
|
||||
#
|
||||
# There is no Windows runner in this setup, so the "win" target (NSIS + portable)
|
||||
# is cross-built on the Linux runner via Wine, the same path
|
||||
# scripts/build-release.ps1 uses for -UseWsl. Unsigned either way.
|
||||
#
|
||||
# Requires two Actions secrets on this repo (Settings > Actions > Secrets), a
|
||||
# token with package read/write *and* repository write (write:repository) scope:
|
||||
# GITEA_USER - registry/API username
|
||||
# GITEA_TOKEN - registry/API token
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Tag to publish (e.g. v1.2.3)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
IMAGE: git.azuze.fr/kawa/motionity
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # full tag history, for the changelog compare link
|
||||
|
||||
- name: Resolve tag
|
||||
id: vars
|
||||
env:
|
||||
EVENT_NAME: ${{ github.event_name }}
|
||||
DISPATCH_TAG: ${{ inputs.tag }}
|
||||
REF_TAG: ${{ github.ref_name }}
|
||||
run: |
|
||||
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
|
||||
echo "tag=$DISPATCH_TAG" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "tag=$REF_TAG" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- name: Install build tools (jq, Wine for the NSIS uninstaller)
|
||||
run: |
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y jq wine
|
||||
|
||||
- name: Ensure PowerShell
|
||||
run: command -v pwsh >/dev/null || sudo snap install powershell --classic
|
||||
|
||||
- name: Build Windows installers + Linux AppImage
|
||||
shell: pwsh
|
||||
env:
|
||||
TAG: ${{ steps.vars.outputs.tag }}
|
||||
run: ./scripts/build-release.ps1 -Targets win,linux-appimage -Tag $env:TAG
|
||||
|
||||
- name: Log in to git.azuze.fr
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.azuze.fr
|
||||
username: ${{ secrets.GITEA_USER }}
|
||||
password: ${{ secrets.GITEA_TOKEN }}
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.IMAGE }}:latest
|
||||
${{ env.IMAGE }}:${{ steps.vars.outputs.tag }}
|
||||
|
||||
- name: Publish Gitea release
|
||||
env:
|
||||
TAG: ${{ steps.vars.outputs.tag }}
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
SERVER_URL: ${{ github.server_url }}
|
||||
REPO_PATH: ${{ github.repository }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
api="$SERVER_URL/api/v1/repos/$REPO_PATH/releases"
|
||||
auth=(-H "Authorization: token $GITEA_TOKEN")
|
||||
|
||||
prev_tag=$(git tag --sort=-v:refname | grep -A1 -x -F "$TAG" | tail -n1)
|
||||
if [ "$prev_tag" = "$TAG" ]; then prev_tag=""; fi
|
||||
if [ -n "$prev_tag" ]; then
|
||||
body="**Full Changelog**: $SERVER_URL/$REPO_PATH/compare/$prev_tag...$TAG"
|
||||
else
|
||||
body="**Full Changelog**: first release"
|
||||
fi
|
||||
echo "changelog: $body"
|
||||
|
||||
status=$(curl -s -o /tmp/release.json -w '%{http_code}' "${auth[@]}" "$api/tags/$TAG")
|
||||
if [ "$status" = "200" ]; then
|
||||
release_id=$(jq -r '.id' /tmp/release.json)
|
||||
echo "reusing release $TAG (id $release_id)"
|
||||
elif [ "$status" = "404" ]; then
|
||||
echo "creating release $TAG"
|
||||
payload=$(jq -n --arg tag "$TAG" --arg name "Motionity $TAG" --arg body "$body" \
|
||||
'{tag_name:$tag, name:$name, body:$body, draft:false}')
|
||||
status=$(curl -s -o /tmp/release.json -w '%{http_code}' -X POST "${auth[@]}" \
|
||||
-H "Content-Type: application/json" -d "$payload" "$api")
|
||||
[ "$status" = "201" ] || { echo "release creation failed ($status):"; cat /tmp/release.json; exit 1; }
|
||||
release_id=$(jq -r '.id' /tmp/release.json)
|
||||
else
|
||||
echo "unexpected status $status fetching release:"; cat /tmp/release.json; exit 1
|
||||
fi
|
||||
|
||||
# Keep the changelog link authoritative even when reusing an existing
|
||||
# release (a manual re-run after a later tag was pushed).
|
||||
curl -sf -X PATCH "${auth[@]}" -H "Content-Type: application/json" \
|
||||
-d "$(jq -n --arg body "$body" '{body:$body}')" \
|
||||
"$api/$release_id" -o /dev/null
|
||||
|
||||
for f in "dist/motionity-$TAG-win-x64-setup.exe" \
|
||||
"dist/motionity-$TAG-win-x64-portable.exe" \
|
||||
"dist/motionity-$TAG-linux-x86_64.AppImage" \
|
||||
"dist/SHA256SUMS.txt"; do
|
||||
[ -f "$f" ] || { echo "expected artifact missing: $f" >&2; exit 1; }
|
||||
name=$(basename "$f")
|
||||
existing_id=$(jq -r --arg n "$name" '.assets[]? | select(.name==$n) | .id' /tmp/release.json)
|
||||
if [ -n "$existing_id" ]; then
|
||||
echo "replacing attachment $name (asset $existing_id)"
|
||||
curl -sf -X DELETE "${auth[@]}" "$api/$release_id/assets/$existing_id" -o /dev/null
|
||||
else
|
||||
echo "adding attachment $name"
|
||||
fi
|
||||
encoded=$(jq -rn --arg n "$name" '$n|@uri')
|
||||
curl -sf "${auth[@]}" -F "attachment=@$f" "$api/$release_id/assets?name=$encoded" -o /dev/null
|
||||
done
|
||||
|
||||
echo "release: $SERVER_URL/$REPO_PATH/releases/tag/$TAG"
|
||||
@@ -616,8 +616,10 @@ try {
|
||||
# it would otherwise download is unusable there (see the header).
|
||||
#
|
||||
# Passed here rather than put in package.json so a native Windows build keeps
|
||||
# behaving exactly as it did.
|
||||
if ($useWslHere -and $wslTargets -contains $target -and $target -like "win*") {
|
||||
# behaving exactly as it did. The same gap exists on a genuine Linux host (no
|
||||
# WSL involved) building "win" directly — e.g. CI — so this checks $onWindows
|
||||
# rather than $useWslHere.
|
||||
if (-not $onWindows -and $target -like "win*") {
|
||||
$builderArgs += "-c.win.signExecutable=false"
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user