# Docs Site Docker Image + Install Page Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Add an Installation page to the Storybook playground, package the playground's static build as a `nychthemeron-docs` Docker image served by nginx, and wire up CI to build/push that image on version tags. **Architecture:** A new MDX docs page (`packages/playground/stories/Install.mdx`) documents installing `@nychthemeron/library` via npm/pnpm/yarn/bun from the private Forgejo npm registry. A new multi-stage `packages/playground/Dockerfile` builds the library and Storybook static output with `oven/bun:1`, then copies `storybook-static` into `nginx:alpine`. A root `.dockerignore` keeps the build context lean. A new `publish-docs` job in `.forgejo/workflows/ci.yml`, parallel to `publish`, builds and pushes the image on `v*` tags. **Tech Stack:** Storybook 10 (`@storybook/addon-docs`), Vue 3, Bun, Docker (multi-stage, nginx:alpine), Forgejo Actions. --- ### Task 1: Add Installation MDX page **Files:** - Create: `packages/playground/stories/Install.mdx` - [ ] **Step 1: Create the MDX file** ```mdx import { Meta } from '@storybook/addon-docs/blocks' # Installation `@nychthemeron/library` is published to a private npm registry hosted on Forgejo. Configure your package manager for the `@nychthemeron` scope, then install the package. ## Configure the registry Add the following to your project's `.npmrc`: ``` @nychthemeron:registry=https://git.mcpeakdev.com/api/packages/mcpeakdev/npm/ ``` ## npm ```bash npm install @nychthemeron/library ``` ## pnpm ```bash pnpm add @nychthemeron/library ``` ## yarn ```bash yarn add @nychthemeron/library ``` ## bun ```bash bun add @nychthemeron/library ``` ``` - [ ] **Step 2: Build Storybook and verify the page is included** Run: `cd packages/playground && bun run build` Expected: Build succeeds with output ending in something like `Output directory: storybook-static`. Then run: `grep -o '"title":"Get Started/Installation"' packages/playground/storybook-static/index.json` Expected: prints `"title":"Get Started/Installation"`. - [ ] **Step 3: Commit** ```bash git add packages/playground/stories/Install.mdx git commit -m "Add installation docs page to playground" ``` --- ### Task 2: Add root `.dockerignore` **Files:** - Create: `.dockerignore` - [ ] **Step 1: Create `.dockerignore`** ``` node_modules **/node_modules **/dist **/storybook-static **/.storybook-static .git docs .tmp .bun-cache .pw-browsers .pw-libs .shots *.log ``` - [ ] **Step 2: Commit** ```bash git add .dockerignore git commit -m "Add .dockerignore for docs Docker build" ``` --- ### Task 3: Add Dockerfile for the docs site **Files:** - Create: `packages/playground/Dockerfile` - [ ] **Step 1: Create the Dockerfile** ```dockerfile # syntax=docker/dockerfile:1 FROM oven/bun:1 AS build WORKDIR /app COPY . . RUN bun install --frozen-lockfile RUN cd packages/library && bun run build RUN cd packages/playground && bun run build FROM nginx:alpine AS runtime COPY --from=build /app/packages/playground/storybook-static /usr/share/nginx/html EXPOSE 80 ``` - [ ] **Step 2: Build and run the image locally** Run (from repo root): ```bash docker build -t nychthemeron-docs:test -f packages/playground/Dockerfile . docker run -d --rm --name nychthemeron-docs-test -p 8080:80 nychthemeron-docs:test sleep 2 curl -sf http://localhost:8080/ | grep -i storybook docker stop nychthemeron-docs-test ``` Expected: `docker build` completes successfully, the container starts, and the `curl` output contains a reference to `storybook` (from `storybook-static/index.html`). > If the Docker CLI/daemon isn't available in the current environment (e.g. > sandboxed dev container), skip running this step here and ask the user to > run it locally before merging. - [ ] **Step 3: Commit** ```bash git add packages/playground/Dockerfile git commit -m "Add Dockerfile for nychthemeron-docs static site" ``` --- ### Task 4: Add `publish-docs` CI job **Files:** - Modify: `.forgejo/workflows/ci.yml` - [ ] **Step 1: Add the `publish-docs` job** Add this job after the existing `publish` job (same indentation level, i.e. as a sibling under `jobs:`): ```yaml publish-docs: needs: build if: startsWith(github.ref, 'refs/tags/v') runs-on: docker steps: - name: Checkout uses: actions/checkout@v4 - name: Log in to registry uses: docker/login-action@v3 with: registry: git.mcpeakdev.com username: ${{ github.actor }} password: ${{ secrets.FORGEJO_TOKEN }} - name: Extract image tag id: meta run: echo "tag=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" - name: Build and push docs image uses: docker/build-push-action@v6 with: context: . file: packages/playground/Dockerfile push: true tags: | git.mcpeakdev.com/mcpeakdev/nychthemeron-docs:${{ steps.meta.outputs.tag }} git.mcpeakdev.com/mcpeakdev/nychthemeron-docs:latest ``` - [ ] **Step 2: Verify YAML structure** Run: `bun run --eval "console.log('ok')"` is not a YAML check — instead, visually confirm in the file that `publish-docs:` is indented exactly like `publish:` (4 spaces under `jobs:`), and that all of its child keys (`needs`, `if`, `runs-on`, `steps`, and each `- name:` step) match the indentation pattern used by the `publish` job. Confirm there are exactly three top-level jobs: `build`, `publish`, `publish-docs`. - [ ] **Step 3: Commit** ```bash git add .forgejo/workflows/ci.yml git commit -m "Add publish-docs job to build and push nychthemeron-docs image" ```