#!/usr/bin/env bash # # release.sh — Build, tag, and release kb-search # # Builds Go client binaries, Docker engine images, creates a Git tag + release, # and pushes container images to the registry. # # Usage: # ./release.sh # auto-increment patch, build, release # ./release.sh --no-increment # release using current VERSION files as-is # ./release.sh --dry-run # show what would happen without doing it # ./release.sh --minor # bump minor version (e.g. 2.0.1 → 2.1.0) # ./release.sh --major # bump major version (e.g. 2.1.0 → 3.0.0) # ./release.sh --gitea # use Gitea (tea) for release creation # ./release.sh --github # use GitHub (gh) for release creation # # One of --gitea or --github is required. # Assumes Docker is already authenticated to the registry. set -euo pipefail #────────────────────────────────────────────────────────────────────── # Config #────────────────────────────────────────────────────────────────────── SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" CLIENT_DIR="$SCRIPT_DIR/client" ENGINE_DIR="$SCRIPT_DIR/engine" CLIENT_VERSION_FILE="$CLIENT_DIR/VERSION" ENGINE_VERSION_FILE="$ENGINE_DIR/VERSION" # Container registry REGISTRY="${REGISTRY:-docker.dcglab.co.uk}" IMAGE_ORG="${IMAGE_ORG:-dcg}" IMAGE_BASE="${REGISTRY}/${IMAGE_ORG}/kb" #────────────────────────────────────────────────────────────────────── # Parse args #────────────────────────────────────────────────────────────────────── DRY_RUN=false INCREMENT=true BUMP="patch" FORGE="" for arg in "$@"; do case "$arg" in --dry-run) DRY_RUN=true ;; --no-increment) INCREMENT=false ;; --minor) BUMP="minor" ;; --major) BUMP="major" ;; --patch) BUMP="patch" ;; --gitea) FORGE="tea" ;; --github) FORGE="gh" ;; *) echo "Unknown argument: $arg" echo "Usage: $0 --gitea|--github [--dry-run] [--no-increment] [--patch|--minor|--major]" exit 1 ;; esac done if [[ -z "$FORGE" ]]; then echo "Error: specify --gitea or --github" echo "Usage: $0 --gitea|--github [--dry-run] [--no-increment] [--patch|--minor|--major]" exit 1 fi if ! command -v "$FORGE" &>/dev/null; then echo "Error: '$FORGE' not found in PATH" exit 1 fi #────────────────────────────────────────────────────────────────────── # Version helpers #────────────────────────────────────────────────────────────────────── read_version() { local file="$1" if [[ ! -f "$file" ]]; then echo "Error: version file not found: $file" >&2 exit 1 fi tr -d '[:space:]' < "$file" } bump_version() { local ver="$1" part="$2" local major minor patch IFS='.' read -r major minor patch <<< "$ver" case "$part" in major) echo "$((major + 1)).0.0" ;; minor) echo "${major}.$((minor + 1)).0" ;; patch) echo "${major}.${minor}.$((patch + 1))" ;; esac } write_version() { local file="$1" ver="$2" echo "$ver" > "$file" } #────────────────────────────────────────────────────────────────────── # Determine release version #────────────────────────────────────────────────────────────────────── CURRENT_VERSION="$(read_version "$CLIENT_VERSION_FILE")" if [[ "$INCREMENT" == true ]]; then VERSION="$(bump_version "$CURRENT_VERSION" "$BUMP")" echo "==> Version bump: $CURRENT_VERSION → $VERSION ($BUMP)" else VERSION="$CURRENT_VERSION" echo "==> Version: $VERSION (no increment)" fi TAG="v${VERSION}" echo " Tag: $TAG" echo " Registry: $IMAGE_BASE" echo " Forge CLI: $FORGE" echo " Dry run: $DRY_RUN" echo "" run() { echo " $ $*" if [[ "$DRY_RUN" == false ]]; then "$@" fi } #────────────────────────────────────────────────────────────────────── # 1. Pre-flight checks #────────────────────────────────────────────────────────────────────── echo "==> Pre-flight checks" if [[ "$DRY_RUN" == false ]]; then # Check tag doesn't already exist if git -C "$SCRIPT_DIR" rev-parse "$TAG" &>/dev/null; then echo "Error: tag $TAG already exists" exit 1 fi fi echo " OK" echo "" #────────────────────────────────────────────────────────────────────── # 2. Update version files #────────────────────────────────────────────────────────────────────── if [[ "$INCREMENT" == true ]]; then echo "==> Updating version files to $VERSION" run write_version "$CLIENT_VERSION_FILE" "$VERSION" run write_version "$ENGINE_VERSION_FILE" "$VERSION" echo "" fi #────────────────────────────────────────────────────────────────────── # 3. Build Go client binaries #────────────────────────────────────────────────────────────────────── echo "==> Building Go client binaries ($VERSION)" run make -C "$CLIENT_DIR" clean run make -C "$CLIENT_DIR" all VERSION="$VERSION" # Collect release assets ASSETS=() if [[ "$DRY_RUN" == false ]]; then for bin in "$CLIENT_DIR"/dist/kb-*; do ASSETS+=("$bin") done echo " Built ${#ASSETS[@]} binaries" else echo " (skipped — dry run)" fi echo "" #────────────────────────────────────────────────────────────────────── # 4. Build Docker engine images #────────────────────────────────────────────────────────────────────── echo "==> Building Docker engine images ($VERSION)" NVIDIA_IMAGE="${IMAGE_BASE}/engine:${TAG}-nvidia" ROCM_IMAGE="${IMAGE_BASE}/engine:${TAG}-rocm" NVIDIA_LATEST="${IMAGE_BASE}/engine:latest-nvidia" ROCM_LATEST="${IMAGE_BASE}/engine:latest-rocm" run docker build -t "$NVIDIA_IMAGE" -t "$NVIDIA_LATEST" -f "$ENGINE_DIR/Dockerfile.nvidia" "$ENGINE_DIR" run docker build -t "$ROCM_IMAGE" -t "$ROCM_LATEST" -f "$ENGINE_DIR/Dockerfile.rocm" "$ENGINE_DIR" echo "" #────────────────────────────────────────────────────────────────────── # 5. Commit version bump, tag, and push #────────────────────────────────────────────────────────────────────── echo "==> Committing and tagging $TAG" if [[ "$INCREMENT" == true ]]; then run git -C "$SCRIPT_DIR" add "$CLIENT_VERSION_FILE" "$ENGINE_VERSION_FILE" run git -C "$SCRIPT_DIR" commit -m "Bump version to $VERSION" fi run git -C "$SCRIPT_DIR" tag -a "$TAG" -m "Release $TAG" run git -C "$SCRIPT_DIR" push origin HEAD run git -C "$SCRIPT_DIR" push origin "$TAG" echo "" #────────────────────────────────────────────────────────────────────── # 6. Create release with assets #────────────────────────────────────────────────────────────────────── echo "==> Creating release via $FORGE" RELEASE_TITLE="$TAG" RELEASE_NOTES="## Docker images \`\`\`bash # NVIDIA GPU docker pull ${NVIDIA_IMAGE} # AMD GPU (ROCm) docker pull ${ROCM_IMAGE} \`\`\` ## Client binaries Download the binary for your platform from the assets below, rename to \`kb\`, and place on your PATH." if [[ "$FORGE" == "gh" ]]; then ASSET_FLAGS=() for f in "${ASSETS[@]+"${ASSETS[@]}"}"; do ASSET_FLAGS+=("$f") done run gh release create "$TAG" \ --title "$RELEASE_TITLE" \ --notes "$RELEASE_NOTES" \ "${ASSET_FLAGS[@]+"${ASSET_FLAGS[@]}"}" elif [[ "$FORGE" == "tea" ]]; then run tea release create \ --tag "$TAG" \ --title "$RELEASE_TITLE" \ --note "$RELEASE_NOTES" # tea attaches assets separately for f in "${ASSETS[@]+"${ASSETS[@]}"}"; do run tea release asset create --tag "$TAG" "$f" done fi echo "" #────────────────────────────────────────────────────────────────────── # 7. Push Docker images to registry #────────────────────────────────────────────────────────────────────── echo "==> Pushing Docker images to $REGISTRY" run docker push "$NVIDIA_IMAGE" run docker push "$NVIDIA_LATEST" run docker push "$ROCM_IMAGE" run docker push "$ROCM_LATEST" echo "" echo "==> Release $TAG complete!" echo "" echo " Images:" echo " $NVIDIA_IMAGE" echo " $ROCM_IMAGE" echo "" echo " Binaries: ${#ASSETS[@]} platform(s) attached to release"