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: 0 additions & 1 deletion .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"Bash(pnpm format:*)",
"Bash(pnpm format:check:*)",
"Bash(pnpm test:*)",
"Bash(pnpm secrets:scan:*)",
"Bash(pnpm knip:*)",
"Bash(pnpm --filter web dev:*)",
"Bash(pnpm --filter web build:*)",
Expand Down
11 changes: 7 additions & 4 deletions .githooks/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ set -eu
pnpm format:check
pnpm lint

# Scan only what is staged. This fails closed: when neither gitleaks nor Docker
# is available, secrets:scan exits 2 and the commit is refused, since the point
# of this hook is to stop a secret before it reaches the remote.
pnpm secrets:scan --staged
# Scan only what is staged, with a local gitleaks (8.19+ for `git --staged`).
# This fails closed: without gitleaks the commit is refused, since the point of
# this hook is to stop a secret before it reaches the remote. CI scans the full
# history through secret-scan.yml from valuecodes/maintain-kit, which pins the
# gitleaks version.
gitleaks git --help >/dev/null 2>&1 || { echo "install gitleaks 8.19+: https://github.com/gitleaks/gitleaks#installing" >&2; exit 2; }
gitleaks git --staged --redact "$(git rev-parse --show-toplevel)"
63 changes: 0 additions & 63 deletions .github/scripts/secrets-scan.sh

This file was deleted.

21 changes: 5 additions & 16 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,12 @@ jobs:
TASK: ${{ matrix.task }}
run: pnpm "$TASK"

secrets-scan:
name: secrets-scan
runs-on: ubuntu-latest
timeout-minutes: 10
# gitleaks over the full git history, at the version pinned in the reusable
# workflow. It reads `.gitleaks.toml` here if one is added.
secret-scan:
permissions:
contents: read
steps:
# Full history: gitleaks scans every commit, not just the checked-out tree.
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0

# Same script as `pnpm secrets:scan`; runs the pinned gitleaks Docker
# image, so no pnpm/Node setup is needed.
- name: Scan for secrets
run: sh .github/scripts/secrets-scan.sh
uses: valuecodes/maintain-kit/.github/workflows/secret-scan.yml@e5bf1d24a413d9312cd2051c39ef44ddd9358be5 # v1.0.0

codeql:
name: codeql (${{ matrix.language }})
Expand Down Expand Up @@ -104,4 +93,4 @@ jobs:
workflow-lint:
permissions:
contents: read
uses: valuecodes/automation/.github/workflows/workflow-lint.yml@27353e6fad6ba1f837490d0daab7c7cc7112d7ca # v0.8.0
uses: valuecodes/maintain-kit/.github/workflows/workflow-lint.yml@e5bf1d24a413d9312cd2051c39ef44ddd9358be5 # v1.0.0
19 changes: 19 additions & 0 deletions .github/workflows/maintain-kit-dashboard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Maintain-kit dashboard

on:
schedule:
# Daily. An off-peak minute avoids the top-of-the-hour queue.
- cron: "41 5 * * *"
workflow_dispatch:

permissions: {}

jobs:
# Keeps one issue listing due catalog, pnpm, Node.js and audit updates. It
# never changes code. A called workflow cannot raise the caller's token, so
# the job grants it here.
maintain-kit-dashboard:
permissions:
contents: read
issues: write
uses: valuecodes/maintain-kit/.github/workflows/dashboard.yml@e5bf1d24a413d9312cd2051c39ef44ddd9358be5 # v1.0.0
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Maintenance updates
name: Maintain-kit updates

on:
schedule:
Expand All @@ -10,15 +10,15 @@ on:
permissions: {}

jobs:
# Keeps one PR per update kind (Node.js, pnpm, gitleaks, catalog) open for
# updates on the current major. Nothing auto-merges. The PRs are opened with
# Keeps one PR per update kind (Node.js, pnpm, catalog) open for updates on
# the current major. Nothing auto-merges. The PRs are opened with
# GITHUB_TOKEN, so their `pull_request` checks wait for someone with write
# access to approve them in the PR. A called workflow cannot raise the
# caller's token, so the job grants it here.
maintenance-updates:
maintain-kit-updates:
permissions:
contents: write # push the update branches
pull-requests: write # open, edit and close the PRs
actions: write # required by the called job even without ci-workflow
issues: read # link the dashboard issue
uses: valuecodes/automation/.github/workflows/maintenance-updates.yml@27353e6fad6ba1f837490d0daab7c7cc7112d7ca # v0.8.0
uses: valuecodes/maintain-kit/.github/workflows/updates.yml@e5bf1d24a413d9312cd2051c39ef44ddd9358be5 # v1.0.0
19 changes: 0 additions & 19 deletions .github/workflows/maintenance-dashboard.yml

