69 lines
4.0 KiB
Bash
69 lines
4.0 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 ]] || 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)"
|