Files
MoShiro/ci/publish-gitea-release.sh
T
justme 4515d7b075
ci/woodpecker/push/web-build Pipeline was successful
ci/woodpecker/push/verify Pipeline was successful
ci/woodpecker/push/dev-image Pipeline failed
scripts: publish unified Agent artifacts and restore CI coverage [CI]
Remove Probe build jobs and release paths. Transfer Gitea publishing credentials to both Agent architecture jobs, publish each platform archive once, and run slow tests after development images are published.
2026-10-21 11:56:06 +08:00

160 lines
7.8 KiB
Bash

#!/usr/bin/env bash
# Create/update the Gitea Release after all platform package workflows succeed.
set -euo pipefail
tag="${CI_COMMIT_TAG:-}"
[[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]] || {
echo "release must run from a vMAJOR.MINOR.PATCH tag" >&2
exit 2
}
[[ -n "${GITEA_TOKEN:-}" ]] || { echo "GITEA_TOKEN is required" >&2; exit 2; }
version="${tag#v}"
gitea_base="${GITEA_BASE:-http://192.168.2.162:3100}"
gitea_base="${gitea_base%/}"
gitea_host="${gitea_base#https://}"
gitea_host="${gitea_host#http://}"
gitea_host="${gitea_host%%/*}"
gitea_host="${gitea_host%%:*}"
api="$gitea_base/api/v1/repos/justme/MoShiro"
tmp="$(mktemp -d)"
netrc="$tmp/netrc"
release_json="$tmp/release.json"
assets_json="$tmp/assets.json"
chmod 700 "$tmp"
printf 'machine %s login justme password %s\n' "$gitea_host" "$GITEA_TOKEN" > "$netrc"
chmod 600 "$netrc"
trap 'rm -rf "$tmp"' EXIT
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
install_sh="$(sha256sum "$root_dir/packaging/install/install.sh" | cut -d" " -f1)"
install_ps1="$(sha256sum "$root_dir/packaging/install/install.ps1" | cut -d" " -f1)"
release_body="Install script SHA-256 (compare with the panel before you run an install command): install.sh $install_sh, install.ps1 $install_ps1. Agent builds: Linux glibc and musl (x64, arm64), macOS (x64, arm64), and Windows (x64, arm64). Linux musl Agent OCI image: registry.oeo.one/moshiro/moshiro-agent:$version (linux/amd64 and linux/arm64). Backend OCI image: registry.oeo.one/moshiro/moshiro-backend:$version (linux/amd64 and linux/arm64). Compose files for Agent and Backend: moshiro-compose-<role>.yaml (images use :latest, or pin MOSHIRO_VERSION in .env). macOS binaries are not Developer ID signed or notarized."
payload="$(printf '{"tag_name":"%s","target_commitish":"%s","name":"MoShiro %s","body":"%s","draft":false,"prerelease":false}' \
"$tag" "${CI_COMMIT_SHA:-}" "$tag" "$release_body")"
release_status="$(curl --silent --show-error --retry 3 --netrc-file "$netrc" \
--output "$release_json" --write-out '%{http_code}' "$api/releases/tags/$tag")"
release_created=false
if [[ "$release_status" == 404 ]]; then
release_status="$(curl --silent --show-error --retry 3 --netrc-file "$netrc" \
--header 'Content-Type: application/json' --data "$payload" \
--output "$release_json" --write-out '%{http_code}' "$api/releases")"
release_created=true
fi
[[ "$release_status" =~ ^2 ]] || {
echo "Gitea Release lookup/create failed (HTTP $release_status)" >&2
cat "$release_json" >&2
exit 1
}
release_id="$(grep -o '"id":[[:space:]]*[0-9][0-9]*' "$release_json" | head -1 | tr -cd '0-9')"
[[ -n "$release_id" ]] || { echo "Could not read Gitea Release ID" >&2; exit 1; }
if [[ "$release_created" == false ]]; then
update_response="$tmp/release-update.json"
update_status="$(curl --silent --show-error --retry 3 --netrc-file "$netrc" \
--request PATCH --header 'Content-Type: application/json' --data "$payload" \
--output "$update_response" --write-out '%{http_code}' "$api/releases/$release_id")"
[[ "$update_status" =~ ^2 ]] || {
echo "Gitea Release update failed (HTTP $update_status)" >&2
cat "$update_response" >&2
exit 1
}
fi
curl --fail-with-body --silent --show-error --retry 3 --netrc-file "$netrc" \
"$api/releases/$release_id/assets" -o "$assets_json"
assets=(
"moshiro-agent-v$version-linux-amd64.tar.gz"
"moshiro-agent-v$version-linux-arm64.tar.gz"
"moshiro-agent-v$version-linux-musl-amd64.tar.gz"
"moshiro-agent-v$version-linux-musl-arm64.tar.gz"
"moshiro-agent-v$version-macos-x64.tar.gz"
"moshiro-agent-v$version-macos-arm64.tar.gz"
"moshiro-agent-v$version-windows-amd64.zip"
"moshiro-agent-v$version-windows-arm64.zip"
)
upload_release_asset() {
local name="$1" file="$tmp/$1" upload_response="$tmp/$1.upload.json" status
curl --fail-with-body --silent --show-error --retry 3 --netrc-file "$netrc" \
"$gitea_base/api/packages/justme/generic/moshiro-node/$version/$name" -o "$file"
status="$(curl --silent --show-error --retry 3 --netrc-file "$netrc" \
--request POST --header 'Content-Type: application/octet-stream' --data-binary "@$file" \
--output "$upload_response" --write-out '%{http_code}' \
"$api/releases/$release_id/assets?name=$name")"
case "$status" in
2*) echo "attached $name" ;;
*) echo "Gitea Release asset upload failed for $name (HTTP $status)" >&2; cat "$upload_response" >&2; return 1 ;;
esac
}
pids=()
for archive in "${assets[@]}"; do
for name in "$archive" "$archive.sha256"; do
if grep -Eq "\"name\"[[:space:]]*:[[:space:]]*\"$name\"" "$assets_json"; then
echo "release asset $name already exists; keeping the existing asset"
continue
fi
upload_release_asset "$name" &
pids+=("$!")
if ((${#pids[@]} >= 8)); then
wait "${pids[0]}"
pids=("${pids[@]:1}")
fi
done
done
for pid in "${pids[@]}"; do
wait "$pid"
done
# 所有压缩包的校验和汇成一份 SHA256SUMS,用发布私钥签名(base64 的 DER ECDSA,和签名的发布清单是同一把密钥、同一种格式):
# 不走容器的安装(直接下载压缩包)先验这份签名再核对压缩包的哈希,发布页被改也骗不到;没有配置签名密钥(例如 fork)就跳过。
upload_local_asset() {
local name="$1" file="$2" upload_response="$tmp/$1.upload.json" status
if grep -Eq "\"name\"[[:space:]]*:[[:space:]]*\"$name\"" "$assets_json"; then
echo "release asset $name already exists; keeping the existing asset"
return 0
fi
status="$(curl --silent --show-error --retry 3 --netrc-file "$netrc" \
--request POST --header 'Content-Type: application/octet-stream' --data-binary "@$file" \
--output "$upload_response" --write-out '%{http_code}' "$api/releases/$release_id/assets?name=$name")"
[[ "$status" == 2* ]] || { echo "Gitea Release asset upload failed for $name (HTTP $status)" >&2; cat "$upload_response" >&2; return 1; }
echo "attached $name"
}
# compose 文件(Agent、Backend 各一份)和节点的 Podman Quadlet 单元(Agent 各一份),都是静态的(镜像用 :latest):作为发布资产,
# 第一次部署 Backend 时还没有面板,就从这里下载;它们也记进 SHA256SUMS(有签名密钥时一起签名)。
for role in agent backend; do
cp "$root_dir/packaging/compose/$role/compose.yaml" "$tmp/moshiro-compose-$role.yaml"
upload_local_asset "moshiro-compose-$role.yaml" "$tmp/moshiro-compose-$role.yaml"
done
for role in agent; do
cp "$root_dir/packaging/quadlet/moshiro-$role.container" "$tmp/moshiro-quadlet-$role.container"
upload_local_asset "moshiro-quadlet-$role.container" "$tmp/moshiro-quadlet-$role.container"
done
if [[ -n "${RELEASE_SIGNING_KEY:-}" ]]; then
sums="$tmp/SHA256SUMS"
: > "$sums"
for archive in "${assets[@]}"; do
curl --fail --silent --show-error --retry 3 --netrc-file "$netrc" \
"$gitea_base/api/packages/justme/generic/moshiro-node/$version/$archive.sha256" -o "$tmp/$archive.sha256"
cat "$tmp/$archive.sha256" >> "$sums"
done
[[ "$(wc -l < "$sums")" -eq "${#assets[@]}" ]] || { echo "SHA256SUMS does not have one line per archive" >&2; exit 1; }
for file in moshiro-compose-agent.yaml moshiro-compose-backend.yaml moshiro-quadlet-agent.container; do
printf '%s %s\n' "$(sha256sum "$tmp/$file" | cut -d' ' -f1)" "$file" >> "$sums"
done
signing_key="$(mktemp)"
chmod 600 "$signing_key"
printf '%s\n' "$RELEASE_SIGNING_KEY" > "$signing_key"
openssl dgst -sha256 -sign "$signing_key" -out "$tmp/SHA256SUMS.sig.bin" "$sums"
rm -f "$signing_key"
base64 -w0 "$tmp/SHA256SUMS.sig.bin" > "$tmp/SHA256SUMS.sig" 2>/dev/null || base64 "$tmp/SHA256SUMS.sig.bin" | tr -d '\n' > "$tmp/SHA256SUMS.sig"
upload_local_asset SHA256SUMS "$sums"
upload_local_asset SHA256SUMS.sig "$tmp/SHA256SUMS.sig"
else
echo "release signing is not configured; SHA256SUMS is not signed"
fi
echo "published Gitea Release $tag: https://git.oeo.one/justme/MoShiro/releases/tag/$tag"