Skip to content
Open
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
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
run: npm ci

- name: Run tests
run: go test -race ./cmd/sync-content/...
run: go test -race ./cmd/sync-content/... ./cmd/doctest/...

- name: Sync content
run: go run ./cmd/sync-content --org complytime --config sync-config.yaml --lock .content-lock.json --write
Expand All @@ -46,3 +46,6 @@ jobs:

- name: Build site
run: hugo --minify --gc

- name: Run documentation tests (informational)
run: make test-docs || true
5 changes: 4 additions & 1 deletion .github/workflows/deploy-gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
fi

- name: Run tests
run: go test -race ./cmd/sync-content/...
run: go test -race ./cmd/sync-content/... ./cmd/doctest/...

- name: Sync content
run: go run ./cmd/sync-content --org complytime --config sync-config.yaml --lock .content-lock.json --write
Expand All @@ -65,6 +65,9 @@ jobs:
- name: Build
run: hugo --minify --gc

- name: Run documentation tests (informational)
run: make test-docs || true

- name: Upload artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9
with:
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@ hugo_stats.json
node_modules/

# ─── Go ──────────────────────────────────────────────────────────────
# Compiled sync engine binary (built by CI or locally).
# Compiled tool binaries (built by CI or locally).
/sync-content
cmd/sync-content/sync-content
/doctest

# ─── Test output ─────────────────────────────────────────────────────
.test-output/

# ─── Synced content (generated by sync-content at build time) ────────
# Per-repo project pages generated by the org scan and config overlay.
Expand Down
21 changes: 21 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Agent Instructions

## Documentation with Shell Commands

When editing documentation that contains shell commands:

1. **Always add `{test="..."}` attributes** to fenced code blocks that contain
runnable shell commands. Use lowercase alphanumeric identifiers with hyphens.

2. **Write a corresponding Bats test** in `tests/docs/` before fixing a snippet
(TDD for docs). The test name must match the `test` attribute value.

3. **Run `make test-docs-coverage`** to check for untested code blocks.

4. **Run `make test-docs`** to verify all annotated snippets pass their tests.

## Go Code

- Run `make check` before committing (includes `go vet`, `gofmt`, race tests,
and doc coverage).
- Follow existing patterns in `cmd/sync-content/` for test structure.
48 changes: 47 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ style: fix indentation in home template
- [ ] No broken links or missing images
- [ ] Frontmatter includes all required fields (`title`, `description`, `weight`)
- [ ] If Go code was changed: `make check` passes (`go vet`, `gofmt`, and `go test -race`)
- [ ] If documentation code blocks were changed: `make test-docs` passes
- [ ] Commit messages follow conventional format
- [ ] DCO sign-off is present

Expand Down Expand Up @@ -447,7 +448,7 @@ The Makefile provides handy targets for all common sync and Go operations
make sync-dry # dry-run — reads GitHub, writes nothing
make sync # apply changes to disk
make sync-single REPO=complytime/complyctl # single-repo dry-run
make check # go vet + fmt-check + race tests (CI equivalent)
make check # go vet + fmt-check + race tests + doc coverage (CI equivalent)
make test-race # tests with race detector only
```

Expand Down Expand Up @@ -483,6 +484,51 @@ echo "Token set, length: ${#GITHUB_TOKEN}, prefix: ${GITHUB_TOKEN:0:4}"
go run ./cmd/sync-content --org complytime --config sync-config.yaml --write
```

### Testing Documentation

Documentation pages with shell commands use **testable code blocks** — fenced
code blocks annotated with a `{test="..."}` attribute that links them to
automated tests.

**Annotating a code block:**

````markdown
```bash {test="install-complyctl"}
go install github.com/complytime/complyctl@latest
```
````

The `test` value must be lowercase alphanumeric with hyphens (`[a-z0-9-]+`).
It becomes both the extracted snippet filename and the Bats test reference.
Each value must be unique within a page.

**Writing the corresponding test:**

Create or update a `.bats` file in `tests/docs/` matching the page name:

```bash
# tests/docs/getting-started.bats

@test "install-complyctl" {
run_snippet "getting-started/01-install-complyctl.bash"
assert_success
}
```

**Opting out a page:** Add `testable_docs: false` to the page's YAML frontmatter
to skip it entirely from extraction and coverage reporting.

**Make targets:**

| Target | What it does |
|--------|-------------|
| `make test-docs-extract` | Extract annotated code blocks to `/tmp/doctest-snippets` |
| `make test-docs` | Extract + run Bats tests |
| `make test-docs-coverage` | Report untested executable code blocks (warnings only) |

Coverage warnings are non-blocking — they show which blocks could benefit from
test annotations but do not fail the build.

