eadd28fac0
P01 — release/install pipeline fix (REQ-097, REQ-098; gate C-21).
release.sh (REQ-097):
- Cross-build linux-amd64 regardless of host arch (GOOS=linux GOARCH=amd64
go build, CGO_ENABLED=0). D-193: the host-arch build produced the wrong
tarball when cut from arm64 — root cause of the v0.8.x asset-less
releases.
- Hardcode tarball name to orca-${VERSION}-linux-amd64.tar.gz (not
host-arch-dependent).
- Post-create asset verification (C-21): after tea releases create, query
the Gitea API and assert the tarball appears in attachments. Retry once
via tea release edit if missing. Fail loudly if still missing. This
catches the tea CLI bug where create exits 0 without attaching the asset.
install.sh (REQ-098):
- Asset fallback walk: if the resolved release (latest or --version) lacks
the matching tarball, query /releases?limit=50, extract all
browser_download_urls from the list response (assets are inline), find
the newest release with a matching orca-*-linux-amd64.tar.gz asset, print
a WARNING, and use that release. Fixes the v0.4.5 install incident where
v0.8.15 had no asset and install.sh errored out with no fallback.
- --check dry-run mode (D-194): prints version + asset URL + install path
+ current version without writing anything.
Tests (scripts/tests/):
- install_test.bash: 5 tests (--help, --check happy path, --check fallback
walk, unknown arg rejection, --system root check).
- release_test.bash: 5 tests (script exists, syntax valid, cross-build
command present, amd64 tarball name hardcoded, asset verification present).
All 30 bats tests pass. make lint clean (no new warnings).
---ci---
project: orca
phase: 1
milestone: v0.10
status: execute
---/ci---
206 lines
7.1 KiB
Bash
Executable File
206 lines
7.1 KiB
Bash
Executable File
#!/bin/bash
|
|
# release.sh - Build a release artifact and create a Gitea release via `tea`
|
|
#
|
|
# Usage:
|
|
# scripts/release.sh [VERSION]
|
|
#
|
|
# If VERSION is not given, it is read from the latest git tag (e.g. v0.1.5).
|
|
# Falls back to "dev" if no tag is found.
|
|
#
|
|
# Steps:
|
|
# 1. Validate toolchain (git, go, tar, tea)
|
|
# 2. Determine version
|
|
# 3. Build orca binary with version injection via -ldflags
|
|
# 4. Package as tarball: orca-${VERSION}-${OS}-${ARCH}.tar.gz
|
|
# 5. Generate release notes from `---ci---` blocks since last tag
|
|
# 6. Invoke `tea releases create` to publish to Gitea
|
|
#
|
|
# Requires:
|
|
# - GITEA_TOKEN environment variable
|
|
# - `tea` CLI on PATH (https://gitea.com/gitea/tea)
|
|
#
|
|
# Idempotent: tea releases create will fail if the release already exists;
|
|
# the script surfaces that error rather than silently swallowing it.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
cd "$REPO_ROOT"
|
|
|
|
# Source .env for GITEA_TOKEN if present
|
|
for env_file in "$REPO_ROOT/.env" "$PWD/.env" "./.env"; do
|
|
if [ -f "$env_file" ]; then
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
. "$env_file"
|
|
set +a
|
|
break
|
|
fi
|
|
done
|
|
|
|
# --- helpers --------------------------------------------------------------
|
|
|
|
err() { echo "release: error: $*" >&2; exit 1; }
|
|
info() { echo "release: $*"; }
|
|
|
|
require_tool() {
|
|
command -v "$1" >/dev/null 2>&1 || err "required tool not found: $1"
|
|
}
|
|
|
|
# --- preflight ------------------------------------------------------------
|
|
|
|
require_tool git
|
|
require_tool go
|
|
require_tool tar
|
|
|
|
if [ -z "${GITEA_TOKEN:-}" ]; then
|
|
err "GITEA_TOKEN is not set. Export it or put it in .env"
|
|
fi
|
|
|
|
if ! command -v tea >/dev/null 2>&1; then
|
|
err "tea CLI not found on PATH. Install from https://gitea.com/gitea/tea"
|
|
fi
|
|
|
|
# --- version detection ----------------------------------------------------
|
|
|
|
VERSION="${1:-}"
|
|
if [ -z "$VERSION" ]; then
|
|
VERSION="$(git describe --tags --abbrev=0 2>/dev/null || echo dev)"
|
|
fi
|
|
# Strip leading 'v' for the tarball name (we keep it in the release tag itself)
|
|
VERSION_NUMBER="${VERSION#v}"
|
|
|
|
info "version: $VERSION"
|
|
info "building..."
|
|
|
|
# --- build with version injection ----------------------------------------
|
|
# Cross-build linux-amd64 regardless of host arch (D-193). The install.sh
|
|
# user base is amd64; the .coreci.yml release step hardcodes the amd64
|
|
# tarball name. Building for the host arch produced the wrong tarball when
|
|
# the release was cut from an arm64 dev machine — the root cause of the
|
|
# v0.4.5 install incident (REQ-097).
|
|
|
|
GIT_COMMIT="$(git rev-parse --short HEAD)"
|
|
BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
LDFLAGS="-s -w -X git.cloudinit.dev/coreci/orca/internal/cli.version=$VERSION -X git.cloudinit.dev/coreci/orca/internal/cli.gitCommit=$GIT_COMMIT -X git.cloudinit.dev/coreci/orca/internal/cli.buildTime=$BUILD_TIME"
|
|
|
|
mkdir -p bin
|
|
info "building orca-${VERSION}-linux-amd64 (cross-compile, CGO_ENABLED=0)..."
|
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="$LDFLAGS" -o bin/orca ./cmd/orca
|
|
info "built: bin/orca (linux-amd64)"
|
|
|
|
# --- tarball --------------------------------------------------------------
|
|
# Always produce the linux-amd64 tarball name that install.sh looks for.
|
|
# (D-193: arm64 is a separate enhancement; this milestone ships amd64 only.)
|
|
|
|
TARBALL="orca-${VERSION}-linux-amd64.tar.gz"
|
|
tar -czf "$TARBALL" -C bin orca
|
|
info "packaged: $TARBALL ($(du -h "$TARBALL" | cut -f1))"
|
|
|
|
# --- release notes from ---ci--- blocks ----------------------------------
|
|
|
|
NOTES_FILE="$(mktemp)"
|
|
trap 'rm -f "$NOTES_FILE"' EXIT
|
|
|
|
{
|
|
echo "# Release $VERSION"
|
|
echo ""
|
|
echo "_Built: $BUILD_TIME from $GIT_COMMIT"
|
|
echo ""
|
|
|
|
PREV_TAG="$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")"
|
|
if [ -n "$PREV_TAG" ]; then
|
|
RANGE="$PREV_TAG..HEAD"
|
|
else
|
|
RANGE="HEAD"
|
|
fi
|
|
|
|
echo "## Changes since $PREV_TAG"
|
|
echo ""
|
|
# Extract messages of ---ci--- tagged commits in the range
|
|
git log --pretty=format:'- %s' "$RANGE" 2>/dev/null | head -100 || true
|
|
echo ""
|
|
} > "$NOTES_FILE"
|
|
|
|
info "release notes: $NOTES_FILE"
|
|
cat "$NOTES_FILE"
|
|
|
|
# --- publish to gitea -----------------------------------------------------
|
|
|
|
info "creating gitea release..."
|
|
tea releases create "$VERSION" \
|
|
--repo "$REPO" \
|
|
--title "Orca $VERSION" \
|
|
--note-file "$NOTES_FILE" \
|
|
--asset "$TARBALL"
|
|
|
|
# --- post-create asset verification (REQ-097, gate C-21) ------------------
|
|
# tea releases create has been observed to exit 0 without attaching the
|
|
# asset in some versions. Verify the asset actually appears in the release
|
|
# via the Gitea API; retry once if missing; fail loudly if still missing.
|
|
# This is the root-cause fix for the v0.8.x releases that shipped with zero
|
|
# binary assets.
|
|
|
|
verify_asset() {
|
|
local tag="$1" want="$2"
|
|
curl -fsSL "${GITEA_URL:-https://git.cloudinit.dev}/api/v1/repos/${GITEA_OWNER:-coreci}/${GITEA_REPO:-orca}/releases/tags/${tag}" \
|
|
| sed -n 's/.*"name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
|
|
| grep -qx "$want"
|
|
}
|
|
|
|
info "verifying asset ${TARBALL} attached to release ${VERSION}..."
|
|
if verify_asset "$VERSION" "$TARBALL"; then
|
|
info "✓ asset verified: ${TARBALL}"
|
|
else
|
|
info "asset missing after tea releases create; retrying upload..."
|
|
# Retry: re-add the asset via tea releases edit
|
|
tea release edit "$VERSION" --repo "$REPO" --asset "$TARBALL" 2>/dev/null \
|
|
|| tea releases edit "$VERSION" --repo "$REPO" --asset "$TARBALL" 2>/dev/null \
|
|
|| true
|
|
sleep 2
|
|
if verify_asset "$VERSION" "$TARBALL"; then
|
|
info "✓ asset verified on retry: ${TARBALL}"
|
|
else
|
|
err "asset ${TARBALL} NOT attached to release ${VERSION} after retry — the release exists but has no binary. Run 'tea releases edit ${VERSION} --repo $REPO --asset $TARBALL' manually. (REQ-097, C-21)"
|
|
fi
|
|
fi
|
|
|
|
info "✓ release $VERSION published with binary asset"
|
|
|
|
# --- publish container image to gitea registry (REQ-046) ------------------
|
|
# Skipped gracefully if docker is not on PATH (e.g. local dev without docker).
|
|
# The .coreci.yml release pipeline has a dedicated container-publish step
|
|
# that runs in a docker:24-cli image with docker-in-docker.
|
|
|
|
CONTAINER_REGISTRY="${CONTAINER_REGISTRY:-git.cloudinit.dev}"
|
|
CONTAINER_OWNER="${CONTAINER_OWNER:-coreci}"
|
|
CONTAINER_IMAGE="${CONTAINER_IMAGE:-orca}"
|
|
IMAGE="${CONTAINER_REGISTRY}/${CONTAINER_OWNER}/${CONTAINER_IMAGE}"
|
|
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
info "docker not found on PATH — skipping container image publish (CI handles it)."
|
|
else
|
|
info "building container image ${IMAGE}:${VERSION}..."
|
|
docker build \
|
|
--build-arg VERSION="$VERSION" \
|
|
--build-arg GIT_COMMIT="$GIT_COMMIT" \
|
|
--build-arg BUILD_TIME="$BUILD_TIME" \
|
|
-t "${IMAGE}:${VERSION}" \
|
|
-t "${IMAGE}:latest" \
|
|
"$REPO_ROOT"
|
|
|
|
if [ -z "${GITEA_TOKEN:-}" ]; then
|
|
info "GITEA_TOKEN not set — skipping docker push (image built locally only)."
|
|
else
|
|
info "logging in to ${CONTAINER_REGISTRY}..."
|
|
echo "$GITEA_TOKEN" | docker login "$CONTAINER_REGISTRY" -u cloudinit-bot --password-stdin
|
|
info "pushing ${IMAGE}:${VERSION}..."
|
|
docker push "${IMAGE}:${VERSION}"
|
|
info "pushing ${IMAGE}:latest..."
|
|
docker push "${IMAGE}:latest"
|
|
docker logout "$CONTAINER_REGISTRY"
|
|
info "✓ container image ${IMAGE}:${VERSION} published"
|
|
fi
|
|
fi
|