feat: Write CONTRIBUTING.md documenting the complete end-to-end templat…

- CONTRIBUTING.md

GSD-Task: S04/T01
This commit is contained in:
John Doe
2026-04-29 17:13:58 -04:00
parent 652f7b2678
commit 226e3006ed
+288
View File
@@ -0,0 +1,288 @@
# Contributing to the Arcane Template Registry
Thank you for contributing! This registry is a curated collection of Docker Compose templates for Arcane. Template folders are the single source of truth — CI validates their structure, assembles `registry.json` from on-disk state, and publishes it to GitHub Pages.
## Repository Overview
```
arcane-repo/
├── templates/ # One directory per template
│ ├── litellm/ # Example: LiteLLM proxy template
│ │ ├── arcane.json # Metadata (id, name, version, author, tags)
│ │ ├── docker-compose.yml # Compose service definition
│ │ ├── .env.example # Documented env var reference
│ │ └── README.md # Template documentation
│ └── ...
├── scripts/
│ └── build-registry.js # Validates and assembles registry.json
├── schema.json # JSON Schema for validation
├── .github/workflows/
│ ├── validate.yml # Runs on PRs touching templates/**
│ └── deploy.yml # Runs on push to main, publishes to Pages
└── README.md
```
## Adding a New Template
### 1. Fork and Branch
Fork the repository, clone your fork, and create a feature branch:
```bash
git checkout -b add-<template-name>
```
### 2. Create the Template Directory
```bash
mkdir templates/<template-name>/
```
Replace `<template-name>` with a lowercase slug using hyphens only (e.g., `my-app`, `custom-service`). This name becomes the template `id` and must match the folder name exactly.
### 3. Write the Four Required Files
#### `arcane.json`
Template metadata file. The `id` field must match the folder name, `version` must be valid semver, and `tags` must be a non-empty array of unique strings.
```json
{
"id": "my-app",
"name": "My App",
"description": "A clear description of what the template provides and its use case",
"version": "1.0.0",
"author": "Your Name or Organization",
"tags": ["category1", "category2"]
}
```
**Conventions:**
- `id` — lowercase slug with hyphens only, must match folder name
- `version` — valid [semver](https://semver.org/) string (e.g., `1.0.0`, `1.2.3-beta.1`)
- `tags` — non-empty array of unique strings; use descriptive categories like `ai`, `llm`, `automation`, `workflow`, `low-code`, `database`, `monitoring`
- You do **not** need to include `compose_url`, `env_url`, `documentation_url`, or `content_hash` — those are auto-generated by the CI build script
- Do **not** include `$schema` in template-level `arcane.json`
#### `docker-compose.yml`
A valid Docker Compose file (version `3.8` or later). Follow these conventions:
```yaml
version: '3.8'
services:
my-service:
image: ghcr.io/org/image:tag
container_name: my-service
hostname: my-service
ports:
- "${PORT:-8080}:8080"
volumes:
- my_data:/app/data
restart: unless-stopped
environment:
- API_KEY=${API_KEY:-}
volumes:
my_data:
name: my_data
```
**Conventions:**
- File name is `docker-compose.yml` (not `compose.yml`)
- Reference env vars using `${VAR:-default}` syntax with secure defaults where possible
- Use `${VAR:?}` mandatory-variable syntax for values that must be user-provided and have no safe default (e.g., encryption keys)
- Always declare volumes under a top-level `volumes:` key
- Set `restart: unless-stopped`
- Use `-` for environment variable prefixes, not `=` (e.g., `- API_KEY=${API_KEY:-}`)
#### `.env.example`
Documents all environment variables the compose file consumes. Include defaults, format hints, and comments:
```bash
# ── Configuration ──────────────────────────────────────────────────────
# Host port for the service (default: 8080)
PORT=8080
# API key for external service (required — generate a random key)
API_KEY=your-api-key
```
**Conventions:**
- Comment sections with dividers for readability
- Every `docker-compose.yml` variable should appear here
- Show default values where applicable
- Mark required variables with comments like `(required)`
#### `README.md`
Documents what the template does and how to use it. Include at minimum:
- One-line description of the template
- Quick Start section with `cp .env.example .env`, env setup, and `docker compose up -d`
- Environment variable reference table (Variable | Default | Description)
- Any post-start verification (e.g., `curl` command for health check)
See the [LiteLLM template](templates/litellm/README.md) as a reference example.
### 4. Validate Locally
Before pushing, run the validation script to catch errors early:
```bash
node scripts/build-registry.js --validate-only
```
This checks every template directory for:
- Valid `arcane.json` JSON syntax
- Required fields: `id`, `name`, `description`, `version`, `author`, `tags`
- `id` matches the folder name and is a valid slug (`^[a-z0-9-]+$`)
- `version` is valid semver
- `tags` is a non-empty array of unique strings
- Required files present: `docker-compose.yml`, `.env.example`, `README.md`
- No duplicate `id` values across templates
### 5. Push and Open a PR
```bash
git add templates/<template-name>/
git commit -m "feat: add <template-name> template"
git push origin add-<template-name>
```
Open a pull request against the `main` branch. The `Validate Templates` workflow runs automatically on any PR that changes paths under `templates/**`.
### 6. After Merge
Once the PR merges to `main`, the **Build and Deploy Registry** workflow triggers:
1. Runs `node scripts/build-registry.js` to generate `registry.json`
2. Configures GitHub Pages
3. Uploads the repository as a Pages artifact
4. Deploys to GitHub Pages (~2 minutes total)
After deployment, the published registry is available at:
```
https://<org>.github.io/arcane-repo/registry.json
```
Replace `<org>` with the organization or owner name.
## Modifying an Existing Template
Edit any of the four required files in the template's directory:
- `arcane.json` — update metadata (version, description, tags)
- `docker-compose.yml` — update service configuration
- `.env.example` — add or update documented variables
- `README.md` — update documentation
The `content_hash` is computed at build time from all four source files (sorted alphabetically: `arcane.json`, `docker-compose.yml`, `.env.example`, `README.md`), so any modification is automatically detected by CI. You do not need to update a hash manually.
Run local validation before pushing:
```bash
node scripts/build-registry.js --validate-only
```
## Removing a Template
Delete the template directory:
```bash
rm -rf templates/<template-name>/
git add -A
git commit -m "feat: remove <template-name> template"
```
Commit and push. The next CI run skips the removed directory — no other files need updating. The `registry.json` is rebuilt from whatever directories remain.
## CI Workflows
### Validate Templates (`validate.yml`)
- **Triggers on:** Pull requests to any branch, when files under `templates/**` change
- **Steps:**
1. Checkout code
2. Set up Node.js 22
3. Run `node scripts/build-registry.js --validate-only`
- **Failing a PR:** If validation errors are found, the workflow exits with a non-zero code, marking the PR check as failed. Fix the reported issue and push again.
### Build and Deploy Registry (`deploy.yml`)
- **Triggers on:** Push to `main` that changes files under `templates/**`, `schema.json`, or `scripts/build-registry.js`
- **Steps:**
1. Checkout code
2. Set up Node.js 22
3. Run `node scripts/build-registry.js` (writes `registry.json`)
4. Configure GitHub Pages
5. Upload the repository as a Pages artifact
6. Deploy to Pages
## Schema
The registry schema lives at [`schema.json`](schema.json) in the repository root. It defines two levels:
### Registry (top-level)
| Field | Type | Required | Description |
|---------------|----------|----------|--------------------------------------------|
| `name` | string | yes | Registry name |
| `description` | string | yes | Registry description |
| `version` | semver | yes | Registry version |
| `author` | string | yes | Registry author |
| `url` | URI | yes | Repository URL |
| `templates` | array | yes | Array of `template` objects (min 1 item) |
### Template
| Field | Type | Required | Description |
|---------------------|-------------|----------|--------------------------------------------------|
| `id` | slug | yes | Unique identifier, matches folder name |
| `name` | string | yes | Human-readable name |
| `description` | string | yes | Detailed description |
| `version` | semver | yes | Template version |
| `author` | string | yes | Template author |
| `compose_url` | URI | yes | Auto-generated raw GitHub URL to compose file |
| `env_url` | URI | yes | Auto-generated raw GitHub URL to env example |
| `documentation_url` | URI | yes | Auto-generated raw GitHub URL to README |
| `content_hash` | SHA-256 hex | yes | Auto-generated hash of all source files |
| `tags` | array | yes | Non-empty array of unique tags (letters, digits, hyphens) |
### Auto-Generated Fields
Template authors **do not write** these fields in `arcane.json`. The CI build script computes them:
- `compose_url``https://raw.githubusercontent.com/<owner>/<repo>/<branch>/templates/<id>/docker-compose.yml`
- `env_url``https://raw.githubusercontent.com/<owner>/<repo>/<branch>/templates/<id>/.env.example`
- `documentation_url``https://raw.githubusercontent.com/<owner>/<repo>/<branch>/templates/<id>/README.md`
- `content_hash` — SHA-256 of the concatenation of all four source files sorted alphabetically
The build script resolves the repository owner and name from `GITHUB_REPOSITORY` in CI, or from `REPO_OWNER`/`REPO_NAME` environment variables locally.
## Validation Rules (from `build-registry.js`)
These are checked by the validation script and CI:
| Rule | Error level |
|---------------------------------------------|-------------|
| `templates/` directory must exist | error |
| Each subdirectory must have `arcane.json` | error |
| `arcane.json` must be valid JSON | error |
| `id` must match folder name | error |
| `id` must match slug pattern `/^[a-z0-9-]+$/` | error |
| `version` must be valid semver | error |
| `tags` must be a non-empty array | error |
| `docker-compose.yml` must exist | error |
| `.env.example` must exist | error |
| `README.md` must exist | error |
| No duplicate template `id` values | error |
| Duplicate tags (in same template) | warning |
## License
By contributing, you agree that your contributions will be licensed under the MIT License (see [LICENSE](LICENSE) — if no LICENSE file exists, the repository defaults to MIT).