Commit 58b7f834 authored by Pierre Smeyers's avatar Pierre Smeyers
Browse files

feat(Claude): add Claude configuration

- doc project instructions
- custom command to generate global TBC rules (can be downloaded by template projects)
- initial commit of TBC rules
parent 82c679f7
Loading
Loading
Loading
Loading
+215 −0
Original line number Diff line number Diff line
Generate optimized Claude Code rules files for _to-be-continuous_ template repositories.

## Arguments

`$ARGUMENTS` may be empty (generate all files) or one of: `design`, `shell`, `workflow`
(regenerate only that specific file).

## Step 1 — Read source documents

Read all of the following files using the Read tool:

- `docs/understand.md`
- `docs/usage.md`
- `docs/dev/guidelines.md`
- `docs/dev/architecture.md`
- `docs/dev/shell.md`
- `docs/dev/workflow.md`

## Step 2 — Apply transformation rules

### Include

- All naming conventions with exact patterns (prefix ≤5 chars, `SCREAMING_SNAKE_CASE` vars, `kebab-case` jobs)
- Mandatory structural patterns (`.xxx-base` hidden job, `spec:inputs`, standard stages and their purpose)
- Hard architecture constraints (no nested includes, GitLab-first, no `latest` tag, multi-instantiation safety)
- Every template's mandatory common features: proxy (`http_proxy`/`https_proxy`/`ftp_proxy`/`no_proxy`),
  custom CA certs (`CUSTOM_CA_CERTS`), scoped variables syntax, `$TRACE` debug mode,
  `PROD_REF`/`INTEG_REF`/`RELEASE_REF` git refs, MR workflow (`.tbc-workflow-rules`),
  adaptive pipeline (`.test-policy` and `.acceptance-policy` rules blocks)
- Secrets management: `@b64@` prefix for Base64-encoded secrets, `@url@` prefix for URL-fetched secrets
- Extended `[skip ci]` syntax (`[ci skip on tag,branch,...]`)
- Job type contracts: which jobs (required/optional) each template kind must implement
  (Build&Test, Code Analysis, Packaging, Deploy&Run, Acceptance)
- Report naming convention and supported format targets (GitLab, SonarQube, DefectDojo)
- Dotenv artifacts: purpose, when to use, variable segregation under multi-instantiation
- Shell compatibility table: Ash-supported vs Ash-unsupported Bash features
- Mandatory shell options (`set -e`, `set -o pipefail`)
- Shell pitfalls: `grep` non-zero exit, Alpine glob pattern matching bug
- DCO sign-off requirement (`git commit -s`)
- Conventional Commits format and which types trigger a semantic-release version bump

### Cut ruthlessly

- All "why we chose X" rationale (unless it prevents a specific, non-obvious mistake)
- Gitflow/DORA metrics discussion, history, "note of reflection" content
- User-facing template usage docs (how a *project* includes and configures a template)
- GitLab CI basics Claude already knows (`when: manual`, `resource_group`, `artifacts`, etc.)
- Docusaurus/MkDocs frontmatter, admonition markup (`> [!note]`, `???`, `===` syntax)
- Cross-references and external URLs (the output files must be self-contained)
- Duplicated content: state each constraint exactly once in the most relevant file

### Rewrite

- Convert descriptive prose into imperative rules:
  `"Jobs shall adopt kebab-case"``**ALWAYS** name jobs in kebab-case: \`xxx-build\`, \`xxx-test-code-quality\``
- Convert "optional but recommended" into explicit opt-in/opt-out semantics
- Replace long explanation + example sequences with a single canonical minimal example in a code block

## Step 3 — Write output files

Unless `$ARGUMENTS` restricts to a single file, write all three.
Create the `claude-rules/` directory at repository root if missing.

---

### File 1 — `claude-rules/tbc-template-design.md`

Source: `docs/understand.md`, `docs/usage.md`, `docs/dev/guidelines.md`, `docs/dev/architecture.md`

Required H2 sections (in this order):

1. `## Template kinds and required jobs`
   Table: kind → required jobs → stage. Mark each job REQUIRED or OPTIONAL.

