mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-14 19:31:14 +00:00
WebFreak001/deploy-nightly still targets Node.js 20, so every main build logged a deprecation warning per deploy step. The action has had no release since 2024 and its Node 24 PR is unmerged. The local action in .github/actions/deploy-nightly uploads the new file under a temporary name and renames it once the upload finishes, so a failed upload keeps the existing nightly asset. It also fails on a missing file before deleting anything, retries transient API errors, times out stalled transfers, handles concurrent runs, and finds the release by its nightly-builds tag. The macOS validator DMG deploy now runs only when that DMG was built.
105 lines
3.3 KiB
Bash
105 lines
3.3 KiB
Bash
#!/usr/bin/env bash
|
|
# Replaces one asset of a GitHub release, uploading under a temporary name
|
|
# first so a failed upload leaves the old asset in place.
|
|
#
|
|
# Environment: GH_TOKEN, ASSET_PATH, ASSET_NAME, CONTENT_TYPE, RELEASE_TAG and
|
|
# the GITHUB_* variables Actions sets.
|
|
|
|
# The $names inside the single-quoted jq programs are jq variables.
|
|
# shellcheck disable=SC2016
|
|
set -euo pipefail
|
|
|
|
repo_api="$GITHUB_API_URL/repos/$GITHUB_REPOSITORY"
|
|
|
|
if [ ! -f "$ASSET_PATH" ]; then
|
|
echo "::error::$ASSET_PATH does not exist"
|
|
exit 1
|
|
fi
|
|
|
|
# The speed limit turns a stalled transfer into an error that --retry handles.
|
|
curl_opts=(-sS --retry 3 --connect-timeout 30 --speed-limit 1024 --speed-time 60
|
|
-H "Authorization: Bearer $GH_TOKEN")
|
|
|
|
# Prints the response body, or reports GitHub's error body and fails.
|
|
api() {
|
|
local out
|
|
if ! out=$(curl "${curl_opts[@]}" --fail-with-body "$@"); then
|
|
# A workflow command ends at the first newline.
|
|
echo "::error::GitHub API request failed: ${out//[$'\r\n']/ }" >&2
|
|
return 1
|
|
fi
|
|
printf '%s\n' "$out"
|
|
}
|
|
|
|
http_status() {
|
|
curl "${curl_opts[@]}" -o /dev/null -w '%{http_code}' "$@"
|
|
}
|
|
|
|
# jq prints CRLF on Windows.
|
|
jqr() {
|
|
jq -r "$@" | tr -d '\r'
|
|
}
|
|
|
|
delete_asset() {
|
|
local code
|
|
code=$(http_status -X DELETE "$repo_api/releases/assets/$1")
|
|
case $code in
|
|
# A 404 means a concurrent run already deleted the asset.
|
|
204 | 404) ;;
|
|
*)
|
|
echo "::error::Deleting release asset $1 failed with HTTP $code" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
list_assets() {
|
|
local page=1 json
|
|
while :; do
|
|
# Callers run this inside $(...), where bash ignores set -e.
|
|
json=$(api "$repo_api/releases/$release_id/assets?per_page=100&page=$page") || exit 1
|
|
printf '%s\n' "$json"
|
|
[ "$(jqr length <<<"$json")" -eq 100 ] || break
|
|
page=$((page + 1))
|
|
done | jq -s add
|
|
}
|
|
|
|
release=$(api "$repo_api/releases/tags/$RELEASE_TAG")
|
|
release_id=$(jqr .id <<<"$release")
|
|
upload_url=$(jqr .upload_url <<<"$release")
|
|
upload_url=${upload_url%%\{*}
|
|
|
|
# Unique per run attempt, so concurrent runs never upload to the same name.
|
|
tmp_name="$ASSET_NAME.partial-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
|
|
|
|
# Deletes leftover partials older than an hour, an age no upload in progress reaches.
|
|
stale_ids=$(list_assets | jqr --arg prefix "$ASSET_NAME.partial-" \
|
|
'.[] | select((.name | startswith($prefix)) and (.created_at | fromdateiso8601) < now - 3600) | .id')
|
|
for id in $stale_ids; do
|
|
delete_asset "$id"
|
|
done
|
|
|
|
name_query=$(jqr -n --arg name "$tmp_name" '$name | @uri')
|
|
# Uploads once, since a retry after an upload that succeeded fails on the taken name.
|
|
new_id=$(api --retry 0 -X POST -H "Content-Type: $CONTENT_TYPE" -T "$ASSET_PATH" \
|
|
"$upload_url?name=$name_query" | jqr .id)
|
|
trap 'delete_asset "$new_id" || true' EXIT
|
|
|
|
rename_body=$(jq -n --arg name "$ASSET_NAME" '{name: $name}')
|
|
# Retries on HTTP 422, which a concurrent run causes by renaming its own upload
|
|
# between this delete and this rename.
|
|
for _ in 1 2 3; do
|
|
old_ids=$(list_assets | jqr --arg name "$ASSET_NAME" '.[] | select(.name == $name) | .id')
|
|
for id in $old_ids; do
|
|
delete_asset "$id"
|
|
done
|
|
code=$(http_status -X PATCH -d "$rename_body" "$repo_api/releases/assets/$new_id")
|
|
if [ "$code" = 200 ]; then
|
|
trap - EXIT
|
|
exit 0
|
|
fi
|
|
[ "$code" = 422 ] || break
|
|
done
|
|
echo "::error::Renaming $tmp_name to $ASSET_NAME failed with HTTP $code" >&2
|
|
exit 1
|