kits / developer-config
Developer Config
Copy-in config for Node.js, TypeScript and Python repos: formatting, linting, git hooks, GitHub Actions CI, Dependabot and a dev container.
- version
- 1.0.0
- files
- 20
- licence
- MIT
- pairs with
- kittybin Dev
What’s inside
developer-config/ ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .prettierignore ├── .prettierrc.json ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── eslint.config.mjs ├── pyproject-snippet.md ├── ruff.toml ├── setup-guide.md ├── .devcontainer/devcontainer.json ├── .github/dependabot.yml ├── .github/workflows/codeql.yml ├── .github/workflows/dependency-review.yml ├── .github/workflows/node-ci.yml ├── .github/workflows/python-ci.yml ├── hooks/commit-msg └── hooks/pre-commit
Preview
setup-guide.mdpreview
Setup guide
This guide explains every file in the kit, what to change for your project, what to delete if you only use one language, and how to adopt the files a step at a time. None of the config files contain comments, so a copied file stays clean in your repository. Everything you need to know about them is here.
Before you start
- Work on a branch, not on
main. - Copy files in small groups and commit each group on its own. If something breaks, you know which file did it.
- When the kit and your existing config disagree, keep your existing setting unless you have a reason to change it. Consistency inside one repository matters more than any single preference.
Editor and formatting
.editorconfig
Most editors read this file directly or through a plugin. It sets UTF-8, LF line endings, a final newline, trimmed trailing whitespace and two-space indentation, with four spaces for Python, tabs for Makefiles and CRLF for Windows batch files. Markdown keeps trailing whitespace, because two trailing spaces are a line break there.
Prettier also reads .editorconfig, so the indentation and line endings stay consistent between your editor and the formatter.
.prettierrc.json
Prettier formats JavaScript, TypeScript, JSON, CSS, HTML, Markdown and YAML. The settings are close to Prettier's defaults on purpose:
| Setting | Value | Why |
|---|---|---|
| `printWidth` | `100` | Fewer wrapped lines than 80 on modern screens, still readable side by side in a diff |
| `semi` | `true` | Avoids the edge cases of automatic semicolon insertion |
| `singleQuote` | `false` | Matches JSON and the Prettier default |
| `trailingComma` | `"all"` | Cleaner diffs when you add an item to a list |
| `endOfLine` | `"lf"` | Same line endings on every platform |
| Markdown `proseWrap` | `"preserve"` | Leaves your paragraph line breaks alone |
If you prefer single quotes or a width of 80, change it once here and run the formatter over the whole repository in a separate commit.
.prettierignore
Keeps Prettier away from dependencies, build output, coverage reports, virtual environments, lock files and minified files. Prettier also reads .gitignore by default, so anything ignored by git is skipped too.
Linting
eslint.config.mjs
A flat config for TypeScript and JavaScript using ESLint's defineConfig helper and typescript-eslint. It is named .mjs so it loads as an ES module whatever your package.json says. If your package already has "type": "module", you can rename it to eslint.config.js.
Install the packages:
npm install --save-dev eslint @eslint/js typescript-eslint typescript globals eslint-config-prettier prettier
What the config does:
- Ignores dependencies, build output and coverage folders.
- Applies ESLint's recommended rules and the type-aware
recommendedTypeCheckedrules fromtypescript-eslintto every JavaScript and TypeScript file. - Uses the TypeScript project service, which finds the right
tsconfig.jsonfor each file. You need atsconfig.jsonthat includes your source and test files. - Turns type-aware rules off for plain JavaScript files, which are usually config files outside the TypeScript project.
- Enables browser and Node.js globals. Delete the set you do not need.
- Adds a few rules that catch real bugs: unhandled promises, loose equality, unused variables (names starting with
_are allowed) and strayconsole.logcalls. - Relaxes
no-consoleand non-null assertions in test files. - Adds
eslint-config-prettierlast, which turns off every rule that would fight the formatter.
Type-aware linting is slower than plain linting, and it finds problems plain linting cannot, such as floating promises. If it is too slow for a large repository, replace recommendedTypeChecked with recommended, remove the parserOptions block and the disableTypeChecked section, and delete the no-floating-promises rule.
For React, add eslint-plugin-react-hooks and follow its flat config instructions. For Vue or Svelte, add the framework's own ESLint plugin.
ruff.toml
Ruff lints and formats Python. It replaces Flake8, isort, pyupgrade and Black in most projects.
line-length = 100matches Prettier.target-version = "py311"should match the oldest Python version you support.- The selected rule sets: pycodestyle errors and warnings (
E,W), Pyflakes (F), import sorting (I), bugbear (B), pyupgrade (UP), simplify (SIM), comprehensions (C4), security checks from Bandit (S), pytest style (PT) and Ruff's own rules (RUF). E501(line too long) is ignored because the formatter handles line length.S101(use ofassert) is allowed in tests, whereassertis how pytest works.- The
[format]section uses double quotes, spaces and LF line endings, and formats code examples inside docstrings.
If you would rather keep Python settings in pyproject.toml, use pyproject-snippet.md and delete ruff.toml. Do not keep both: Ruff uses ruff.toml and ignores the pyproject.toml settings when both exist.
Git
.gitignore
Covers secrets first (.env files except .env.example, key and certificate files), then Node.js, front-end build output, test reports, Python caches and virtual environments, editor folders, operating system files and logs. Add project-specific entries at the end. Shared editor settings in .vscode/settings.json and .vscode/extensions.json are kept; everything else in .vscode/ is ignored.
If a file you want to commit is ignored, check which rule matches with git check-ignore -v <file>.
.gitattributes
Normalises line endings so every file is stored with LF in the repository, whatever the contributor's operating system. Windows batch files keep CRLF, and the hook scripts are forced to LF so they run on macOS and Linux. Common binary types are marked as binary so git never tries to change or diff them. Lock files are hidden from diffs and marked as generated so they do not clutter reviews.
When you add this file to an existing repository, renormalise once in its own commit:
git add --renormalize .
git commit -m "chore: normalise line endings"
hooks/pre-commit
A POSIX shell hook that runs before every commit. It:
- Blocks staged files whose names look like secrets:
.envand.env.(except.env.example,.env.sampleand.env.template),.env,.pem,.key,.p12,.pfx,.jks,.keystoreand common SSH private key names. - Blocks staged changes that contain a PEM private key header.
- Runs a Prettier format check and ESLint on staged JavaScript, TypeScript and other formattable files, if both are installed in
node_modules. - Runs
ruff format --checkandruff checkon staged Python files, ifruffis on yourPATH.
Checks for tools that are not installed are skipped, so the hook works in Node-only and Python-only repositories without changes.
The hook checks the files as they are on disk. If you stage part of a file and keep other edits unstaged, the check sees the unstaged edits too. For strict staged-only checks, use a dedicated tool such as lint-staged or the pre-commit framework, and keep the secret checks from this hook.
If the hook blocks a file that is safe, rename the file or adjust the pattern in the hook for the whole team. Avoid bypassing hooks with --no-verify; that also skips the secret checks.
hooks/commit-msg
Checks that the first line of the commit message follows Conventional Commits: type(scope): summary, with an optional ! for breaking changes. Allowed types are feat, fix, docs, style, refactor, perf, test, build, ci, chore and revert. The scope is optional. The summary must not end with a full stop, and the whole line must be 72 characters or fewer. Merge, revert, fixup, squash and amend messages created by git are allowed through.
To change the allowed types, edit the types line in the hook.
Enabling the hooks
Git looks for hooks in .git/hooks by default, which is not shared. Point it at the committed hooks folder instead, once per clone:
git config core.hooksPath hooks
On macOS and Linux, make the scripts executable and commit that permission:
chmod +x hooks/pre-commit hooks/commit-msg
git update-index --chmod=+x hooks/pre-commit hooks/commit-msg
On Windows, Git for Windows runs the hooks through its bundled shell. Run the git update-index line above so that teammates on other systems get executable files.
To make this automatic, add a prepare script to package.json ("prepare": "git config core.hooksPath hooks"), which npm runs after npm install. The dev container in this kit also runs it on creation.
Hooks run on each developer's machine and can be skipped. CI is the check that cannot be skipped, so keep the CI workflows even when the hooks are in place.
Continuous integration
All workflows use least-privilege permissions (contents: read unless a job needs more), turn off persisted git credentials after checkout, set a timeout, and cancel superseded runs on pull requests. Runs on main are never cancelled, so every commit on main gets a full result.
The actions are pinned to major version tags. Dependabot (below) opens pull requests when new versions come out. For stricter supply-chain control, pin each action to a full commit SHA instead; Dependabot updates those too.
.github/workflows/node-ci.yml
Runs on pushes to main, on pull requests and on demand. It tests on the Node.js LTS lines in the matrix (22 and 24). Update the matrix when a new LTS line is released and when an old one reaches end of life.
Steps: check out, set up Node.js with the npm cache, npm ci, then the scripts below. Steps marked "if present" are skipped when the script does not exist.
Add these scripts to package.json:
{
"scripts": {
"format": "prettier --write .",
"format:check": "prettier --check .",
"lint": "eslint .",
"typecheck": "tsc --noEmit",
"test": "[your test runner]",
"build": "[your build command]"
}
}
If you use pnpm or Yarn, change cache: npm to cache: pnpm or cache: yarn, replace npm ci with pnpm install --frozen-lockfile or yarn install --immutable, and for pnpm add the pnpm/action-setup step before actions/setup-node.
.github/workflows/python-ci.yml
Runs on the same triggers and tests Python 3.11 to 3.14. Steps: check out, set up Python with the pip cache, install the dev dependency group from pyproject.toml, check formatting, lint with annotations on the pull request, and run pytest.
- Using
requirements-dev.txt: change the install line topython -m pip install -r requirements-dev.txtandcache-dependency-pathtorequirements*.txt. - Using uv: replace
actions/setup-pythonwithastral-sh/setup-uv, install withuv sync --locked, and prefix the tool commands withuv run. - Supporting fewer versions: remove them from the matrix and raise
target-versioninruff.tomlto match.
.github/workflows/dependency-review.yml
Runs on pull requests and fails when a change adds a dependency with a known vulnerability of high severity or worse. It needs the dependency graph to be enabled. It works on public repositories; on private repositories it needs GitHub's code security features. Change fail-on-severity to moderate for a stricter gate. You can also add allow-licenses with a list of licence identifiers your project accepts.
.github/workflows/codeql.yml
Runs GitHub's CodeQL code scanning on pushes to main, pull requests against main, and once a week. It analyses JavaScript and TypeScript, Python and the workflow files themselves. Results appear under the repository's Security tab. Remove languages you do not use from the matrix. Like dependency review, it is available on public repositories and on private repositories with code security enabled. If you already use GitHub's default code scanning setup, you do not need this file.
.github/dependabot.yml
Checks npm, pip and GitHub Actions dependencies every Monday. Minor and patch updates for each ecosystem are grouped into one pull request, so you review one change instead of many. Major updates arrive on their own because they are more likely to need code changes. The seven-day cooldown waits a week after a release before proposing it, which avoids most broken or compromised releases that are pulled quickly. Delete the ecosystem blocks you do not use.
Dev container
.devcontainer/devcontainer.json
Defines a reproducible development environment for editors and hosted environments that support the Dev Containers specification. It starts from a small Ubuntu base image and adds Node.js 24 and Python 3.13 through the official features. After the container is created, it enables the hooks, runs npm ci if there is a lock file, and installs the Python dev group if there is a pyproject.toml. Ports 3000 and 8000 are forwarded for typical dev servers.
The customizations block suggests editor extensions for EditorConfig, Prettier, ESLint, Python and Ruff in editors that read that block. Delete the block if your team uses other editors; the container works without it.
Single-language projects
Node.js or TypeScript only
Delete:
ruff.tomlandpyproject-snippet.md.github/workflows/python-ci.yml- the
pipblock in.github/dependabot.yml pythonin the CodeQL matrix- the Python feature and the Python part of
postCreateCommandin.devcontainer/devcontainer.json, andms-python.pythonandcharliermarsh.rufffrom the extensions - the Python section of
.gitignoreif you like a short file; it does no harm if you keep it
The hooks need no changes.
Python only
Delete:
.prettierrc.json,.prettierignoreandeslint.config.mjs.github/workflows/node-ci.yml- the
npmblock in.github/dependabot.yml javascript-typescriptin the CodeQL matrix- the Node.js feature and the
npm cipart ofpostCreateCommandin.devcontainer/devcontainer.json, and the Prettier and ESLint extensions - the lock file lines at the end of
.gitattributes
The hooks need no changes. You may want to keep Prettier for Markdown, YAML and JSON files; in that case keep .prettierrc.json and .prettierignore and add a small package.json with Prettier as a dev dependency.
Adopting gradually
You do not need to adopt everything at once. A sensible order:
- Add
.editorconfig,.gitignoreand.gitattributes. They change nothing about your code. Run the renormalise step once. - Add the CI workflows with only the steps that already pass, for example tests. A green build you trust is the base for everything else.
- Add the formatter (Prettier or Ruff format). Run it over the whole repository in a single commit with no other changes. Add that commit's hash to a
.git-blame-ignore-revsfile sogit blameskips it, and turn on the format check in CI. - Add the linter. On an existing codebase, start by fixing the errors that point to real bugs. If there are too many to fix at once, temporarily set the noisiest rules to
"warn"ineslint.config.mjs, or add them toignoreinruff.toml, and remove those exceptions one at a time. - Add the hooks. Ask everyone on the team to run
git config core.hooksPath hooks. - Add Dependabot, dependency review and CodeQL. Expect a burst of update pull requests in the first week.
- Add the dev container when someone new joins or when "it works on my machine" becomes a recurring problem.
Checking your setup
After copying the files, run each check once by hand:
npx prettier --check .
npx eslint .
ruff format --check .
ruff check .
git commit --allow-empty -m "bad message"
The last command should be rejected by the commit-msg hook. Then push a branch and open a pull request to see the workflows run.
.github/workflows/node-ci.ymlpreview
name: Node CI
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
test:
name: Node ${{ matrix.node-version }}
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
node-version: [22, 24]
steps:
- name: Check out code
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: ${{ matrix.node-version }}
cache: npm
- name: Install dependencies
run: npm ci
- name: Check formatting
run: npm run format:check --if-present
- name: Lint
run: npm run lint
- name: Type check
run: npm run typecheck --if-present
- name: Test
run: npm test
- name: Build
run: npm run build --if-present
.editorconfigpreview
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.py]
indent_size = 4
[*.md]
trim_trailing_whitespace = false
[{Makefile,*.mk}]
indent_style = tab
[*.{bat,cmd}]
end_of_line = crlf