2. `## Standard stages`
   Table: stage name → purpose (max 1 line). All 10 TBC stages.

3. `## Naming conventions`
   - Job prefix: ≤5 chars (examples: `mvn`, `py`, `docker`, `tf`, `k8s`)
   - Job names: `kebab-case`, pattern `<prefix>-<action>` (examples)
   - Global variables: `SCREAMING_SNAKE_CASE`, prefixed with template/tool name (examples)
   - Local dynamic variables: `snake_case`

4. `## Base job prototype`
   - Every template with 2+ jobs **MUST** define a hidden `.xxx-base` job
   - Must include: default image (`$XXX_IMAGE`), TBC tracking service, cache
   - Minimal YAML example

5. `## Common features (mandatory for every template)`
   One bullet per feature — variable name, behavior, constraints.
   - Proxy: `http_proxy`, `https_proxy`, `ftp_proxy`, `no_proxy`
   - Custom CA: `CUSTOM_CA_CERTS` (PEM format, file-typed or regular variable)
   - Scoped variables: syntax `scoped__<var>__<cond>__<cond_var>__<op>[__<val>]=<target_val>`;
     works only in `script`/`before_script`**NOT** in `image`, `rules`, `cache`, `artifacts`
   - Debug: `$TRACE=true` enables template debug logs (distinct from `CI_DEBUG_TRACE`)
   - Git refs: `PROD_REF` (default `/^(master|main)$/`), `INTEG_REF` (default `/^develop$/`),
     `RELEASE_REF` (semver pattern)
   - MR workflow: expose `.tbc-workflow-rules` with `prefer-mr-pipeline` as default strategy
   - Adaptive pipeline: all test/analysis jobs must extend `.test-policy`;
     all acceptance jobs must extend `.acceptance-policy`
   - Secrets: handle `@b64@<base64>` (decode), `@url@<url>` (fetch, default timeout 5s via `TBC_SECRET_URL_TIMEOUT`)
   - Extended skip-ci: support `[ci skip on <words>]` with words: `tag`, `branch`, `mr`, `dev`, `prod`, `integ`, `default`

6. `## Architecture constraints`
   Bullet list with severity markers.
   - **NEVER** use nested includes (breaks remote include, R2Devops compat, version determinism)
   - **ALWAYS** use fully qualified image names including registry (supply chain protection)
   - **NEVER** hardcode `latest` for build/IaC/acceptance/private-cloud-CLI images
   - `latest` is **acceptable** for DevSecOps tools and public cloud CLI clients
   - **ALWAYS** expose image and CLI options as overridable variables
   - **NEVER** use `parallel:matrix` inside a template — use explicit named job inheritance

7. `## Multi-instantiation`
   - All output paths must include a variable component (e.g. `$XXX_PROJECT_DIR/reports/...`)
   - All output dotenv variables must support optional namespacing: when `$JOB_MATRIX_NAMESPACE` is set,
     prefix output variable names with it (e.g. `XXX_${JOB_MATRIX_NAMESPACE}_COMPUTED_VAR`)
   - Dotenv report files must also be path-segregated

8. `## Reports`
   - Path convention: `$XXX_PROJECT_DIR/reports/<job_name>.<format_name>.<ext>`
   - Use `native` as format name for the tool's own format
   - **ALWAYS** produce human-readable output in the console
   - **ALWAYS** produce the GitLab-supported artifact format when available
   - When `$SONAR_HOST_URL` is set: produce SonarQube-compatible format (test execution, coverage, external analyzer)
   - When DefectDojo variable is set: produce DefectDojo-compatible format

9. `## Command-line options`
   Table: category → rule.
   - Template-controlled (hard-coded): report paths, JUnit format, coverage format, fail-on-error behavior
   - User-controlled (inputs/variables): documented with sensible defaults, not hard-coded

Target: 280–350 lines. Tables and bullet lists only. No prose paragraphs.
Severity markers: `**ALWAYS**` / `**NEVER**` / `**REQUIRED**` / `**OPTIONAL**` / `**BEWARE**`.

