Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Deploy Configurator to Pages

on:
push:
branches: [main]
paths:
- 'configurator/**'
- '.github/workflows/pages.yml'
pull_request:
paths:
- 'configurator/**'
- '.github/workflows/pages.yml'
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

# PR pushes cancel each other (avoid wasting runners on stale commits);
# main-branch runs queue serially under the shared `pages` group so
# Pages deploys never overlap.
concurrency:
group: ${{ github.event_name == 'pull_request' && format('pages-pr-{0}', github.ref) || 'pages' }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: configurator
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
# Pinned to 20.19.0 (not bare '20') because vite@8 declares
# engines.node = '^20.19.0 || >=22.12.0'. A runner image stuck on
# an earlier 20.x would fail npm ci with EBADENGINE.
node-version: '20.19.0'
cache: 'npm'
cache-dependency-path: configurator/package-lock.json

- name: Install dependencies
run: npm ci

- name: Typecheck
run: npm run typecheck

- name: Test (unit)
run: npm test

- name: Install Playwright browser
run: npx playwright install --with-deps chromium

- name: Test (e2e)
run: npm run test:e2e

- name: Build
run: npm run build

- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: configurator/dist

- name: Upload Playwright report
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: configurator/playwright-report
retention-days: 7

deploy:
# Only deploy on push-to-main; PR runs validate the build but must not
# publish a PR's contents to the live site.
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
ImageJournal.md
docker-compose*.yml
qnap.docker.*.txt
.playwright-mcp/
8 changes: 8 additions & 0 deletions configurator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules/
dist/
*.log
.DS_Store
.vite/
/playwright-report/
/test-results/
/.playwright-mcp/
89 changes: 89 additions & 0 deletions configurator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# RoonServer Docker Configuration Generator

A static web app that generates `docker-compose.yml` and `docker run`
commands for the official RoonServer Docker image. Built with TypeScript
and Vite, deployed to GitHub Pages.

## Development

```sh
npm install
npm run dev # dev server with hot reload at http://localhost:5173
npm run typecheck # tsc --noEmit
npm test # vitest
npm run build # typecheck + Vite build into dist/
npm run preview # serve the built dist/ for a final sanity check
```

## Adding a platform

Platforms are declarative JSON under [`public/platforms/`](public/platforms/).
To add one:

1. Create `public/platforms/<id>.json` matching the shape below.
2. Add the `<id>` to the `public/platforms/index.json` manifest in the
order it should appear in the dropdown.
3. Run `npm test` — the platform-file tests will validate the shape and
confirm the manifest is in sync.

The manifest's **first visible entry is the default platform**. To change
the default, reorder the manifest.

### Platform JSON shape

```json
{
"id": "qnap",
"label": "QNAP",
"roon": "/share/Container/roon",
"music": "/share/Music",
"backup": "/share/Container/roon-backups",
"prefix": "/share/",
"hint": "Paths set for QNAP. Container Station stores app data under /share/Container/.",
"rootPattern": "^/share/",
"hidden": false
}
```

| Field | Required | Purpose |
|---------------|----------|----------------------------------------------------------------------------|
| `id` | yes | Must match the filename (`qnap.json` → `"id": "qnap"`). |
| `label` | yes | Display text in the dropdown and inline warnings. |
| `roon` | yes | Default host path for the `/Roon` container mount. |
| `music` | yes | Default host path for the `/Music` container mount. |
| `backup` | yes | Default host path for the `/RoonBackups` container mount. |
| `prefix` | yes | Placeholder text for the "add mount" host input. |
| `hint` | yes | One-sentence help text shown under the platform dropdown. |
| `rootPattern` | no | Regex string. If present, host paths not matching it show a soft warning. |
| `hidden` | no | `true` keeps the platform loaded but omits it from the dropdown. |

## Tests

- **`src/*.test.ts`** — unit tests for the pure modules (`generator`,
`platforms`), plus structural checks on every platform JSON file. Uses
[Vitest](https://vitest.dev/).
- **`e2e/`** — end-to-end tests against the built site, driven by
[Playwright](https://playwright.dev/). Run with `npm run test:e2e`.

CI runs `typecheck`, `test`, and `test:e2e` before every deploy.

## Architecture

Each source module has one job. Keeping them separate makes the pure
logic unit-testable without a DOM.

| File | Responsibility |
|------------------------|------------------------------------------------------------------|
| `src/types.ts` | Shared types: `Config`, `Platform`, `ValidationIssue`. |
| `src/generator.ts` | Pure functions: `Config` → compose/run output lines. |
| `src/platforms.ts` | Platform loader + validation helpers. No DOM. |
| `src/highlight.ts` | DOM-based syntax highlighting for the output editor. |
| `src/main.ts` | DOM wiring, event handlers, app init. |
| `public/platforms/` | Platform data files (copied verbatim to the build output). |

## Deployment

Deployment is automated via [`.github/workflows/pages.yml`](../.github/workflows/pages.yml).
Every push to `main` that touches `configurator/**` triggers a build
and, on success, a Pages deploy. To deploy, the repository's **Settings
→ Pages → Source** must be set to **GitHub Actions**.
Loading
Loading