This file was deleted.

47 changes: 22 additions & 25 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Guidelines for AI agents and contributors working in this Turborepo monorepo.
| typescript | `@repo/typescript` | Shared tsconfig presets (`base.json`, `node.json`, `react.json`) |

CI helpers are not workspaces: the composite setup action lives in
`.github/actions/setup/` and the gitleaks script in `.github/scripts/`.
`.github/actions/setup/`.

Apps may import packages; packages must never import apps. oxlint bans `../`
imports and deep `@repo/*/src` imports: import other workspaces by package name,
Expand All @@ -50,7 +50,6 @@ pnpm test # turbo run test
pnpm build # turbo run build
pnpm format # oxfmt, writes in place
pnpm format:check # oxfmt --check (no writes)
pnpm secrets:scan # gitleaks over the full git history
pnpm clean # turbo run clean
```

Expand All @@ -73,22 +72,22 @@ Git hooks live in `.githooks/` and are plain shell scripts, with no hook manager
`pnpm install` enables them through the root `prepare` script. The setting goes in
the clone's shared git config, so it covers every worktree, and a branch without
`.githooks/` simply runs no hooks. If you cloned before the hooks existed and have
not run `pnpm install` since, run `git config core.hooksPath .githooks` once. `pre-commit` runs `format:check`, `lint` and `secrets:scan --staged`. The
secrets scan fails closed, so the commit is refused when neither gitleaks nor
Docker is available. `pre-push` runs `typecheck`, `test` and `knip`. CI still
not run `pnpm install` since, run `git config core.hooksPath .githooks` once. `pre-commit` runs `format:check`, `lint` and `gitleaks git --staged` with the local
gitleaks (v8.19+). The secrets scan fails closed, so the commit is refused when
gitleaks is not installed. `pre-push` runs `typecheck`, `test` and `knip`. CI still
runs every check. `format:check` and `lint` read the working tree, not the staged
snapshot, so stage everything you mean to commit.

`secrets:scan` runs gitleaks (`.github/scripts/secrets-scan.sh`, version pinned
there) using a local `gitleaks` v8.19+ if one is on PATH, otherwise the pinned Docker image.
It exits 0 when clean and 1 when it finds a leak.
The hook runs whatever gitleaks is installed; CI scans the full git history with the
version pinned in `secret-scan.yml` from `valuecodes/maintain-kit`, so this repo holds
no gitleaks pin.

CI checks are defined once in the reusable `.github/workflows/checks.yml`: typecheck,
lint, knip, format:check, test, build, secrets-scan, CodeQL code scanning
(`javascript-typescript`) and workflow-lint. `feature.yml` calls it on PRs and
`main.yml` on push to `main` (deploy jobs go there). workflow-lint calls the reusable
workflow from `valuecodes/automation`: actionlint (with shellcheck) and zizmor over the
workflow and action files. Each tool reads `.github/actionlint.yaml` / `zizmor.yml` if
lint, knip, format:check, test, build, CodeQL code scanning
(`javascript-typescript`), secret-scan and workflow-lint. `feature.yml` calls it on PRs and
`main.yml` on push to `main` (deploy jobs go there). secret-scan and workflow-lint call the
reusable workflows from `valuecodes/maintain-kit`: gitleaks over the full git history,
and actionlint (with shellcheck) and zizmor over the workflow and action files. Each tool reads `.github/actionlint.yaml` / `zizmor.yml` if
added. `all-passed` in `feature.yml` fans the checks in and is the one check to require
in branch protection. The typecheck, test and build
jobs restore Turbo's `.turbo` cache from the previous run of the same task, so
Expand All @@ -97,28 +96,26 @@ unchanged packages replay instead of rerunning.
Dependabot (`.github/dependabot.yml`) bumps the SHA-pinned actions and reusable workflows
monthly in one grouped PR; npm dependencies are updated by hand.

`maintenance-dashboard.yml` calls the maintenance dashboard workflow from
`valuecodes/automation` daily.
It keeps one issue, **Maintenance dashboard** (label `maintenance-dashboard`), listing
what is due: `catalog:` updates, pnpm (`packageManager`), gitleaks (`GITLEAKS_VERSION`
in the secrets-scan script), Node.js (`.nvmrc`) and `pnpm audit` findings. Like
`maintain-kit-dashboard.yml` calls the dashboard workflow from
`valuecodes/maintain-kit` daily.
It keeps one issue, **Maintenance dashboard** (label `maintain-kit-dashboard`), listing
what is due: `catalog:` updates, pnpm (`packageManager`), Node.js (`.nvmrc`) and
`pnpm audit` findings. Like
`minimumReleaseAge`, a release shows up only once it is 14 days old; a Node.js security
release shows up right away and is flagged. It never opens PRs or commits.

`maintenance-updates.yml` calls the maintenance updates workflow from
`valuecodes/automation` weekly. It keeps one PR per kind open (Node.js, pnpm,
gitleaks, `catalog:`) on a `maintenance-updates/<kind>` branch, for updates on the
`maintain-kit-updates.yml` calls the updates workflow from
`valuecodes/maintain-kit` weekly. It keeps one PR per kind open (Node.js, pnpm,
`catalog:`) on a `maintain-kit/<kind>` branch, for updates on the
current major only; majors stay manual. Those PRs are opened with `GITHUB_TOKEN`, so
their `pull_request` checks wait until someone with write access clicks **Approve
workflows to run** in the PR. It needs **Allow GitHub Actions to create and approve
pull requests** turned on under Settings → Actions → General. Nothing auto-merges.
Before merging a Node.js or pnpm update, set the Cloudflare `NODE_VERSION` or
`PNPM_VERSION` build variable of the `apps/web` deploy to the new version, so the
build of the merge commit already uses it (a newer patch or minor on the old
lockfile is harmless). If `GITLEAKS_VERSION` moves out of
`.github/scripts/secrets-scan.sh`, pass the new path as the `gitleaks-script` input to
both callers. The dashboard, updates and workflow-lint callers pin the same
`valuecodes/automation` commit, so bump them together.
lockfile is harmless). The dashboard, updates, secret-scan and workflow-lint callers
pin the same `valuecodes/maintain-kit` commit, so bump them together.

---

Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,10 @@ Then open http://localhost:3000.

`pnpm install` also turns on the git hooks: format, lint and a staged secrets scan
before each commit; typecheck, tests and Knip before each push. Committing needs
`gitleaks` v8.19+ on PATH or a running Docker daemon for the staged secrets scan.
`gitleaks` v8.19+ on PATH for the staged secrets scan: `brew install gitleaks` (macOS,
Linux and WSL), or unpack the `linux_x64` tarball from the
[releases page](https://github.com/gitleaks/gitleaks/releases) into a directory on PATH
such as `~/.local/bin`.

## How it works

Expand Down Expand Up @@ -132,7 +135,6 @@ pnpm test # test suite
pnpm build # production builds
pnpm format # oxfmt write
pnpm format:check # oxfmt check (CI gate)
pnpm secrets:scan # gitleaks secret scan over git history
```

