diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..ec48995 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -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" diff --git a/scripts/build-release.ps1 b/scripts/build-release.ps1 index 91a5b08..2359802 100644 --- a/scripts/build-release.ps1 +++ b/scripts/build-release.ps1 @@ -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" }