---

### File 2 — `claude-rules/tbc-shell.md`

Source: `docs/dev/shell.md`

Required H2 sections:

1. `## Compatibility baseline`
   - Scripts run in Alpine (Ash/POSIX) and Debian/Fedora (Bash) — write for Ash by default
   - Exception: if a template exclusively uses Debian/Fedora images, Bash-only features are acceptable

2. `## Mandatory shell options`
   ```bash
   set -e          # exit immediately on non-zero exit status
   set -o pipefail # propagate failures through pipes
   ```

3. `## Ash-supported Bash features`
   Compact two-column table (feature / example). Include only ✅ entries from the source.

4. `## Forbidden in Ash`
   Compact two-column table (feature / forbidden example). Include only ❌ entries from the source.

5. `## Pitfalls`
   - **`grep` exits non-zero when no match found** — use `awk '/pattern/'` to filter safely;
     use `grep` only when explicitly testing its return code with `if`
   - **Alpine glob pattern matching bug**: `[[ "$s" == "prefix"* ]]` fails when a file named `prefix.*`
     exists in cwd — **ALWAYS** use the regex operator: `[[ "$s" =~ ^prefix ]]`
   - **`$BASH_REMATCH`** unavailable in Ash after `=~`

Target: 80–120 lines. Tables and bullet lists only.

---

### File 3 — `claude-rules/tbc-git-workflow.md`

Source: `docs/dev/workflow.md`

Required H2 sections:

1. `## DCO sign-off`
   - **REQUIRED** on every commit: `git commit -s`
   - Adds `Signed-off-by: Name <email>` footer line
   - MRs without DCO cannot be merged

2. `## Conventional Commits`
   Compact table: type → description → release bump.
   `feat` → minor, `fix` → patch, all others → none.
   - Breaking changes: `BREAKING CHANGE:` in footer → major bump
   - Commits must be **atomic** — squash fixups before marking MR as Ready

