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
1 change: 1 addition & 0 deletions .github/wiki/Home.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ See [[All Plugins|#plugin-directory-64-of-129-shown]] below for a sample; the fu
- [[Deployment|guides/Deployment]]
- [[Migration|guides/Migration]]
- [[Best Practices|guides/Best-Practices]]
- [[Adopting the nself-ci Gate|guides/Nself-CI-Adoption]]
- [[Troubleshooting FAQ|troubleshooting/FAQ]]

### Governance and Reference
Expand Down
65 changes: 65 additions & 0 deletions .github/wiki/guides/Nself-CI-Adoption.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Adopting the `nself-ci` Gate (Dogfood Pattern)

**Status:** wired into `plugins` (this repo) 2026-09-11 — the first of 13 nself-org repos to adopt it. Zero repos had it wired before this, including `plugins` itself, which owns the source at `free/ci`.

This guide is the copy-paste pattern for the other 12 repos. It also records, honestly, what the gate does and does not check today — read "Known gaps" before requiring it as a sole merge gate.

---

## Why `plugins` first

- It owns the `nself-ci` source (`free/ci/`) and the wrapper (`scripts/nself-ci.sh`) — self-hosting the gate here is the purest dogfood: a regression in the gate breaks its own repo's checks first.
- It is public, so the workflow runs on free GitHub-hosted Actions — no runner-fleet dependency to prove the pattern.
- Its own `.claude/docs/CI-LOCAL.md` (project-level) already documents the branch-protection recipe; this guide is the repo-level companion showing the wiring actually in place.

## The pattern

1. **Workflow** — `.github/workflows/nself-ci.yml`: checkout → `setup-go` (builds the binary) → `setup-node`/`pnpm` (for the Node gate path) → install `gitleaks` → run `scripts/nself-ci.sh .` with `permissions: statuses: write` and `GH_TOKEN: ${{ github.token }}` so `gh api` can post the status without a PAT.
2. **Wrapper** — `scripts/nself-ci.sh` builds `free/ci/nself-ci` if missing/stale, then `exec`s it. Same script works locally and in CI.
3. **Status** — the binary posts a `nself-ci` commit status via `gh api repos/{owner}/{repo}/statuses/{sha}` (OAuth, no token in a URL).
4. **Branch protection (NOT applied by this PR — owner's call)** — add `nself-ci` to `required_status_checks.contexts` per the exact `gh api` command in `~/Sites/nself/.claude/docs/CI-LOCAL.md` § "Requiring nself-ci in branch protection". Do this as an **additional** required check alongside the repo's existing checks, not a replacement — see gaps below.

Copy `.github/workflows/nself-ci.yml` from this repo into each of the other 12 (`cli`, `admin`, `web`, `nchat`, `nclaw`, `nsentry`, `ntask`, `nfamily`, `clawde`, `homebrew-nself`, `packages`, `plugins-pro`), adjusting the toolchain setup steps to match that repo's stack (e.g. Flutter repos need `subosito/flutter-action` instead of `setup-node`).

---

## Verified locally (2026-09-11, this repo, HEAD `5c17916`)

```
$ scripts/nself-ci.sh --check -v .
[nself-ci] building gate binary...
[nself-ci] built .../free/ci/nself-ci
[nself-ci] running: gitleaks detect --source . --exit-code 1 --config .github/gitleaks.toml

nself-ci gate results — .
Stacks: node
────────────────────────────────────────────────────────────
secrets:gitleaks PASS (3.617s)
────────────────────────────────────────────────────────────
Overall: PASSED (4s)
```

That is the real, complete output — one gate ran (`secrets:gitleaks`), it passed, in under 4 seconds. Read that literally: **no lint, typecheck, test, or build gate ran**, for the reason below.

---

## Known gaps — read before requiring this as the only gate

**1. This repo's Node gate is a no-op.** `nself-ci` detects Node stacks by checking the root `package.json` for `lint` / `typecheck` / `test` / `build` scripts (or workspace members' scripts, if `pnpm-workspace.yaml` exists). This repo's root `package.json` has neither: it has one custom script, `ci:local` (a stub — `tsc --noEmit --allowJs || echo skip; echo passed`), and no `pnpm-workspace.yaml`, even though `shared/` has its own `package.json`. Result: the workspace-recursion path in `free/ci/internal/gate_runners.go` (`runNodeGates`) never triggers, the non-workspace path finds no matching scripts, and the gate silently runs nothing but the secret scan — while still printing `Overall: PASSED`. This is exactly the shape of failure flagged in `.claude/memory/lesson_hollow_ci_gates.md`: a gate that passes while checking nothing.
- **Fix options (not applied by this PR):** (a) add real `lint`/`typecheck`/`test`/`build` scripts to root `package.json` (or add `pnpm-workspace.yaml` so member scripts are picked up), or (b) extend `gate_runners.go` to also recognize this repo's `ci:local` convention. Until one of these lands, `nself-ci` on this repo is a secrets-only gate, not a lint/test/build gate.
2. **No per-plugin checks.** The real quality bar for this repo is per-plugin (schema validation, duplicate-port checks, registry consistency — see `validate.yml`, `registry-check.yml`, `hygiene.yml`, `license-gate.yml`). `nself-ci` only inspects the repo root; it does not walk `free/*/plugin.json` or run plugin-level TypeScript builds. It does not replace any of those workflows.
3. **No lint/typecheck/test/build for Go-only subpackages either.** `free/ci` itself (and other Go-based plugins) has a `go.mod`, but stack detection only looks at the *repo root*, which has no root `go.mod`. A regression inside `free/ci`'s own Go code would not be caught by `nself-ci` running against repo root — only by its own `go test ./...` if invoked directly in that subdirectory, or by `build-test.yml`.
4. **Per the plugin's own README** ("What it explicitly does NOT do"): `nself ci` is not a GitHub Actions emulator. E2E, Lighthouse, axe-a11y, CodeQL, Trivy, SBOM, license audit, coverage ratchet, bundle-size, commitlint, i18n-check, and deploy jobs all still need to run somewhere else — free-runner Actions for public repos (this repo), or a dedicated additional gate for private ones.
5. **Fork PRs:** `GH_TOKEN: ${{ github.token }}` on a fork PR is read-only for status writes; the binary treats a failed status post as a warning (non-fatal), so the job's pass/fail still reflects the real gate result, but the `nself-ci` check may not appear on fork PRs.

**Bottom line:** this PR adds `nself-ci` as evidence the wiring works end-to-end (workflow → build → gates → status), not as proof the gate is a complete substitute for this repo's existing checks. Recommend requiring it in branch protection **in addition to**, not instead of, `validate.yml` / `build-test.yml` / `gitleaks.yml` / `registry-check.yml` until gap 1 is closed.

---

## Rollout checklist for the other 12 repos

- [ ] Copy `.github/workflows/nself-ci.yml`, adjust toolchain setup for the repo's stack.
- [ ] Run `scripts/nself-ci.sh --check -v .` (or `plugins/free/ci/nself-ci` directly) locally first — confirm which stacks it detects and which gates actually execute. Do not assume; read the printed gate table.
- [ ] If the repo's real lint/typecheck/test/build live under a non-standard script name (as `ci:local` does here) or in workspace members without `pnpm-workspace.yaml`, fix that *before* treating a green `nself-ci` as meaningful.
- [ ] Open a PR, let Actions run it once, confirm the `nself-ci` commit status appears on the PR.
- [ ] Propose (do not silently apply) the branch-protection addition per `CI-LOCAL.md` — owner approves it.
53 changes: 53 additions & 0 deletions .github/workflows/nself-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: nself-ci

# Dogfood workflow for the nself-ci local-gate plugin (free/ci) — the FIRST of the
# 13 nself-org repos to wire this in (owner audit 2026-09-11 found ZERO adoption,
# not even in this repo, which owns the source). See .github/wiki/guides/nself-ci-gate.md
# for the pattern the other 12 repos should copy, and that doc's "Known gaps" section
# for what this gate does NOT currently check on this repo (it supplements
# validate.yml/build-test.yml/gitleaks.yml here — it does not replace them).

on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:

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

permissions:
contents: read
statuses: write

jobs:
nself-ci:
name: nself-ci gate
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7

- uses: actions/setup-go@v7
with:
go-version: '1.26.4'

- uses: actions/setup-node@v7
with:
node-version: '22'

- uses: pnpm/action-setup@v6
with:
version: 9

- name: Install gitleaks
run: |
curl -sSfL https://github.com/gitleaks/gitleaks/releases/download/v8.21.2/gitleaks_8.21.2_linux_x64.tar.gz \
| tar xz -C /usr/local/bin gitleaks

- name: Run nself ci gate
env:
GH_TOKEN: ${{ github.token }}
run: scripts/nself-ci.sh .
Loading