# Building and Publishing

How to ship `retold-data-service` to npm and to GitHub Container Registry
(GHCR). Generated by `quack docker-init`; the structure matches the
shared template across all dockerized retold tools.

`retold-data-service` is a **long-running service**. Restart policy in compose / k8s should be `unless-stopped` (or equivalent). The Dockerfile should declare a HEALTHCHECK against the service's health endpoint.

---

## TL;DR

```bash
# npm-only release (the default — most common case)
npm run release:patch

# npm release that ALSO rebuilds the GHCR image
npm run release:patch:image
```

The default release is npm-only. Docker images are deliberate, opt-in
artifacts because each multi-arch build burns several minutes of CI
time. Use `:image` (or set `BUILD_DOCKER=1`) when runtime code,
dependencies, env-var contract, or the Dockerfile changed.

---

## Prerequisites (one-time setup)

- **npm login** — `npm whoami` should print your username.
- **Git remote configured** — `git remote get-url origin` should print
  `git@github.com:fable-retold/retold-data-service.git` (or the
  HTTPS equivalent).
- **Push access to the repo** — required so `postversion` /
  `postpublish` hooks can push commits and tags.
- **Docker** (only if you want to test the image locally before tag).

---

## Ecosystem convention: lockfiles are gitignored

`package-lock.json` is in this repo's `.gitignore` (Quackage convention
shared across the retold ecosystem). The Dockerfile must use `npm install`,
not `npm ci` — `npm ci` requires the lockfile to be in the build context
and CI runners only check out what's in git. If you see EUSAGE errors in
GHCR build logs, change `RUN npm ci` to `RUN npm install` in the
Dockerfile.

---

## Releasing

| Command                              | npm registry | GHCR image rebuild |
|--------------------------------------|--------------|--------------------|
| `npm run release:patch`              | yes          | no                 |
| `npm run release:patch:image`        | yes          | yes                |
| `npm run release:minor`              | yes          | no                 |
| `npm run release:minor:image`        | yes          | yes                |
| `npm run release:major`              | yes          | no                 |
| `npm run release:major:image`        | yes          | yes                |

The non-`:image` variants are the default because most patch releases
don't change runtime behavior. The `:image` variants tell the pipeline
"this release does change runtime — build me a new image."

### Direct CLI (also works)

```bash
npm publish                              # npm only
npm run publish:docker                   # npm + docker (sets BUILD_DOCKER=1)
```

### From `retold-manager` TUI

- `[!]` Publish — npm only
- `[D]` Publish with docker image — npm + GHCR build

### Promoting a previous npm release to docker later

If you released `v<x>` to npm only, then later decide you do want a
docker image:

```bash
git push origin v<x>    # pushes the local tag → GHCR fires
```

The local tag is still sitting there from the original `npm version`
step. Pushing it triggers the workflow without touching npm.

---

## The chain

The lifecycle hooks all live in `package.json` and delegate to
`npx quack release …`. Default path (`BUILD_DOCKER` unset):

```
npm publish
  → prepublishOnly: npm test                     ← test gate
  → publish to npm
  → postpublish: BUILD_DOCKER unset → no-op      ← image NOT triggered
```

Docker-included path (`BUILD_DOCKER=1`):

```
BUILD_DOCKER=1 npm publish    (or: npm run publish:docker)
  → prepublishOnly: npm test
  → publish to npm
  → postpublish: BUILD_DOCKER=1 → tag + push     ← image trigger
  → .github/workflows/publish-image.yml fires
  → docker buildx build linux/amd64,linux/arm64
  → docker push ghcr.io/stevenvelozo/retold-data-service:<version>
```

---

## Verifying a release

1. **npm**: `npm view retold-data-service version`
2. **Workflow**: `https://github.com/fable-retold/retold-data-service/actions`
3. **Image**: `docker pull ghcr.io/stevenvelozo/retold-data-service:latest`

If the first `docker pull` returns `denied`, the package is private by
default — flip visibility to public via Package Settings → Danger Zone
on the package page.

---

## Image consumption

```bash
docker pull ghcr.io/stevenvelozo/retold-data-service:latest
docker run --rm ghcr.io/stevenvelozo/retold-data-service:latest
```

Configuration via env vars: see this module's README for the supported
`<MODULE>_*` variables. Any secret-bearing var also accepts `<NAME>_FILE`
pointing at a file whose contents become the value (mysql/postgres
convention; works with docker secrets and k8s Secrets).