3. `## Contribution workflow`
   - Always work from a **fork** — never push directly to the canonical repo
   - Open MR in **Draft** mode immediately; mark Ready only when complete
   - All submissions require review before merge (including maintainers')
   - New template: announce on Discord, wait for core team agreement before starting

4. `## Release process`
   - Automated via semantic-release on commits to `main`
   - `bumpversion.sh` updates version refs in README and template files
   - `post-release.sh` creates minor/major alias tags (e.g. `9.3` and `9` for `9.3.0`)

5. `## kicker.json sync`
   - **ALWAYS** keep `kicker.json` in sync with `spec:inputs` in every template file
   - Every added/renamed/removed input must be reflected in both places
   - CI validates this — pipeline fails on divergence

Target: 70–100 lines. Tables and bullet lists only.

---

## Step 4 — Confirm

After writing all files, print:

| File | Lines | Status |
|------|-------|--------|
| `claude-rules/tbc-template-design.md` | N | written / skipped |
| `claude-rules/tbc-shell.md`           | N | written / skipped |
| `claude-rules/tbc-git-workflow.md`    | N | written / skipped |

CLAUDE.md

0 → 100644
+99 −0
Original line number Diff line number Diff line
# CLAUDE.md

Guidance for Claude Code (claude.ai/code) when working in this repository.

## Project overview

This is the **documentation website** for [to-be-continuous](https://to-be-continuous.gitlab.io/doc/) (TBC),
a framework of reusable GitLab CI/CD templates. The site is built with
[MkDocs Material](https://squidfunk.github.io/mkdocs-material/) and published via GitLab Pages.

- **Repo:** `gitlab.com/to-be-continuous/doc`
- **Published at:** `https://to-be-continuous.gitlab.io/doc`
- **Branch:** `main`

## Repository structure

```
docs/                         Source documentation (Markdown)
  intro.md                    Project introduction
  understand.md               Core concepts: pipeline model, stages, delivery modes, branching
  usage.md                    Common features: proxy, CA, scoped vars, adaptive pipeline, secrets...
  advanced-cd.md              Advanced CD patterns
  dev/
    architecture.md           Architectural principles for template developers
    guidelines.md             Template development guidelines and naming conventions
    shell.md                  Shell scripting rules (Ash/Bash compatibility)
    workflow.md               Git contribution workflow and release process
  ref/                        Per-template reference pages (auto-generated sections)
  self-managed/               Self-managed GitLab usage
  secu/                       Security reports

static/                       Static assets served as-is (images, fonts, JS, CSS)
overrides/                    MkDocs Material theme overrides

claude-rules/                 Claude Code rules files (generated — see /gen-tbc-rules)
  tbc-template-design.md      Template design rules (generated from docs/)
  tbc-shell.md                Shell scripting rules (generated from docs/dev/shell.md)
  tbc-git-workflow.md         Git workflow rules (generated from docs/dev/workflow.md)

.claude/commands/
  gen-tbc-rules.md            /gen-tbc-rules slash command

mkdocs.yml                    MkDocs configuration
requirements.txt              Python dependencies (MkDocs + plugins)
process-templates.sh          Fetches per-template READMEs to build the Templates Reference section
scan-images.sh                Scans Docker images used across all templates (security)
```

## Local development

```bash
# Install dependencies
pip install -r requirements.txt

# Serve locally with live reload
mkdocs serve

# Build static site
mkdocs build
```

The site is served at `http://localhost:8000` by default.

## Content conventions

- All documentation is in **English**
- Markdown files live under `docs/` — MkDocs processes them according to `nav:` in `mkdocs.yml`
- To add a new page: create the `.md` file and add it to the `nav:` section in `mkdocs.yml`
- MkDocs Material admonitions: `> [!note]`, `> [!tip]`, `> [!important]`, `> [!warning]`
- Code tabs: `=== "Tab title"` with indented content (pymdownx.tabbed)
- Mermaid diagrams: fenced code block with ` ```mermaid `
- Snippets: `--8<-- "path/to/file"` (pymdownx.snippets, paths relative to `docs/`)

## The `claude-rules/` directory

Files in `claude-rules/` are **generated** from the source documentation by the `/gen-tbc-rules`
slash command — do not edit them manually.

**To regenerate after updating source docs:**

```
/gen-tbc-rules           # regenerate all 3 files
/gen-tbc-rules shell     # regenerate tbc-shell.md only
```

These files are fetched at session start by every TBC template repository via their
`.claude/fetch-tbc-doc.sh` hook. Commit and push to `main` to publish updates.

## CI/CD

The pipeline uses the `to-be-continuous/mkdocs` template (ref `2`):
- **Link checking** via Lychee (`MKD_LYCHEE_ENABLED: true`) — checks `README.md`, `docs/`, `static/`
- **Build and publish** to GitLab Pages on `main`

## Git & contribution conventions

- **Commits:** [Conventional Commits](https://www.conventionalcommits.org/) format
- **DCO:** all commits must be signed-off (`git commit -s`)
- **Contributions:** MRs from forks on GitLab

claude-rules/README.md

0 → 100644
+49 −0
Original line number Diff line number Diff line
# TBC Claude Code Rules

Optimized Claude Code rules files for _to-be-continuous_ template repository contributors.

## Files

| File | Content |
|------|---------|
| `tbc-template-design.md` | Template design rules: kinds, stages, naming, base job, common features, architecture constraints, multi-instantiation, reports |
| `tbc-shell.md` | Shell scripting rules: Ash/Bash compatibility, mandatory options, pitfalls |
| `tbc-git-workflow.md` | Contribution workflow: DCO, conventional commits, release process, kicker.json sync |

## Usage in template repositories

Each template repo fetches these files at Claude Code session start via `.claude/fetch-tbc-rules.sh`
and imports them through `CLAUDE.md`:

```markdown
@.claude/tbc-rules/tbc-template-design.md
@.claude/tbc-rules/tbc-shell.md
@.claude/tbc-rules/tbc-git-workflow.md
```

Raw URLs (for `curl` in fetch scripts):

```
https://gitlab.com/to-be-continuous/doc/-/raw/main/claude-rules/tbc-template-design.md
https://gitlab.com/to-be-continuous/doc/-/raw/main/claude-rules/tbc-shell.md
https://gitlab.com/to-be-continuous/doc/-/raw/main/claude-rules/tbc-git-workflow.md
```

## Regenerating

From within the `doc/` repository, run the Claude Code slash command:

```
/gen-tbc-rules
```

Or regenerate a single file:

```
/gen-tbc-rules design
/gen-tbc-rules shell
/gen-tbc-rules workflow
```

The command reads the source documentation from `docs/` and rewrites the rules files.
Commit and push the result to publish the update to all template repositories.
+66 −0
Original line number Diff line number Diff line
# TBC Git Workflow Rules

Contribution rules for _to-be-continuous_ template repositories.

## DCO sign-off

**REQUIRED** on every commit — no exceptions:

```bash
git commit -s -m "feat: my change"
# Automatically appends: Signed-off-by: Name <email>
```

MRs without DCO sign-off on all commits cannot be merged.

## Conventional Commits

Header format: `<type>(<scope>): <subject>`
- Max 100 characters per line
- Subject: imperative mood, lowercase first letter, no trailing period

| Type | Description | Release bump |
|------|-------------|-------------|
| `feat` | New feature | **minor** |
| `fix` | Bug fix | **patch** |
| `docs` | Documentation only | none |
| `refactor` | Code change (no bug fix, no feature) | none |
| `perf` | Performance improvement | none |
| `test` | Add or fix tests | none |
| `style` | Formatting, whitespace | none |
| `chore` | Build process, tooling, CI | none |

**Breaking changes** → add `BREAKING CHANGE: <description>` in the commit footer → **major** bump.

Commits must be **atomic**: one logical change per commit.
Squash all fixup commits before marking an MR as Ready.

## Contribution workflow

- **ALWAYS** work from a **fork** — never push feature branches to the canonical repo
- Open MR in **Draft** mode immediately; mark as Ready only when complete
- All submissions require review before merge — including maintainers' own changes
- For a **new template**: announce on the TBC Discord first and wait for core team agreement
  before starting implementation

## Release process

Releases are fully automated via semantic-release on commits to `main`:

- `feat` commits → minor version bump
- `fix` commits → patch version bump
- `BREAKING CHANGE` footer → major version bump
- All other types → no release triggered

Post-release scripts:
- `bumpversion.sh` — updates version references in README and template files
- `post-release.sh` — creates minor and major alias tags (e.g. `9.3` and `9` for `9.3.0`)

**NEVER** manually create version tags — semantic-release owns the tag lifecycle.

## kicker.json sync

- **ALWAYS** keep `kicker.json` in sync with `spec:inputs` in every template YAML file
- Every input added, renamed, or removed in `spec:inputs` **MUST** be reflected in `kicker.json`
- CI validates this automatically — the pipeline fails when they diverge
- When adding a new input: update both files in the same commit
+103 −0
Original line number Diff line number Diff line
# TBC Shell Scripting Rules

Rules for shell scripts embedded in _to-be-continuous_ GitLab CI/CD templates.

## Compatibility baseline

Scripts run in a wide range of container images:
- **Alpine** images → default shell is **Ash** (Almquist Shell, POSIX-compliant, minimalist)
- **Debian / Fedora / Ubuntu** images → default shell is **Bash**

**ALWAYS write for Ash compatibility** unless the template exclusively targets Debian/Fedora images
(document this exception explicitly in the template if so).

## Mandatory shell options

Add at the top of every embedded script:

```bash
set -e          # exit immediately on any non-zero exit status
set -o pipefail # propagate failures through pipes (entire pipeline fails if any command fails)
```

## Ash-supported Bash features

Safe to use in any TBC template:

| Feature | Example |
|---------|---------|
| File conditionals | `[[ -f file ]]`, `[[ -d dir ]]`, `[[ -x bin ]]`, `[[ -a path ]]` |
| String conditionals | `[[ -z "$s" ]]`, `[[ -n "$s" ]]`, `[[ "$a" == "$b" ]]`, `[[ "$a" != "$b" ]]` |
| Arithmetic conditionals | `[[ $a -eq $b ]]`, `[[ $a -ne $b ]]`, `[[ $a -lt $b ]]`, `[[ $a -ge $b ]]` |
| Regex operator | `[[ "$text" =~ ^prefix ]]`**not** `$BASH_REMATCH` (see Pitfalls) |
| Default expansion | `${var:-default}` (unset or empty → default) |
| Unset expansion | `${var-default}` (unset → default, empty string is kept) |
| Alternate expansion | `${var:+word}` (set and non-empty → word, else empty) |
| Substring | `${var:2}`, `${var:2:5}` |
| Length | `${#var}` |
| Prefix removal | `${var#pattern}`, `${var##pattern}` |
| Suffix removal | `${var%pattern}`, `${var%%pattern}` |
| Substitution | `${var/pattern/str}`, `${var//pattern/str}` |
| Local variables | `local myvar="$1"` (inside functions) |
| Arithmetic expression | `result=$(( a + b ))`, `$(( ! val ))`, `$(( bits << 2 ))` |
| Heredoc | `command <<EOF … EOF` |
| Process substitution | `diff <(ls "$dir1") <(ls "$dir2")` |

## Forbidden in Ash

**NEVER** use the following in TBC template scripts:

| Feature | Forbidden example | Alternative |
|---------|-------------------|-------------|
| Advanced conditionals | `[[ -v varname ]]`, `[[ -o optname ]]`, `[[ -R varname ]]` | Use `[ -n "${varname+x}" ]` to test if set |
| Indirect expansion | `${!varname}` | Use `eval` carefully or restructure |
| Prefix/suffix replace | `${var/#pat/str}`, `${var/%pat/str}` | Use `sed` |
| Case conversion | `${var^^}`, `${var,,}`, `${var^}`, `${var,}` | Use `tr '[:lower:]' '[:upper:]'` |
| Operator expansion | `${var@u}`, `${var@U}`, `${var@L}` | Use `tr` |
| Typed `declare` | `declare -A dict`, `declare -i num` | Use plain variables or files |
| Arrays | `arr=(a b c)`, `${arr[@]}` | Use space-separated strings or files |
| Arithmetic command | `(( index++ ))`, `(( total += 10 ))` | Use `index=$(( index + 1 ))` |
| Here strings | `tr '[:lower:]' '[:upper:]' <<< "$str"` | Use `echo "$str" \| tr …` |
| `$BASH_REMATCH` | `[[ "$s" =~ (pat) ]]; echo "${BASH_REMATCH[1]}"` | Use `sed` or `awk` to extract groups |

## Pitfalls

### `grep` exits non-zero when no match is found

With `set -e`, a `grep` that finds nothing will **abort the script**:

```bash
# WRONG — aborts if no titles found
titles=$(grep '^#' README.md)

# CORRECT — use awk to filter without risk of non-zero exit
titles=$(awk '/^#/' README.md)
```

Use `grep` only when explicitly testing its return code:

```bash
# OK — return code is tested
if grep -q '^# ' README.md; then
  echo "has titles"
fi
```

### Alpine glob pattern matching bug

In Alpine Ash, `[[ "$str" == "prefix"* ]]` silently fails when a file named `prefix.*` exists in the current directory because `"prefix"*` undergoes filename expansion.

```bash
# WRONG — breaks on Alpine if a file named 'prefix.*' exists in cwd
[[ "$str" == "prefix"* ]]

# CORRECT — use regex operator (immune to filename expansion)
[[ "$str" =~ ^prefix ]]
```

**ALWAYS** use the regex operator `=~` for prefix/suffix/substring matching — never glob patterns.

### `$BASH_REMATCH` is unavailable in Ash

After a successful `=~` match, capture groups are accessible via `$BASH_REMATCH` in Bash but **not in Ash**.
Use `sed` or `awk` to extract matched groups when portability is required.
Loading