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
66 changes: 66 additions & 0 deletions .github/workflows/docs-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: Check docs

# Checks the Zensical docs site in docs/content, the same way ddev/ddev checks
# its docs. Read the Docs builds the PR preview and publishes
# https://docs.coder.ddev.com/ from main; this workflow only checks.

on:
push:
branches: [main]
pull_request:
paths:
- "docs/**"
- "mkdocs.yml"
- ".readthedocs.yaml"
- ".markdownlint.jsonc"
- ".linkspector.yml"
- "scripts/check-doc-params.sh"
- "drupal-core/template.tf"
- "drupal-contrib/template.tf"
- "freeform/template.tf"
- "modules/**"
- ".github/workflows/docs-check.yml"
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
checks: write
pull-requests: read

jobs:
docs-check:
name: Docs check (build, markdownlint, links, deep-link parameters)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: actions/setup-python@v6
with:
python-version: "3.x"

- name: Build with Zensical (strict)
run: |
pip install -r docs/requirements.txt
make zensical

- name: Run markdownlint
run: |
npm install -g markdownlint-cli
make markdownlint

- name: Check deep-link parameters
run: make check-doc-params

- name: Run linkspector to check external links
uses: umbrelladocs/action-linkspector@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-check
fail_on_error: true
# Check links only in added lines for PRs, but check everything otherwise
filter_mode: ${{ github.event_name == 'pull_request' && 'added' || 'nofilter' }}
config_file: ".linkspector.yml"
10 changes: 6 additions & 4 deletions .github/workflows/docs-deploy.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
name: Deploy Docs to GitHub Pages

# Builds and deploys /docs to the root of the gh-pages branch on every push
# to main that touches the docs directory. PR previews (in /pr-preview/) are
# preserved across production deployments.
# Builds and deploys the start.coder.ddev.com landing pages in /docs to the root
# of the gh-pages branch on every push to main that touches them. The user docs
# in /docs/content are a separate Zensical site published by Read the Docs to
# https://docs.coder.ddev.com and are excluded from this Jekyll build.
# PR previews (in /pr-preview/) are preserved across production deployments.
#
# GitHub Pages must be configured to serve from:
# Branch: gh-pages / (root)

on:
push:
branches: [main]
paths: ['docs/**']
paths: ['docs/**', '!docs/content/**']
workflow_dispatch:

permissions:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ composer.json.tmp
**/.claude/settings.local.json
CLAUDE.local.md
.claude/agent-memory-local/

# Zensical build output and cache (make zensical)
site/
.cache/
30 changes: 30 additions & 0 deletions .linkspector.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
dirs:
- docs/content
# Message templates for the coder-ddev-com GitHub org; they contain
# [PLACEHOLDER] URLs that operators fill in before sending
excludedDirs:
- docs/content/self-hosting/coder-ddev-com
ignorePatterns:
# Internal anchors are already checked by `zensical build --strict`
- pattern: ".md#"
# Behind GitHub sign-in or org membership, so linkspector can't load them
- pattern: "^https://coder.ddev.com"
- pattern: "^https://staging-coder.ddev.com"
- pattern: "^https://github.com/orgs/"
- pattern: "^https://github.com/settings/"
- pattern: "^https://gitlab.com/-/"
- pattern: "^https://www.drupal.org/user"
- pattern: "^https://claude.ai"
# Example hostnames used in docs
- pattern: "coder.example.com"
- pattern: "localhost"
- pattern: "127.0.0.1"
# Private to DDEV maintainers
- pattern: "^https://github.com/ddev/maintainer-info"
- pattern: "^https://monitor.ddev.com"
# Rate-limits automated requests
- pattern: "^https://robot.hetzner.com"
aliveStatusCodes:
- 200
- 206
- 301
43 changes: 43 additions & 0 deletions .markdownlint.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
// These rules mostly come from mkdocs rules
// https://github.com/mkdocs/mkdocs/blob/master/.markdownlintrc
// Using markdownlint from https://github.com/DavidAnson/markdownlint
// and https://github.com/igorshubovych/markdownlint-cli
// npm install --global markdownlint-cli