oxlint prints nothing when there are no findings, so silent output means clean.
Expand All @@ -141,8 +143,7 @@ CI runs typecheck, lint, knip, format:check, test, build and a gitleaks secret s
PRs and on push to `main`, plus CodeQL code scanning and actionlint + zizmor over the
workflow files. The checks live once in `checks.yml`, called by `feature.yml` (PRs) and
`main.yml` (push to `main`). The `all-passed` job fans them in:
require only that check in branch protection. `secrets:scan` needs either `gitleaks`
v8.19+ on PATH or a running Docker daemon.
require only that check in branch protection.

The repository settings this CI expects:

Expand All @@ -153,8 +154,8 @@ The repository settings this CI expects:
3. **Code security → Dependabot**: **alerts** on, **security updates** off.
`.github/dependabot.yml` only bumps the SHA-pinned GitHub Actions monthly; npm
dependencies are updated by hand.
4. **Issues**: the `Maintenance dashboard` workflow keeps one pinned issue listing the
updates that are due. Run it once by hand (**Actions → Maintenance dashboard → Run
4. **Issues**: the `Maintain-kit dashboard` workflow keeps one pinned issue listing the
updates that are due. Run it once by hand (**Actions → Maintain-kit dashboard → Run
workflow**) to create it.

## Limits
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"format": "oxfmt",
"format:check": "oxfmt --check",
"knip": "knip",
"secrets:scan": "sh .github/scripts/secrets-scan.sh",
"clean": "turbo run clean",
"prepare": "git rev-parse --git-dir >/dev/null 2>&1 && git config --local core.hooksPath .githooks || true"
},
Expand Down
Loading