159 lines
7.8 KiB
Bash
159 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 (x64, arm64) and musl (arm64), macOS (x64, arm64), and Windows (x64, arm64). Linux 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-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"
|