// Enable all markdownlint rules
"default": true,

// Fine with long lines, editor can wrap
"line-length": false,

// Unfortunately, mkdocs messes up nearly everything if you insist on this;
// the problem is that it's concatenating files, so you lose important levels
// of sidebar headings if every file starts with H1.
"first-line-h1": false,

"MD004": { "style": "consistent" },

// Set Ordered list item prefix to "ordered" (use 1. 2. 3. not 1. 1. 1.)
"MD029": { "style": "ordered" },

// Set list indent level to 4 which Python-Markdown requires
"MD007": { "indent": 4 },

// Embedded html allowed; often used for named anchors with mkdocs/readthedocs
"no-inline-html": false,

// Fenced code blocks do not need language spec
"MD040": false,

// Exclude code block style
"MD046": false,

// Repeated headings such as "Verify" or "Troubleshooting" under different
// sections are fine; only flag duplicates among siblings
"MD024": { "siblings_only": true },

// Table column alignment style - disabled as it creates maintenance burden
// without improving rendered output
"MD060": false
}
23 changes: 23 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# .readthedocs.yaml
# Read the Docs configuration file, modeled on ddev/ddev's.
# Publishes the Zensical site in docs/content to https://docs.coder.ddev.com/
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details

# Required
version: 2

build:
os: ubuntu-24.04
tools:
python: "3.14"
jobs:
install:
- pip install -r docs/requirements.txt
- 'sed -i "s|site_url:.*|site_url: $READTHEDOCS_CANONICAL_URL|" mkdocs.yml'
- grep site_url mkdocs.yml
build:
html:
- zensical build --clean --strict
post_build:
- mkdir -p $READTHEDOCS_OUTPUT/html/
- cp -rv site/* $READTHEDOCS_OUTPUT/html/
19 changes: 17 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,22 @@ This project provides a Coder v2+ template for DDEV-based development environmen
- **VS Code for Web** - Browser-based IDE via official Coder module
- **Ubuntu 24.04** - Base container OS

**Note:** User-facing documentation lives in `/docs/`. This file focuses on developer/contributor guidance.
**Note:** User-facing documentation lives in `docs/content/` and is published to https://docs.coder.ddev.com. This file focuses on developer/contributor guidance.

## Documentation

Two separate sites are built from `docs/`:

| Site | Source | Built with | Published by |
| ---- | ------ | ---------- | ------------ |
| https://docs.coder.ddev.com (user docs) | `docs/content/**/*.md`, config in root `mkdocs.yml` | [Zensical](https://zensical.org), set up the same way as ddev/ddev's docs | Read the Docs (`.readthedocs.yaml`), including PR previews |
| https://start.coder.ddev.com (landing pages, deep-link builders) | `docs/*.html`, `docs/*.js`, `docs/coder-ddev-start.css` | Jekyll (`docs/_config.yml` excludes `content/`) | GitHub Pages (`docs-deploy.yml`, previews via `docs-preview.yml`) |

- **Audience:** the user docs are written for people using the hosted service at coder.ddev.com. Self-hosting material goes under `docs/content/self-hosting/`.
- **Add a page:** create the Markdown file under `docs/content/` and add it to `nav:` in `mkdocs.yml`; links between pages are relative `.md` links.
- **Before pushing docs changes:** `make docs-check` (strict Zensical build, markdownlint with `.markdownlint.jsonc`, and `scripts/check-doc-params.sh`). Needs `pip install -r docs/requirements.txt` and `npm install -g markdownlint-cli`. `make zensical-serve` previews at http://localhost:8000. CI runs the same checks plus linkspector in `.github/workflows/docs-check.yml`.
- **Moved or renamed a page?** Add a `redirect_maps` entry in `mkdocs.yml`. Old `start.coder.ddev.com` Markdown URLs are kept alive by the `redirect_to` stubs in `docs/user/`, `docs/admin/`, `docs/architecture/` and `docs/reference/`.
- **When a template's user-facing behavior changes** (parameters, app buttons, URLs, `ddev coder-setup`/`coder-routes`/`launch`), update the matching page in `docs/content/` in the same PR — especially `docs/content/freeform/create.md` for freeform parameters.

## Coder CLI Commands in Docs

Expand Down Expand Up @@ -57,7 +72,7 @@ When writing or reviewing `coder` CLI commands in documentation, **always verify

- **Never change an existing `coder_parameter`'s `type` in place — always give it a new `name`.** The `coder_parameter` data source validates the incoming value against the declared `type` *before* any of the template's own `locals` ever run, so a `try(tobool(...), false)`-style fallback in a `locals` block cannot rescue a stale value — the plan fails hard first, with an opaque `Error: Parameter value is not of type "bool"`. Any client still holding an old-typed value for that parameter name (a bookmarked workspace-creation deep link, a cached copy of a `docs/*.html` page, an already-open browser tab) will hit this on every future `terraform plan`, including the Coder UI's live dynamic-parameter preview during workspace creation. Renaming the parameter is the only fix — `terraform` code cannot intercept the provider's own type validation. Root-caused and fixed for `share_drupal_site` → `drupal_site_public` in #206 (`drupal-core`/`drupal-contrib`; both changed the parameter from a 3-way `owner`/`authenticated`/`public` string to a `bool` switch in #205, one PR earlier — the type change and the deep-link doc pages that still emitted the old string values shipped in the same PR and nothing caught the mismatch).
- **`terraform test`'s `mock_provider "coder"` cannot catch the above class of bug.** The mock replaces the real `coder_parameter` data source entirely (including its type validation), so a run block asserting behavior for a stale/incompatible parameter value will pass locally and in CI even when the real provider would hard-fail. There is no terraform-test-layer way to close this gap; the only defense is the naming discipline above.
- **`docs/drupal-core.html`, `docs/drupal-issue.html`, and `docs/coder-workspace-name.js` build Coder workspace-creation deep links (`?param.<name>=<value>`) and are not covered by any CI check against the live `coder_parameter` definitions in `template.tf`.** Whenever a template's parameter names, types, or allowed values change, grep these docs files for the old name/values and update them in the same PR — nothing currently fails CI if they drift out of sync.
- **`docs/drupal-core.html`, `docs/drupal-issue.html`, and `docs/coder-workspace-name.js` build Coder workspace-creation deep links (`?param.<name>=<value>`).** `scripts/check-doc-params.sh` (`make check-doc-params`, run in `docs-check.yml`) fails if a `param.<name>` used in `docs/` no longer exists as a `coder_parameter` in any template or shared module, which catches renames and removals. It does **not** check parameter types or allowed values, or that a link targets the template that defines the parameter — whenever a template's parameter types or allowed values change, grep these docs files for the old values and update them in the same PR.

## Template Architecture Overview

Expand Down
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,29 @@ test-templates: sync-shared ## Run Terraform mock unit tests for all templates (
done
@echo "All template tests passed."

# --- Documentation (Zensical, same setup as ddev/ddev; published by Read the Docs) ---
# Install the docs toolchain with: pip install -r docs/requirements.txt
# and markdownlint with: npm install -g markdownlint-cli

.PHONY: zensical
zensical: ## Build the docs site in docs/content with Zensical (strict, like Read the Docs)
zensical build --clean --strict

.PHONY: zensical-serve
zensical-serve: ## Preview the docs site locally at http://localhost:8000 with live reload
zensical serve

.PHONY: markdownlint
markdownlint: ## Lint the docs Markdown with markdownlint (config in .markdownlint.jsonc)
markdownlint --config .markdownlint.jsonc docs/content

.PHONY: check-doc-params
check-doc-params: ## Check that deep-link param.<name> values in the docs exist as coder_parameters
scripts/check-doc-params.sh

.PHONY: docs-check
docs-check: zensical markdownlint check-doc-params ## Run all local docs checks

.PHONY: push-staging
push-staging: ## Push all templates to staging-coder.ddev.com inactive (ACTIVATE=false); requires CODER_URL set to staging
$(MAKE) push-all-templates ACTIVATE=false
Expand Down
28 changes: 12 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Coder workspace template for DDEV-based development with Docker-in-Docker support, Node.js, and Git.

📚 **Documentation:** [docs.coder.ddev.com](https://docs.coder.ddev.com/)

**Get started (recommended paths):**

- **Drupal.org issue or project** → [Drupal Issue Picker](https://start.coder.ddev.com/drupal-issue) (auto-detects core vs contrib and prefills sensible defaults)
Expand Down Expand Up @@ -62,7 +64,7 @@ make help # See all available commands

### Version Management

The `VERSION` file in the root directory controls the image tag. The Makefile automatically copies it into each template directory before pushing, and `template.tf` reads it from there — no manual edits to `template.tf` are needed.
The `VERSION` file in the root directory controls the image tag. The Makefile passes it to every template as `--variable image_version=...` at push time, so no edits to `template.tf` are needed.

**To release a new version:**
1. Update the `VERSION` file (e.g., `v0.7`)
Expand All @@ -77,22 +79,16 @@ make build-and-push-no-cache && make push-all-templates # Clean build and deplo

## Documentation

**New to Coder?**
- 📘 [Getting Started Guide](./docs/user/getting-started.md) - Create your first workspace
- 📗 [Using Workspaces](./docs/user/using-workspaces.md) - Daily workflows and tips

**Administrators:**
- 📕 [Operations Guide](./docs/admin/operations-guide.md) - Deploy and manage template
- 📙 [User Management](./docs/admin/user-management.md) - Users, roles, permissions
- 📔 [Troubleshooting](./docs/admin/troubleshooting.md) - Debug common issues
📚 **User documentation: [docs.coder.ddev.com](https://docs.coder.ddev.com/)**, built with [Zensical](https://zensical.org) from [`docs/content/`](./docs/content/) and published by Read the Docs, the same way as [docs.ddev.com](https://docs.ddev.com).

**DDEV Experts:**
- 🔍 [Comparison to Local DDEV](./docs/architecture/comparison-to-local.md) - Architecture, tradeoffs, migration
- [Getting started](https://docs.coder.ddev.com/getting-started/): access, sign-in, choosing a template
- [Freeform workspaces](https://docs.coder.ddev.com/freeform/): running your own DDEV projects
- [Drupal quickstart](https://docs.coder.ddev.com/drupal/quickstart/)
- [Self-hosting](https://docs.coder.ddev.com/self-hosting/overview/): server setup, operations, user management

**Developers/Contributors:**
- 🤖 [AGENTS.md](./AGENTS.md) - AI-assisted development guide
Preview the docs locally with `pip install -r docs/requirements.txt` and `make zensical-serve`. Developer and contributor guidance is in [AGENTS.md](./AGENTS.md).

📚 **[Full Documentation Index](./docs/README.md)**
The landing pages at [start.coder.ddev.com](https://start.coder.ddev.com) (`docs/*.html`) are a separate Jekyll site deployed to GitHub Pages.

## Template Structure

Expand Down Expand Up @@ -205,7 +201,7 @@ coder create --template drupal-contrib <workspace-name> \
- Find your workspace
- Click on port **80** or **443** under "Apps"

**📖 [Full Getting Started Guide](./docs/user/getting-started.md)**
**📖 [Full Getting Started Guide](https://docs.coder.ddev.com/getting-started/)**

### For Administrators

Expand All @@ -225,4 +221,4 @@ make build-and-push # Build + push image
make deploy-freeform # Push freeform template (uses existing image)
```

**📖 [Full Operations Guide](./docs/admin/operations-guide.md)**
**📖 [Full Operations Guide](https://docs.coder.ddev.com/self-hosting/operations-guide/)**
3 changes: 3 additions & 0 deletions docs/README.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
redirect_to: https://docs.coder.ddev.com/self-hosting/overview/
---
Loading
Loading