Skip to content
kitty/bin
Your files never leave this device

kits / developer-config

v1.0.0

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 recommendedTypeChecked rules from typescript-eslint to every JavaScript and TypeScript file.
  • Uses the TypeScript project service, which finds the right tsconfig.json for each file. You need a tsconfig.json that 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 stray console.log calls.
  • Relaxes no-console and non-null assertions in test files.
  • Adds eslint-config-prettier last, 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 = 100 matches 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 of assert) is allowed in tests, where assert is 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:

  1. Blocks staged files whose names look like secrets: .env and .env. (except .env.example, .env.sample and .env.template), .env, .pem, .key, .p12, .pfx, .jks, .keystore and common SSH private key names.
  2. Blocks staged changes that contain a PEM private key header.
  3. Runs a Prettier format check and ESLint on staged JavaScript, TypeScript and other formattable files, if both are installed in node_modules.
  4. Runs ruff format --check and ruff check on staged Python files, if ruff is on your PATH.

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 to python -m pip install -r requirements-dev.txt and cache-dependency-path to requirements*.txt.
  • Using uv: replace actions/setup-python with astral-sh/setup-uv, install with uv sync --locked, and prefix the tool commands with uv run.
  • Supporting fewer versions: remove them from the matrix and raise target-version in ruff.toml to 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.toml and pyproject-snippet.md
  • .github/workflows/python-ci.yml
  • the pip block in .github/dependabot.yml
  • python in the CodeQL matrix
  • the Python feature and the Python part of postCreateCommand in .devcontainer/devcontainer.json, and ms-python.python and charliermarsh.ruff from the extensions
  • the Python section of .gitignore if you like a short file; it does no harm if you keep it

The hooks need no changes.

Python only

Delete:

  • .prettierrc.json, .prettierignore and eslint.config.mjs
  • .github/workflows/node-ci.yml
  • the npm block in .github/dependabot.yml
  • javascript-typescript in the CodeQL matrix
  • the Node.js feature and the npm ci part of postCreateCommand in .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:

  1. Add .editorconfig, .gitignore and .gitattributes. They change nothing about your code. Run the renormalise step once.
  2. 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.
  3. 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-revs file so git blame skips it, and turn on the format check in CI.
  4. 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" in eslint.config.mjs, or add them to ignore in ruff.toml, and remove those exceptions one at a time.
  5. Add the hooks. Ask everyone on the team to run git config core.hooksPath hooks.
  6. Add Dependabot, dependency review and CodeQL. Expect a burst of update pull requests in the first week.
  7. 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