# VDL Release Playbook

PyPI publish runs in GitLab CI. macOS arm64 binaries are built locally and
uploaded to the GitLab Release manually. Linux and Windows binaries are
optional CI jobs gated by release variables.

## Prerequisites

One-time setup:

- macOS arm64 (Apple Silicon) for the binary build
- Python 3.13 + [`uv`](https://docs.astral.sh/uv/)
- [`glab`](https://gitlab.com/gitlab-org/cli) authenticated against this project
- Working `ffmpeg` / `ffprobe` on the build machine for the post-build
  `vdl doctor` smoke check

GitLab / PyPI setup:

- PyPI Trusted Publishing configured for project `vdl`
- GitLab environment named `pypi`
- GitLab CI/CD variables:
  - `VDL_BUILD_LINUX_CI=0` or `1`
  - `VDL_BUILD_WINDOWS_CI=0` or `1`
  - optional fallback `PYPI_API_TOKEN=pypi-...`
- GitLab API credentials available locally, either via `.env`
  (`GITLAB_TOKEN`) or `glab auth login`, with a token that has `api`
  and `write_repository` scope for `glab release create`

PyPI Trusted Publisher settings:

- Platform: `GitLab CI/CD`
- PyPI project name: `vdl`
- GitLab namespace: `bildcraft/products`
- GitLab project: `video-downloader`
- Workflow file: `.gitlab-ci.yml`
- Environment: `pypi`

Per-release inputs:

- A version (e.g. `1.0.0`) matching `pyproject.toml`'s `version`
- No tracked or staged changes in the working tree; untracked scratch files are allowed and will be ignored by `task release`

## Fast path

On an Apple Silicon Mac, the happy path is now:

```bash
task release
```

That task runs the current macOS-first release flow end to end:

- preflight checks
- local macOS binary build
- tag creation + push
- GitLab Release asset upload
- wait for the tag pipeline
- trigger and wait for `vdl-publish-pages`

For CI-friendly automation, use:

```bash
task release:yes
```

The non-MCP tool facade in `vdl.tools` ships as part of the normal Python
package. It does not require a separate artifact, installer path, or optional
dependency.

## 1. Pre-flight

```bash
# Confirm tests pass
PYTHONPATH=src uv run pytest -q

# Confirm version source of truth
grep '^version' pyproject.toml

# Confirm tag does not already exist
git tag -l "vdl-v$(awk -F'"' '/^version/ {print $2}' pyproject.toml)"
```

If the tag already exists, bump `pyproject.toml::version` first.

## 2. Build the macOS arm64 binary locally

```bash
./scripts/build-macos.sh
```

This produces:

- `out/vdl-<version>-aarch64-apple-darwin.tar.gz`
- `out/SHA256SUMS`

The script smoke-tests `vdl --help` and `vdl doctor` on the built binary
before archiving. If `vdl doctor` flags issues, fix them before continuing —
binary releases must pass doctor on a clean Mac.

## 3. Tag and push

```bash
VERSION="$(awk -F'"' '/^version/ {print $2}' pyproject.toml)"
git tag "vdl-v${VERSION}"
git push origin "vdl-v${VERSION}"
```

The CI pipeline (`ci/release-vdl.yml`) starts here:

- `vdl-test` runs the suite
- `vdl-build-wheel` produces sdist + wheel
- `vdl-publish-pypi` uploads to PyPI through Trusted Publishing when available,
  or `PYPI_API_TOKEN` fallback when explicitly configured
- `vdl-build-linux` runs only when `VDL_BUILD_LINUX_CI=1`
- `vdl-build-windows` runs only when `VDL_BUILD_WINDOWS_CI=1`

`vdl-publish-pages` will fail until step 4 is done; that is expected.

## 4. Upload binary to the GitLab Release

```bash
VERSION="$(awk -F'"' '/^version/ {print $2}' pyproject.toml)"
glab release create "vdl-v${VERSION}" \
  "out/vdl-${VERSION}-aarch64-apple-darwin.tar.gz" \
  out/SHA256SUMS \
  --notes "VDL ${VERSION} (macOS arm64)"
```

Then re-run the `vdl-publish-pages` job. It downloads `SHA256SUMS` from the
release, builds `release.json`, and publishes the install manifest to
GitLab Pages alongside `install.sh`.

> Do not commit binaries to the repo. Releases are the canonical home.

## 5. Smoke test on a clean Mac

On a different Mac (or a fresh user account):

```bash
curl -fsSL https://bildcraft.gitlab.io/products/video-downloader/vdl/install.sh | sh
~/.local/bin/vdl doctor
~/.local/bin/vdl --version
~/.local/bin/vdl download <known-good-url>
```

The installer must:

- detect platform `aarch64-apple-darwin`
- download the release asset
- pass checksum verification
- install `vdl` and `vdl-mcp` into `~/.local/bin`
- warn (not fail) if `~/.local/bin` is not on `PATH`
- warn (not fail) if `ffmpeg` / `ffprobe` are missing
- run `vdl doctor` and print `ok`

Current installer scope:

- supported now: `aarch64-apple-darwin`, `x86_64-unknown-linux-gnu`
- not yet released through `install.sh`: Windows installer flow
- not currently published: `x86_64-apple-darwin`, `aarch64-unknown-linux-gnu`

If the smoke test fails, yank the release and start over after the fix.

## Upgrade

Re-running `install.sh` overwrites the binary in-place. Users running an
older version simply re-execute the curl command.

```bash
curl -fsSL https://bildcraft.gitlab.io/products/video-downloader/vdl/install.sh | sh
```

The installer logs the existing `vdl` location before overwrite so users see
which binary is being replaced.

## Uninstall

```bash
rm -f ~/.local/bin/vdl ~/.local/bin/vdl-mcp
# Optional: remove user data
rm -rf ~/.config/vdl ~/.local/state/vdl ~/.cache/vdl
```

`~/Downloads/VDL` (the default downloads directory) is left alone — users
keep their downloaded media.

## Yanking a bad release

```bash
glab release delete "vdl-v${VERSION}"
git push --delete origin "vdl-v${VERSION}"
git tag -d "vdl-v${VERSION}"
```

PyPI yanks (not deletes) the version:

```bash
# Manually in the PyPI UI, or via twine if you keep a separate yank tool.
```

After yanking, bump the patch version and start the playbook over.
