# Investibook CLI Release System

> Last updated: 2026-03-11

---

## Overview

The CLI release system is built on two cooperating GitHub Actions workflows and supports two release modes:

1. **Linked release**: pushing an `investibook/v*` tag triggers the full pipeline; the CLI is built and published as part of that flow.
2. **Standalone CLI release**: pushing an `investibook/cli/v*` tag directly triggers only the CLI build and publish, leaving server-side images untouched.

---

## Tag Convention

| Tag format | Meaning | Created by |
|---|---|---|
| `investibook/v1.2.3` | Investibook version release | Developer pushes manually |
| `investibook/cli/v1.2.3` | CLI version release | CI creates automatically (linked); developer pushes manually (standalone) |

Both tags point to the same commit, so the CLI binary and the server-side code always correspond to the same snapshot.

---

## Workflow Files

### `investibook-ci-prod.yaml`

**Trigger**: `investibook/v*` tag push or `investibook/release-*` branch push (`workflow_dispatch` for manual runs)

**Jobs**:

```
push tag: investibook/v1.2.3
    ├─ build-push-frontend   —— build frontend image and push to ECR
    ├─ build-push-backend    —— build backend image and push to ECR
    └─ trigger-cli-release   —— (tag push only; requires the two jobs above to succeed)
            └─ create tag investibook/cli/v1.2.3 via GitHub API
```

`trigger-cli-release` uses `actions/github-script` to call `github.rest.git.createRef`, requiring no extra SSH key or PAT configuration. It also correctly dereferences annotated tags so that the CLI tag always points to a real commit SHA rather than a tag object.

The guard `if: startsWith(github.ref, 'refs/tags/investibook/v')` prevents this job from running on `investibook/release-*` branch pushes.

### `investibook-cli-release.yaml`

**Trigger**: `investibook/cli/v*` tag push (regardless of origin — auto-created by CI or pushed manually)

**Steps**:

1. **Extract version** — strip the `investibook/cli/` prefix from `github.ref_name` to obtain `v1.2.3`
2. **Cross-compile** — build for 4 target platforms (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64), producing `.tar.gz` and `.sha256` files
3. **Upload to S3** — write to both the versioned path (`releases/v1.2.3/`) and the `latest/` path so the install script always fetches the newest binary
4. **Update `version.json`** — write `s3://investibook-build/version.json`; the CLI auto-update check reads this file
5. **Create GitHub Release** — tagged `investibook/cli/v1.2.3`, with install instructions and per-platform download links

---

## End-to-End Flow

```
Developer pushes tag
investibook/v1.2.3
        │
        ▼
┌─────────────────────────────────┐
│   investibook-ci-prod.yaml      │
│                                 │
│  build-push-frontend ──────┐   │
│  build-push-backend ───────┤   │
│                             ▼   │
│  trigger-cli-release            │
│  (GitHub API createRef)         │
└─────────────────────────────┼───┘
                              │ new tag
                              ▼
                   investibook/cli/v1.2.3
                              │
                              ▼
              ┌───────────────────────────────┐
              │  investibook-cli-release.yaml │
              │                               │
              │  1. Cross-compile (Go)        │
              │  2. Upload to S3              │
              │     (releases/ + latest/)     │
              │  3. Update version.json       │
              │  4. Create GitHub Release     │
              └───────────────────────────────┘
```

---

## Standalone CLI Release

When the CLI needs a patch that does not involve any server-side change, push a CLI tag directly:

```bash
git tag investibook/cli/v1.2.4
git push origin investibook/cli/v1.2.4
```

This triggers `investibook-cli-release.yaml` directly, skipping the server image builds entirely. The rest of the process is identical to the second half of a linked release.

---

## S3 Directory Layout

```
s3://investibook-build/
├── version.json                          # latest version metadata, read by CLI auto-update
├── install.sh                            # one-line install script
├── latest/
│   ├── investibook-linux-amd64.tar.gz
│   ├── investibook-linux-amd64.sha256
│   ├── investibook-linux-arm64.tar.gz
│   ├── investibook-linux-arm64.sha256
│   ├── investibook-darwin-amd64.tar.gz
│   ├── investibook-darwin-amd64.sha256
│   ├── investibook-darwin-arm64.tar.gz
│   └── investibook-darwin-arm64.sha256
└── releases/
    └── v1.2.3/
        ├── investibook-linux-amd64.tar.gz
        ├── ...
```

`latest/` is overwritten on every release. `releases/{version}/` is retained permanently for rollback and version traceability.

---

## Common Operations

### Release a new version (server + CLI linked)

```bash
git tag investibook/v1.2.3
git push origin investibook/v1.2.3
```

### Release a CLI-only patch

```bash
git tag investibook/cli/v1.2.4
git push origin investibook/cli/v1.2.4
```

### Verify the release

```bash
# Check the latest version in S3
aws s3 cp s3://investibook-build/version.json - | jq .

# List CLI releases on GitHub
gh release list --repo tidbcloud/aiops | grep investibook/cli
```
