Shared build configuration monorepo for JavaScript/TypeScript projects.
| Package | Description |
|---|---|
| @gtbuchanan/cli | Shared build CLI (gtb) |
| @gtbuchanan/eslint-config | Shared ESLint configuration |
| @gtbuchanan/markdownlint-config | Shared markdownlint configuration |
| @gtbuchanan/oxfmt-config | Shared oxfmt configuration |
| @gtbuchanan/oxlint-config | Shared oxlint configuration |
| @gtbuchanan/tsconfig | Shared TypeScript base configuration |
| @gtbuchanan/vitest-config | Shared Vitest configuration |
All workflows are reusable via workflow_call. Consuming repos create
thin wrappers that delegate to this repo's workflows:
# .github/workflows/ci.yml
on:
pull_request:
branches: [main]
jobs:
ci:
uses: gtbuchanan/tooling/.github/workflows/ci.yml@mainEach workflow follows this same pattern — only the filename and trigger differ:
| Workflow | Trigger | Description |
|---|---|---|
ci.yml |
PR | Build + e2e + optional slow tests |
cd.yml |
Push to main | CI + changesets version + publish (OIDC) |
changeset-check.yml |
PR | Verify changeset exists |
pre-commit.yml |
PR | Run prek hooks on changed files |
pre-commit-seed.yml |
Push to main | Warm prek cache for PR builds |
CI automatically uploads coverage to Codecov via
a per-package turbo task. The workflow installs the
Codecov CLI and runs
coverage:codecov:upload per package. Turbo caches uploads based on
coverage content — unchanged packages are skipped and Codecov carries
forward their last known data.
Add a CODECOV_TOKEN repository secret and pass it through
workflow_call:
jobs:
ci:
uses: gtbuchanan/tooling/.github/workflows/ci.yml@main
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}To customize coverage targets, add a codecov.yml to your repo root.
Drift risk: The Codecov job uses continue-on-error so upload
failures never block PRs. Flags use carryforward: true because
Codecov treats missing flags as 0% coverage, which would cause false
failures on PRs that only touch a subset of packages. The tradeoff is
that a failed upload carries forward stale data. In practice this
self-corrects: cd.yml re-runs CI on merge to main, uploading fresh
coverage. Drift only persists if that upload also fails — re-run the
main workflow to correct it.
Repos customize behavior through Turborepo task graphs generated by
gtb turbo:init. Per-package scripts delegate to gtb leaf commands,
and consumers can replace script values to customize individual tools.
ci.yml also accepts workflow inputs (run-e2e, run-slow-tests) for
toggling test tiers. See the CLI package for available
commands.
Turborepo manages task orchestration. The task graph is defined in
turbo.json (generated by gtb turbo:init) and declares dependencies
between leaf tasks:
typecheck:ts ─┬─ lint:eslint ─┐
├─ lint:oxlint ─┤ lint ─┐
└─ test:vitest:fast ─────┤ check ─┬─ compile:ts → pack
│ ├─ test:vitest:slow
│ └─ test:vitest:e2e
└────────────────── build
Turbo resolves the graph, caches results, and runs tasks in parallel where dependencies allow.
The build:ci task produces two outputs:
packages/*/dist/source/— publishable contents per packagedist/packages/*.tgz— tarballs for e2e tests
Consumer customization:
- Consumers replace the values of generated
package.jsonscripts to override individual tools. There is no hook system —turbo:initgenerates the scripts, and consumers edit them directly. publishConfig.directory— Set todist/sourcefor packages that need a clean publish directory. Thepackcommand generatespackage.jsonand.npmignorethere automatically.
CD requires:
releaseGitHub environment with npm trusted publishing (OIDC)- A GitHub App with
contents: writeandpull-requests: writepermissions installed on the repo (so changeset PRs trigger checks) - Repo variable
APP_IDand repo secretAPP_PRIVATE_KEYfrom the App @changesets/clias a devDependency
pnpm install
pnpm buildTop-level scripts delegate to Turborepo:
pnpm check— Typecheck, lint, and test:fast (use during development)pnpm build— Full pipeline: check + test:slow + pack + test:e2epnpm build:ci— CI pipeline: check + pack (slow/e2e run as separate jobs)
Turbo tasks can also be run individually:
turbo run typecheck:ts— TypeScript type-checkingturbo run lint— oxlint + ESLint (aggregate)turbo run test:vitest:fast— Fast source tests onlyturbo run test:vitest:slow— Slow source tests only (taggedslow)
All commands go through Turbo for caching:
pnpm pack— Pack tarballs (per-packagepack:npmvia Turbo)pnpm test:e2e— E2E tests (Turbo cache restores tarballs)
Run gtb turbo:init to generate turbo.json and per-package scripts
from project discovery. Use --force to overwrite existing scripts.
Run gtb turbo:check to verify generated config hasn't drifted.
See the CLI package for all available commands.
Uses changesets for per-package versioning. Every PR requires a changeset — CI enforces this.
pnpm exec changeset— declare which packages changed and the bump typepnpm exec changeset --empty— for PRs that don't need a version bump (CI changes, docs, etc.)