### Testing Tips

- Always test with **browser cache disabled** (DevTools → Network →
Expand Down
36 changes: 28 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
# make sync-dry — dry-run content sync (reads GitHub, writes nothing)
# make sync — apply content sync to disk
# make dev — start Hugo dev server (after syncing content)
# make check — vet + fmt-check + race tests
# make check — vet + fmt-check + race tests + doc coverage
# ---------------------------------------------------------------------------

# Overridable variables
Expand All @@ -22,6 +22,7 @@ REPO ?=

SYNC_BIN := cmd/sync-content/sync-content
SYNC_PKG := ./cmd/sync-content/...
DOCTEST_DIR ?= /tmp/doctest-snippets

# Common flags passed to every sync invocation
SYNC_FLAGS := --org $(ORG) --config $(CONFIG) --output $(OUTPUT) --workers $(WORKERS) --timeout $(TIMEOUT)
Expand Down Expand Up @@ -49,32 +50,35 @@ build: ## Compile the sync-content binary
go build -o $(SYNC_BIN) ./cmd/sync-content

.PHONY: test
test: ## Run all Go unit tests
go test $(SYNC_PKG)
test: ## Run all tests (Go unit + doc tests + doc coverage)
go test $(SYNC_PKG) ./cmd/doctest/...
-$(MAKE) test-docs
-$(MAKE) test-docs-coverage

.PHONY: test-race
test-race: ## Run Go tests with the race detector
go test -race $(SYNC_PKG)
go test -race $(SYNC_PKG) ./cmd/doctest/...

.PHONY: vet
vet: ## Run go vet
go vet $(SYNC_PKG)
go vet $(SYNC_PKG) ./cmd/doctest/...

.PHONY: fmt
fmt: ## Format Go source files with gofmt
gofmt -w cmd/sync-content/
gofmt -w cmd/sync-content/ cmd/doctest/

.PHONY: fmt-check
fmt-check: ## Check Go formatting (non-destructive)
@out=$$(gofmt -l cmd/sync-content/); \
@out=$$(gofmt -l cmd/sync-content/ cmd/doctest/); \
if [ -n "$$out" ]; then \
echo "The following files need formatting:"; \
echo "$$out"; \
exit 1; \
fi

.PHONY: check
check: vet fmt-check test-race ## Run vet + fmt-check + race tests (CI equivalent)
check: vet fmt-check test-race ## Run vet + fmt-check + race tests + doc coverage (CI equivalent)
-$(MAKE) test-docs-coverage

# ---------------------------------------------------------------------------
# Content sync — uses GITHUB_TOKEN from the environment
Expand Down Expand Up @@ -106,6 +110,22 @@ sync-single: ## Apply sync for one repo (REPO=complytime/complyctl)
@if [ -z "$(REPO)" ]; then echo "Usage: make sync-single REPO=complytime/<name>"; exit 1; fi
$(MAKE) sync REPO=$(REPO)

# ---------------------------------------------------------------------------
# Documentation tests — extract, validate, and test code blocks
# ---------------------------------------------------------------------------

.PHONY: test-docs-extract
test-docs-extract: ## Extract testable code blocks from documentation
@go run ./cmd/doctest extract --content-dir content/docs --output-dir $(DOCTEST_DIR)

.PHONY: test-docs
test-docs: test-docs-extract ## Run documentation tests (Bats)
@SNIPPETS_DIR=$(DOCTEST_DIR) node_modules/.bin/bats --formatter pretty tests/docs/

.PHONY: test-docs-coverage
test-docs-coverage: ## Report untested code blocks in documentation
@go run ./cmd/doctest coverage --content-dir content/docs

# ---------------------------------------------------------------------------
# Hugo / Node — site build and dev server
# ---------------------------------------------------------------------------
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,19 @@ The site will be available at `http://localhost:1313/`.

**Production build**: `npm run build` (output in `public/`).

**Documentation tests**: `make test-docs` extracts annotated code blocks and runs Bats tests against them.

## Project Structure

```
website/
├── cmd/sync-content/ # Go content sync tool (10 source files, package main)
├── cmd/doctest/ # Go documentation test extraction tool
├── config/_default/ # Hugo configuration (TOML)
├── content/docs/ # Markdown content (projects/ is generated by sync tool)
├── data/projects.json # Generated landing page cards (gitignored)
├── layouts/ # Custom Hugo layout overrides
├── tests/docs/ # Bats documentation tests
├── sync-config.yaml # Declarative sync configuration
├── .content-lock.json # Approved upstream SHAs per repo (committed)
└── .github/workflows/ # CI, deploy, weekly content check
Expand Down
Loading