Files
OrcaSlicer/.github/workflows/publish_release.yml

198 lines
9.2 KiB
YAML

name: Publish to draft release
# Manually pulls the platform binaries produced by a "Build all" run and uploads
# them to an existing DRAFT release. Decoupled from the build so you can test a
# build first, then publish exactly that run's artifacts once you're happy.
# It also writes an ordered download table into the draft's release notes.
#
# Trigger: Actions tab -> "Publish to draft release" -> Run workflow.
# Locked to a single person: the first step aborts unless the actor matches the
# `RELEASE_PUBLISHER` repo variable (set to "SoftFever"). To allow someone else,
# change that variable: gh variable set RELEASE_PUBLISHER --body "<login>".
on:
workflow_dispatch:
inputs:
run_id:
description: 'Run ID of the "Build all" workflow to pull binaries from (the number in the Actions run URL)'
required: true
type: string
tag:
description: 'Tag of the draft release to upload to (e.g. v2.4.0-beta)'
required: true
type: string
permissions:
contents: write # upload release assets
actions: read # download artifacts from another run
jobs:
publish:
name: Publish binaries to draft release
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RUN_ID: ${{ inputs.run_id }}
TAG: ${{ inputs.tag }}
steps:
- name: Restrict to the release publisher
env:
PUBLISHER: ${{ vars.RELEASE_PUBLISHER }}
run: |
if [ -z "$PUBLISHER" ]; then
echo "::error::RELEASE_PUBLISHER repo variable is not set."
exit 1
fi
if [ "${{ github.actor }}" != "$PUBLISHER" ]; then
echo "::error::Only @$PUBLISHER may run this workflow (you are @${{ github.actor }})."
exit 1
fi
echo "Authorized: @${{ github.actor }}"
- name: Verify target is a draft release
run: |
if ! gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json isDraft >/dev/null 2>&1; then
echo "::error::Release '$TAG' not found. Create the draft (with this tag) first."
exit 1
fi
is_draft=$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json isDraft --jq '.isDraft')
if [ "$is_draft" != "true" ]; then
echo "::error::Release '$TAG' is published, not a draft. Aborting to avoid touching a live release."
exit 1
fi
- name: Verify source build run
run: |
info=$(gh run view "$RUN_ID" --repo "$GITHUB_REPOSITORY" --json workflowName,conclusion,headBranch)
name=$(echo "$info" | jq -r '.workflowName')
conclusion=$(echo "$info" | jq -r '.conclusion')
branch=$(echo "$info" | jq -r '.headBranch')
echo "Source run #$RUN_ID: '$name' on '$branch' (conclusion: $conclusion)"
if [ "$conclusion" != "success" ]; then
echo "::warning::Source run did not conclude 'success' ($conclusion). Some assets may be missing."
fi
- name: Download release artifacts from build run
run: |
# Windows_V* (not Windows_*) keeps the MSIX Store artifact out: it goes to Partner Center, not GitHub releases.
gh run download "$RUN_ID" --repo "$GITHUB_REPOSITORY" --dir artifacts \
-p 'OrcaSlicer_Windows_V*' \
-p 'OrcaSlicer_Mac_universal_*' \
-p 'OrcaSlicer_Linux_ubuntu_*' \
-p 'OrcaSlicer-Linux-flatpak_*' \
-p 'PDB'
echo "Downloaded artifact folders:"
ls -1 artifacts
- name: Assemble release assets
run: |
set -euo pipefail
mkdir -p upload
# gh run download auto-extracts each artifact into a folder, so the inner
# binaries are already unzipped. Copy the inner binary for each platform.
# -type f is required (some artifact *folders* are named "*.flatpak").
# Windows installers (x64 + arm64): the .exe inside each installer
# artifact, NOT the orca-slicer.exe in the portable app folder. CPack
# now bakes the arch into the filename (…_x64.exe / …_arm64.exe), so
# copy them straight through.
find artifacts -type f -name '*.exe' -path '*OrcaSlicer_Windows_V*' ! -path '*_portable*' -exec cp -v {} upload/ \;
# macOS universal DMG (profile-validator DMG isn't downloaded).
find artifacts -type f -name '*.dmg' -path '*OrcaSlicer_Mac_universal_*' -exec cp -v {} upload/ \;
# Linux AppImage.
find artifacts -type f -name '*.AppImage' -exec cp -v {} upload/ \;
# Flatpak bundles (x86_64 + aarch64).
find artifacts -type f -name '*.flatpak' -exec cp -v {} upload/ \;
# Windows debug symbols (PDB archive, for developers).
find artifacts -type f -name 'Debug_PDB_*.7z' -exec cp -v {} upload/ \;
# Portable Windows builds (x64 + arm64) are unzipped folder artifacts;
# re-zip each to its released filename (these stay .zip on the release).
mapfile -t portable_dirs < <(find artifacts -maxdepth 1 -type d -name 'OrcaSlicer_Windows_*_portable')
if [ ${#portable_dirs[@]} -eq 0 ]; then
echo "::warning::Windows portable artifact not found."
fi
for portable_dir in "${portable_dirs[@]}"; do
( cd "$portable_dir" && zip -qr "$GITHUB_WORKSPACE/upload/$(basename "$portable_dir").zip" . )
echo "Zipped portable -> $(basename "$portable_dir").zip"
done
echo "Assets to upload:"
ls -lh upload
if [ -z "$(ls -A upload)" ]; then
echo "::error::No assets assembled. Check the run_id and that its artifacts haven't expired."
exit 1
fi
- name: Upload assets to draft release
run: |
gh release upload "$TAG" upload/* --repo "$GITHUB_REPOSITORY" --clobber
echo "Uploaded to draft release: $TAG"
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets[].name'
# GitHub lists release assets alphabetically by filename whatever order they
# are uploaded in, so the platform order users see comes from this table in
# the release notes. The table sits between the two markers: a rerun
# replaces it in place, and notes without markers get it appended. Text
# outside the markers is kept. Links use the tag's download URL, which only
# resolves once the draft is published.
- name: Write download table into release notes
run: |
set -euo pipefail
start='<!-- downloads:start -->'
end='<!-- downloads:end -->'
# Sort key and label of an asset, in the order the table lists them.
describe() {
case "$1" in
*Windows_Installer_*_x64.exe) echo "01|Windows installer (x64)" ;;
*Windows_Installer_*_arm64.exe) echo "02|Windows installer (ARM64)" ;;
*_x64_portable.zip) echo "03|Windows portable (x64)" ;;
*_arm64_portable.zip) echo "04|Windows portable (ARM64)" ;;
*.dmg) echo "05|macOS (Apple Silicon and Intel)" ;;
*_aarch64_*.AppImage) echo "07|Linux AppImage (aarch64)" ;;
*.AppImage) echo "06|Linux AppImage (x86_64)" ;;
*_x86_64.flatpak) echo "08|Linux Flatpak (x86_64)" ;;
*_aarch64.flatpak) echo "09|Linux Flatpak (aarch64)" ;;
Debug_PDB_*) echo "10|Windows debug symbols (for developers)" ;;
*) echo "99|$1" ;;
esac
}
{
echo "$start"
echo
echo "## Downloads"
echo
echo "| Platform | File |"
echo "| --- | --- |"
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets[].name' | while read -r name; do
echo "$(describe "$name")|$name"
done | sort | while IFS='|' read -r _ label name; do
echo "| $label | [$name](https://github.com/$GITHUB_REPOSITORY/releases/download/$TAG/$name) |"
done
echo
echo "$end"
} > table.md
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json body --jq '.body' > notes.md
starts=$(grep -cF "$start" notes.md || true)
ends=$(grep -cF "$end" notes.md || true)
if [ "$starts" -ne "$ends" ] || [ "$starts" -gt 1 ]; then
echo "::error::The release notes must contain each download-table marker exactly once, or neither. Fix them by hand and rerun."
exit 1
fi
if [ "$starts" -eq 1 ]; then
awk -v start="$start" -v end="$end" '
index($0, start) { while ((getline line < "table.md") > 0) print line; skip = 1; next }
index($0, end) { skip = 0; next }
!skip
END { exit skip }
' notes.md > notes.new || { echo "::error::The download-table end marker must be on its own line, after the start marker."; exit 1; }
else
{ cat notes.md; echo; cat table.md; } > notes.new
fi
gh release edit "$TAG" --repo "$GITHUB_REPOSITORY" --notes-file notes.new
cat table.md