forked from 1b-agent/fieldcraft-upstream
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
416 lines
21 KiB
YAML
416 lines
21 KiB
YAML
# Release builds (docs/releasing.md), after SolidCraft's and VectorCraft's release.yml
|
||
# (MIT OR Apache-2.0; see ATTRIBUTION.md).
|
||
#
|
||
# A push to `main` (fast-forwarded from a verified `develop` commit; main holds released code only)
|
||
# builds every package and creates or updates the
|
||
# DRAFT GitHub Release "FieldCraft v<version>", with SHA256SUMS.txt (signed with the release key
|
||
# into SHA256SUMS.txt.minisig when the FIELDCRAFT_SIGNING_KEY secret exists) and generated notes. The
|
||
# version is [workspace.package] version in Cargo.toml; a suffix (0.2.0-rc.1) makes a
|
||
# pre-release. Every package, and the release itself, carries THIRD-PARTY-LICENSES.txt, generated
|
||
# here by `cargo xtask licences` (it is not committed). A person verifies the draft and publishes
|
||
# it; this workflow never publishes, and pushes to develop or tags never start it.
|
||
#
|
||
# macOS universal (arm64 + x86_64) .dmg with FieldCraft.app, the CLI as a .zip packaging/macos
|
||
# Linux x86_64 (glibc 2.35+) .tar.gz, .AppImage, .deb and .rpm packaging/linux
|
||
# Windows x64 .zip with fieldcraft.exe and fieldcraft-cli.exe packaging/windows
|
||
#
|
||
# macOS: signed with a Developer ID and notarized when the APPLE_* secrets exist, ad-hoc
|
||
# otherwise (the release notes say which). Linux and Windows builds are not signed.
|
||
#
|
||
# Before anything is packaged, the verify jobs run everything CI can: the full `cargo xtask ci`,
|
||
# `cargo xtask test --heavy` and `cargo xtask oracle` on Linux, and the oracle on macOS. Develop's
|
||
# CI tests only what each change affects (ci.yml), so this is where a release proves all of it.
|
||
#
|
||
# Dry run: `gh workflow run release.yml --ref <branch>` verifies and builds everything and uploads the
|
||
# `release-assets` artifact without creating a release. `-f draft=true` on `main` retries
|
||
# the draft.
|
||
name: Release
|
||
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
workflow_dispatch:
|
||
inputs:
|
||
draft:
|
||
description: "Create/update the draft release (main only; off: build artifacts only)"
|
||
type: boolean
|
||
default: false
|
||
|
||
permissions:
|
||
contents: read
|
||
|
||
concurrency:
|
||
group: release-${{ github.ref }}
|
||
# Never cancel a run mid-notarization or mid-upload; a newer push waits its turn.
|
||
cancel-in-progress: false
|
||
|
||
env:
|
||
CARGO_TERM_COLOR: always
|
||
CARGO_INCREMENTAL: 0
|
||
|
||
jobs:
|
||
version:
|
||
runs-on: ubuntu-24.04
|
||
outputs:
|
||
version: ${{ steps.v.outputs.version }}
|
||
prerelease: ${{ steps.v.outputs.prerelease }}
|
||
draft: ${{ steps.v.outputs.draft }}
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- id: v
|
||
name: Resolve the version
|
||
env:
|
||
REQUEST_DRAFT: ${{ github.event_name == 'push' || inputs.draft }}
|
||
run: |
|
||
. packaging/env.sh
|
||
if ! printf '%s' "$VERSION" | grep -Eq '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$'; then
|
||
echo "::error::Cargo.toml must contain a version like 1.2.3 or 1.2.3-rc.1, not '$VERSION'"
|
||
exit 1
|
||
fi
|
||
if [ "$REQUEST_DRAFT" = true ] && [ "$GITHUB_REF" != refs/heads/main ]; then
|
||
echo "::error::Draft releases are built from main only"
|
||
exit 1
|
||
fi
|
||
case "$VERSION" in *-*) pre=true ;; *) pre=false ;; esac
|
||
{ echo "version=$VERSION"; echo "prerelease=$pre"; echo "draft=$REQUEST_DRAFT"; } >>"$GITHUB_OUTPUT"
|
||
echo "### FieldCraft v$VERSION (\`${GITHUB_SHA::9}\`)" >>"$GITHUB_STEP_SUMMARY"
|
||
|
||
# The full gate, the heavy tests and the oracle on Linux and macOS: every packaging job needs
|
||
# them, so a release (or a dry run) never builds from a commit that fails any of them.
|
||
verify-ci:
|
||
name: Verify (cargo xtask ci)
|
||
runs-on: ubuntu-24.04
|
||
timeout-minutes: 60
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
with:
|
||
components: rustfmt, clippy
|
||
targets: wasm32-unknown-unknown
|
||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||
with:
|
||
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
|
||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||
- run: cargo xtask ci
|
||
|
||
verify-heavy:
|
||
name: Verify (cargo xtask test --heavy)
|
||
runs-on: ubuntu-24.04
|
||
timeout-minutes: 60
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||
with:
|
||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||
- run: cargo xtask test --heavy
|
||
|
||
verify-oracle:
|
||
name: Verify (cargo xtask oracle, ${{ matrix.os }})
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
- os: ubuntu-24.04
|
||
cache: release-oracle-linux
|
||
- os: macos-14
|
||
cache: release-oracle-macos
|
||
runs-on: ${{ matrix.os }}
|
||
timeout-minutes: 60
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||
with:
|
||
key: ${{ matrix.cache }}
|
||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||
- run: cargo xtask oracle
|
||
- name: docs/oracle.md is current
|
||
run: git diff --exit-code docs/oracle.md
|
||
|
||
macos:
|
||
name: macOS universal
|
||
needs: [version, verify-ci, verify-heavy, verify-oracle]
|
||
# Signing, notarization and analytics secrets live in the `release` environment, which only
|
||
# `main` may use; a dry run on another branch asks for no environment and builds unsigned.
|
||
environment: ${{ github.ref == 'refs/heads/main' && 'release' || '' }}
|
||
runs-on: macos-14
|
||
timeout-minutes: 120
|
||
outputs:
|
||
signing: ${{ steps.package.outputs.signing }}
|
||
env:
|
||
FIELDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
with:
|
||
targets: aarch64-apple-darwin, x86_64-apple-darwin
|
||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||
with:
|
||
key: release-macos-universal
|
||
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
|
||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||
- name: Generate THIRD-PARTY-LICENSES.txt (fails when a crate's licence can't be resolved)
|
||
run: cargo xtask licences
|
||
- name: Import the signing certificate (skipped without APPLE_CERTIFICATE)
|
||
env:
|
||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||
run: packaging/macos/import-cert.sh
|
||
- name: Build, sign, notarize and package (the dmg is mounted and checked)
|
||
id: package
|
||
env:
|
||
# The analytics project key (docs/privacy.md), built in; never printed.
|
||
FIELDCRAFT_POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
|
||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||
run: packaging/macos/package.sh --arch universal
|
||
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||
with:
|
||
name: release-macos
|
||
path: dist/release/*
|
||
if-no-files-found: error
|
||
- name: Remove the temporary keychain
|
||
if: always()
|
||
run: |
|
||
if [ -n "${MACOS_KEYCHAIN:-}" ]; then security delete-keychain "$MACOS_KEYCHAIN" || true; fi
|
||
|
||
linux:
|
||
name: Linux x86_64
|
||
needs: [version, verify-ci, verify-heavy, verify-oracle]
|
||
# The oldest GitHub-hosted image, so the binaries need only glibc >= 2.35.
|
||
# Signing, notarization and analytics secrets live in the `release` environment, which only
|
||
# `main` may use; a dry run on another branch asks for no environment and builds unsigned.
|
||
environment: ${{ github.ref == 'refs/heads/main' && 'release' || '' }}
|
||
runs-on: ubuntu-22.04
|
||
timeout-minutes: 90
|
||
env:
|
||
FIELDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||
# The .deb and .rpm builders (both MIT), pinned.
|
||
CARGO_DEB_VERSION: 3.8.0
|
||
CARGO_GENERATE_RPM_VERSION: 0.21.0
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- name: Install packaging tools
|
||
run: |
|
||
sudo apt-get update
|
||
sudo apt-get install -y --no-install-recommends desktop-file-utils lintian rpm
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||
with:
|
||
key: release-linux-x86_64
|
||
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
|
||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||
- name: Generate THIRD-PARTY-LICENSES.txt (fails when a crate's licence can't be resolved)
|
||
run: cargo xtask licences
|
||
- name: Install cargo-deb and cargo-generate-rpm
|
||
run: |
|
||
cargo install --locked --version "$CARGO_DEB_VERSION" cargo-deb
|
||
cargo install --locked --version "$CARGO_GENERATE_RPM_VERSION" cargo-generate-rpm
|
||
- name: Build and package (tar.gz, AppImage, deb, rpm)
|
||
env:
|
||
# The analytics project key (docs/privacy.md), built in; never printed.
|
||
FIELDCRAFT_POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
|
||
run: packaging/linux/package.sh
|
||
- name: Check the packages
|
||
run: |
|
||
. packaging/env.sh
|
||
base="fieldcraft-$VERSION-linux-x86_64"
|
||
deb="dist/release/fieldcraft_${VERSION}_amd64.deb"
|
||
rpm="dist/release/fieldcraft-$VERSION.x86_64.rpm"
|
||
list="$(tar -tzf "dist/release/$base.tar.gz")"
|
||
for f in bin/fieldcraft bin/fieldcraft-cli "share/applications/$APP_ID.desktop" \
|
||
share/doc/fieldcraft/THIRD-PARTY-LICENSES.txt; do
|
||
grep -Fx -- "$base/$f" <<<"$list"
|
||
done
|
||
# The .deb and .rpm carry the tar.gz's files under /usr (the .deb adds its copyright
|
||
# file); their lists live in apps/fieldcraft/Cargo.toml, so a drift fails here.
|
||
files() { grep -v '/$' | sed -E "s#^(\./|/|$base/)##; s#^usr/##" | grep -vx 'share/doc/fieldcraft/copyright' | sort; }
|
||
diff <(files <<<"$list") <(dpkg-deb --fsys-tarfile "$deb" | tar -t | files)
|
||
diff <(files <<<"$list") <(rpm -qpl "$rpm" | files)
|
||
dpkg-deb --info "$deb"
|
||
rpm -qpi "$rpm"
|
||
echo "rpm requires:"; rpm -qpR "$rpm"
|
||
# The release notes live on GitHub, so the package has no Debian changelog (a native
|
||
# package built upstream, not by a distribution).
|
||
lintian --info --fail-on error --suppress-tags no-changelog "$deb"
|
||
ldd target/release/fieldcraft
|
||
for appimage in dist/release/*.AppImage; do
|
||
chmod +x "$appimage"
|
||
APPIMAGE_EXTRACT_AND_RUN=1 "$appimage" --version
|
||
done
|
||
- name: Install the .deb and .rpm in containers
|
||
run: |
|
||
# The oldest supported Ubuntu (the build's glibc) and a current Fedora. Recommended and
|
||
# weak dependencies (GPU drivers) are skipped to keep it quick; the hard ones must resolve.
|
||
# Both images leave out documentation when installing (dpkg path-exclude, dnf nodocs), so
|
||
# the licences are checked in the package's file list rather than on disk.
|
||
docker run --rm -v "$PWD/dist/release:/dist:ro" ubuntu:22.04 bash -euxc '
|
||
apt-get update -qq
|
||
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends /dist/*.deb
|
||
fieldcraft-cli --version
|
||
fieldcraft --version
|
||
dpkg -L fieldcraft | grep -Fx /usr/share/doc/fieldcraft/THIRD-PARTY-LICENSES.txt'
|
||
docker run --rm -v "$PWD/dist/release:/dist:ro" fedora:44 bash -euxc '
|
||
dnf install -y -q --setopt=install_weak_deps=False /dist/*.rpm
|
||
fieldcraft-cli --version
|
||
fieldcraft --version
|
||
rpm -ql fieldcraft | grep -Fx /usr/share/doc/fieldcraft/THIRD-PARTY-LICENSES.txt
|
||
dnf install -y -q rpmlint
|
||
rpmlint /dist/*.rpm || true'
|
||
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||
with:
|
||
name: release-linux-x86_64
|
||
path: dist/release/*
|
||
if-no-files-found: error
|
||
|
||
windows:
|
||
name: Windows x64
|
||
needs: [version, verify-ci, verify-heavy, verify-oracle]
|
||
# Signing, notarization and analytics secrets live in the `release` environment, which only
|
||
# `main` may use; a dry run on another branch asks for no environment and builds unsigned.
|
||
environment: ${{ github.ref == 'refs/heads/main' && 'release' || '' }}
|
||
runs-on: windows-latest
|
||
timeout-minutes: 90
|
||
env:
|
||
FIELDCRAFT_VERSION: ${{ needs.version.outputs.version }}
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
with:
|
||
targets: x86_64-pc-windows-msvc
|
||
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||
with:
|
||
key: release-windows-x64
|
||
# Release runs are rare: only release-branch runs keep a cache, so dry runs don't evict CI's.
|
||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||
- name: Generate THIRD-PARTY-LICENSES.txt (fails when a crate's licence can't be resolved)
|
||
run: cargo xtask licences
|
||
- name: Build and package (zip)
|
||
shell: pwsh
|
||
env:
|
||
# The analytics project key (docs/privacy.md), built in; never printed.
|
||
FIELDCRAFT_POSTHOG_KEY: ${{ secrets.POSTHOG_KEY }}
|
||
run: ./packaging/windows/package.ps1
|
||
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||
with:
|
||
name: release-windows-x64
|
||
path: dist/release/*
|
||
if-no-files-found: error
|
||
|
||
release:
|
||
name: Draft release (or dry-run artifacts)
|
||
needs: [version, macos, linux, windows]
|
||
# Signing, notarization and analytics secrets live in the `release` environment, which only
|
||
# `main` may use; a dry run on another branch asks for no environment and builds unsigned.
|
||
environment: ${{ github.ref == 'refs/heads/main' && 'release' || '' }}
|
||
runs-on: ubuntu-24.04
|
||
permissions:
|
||
contents: write
|
||
env:
|
||
VERSION: ${{ needs.version.outputs.version }}
|
||
PRERELEASE: ${{ needs.version.outputs.prerelease }}
|
||
MACOS_SIGNING: ${{ needs.macos.outputs.signing }}
|
||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||
GH_REPO: ${{ github.repository }}
|
||
steps:
|
||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
||
with:
|
||
pattern: release-*
|
||
path: artifacts
|
||
merge-multiple: true
|
||
- uses: dtolnay/rust-toolchain@stable
|
||
# The same file every package carries, attached to the release on its own.
|
||
- name: Generate THIRD-PARTY-LICENSES.txt
|
||
run: cargo xtask licences && cp THIRD-PARTY-LICENSES.txt artifacts/
|
||
- name: Check the set and write the checksums
|
||
run: |
|
||
for f in "fieldcraft-$VERSION-macos-universal.dmg" "fieldcraft-cli-$VERSION-macos-universal.zip" \
|
||
"fieldcraft-$VERSION-linux-x86_64.tar.gz" "fieldcraft-$VERSION-linux-x86_64.AppImage" \
|
||
"fieldcraft_${VERSION}_amd64.deb" "fieldcraft-$VERSION.x86_64.rpm" \
|
||
"fieldcraft-$VERSION-windows-x64.zip" THIRD-PARTY-LICENSES.txt; do
|
||
test -s "artifacts/$f" || { echo "::error::missing $f"; exit 1; }
|
||
done
|
||
(cd artifacts && sha256sum -- * | tee SHA256SUMS.txt)
|
||
- name: Sign SHA256SUMS.txt with the release key (skipped without FIELDCRAFT_SIGNING_KEY)
|
||
id: sign
|
||
env:
|
||
FIELDCRAFT_SIGNING_KEY: ${{ secrets.FIELDCRAFT_SIGNING_KEY }}
|
||
# rsign2 (MIT), a minisign-compatible signer, pinned.
|
||
RSIGN_VERSION: 0.6.7
|
||
run: |
|
||
if [ -z "$FIELDCRAFT_SIGNING_KEY" ]; then
|
||
echo "::warning::FIELDCRAFT_SIGNING_KEY is not set: SHA256SUMS.txt is not signed, so the app cannot install this release itself (docs/releasing.md)"
|
||
echo "signed=false" >>"$GITHUB_OUTPUT"
|
||
exit 0
|
||
fi
|
||
cargo install --locked --quiet --root "$RUNNER_TEMP/rsign" rsign2 --version "$RSIGN_VERSION"
|
||
rsign="$RUNNER_TEMP/rsign/bin/rsign"
|
||
key="$RUNNER_TEMP/release.key"
|
||
(umask 077 && printf '%s\n' "$FIELDCRAFT_SIGNING_KEY" >"$key")
|
||
"$rsign" sign -W -s "$key" -t "FieldCraft v$VERSION SHA256SUMS.txt" -x artifacts/SHA256SUMS.txt.minisig artifacts/SHA256SUMS.txt
|
||
rm -f "$key"
|
||
# The signature must verify with the public key the app is built with.
|
||
public="$(sed -n 's/^pub const PUBLIC_KEY: &str = "\(.*\)";$/\1/p' crates/update/src/lib.rs)"
|
||
"$rsign" verify -q -P "$public" -x artifacts/SHA256SUMS.txt.minisig artifacts/SHA256SUMS.txt
|
||
echo "signed=true" >>"$GITHUB_OUTPUT"
|
||
- name: Write the notes
|
||
env:
|
||
SIGNED: ${{ steps.sign.outputs.signed }}
|
||
run: |
|
||
# The macOS paragraph says how the app was signed (packaging/macos/package.sh).
|
||
case "$MACOS_SIGNING" in
|
||
notarized) macos="The macOS app is signed with a Developer ID and notarized by Apple." ;;
|
||
signed) macos="The macOS app is signed with a Developer ID but **not notarized**: the first time, right-click \`FieldCraft.app\` in Applications and choose **Open**." ;;
|
||
*) macos="The macOS app is **ad-hoc signed and not notarized**: macOS blocks the first launch. Right-click \`FieldCraft.app\` in Applications and choose **Open** (then **Open** again), or run \`xattr -dr com.apple.quarantine /Applications/FieldCraft.app\`." ;;
|
||
esac
|
||
# The updates paragraph says whether the app can install this release itself.
|
||
if [ "$SIGNED" = true ]; then
|
||
updates="\`SHA256SUMS.txt\` is signed with the FieldCraft release key (\`SHA256SUMS.txt.minisig\`), so FieldCraft's Help › Check for Updates can verify and install this release."
|
||
else
|
||
updates="This release's \`SHA256SUMS.txt\` is **not signed**: FieldCraft's in-app updates can't verify it and won't install it; download it from this page instead."
|
||
fi
|
||
awk -v v="$VERSION" -v m="$macos" -v u="$updates" '{ gsub(/\{version\}/, v); gsub(/\{macos_signing\}/, m); gsub(/\{update_signing\}/, u); print }' \
|
||
.github/release-notes.md >notes.md
|
||
cat notes.md >>"$GITHUB_STEP_SUMMARY"
|
||
- name: Upload the dry-run artifacts
|
||
if: needs.version.outputs.draft != 'true'
|
||
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
||
with:
|
||
name: release-assets
|
||
path: |
|
||
artifacts/*
|
||
notes.md
|
||
if-no-files-found: error
|
||
- name: Create or update the draft release
|
||
if: needs.version.outputs.draft == 'true'
|
||
working-directory: artifacts
|
||
run: |
|
||
tag="v$VERSION"
|
||
title="FieldCraft v$VERSION"
|
||
if gh api "repos/$GH_REPO/git/matching-refs/tags/$tag" --jq '.[].ref' | grep -Fxq "refs/tags/$tag"; then
|
||
echo "::error::Tag $tag already exists. Choose a new version; never move a release tag."
|
||
exit 1
|
||
fi
|
||
if draft="$(gh release view "$tag" --json isDraft --jq .isDraft 2>/dev/null)"; then
|
||
if [ "$draft" != true ]; then
|
||
echo "::error::Release $tag is already published. Bump the version before releasing."
|
||
exit 1
|
||
fi
|
||
# A rebuild of the same version replaces the draft's assets (removing names it no longer
|
||
# makes); its notes are kept.
|
||
assets="$(gh release view "$tag" --json assets --jq '.assets[].name')"
|
||
while IFS= read -r asset; do
|
||
if [ -n "$asset" ]; then gh release delete-asset "$tag" "$asset" --yes; fi
|
||
done <<<"$assets"
|
||
gh release edit "$tag" --draft --title "$title" --target "$GITHUB_SHA" --prerelease="$PRERELEASE"
|
||
gh release upload "$tag" -- *
|
||
else
|
||
gh release create "$tag" --draft --title "$title" --target "$GITHUB_SHA" \
|
||
--notes-file ../notes.md --generate-notes --prerelease="$PRERELEASE" -- *
|
||
fi
|
||
echo "Draft release **$title** at \`${GITHUB_SHA::9}\`: $(gh release view "$tag" --json url --jq .url)" >>"$GITHUB_STEP_SUMMARY"
|