# GitLab Releases with CalVer Auto-Versioning

**Date**: 2026-03-07
**Status**: Approved

## Summary

Replace MinIO binary distribution with GitLab Generic Package Registry + Releases.
Every push to `main` that touches code auto-creates a CalVer tag, compiles, uploads
binaries as GitLab generic packages, creates a GitLab Release, and deploys.

MinIO retains sccache, training data, and model checkpoints (not release artifacts).

## Versioning

**Scheme**: CalVer `vYYYY.MM.N` where N auto-increments per month.

Examples: `v2026.03.1`, `v2026.03.2`, `v2026.04.1`

**Increment logic**: Query GitLab tags API for latest `vYYYY.MM.*`, parse N, increment.
If no tag exists for current month, start at 1.

**Cargo.toml**: Workspace `version` stays at `1.0.0` (Cargo version != release version).
Runtime version comes from `FOXHUNT_BUILD_VERSION` env var baked at compile time
via `option_env!()` in `common/src/build_info.rs` (existing design from 2026-03-04).

## Branching Model

Trunk-based. No develop/release branches.

```
feature/* --merge--> main --auto--> tag vYYYY.MM.N -> compile -> release -> deploy
```

## Pipeline Changes (ci-pipeline-template.yaml)

### Current DAG

```
detect-changes -> [compile-services | compile-training | build-web-dashboard] -> deploy-services
```

### New DAG

```
detect-changes -> create-tag (if code changed)
                    |
          +---------+---------+
          |                   |
  compile-services    compile-training
          |                   |
          +---------+---------+
                    |
              upload-release -> deploy-services
```

Non-code changes (docker images, dashboard) remain unchanged and don't create tags.

### New template: create-tag

- Runs on `platform` node (lightweight)
- Uses GitLab API via deploy token with `api` scope
- Computes version: `YYYY.MM` from date, queries `GET /api/v4/projects/:id/repository/tags?search=vYYYY.MM`
- Increments N, creates tag via `POST /api/v4/projects/:id/repository/tags`
- Outputs the tag name for downstream steps

### Modified: compile-services / compile-training

- `FOXHUNT_BUILD_VERSION` = tag from create-tag step (was `YYYY.MM.argo`)
- Upload destination changes from MinIO to GitLab Generic Package Registry:
  ```
  PUT /api/v4/projects/:id/packages/generic/foxhunt-services/<tag>/<binary-name>
  PUT /api/v4/projects/:id/packages/generic/foxhunt-training/<tag>/<binary-name>
  ```
- Removes rclone + MinIO credentials for binary upload
- Adds `GITLAB_API_TOKEN` from secret

### New template: upload-release

- Creates GitLab Release via `POST /api/v4/projects/:id/releases`
- Attaches package links as release assets
- Auto-generates release notes from commits since last tag:
  `GET /api/v4/projects/:id/repository/changelog?version=<tag>`
- Runs after both compile steps complete

### Modified: deploy-services

- Sets `FOXHUNT_RELEASE` annotation on deployments to trigger rollout
- initContainers now know which version to fetch

## Service Deployment Changes (infra/k8s/services/*.yaml)

### initContainer: fetch-binary

Before (MinIO):
```yaml
- name: fetch-binary
  args:
    - |
      rclone copyto ":s3:foxhunt-binaries/services/trading-service" "/binaries/trading-service" \
        --s3-provider=Minio --s3-endpoint=http://minio.foxhunt.svc.cluster.local:9000 ...
  env:
    - name: MINIO_ACCESS_KEY ...
    - name: MINIO_SECRET_KEY ...
```

After (GitLab Generic Packages):
```yaml
- name: fetch-binary
  args:
    - |
      set -e
      curl -fSL -o /binaries/trading-service \
        --header "DEPLOY-TOKEN: ${GITLAB_DEPLOY_TOKEN}" \
        "${GITLAB_API}/projects/1/packages/generic/foxhunt-services/${FOXHUNT_RELEASE}/trading-service"
      chmod +x /binaries/trading-service
  env:
    - name: GITLAB_DEPLOY_TOKEN
      valueFrom:
        secretKeyRef:
          name: gitlab-deploy-token
          key: token
    - name: GITLAB_API
      value: "http://gitlab-webservice-default.foxhunt.svc.cluster.local:8181/api/v4"
    - name: FOXHUNT_RELEASE
      value: "v2026.03.1"  # Updated by deploy-services step
```

### Training job-template.yaml

Same pattern: replace rclone/MinIO with curl/GitLab for binary fetch.
Training data sync stays on MinIO (not a release artifact).

## Secrets

### New
- `gitlab-deploy-token`: GitLab deploy token with `read_package_registry` scope (for service pods)
- `gitlab-api-token`: GitLab PAT or deploy token with `api` scope (for CI tag/release creation)

### Removed from binary path
- `minio-credentials` no longer needed in service deployments or compile upload steps
- MinIO credentials remain for: sccache, training data sync, model checkpoints

## Rollback

```bash
# Roll back to previous release
kubectl -n foxhunt set env deployment/trading-service FOXHUNT_RELEASE=v2026.03.1
kubectl -n foxhunt rollout restart deployment/trading-service
```

All previous binaries remain available in GitLab's package registry indefinitely.

## What stays on MinIO

| Bucket | Purpose | Change |
|--------|---------|--------|
| foxhunt-sccache | Rust build cache | No change |
| foxhunt-training-data | DBN market data | No change |
| foxhunt-training-results | Training outputs | No change |
| foxhunt-models | Model checkpoints | No change |
| foxhunt-binaries | **Service/training binaries** | **Decommission after migration** |

## Migration

1. Deploy new CI pipeline with dual-write (MinIO + GitLab) for one release
2. Switch service initContainers to GitLab fetch
3. Verify all services boot correctly
4. Remove MinIO binary upload from CI
5. Delete `foxhunt-binaries` bucket contents (keep bucket for sccache)

## Files Changed

### New files
- `crates/common/src/build_info.rs` — version function (from 2026-03-04 design)

### Modified files
- `infra/k8s/argo/ci-pipeline-template.yaml` — new create-tag + upload-release templates, modified compile steps
- `infra/k8s/services/*.yaml` (8 files) — initContainer fetch from GitLab
- `infra/k8s/training/job-template.yaml` — binary fetch from GitLab
- `infra/k8s/secrets/` — new gitlab-deploy-token, gitlab-api-token
- `crates/common/src/lib.rs` — re-export build_info
