Files
MoShiro/ci/publish-openwrt.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

69 lines
4.1 KiB
Bash

#!/usr/bin/env bash
# Package an already-built native Agent and publish an independently signed feed.
# Used after verify/web-build gates for dev, and after all release builds for tags.
set -euo pipefail
channel="${1:?usage: publish-openwrt.sh <dev|release> <rid> <binary-directory>}"
rid="${2:?}"; source_dir="${3:?}"
[[ "$channel" == dev || "$channel" == release ]] || exit 2
[[ "$rid" == linux-musl-arm64 || "$rid" == linux-musl-x64 ]] || exit 2
if [[ "$channel" == release ]]; then
[[ "${CI_COMMIT_TAG:-}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo 'release feed requires a stable version tag' >&2; exit 2; }
else
[[ -z "${CI_COMMIT_TAG:-}" && "${CI_PIPELINE_EVENT:-}" == push && "${CI_COMMIT_BRANCH:-}" == main && "${CI_COMMIT_MESSAGE:-}" == *'[CI]'* ]] \
|| { echo 'dev feed requires a main push with [CI]' >&2; exit 2; }
fi
[[ -n "${RELEASE_SIGNING_KEY:-}" && -n "${GITEA_TOKEN:-}" ]] || { echo 'OpenWrt publishing requires signing and Gitea secrets' >&2; exit 2; }
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "$root/ci/version.sh"
version="$MOSHIRO_VERSION"
work="$(mktemp -d)"
trap 'rm -rf "$work"' EXIT
mkdir "$work/stage"
install -m 755 "$source_dir/moshiro-agent" "$source_dir/fleet-enroll" "$work/stage/"
for library in "$source_dir"/lib*.so.*; do
[[ ! -f "$library" ]] || install -m 644 "$library" "$work/stage/"
done
install -m 755 "$root/packaging/openwrt/install.sh" "$work/stage/openwrt.sh"
install -m 755 "$root/packaging/openwrt/native-agent.init" "$work/stage/agent.init"
install -m 755 "$root/packaging/openwrt/updater.init" "$work/stage/updater.init"
members=()
for file in "$work/stage/"*; do members+=("$(basename "$file")"); done
tar --sort=name --owner=0 --group=0 --numeric-owner -czf "$work/package.tar.gz" -C "$work/stage" "${members[@]}"
hash="$(sha256sum "$work/package.tar.gz" | cut -d' ' -f1)"
size="$(stat -c%s "$work/package.tar.gz")"
package="agent-$rid-$hash.tar.gz"
seq="$(git -C "$root" show -s --format=%ct HEAD)"
# Commit time is stable across retries; equal timestamps intentionally do not auto-upgrade.
python3 - "$channel" "$rid" "$version" "$package" "$hash" "$size" "$seq" > "$work/manifest.json" <<'PY'
import json, sys
channel, rid, version, package, sha256, size, seq = sys.argv[1:]
print(json.dumps(dict(schema=1, component='agent', channel=channel, rid=rid,
version=version, package=package, sha256=sha256, size=int(size), seq=int(seq)), separators=(',', ':')), end='')
PY
chmod 700 "$work"
printf '%s\n' "$RELEASE_SIGNING_KEY" > "$work/key.pem"
chmod 600 "$work/key.pem"
openssl dgst -sha256 -sign "$work/key.pem" -out "$work/signature" "$work/manifest.json"
base64 -w0 "$work/signature" > "$work/manifest.sig"
base="${GITEA_BASE:-http://127.0.0.1:3100}"
base="${base%/}/api/packages/justme/generic/moshiro-openwrt"
# Credentials only in a protected curl config, never command-line arguments or output.
printf 'header = "Authorization: token %s"\n' "$GITEA_TOKEN" > "$work/curl.conf"
chmod 600 "$work/curl.conf"
upload() {
local path="$1" file="$2" mutable="${3:-false}" code
if [[ "$mutable" == true ]]; then
code="$(curl --silent --show-error --config "$work/curl.conf" --request DELETE --output "$work/response" --write-out '%{http_code}' "$base/$path")"
[[ "$code" == 204 || "$code" == 200 || "$code" == 404 ]] || { echo "cannot replace OpenWrt feed: HTTP $code" >&2; return 1; }
fi
code="$(curl --silent --show-error --retry 3 --config "$work/curl.conf" --upload-file "$file" --output "$work/response" --write-out '%{http_code}' "$base/$path")"
# Immutable content-addressed archives may already exist after a retry.
[[ "$code" == 2* || ( "$mutable" == false && "$code" == 409 ) ]] || { echo "OpenWrt upload failed: HTTP $code" >&2; return 1; }
}
upload "$version/$package" "$work/package.tar.gz"
# A transient manifest/signature mismatch is rejected, then retried by the device.
# Publish the channel pointer only after its immutable payload is downloadable.
upload "$channel/manifest-$rid.sig" "$work/manifest.sig" true
upload "$channel/manifest-$rid.json" "$work/manifest.json" true
echo "published signed OpenWrt Agent $version ($channel/$rid)"