Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,15 @@ bash <(curl -fsSL https://raw.githubusercontent.com/cnoe-io/caipe-cli/main/setup
Then verify the installation with `caipe --version`. If `~/.local/bin` is not
already on your `PATH`, the setup script prints the exact command to add it.

The npm package and downloadable release binaries are not published yet. Do
not use `npm install caipe`, `npx github:cnoe-io/caipe-cli`, or `install.sh`
until the first multi-architecture release is available.
The installer downloads the latest multi-architecture GitHub release and
verifies its SHA-256 checksum. Pin a release when reproducibility matters:

```bash
curl -fsSL https://raw.githubusercontent.com/cnoe-io/caipe-cli/main/install.sh \
| CAIPE_VERSION=0.2.22 sh
```

The npm package is not published yet; use `install.sh` until it is available.

### Updates

Expand Down
29 changes: 10 additions & 19 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ set -e

REPO="cnoe-io/caipe-cli"
INSTALL_DIR="${CAIPE_INSTALL_DIR:-/usr/local/bin}"
VERSION="${CAIPE_VERSION:-}"
DEFAULT_VERSION="latest"
VERSION="${CAIPE_VERSION:-$DEFAULT_VERSION}"
TAG=""
NO_VERIFY="${CAIPE_NO_VERIFY:-0}"

Expand Down Expand Up @@ -55,35 +56,25 @@ detect_platform() {
# ── resolve latest version ────────────────────────────────────────────────────

resolve_version() {
if [ -n "$VERSION" ]; then
if [ "$VERSION" != "$DEFAULT_VERSION" ]; then
TAG="$VERSION"
info "Using pinned version: $VERSION"
return
fi

info "Resolving latest release…"
need_cmd curl

TAG=$(curl -fsSL \
"https://api.github.com/repos/${REPO}/releases/latest" \
| grep '"tag_name"' \
| head -1 \
| sed 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/')

if [ -z "$TAG" ]; then
die "Could not determine latest caipe release. Set CAIPE_VERSION to install a specific version."
fi

VERSION="${TAG#caipe/}"
VERSION="${VERSION#v}"
info "Latest version: $VERSION"
info "Using latest release"
}

# ── download and verify ───────────────────────────────────────────────────────

download_binary() {
BINARY_NAME="caipe-${PLATFORM}"
BASE_URL="https://github.com/${REPO}/releases/download/${TAG}"
if [ -n "$TAG" ]; then
BASE_URL="https://github.com/${REPO}/releases/download/${TAG}"
else
# This redirect is not subject to the unauthenticated GitHub API rate limit.
BASE_URL="https://github.com/${REPO}/releases/latest/download"
fi
BINARY_URL="${BASE_URL}/${BINARY_NAME}"
CHECKSUMS_URL="${BASE_URL}/caipe-checksums.txt"

Expand Down
16 changes: 16 additions & 0 deletions tests/installer.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";

const installer = readFileSync(new URL("../install.sh", import.meta.url), "utf8");

describe("release installer", () => {
it("uses the API-free latest-release redirect by default", () => {
expect(installer).toContain('DEFAULT_VERSION="latest"');
expect(installer).toContain('BASE_URL="https://github.com/${REPO}/releases/latest/download"');
expect(installer).not.toContain("api.github.com");
});

it("keeps explicit version pins on immutable release URLs", () => {
expect(installer).toContain('BASE_URL="https://github.com/${REPO}/releases/download/${TAG}"');
});
});