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 = crlfkits / 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.
* text=auto eol=lf
*.bat text eol=crlf
*.cmd text eol=crlf
hooks/* text eol=lf
*.sh text eol=lf
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.webp binary
*.avif binary
*.ico binary
*.pdf binary
*.zip binary
*.gz binary
*.woff binary
*.woff2 binary
*.ttf binary
*.otf binary
*.mp3 binary
*.mp4 binary
*.webm binary
package-lock.json -diff linguist-generated=true
pnpm-lock.yaml -diff linguist-generated=true
yarn.lock -diff linguist-generated=true.env
.env.*
!.env.example
!.env.sample
*.pem
*.key
*.p12
*.pfx
node_modules/
.npm/
.pnpm-store/
.yarn/*
!.yarn/releases
!.yarn/plugins
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
.eslintcache
*.tsbuildinfo
dist/
build/
out/
.next/
.nuxt/
.output/
.svelte-kit/
.turbo/
.vercel/
.netlify/
.wrangler/
storybook-static/
coverage/
.nyc_output/
playwright-report/
test-results/
__pycache__/
*.py[cod]
*.egg-info/
.eggs/
.venv/
venv/
.pytest_cache/
.ruff_cache/
.mypy_cache/
.pyright/
.tox/
.nox/
.coverage
.coverage.*
htmlcov/
*.whl
.idea/
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
*.swp
*.swo
.DS_Store
Thumbs.db
desktop.ini
logs/
*.log
tmp/
.cache/node_modules/
dist/
build/
out/
coverage/
.next/
.nuxt/
.svelte-kit/
.turbo/
.venv/
venv/
__pycache__/
.ruff_cache/
.pytest_cache/
package-lock.json
pnpm-lock.yaml
yarn.lock
*.min.js
*.min.css{
"printWidth": 100,
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"endOfLine": "lf",
"overrides": [
{
"files": "*.md",
"options": {
"proseWrap": "preserve"
}
}
]
}Changelog
- 1.0.0, 22 October 2026: first release
MIT License
Copyright (c) 2026 Snowkit
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.Developer Config
Version 1.0.0
A set of current, sensible config files you can copy into a Node.js, TypeScript or Python repository: editor settings, formatting, linting, git rules, two git hooks, GitHub Actions workflows, Dependabot and a dev container. The files contain no comments, so they stay clean in your project. Every choice is explained in setup-guide.md.
Who it's for
- Developers starting a new repository who want a solid baseline in minutes
- Teams tidying up an older repository one step at a time
- Maintainers who want CI with least-privilege permissions and dependency checks from day one
How to use it in 5 minutes
- Copy
.editorconfig,.gitignoreand.gitattributesinto the root of your repository. - Copy the formatter and linter files for your language:
.prettierrc.json,.prettierignoreandeslint.config.mjsfor Node.js and TypeScript,ruff.tomlfor Python. Install the packages listed insetup-guide.md. - Copy the
hooksfolder and rungit config core.hooksPath hooks. - Copy the
.githubfolder and delete the workflows and Dependabot blocks for languages you do not use. - Commit on a branch, open a pull request and watch the workflows run.
Read the "Single-language projects" and "Adopting gradually" sections of setup-guide.md before you apply this to an existing codebase.
File map
| File | What it is |
|---|---|
README.md | This guide |
LICENSE.txt | MIT licence |
CHANGELOG.md | Version history |
setup-guide.md | What every file does, what to change, what to delete for one language and how to adopt it step by step |
.editorconfig | Shared editor settings: UTF-8, LF, indentation, final newline |
.prettierrc.json | Prettier formatting settings |
.prettierignore | Files and folders Prettier skips |
eslint.config.mjs | ESLint flat config for TypeScript and JavaScript with type-aware rules |
ruff.toml | Ruff lint and format settings for Python |
pyproject-snippet.md | The same Python settings for pyproject.toml, plus a dev dependency group and pytest settings |
.gitignore | Ignore rules for Node.js and Python projects, with secrets first |
.gitattributes | Line ending normalisation, binary files and lock file diffs |
hooks/pre-commit | Blocks secret-looking files and private keys, runs format and lint checks on staged files |
hooks/commit-msg | Checks the commit message against Conventional Commits |
.github/workflows/node-ci.yml | Node.js CI: install, format check, lint, type check, test and build on current LTS versions |
.github/workflows/python-ci.yml | Python CI: Ruff format and lint, pytest on supported Python versions |
.github/workflows/dependency-review.yml | Fails pull requests that add dependencies with known high-severity vulnerabilities |
.github/workflows/codeql.yml | CodeQL code scanning for JavaScript, TypeScript, Python and workflow files |
.github/dependabot.yml | Weekly grouped dependency updates for npm, pip and GitHub Actions |
.devcontainer/devcontainer.json | A dev container with Node.js and Python that installs dependencies and enables the hooks |
A note on limits
These files are a starting point, not a standard every project must follow. Tool versions move: check the release notes of ESLint, typescript-eslint, Ruff and the GitHub Actions used here when you adopt the kit, and let Dependabot keep the action versions current. Hooks run on each developer's machine and can be skipped, and the secret checks only catch common file names and key formats, so keep CI, branch protection and a secret scanner in place as well. Dependency review and CodeQL on private repositories depend on your GitHub plan.
Licence
MIT License. See LICENSE.txt.
import js from "@eslint/js";
import { defineConfig, globalIgnores } from "eslint/config";
import prettier from "eslint-config-prettier/flat";
import globals from "globals";
import tseslint from "typescript-eslint";
export default defineConfig([
globalIgnores([
"**/node_modules/",
"**/dist/",
"**/build/",
"**/out/",
"**/coverage/",
"**/.next/",
"**/.venv/",
]),
{
files: ["**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}"],
extends: [js.configs.recommended, tseslint.configs.recommendedTypeChecked],
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
...globals.browser,
...globals.node,
},
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
linterOptions: {
reportUnusedDisableDirectives: "error",
},
rules: {
eqeqeq: ["error", "smart"],
"no-console": ["warn", { allow: ["warn", "error"] }],
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{ argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_" },
],
},
},
{
files: ["**/*.{js,mjs,cjs,jsx}"],
extends: [tseslint.configs.disableTypeChecked],
},
{
files: ["**/*.{test,spec}.{js,jsx,ts,tsx}", "**/__tests__/**"],
rules: {
"no-console": "off",
"@typescript-eslint/no-non-null-assertion": "off",
},
},
prettier,
]);pyproject.toml snippet
Use this if you prefer to keep Python tool settings in pyproject.toml instead of a separate ruff.toml. Pick one place: when a ruff.toml exists, Ruff ignores the [tool.ruff] table in pyproject.toml.
The settings below match ruff.toml in this kit, with the table names prefixed by tool.ruff. They also add a dev dependency group, which the Python CI workflow and the dev container install, and basic pytest settings.
[project]
name = "[your-project]"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = []
[dependency-groups]
dev = [
"pytest>=8.3",
"ruff>=0.9",
]
[tool.ruff]
line-length = 100
target-version = "py311"
extend-exclude = ["migrations", "node_modules"]
[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "UP", "SIM", "C4", "S", "PT", "RUF"]
ignore = ["E501"]
[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = ["S101"]
"**/test_*.py" = ["S101"]
"**/*_test.py" = ["S101"]
"**/conftest.py" = ["S101"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"
docstring-code-format = true
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
addopts = "-ra --strict-markers --strict-config"
xfail_strict = true
Notes
[dependency-groups]is the standard way to list development-only dependencies (PEP 735). Install the group withpython -m pip install --group dev(pip 25.1 or newer) oruv sync, which installs thedevgroup by default.- If you use a
requirements-dev.txtfile instead, change the install step in.github/workflows/python-ci.ymltopython -m pip install -r requirements-dev.txt, and changecache-dependency-pathtorequirements*.txt. pythonpath = ["src"]lets tests import a package in asrc/layout without installing it. Delete the line if your code sits at the repository root, or if you install the project in editable mode.target-versionshould match the oldest Python version you support. Keep it in step withrequires-pythonand the CI matrix.--strict-markersand--strict-configturn typos in markers and settings into errors instead of silent no-ops.xfail_strict = truemakes a test marked as an expected failure fail the run when it unexpectedly passes, so stale markers get cleaned up.
line-length = 100
target-version = "py311"
extend-exclude = ["migrations", "node_modules"]
[lint]
select = ["E", "W", "F", "I", "B", "UP", "SIM", "C4", "S", "PT", "RUF"]
ignore = ["E501"]
[lint.per-file-ignores]
"tests/**/*.py" = ["S101"]
"**/test_*.py" = ["S101"]
"**/*_test.py" = ["S101"]
"**/conftest.py" = ["S101"]
[format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"
docstring-code-format = trueSetup 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.
{
"name": "Node and Python",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/node:1": {
"version": "24"
},
"ghcr.io/devcontainers/features/python:1": {
"version": "3.13"
}
},
"forwardPorts": [3000, 8000],
"postCreateCommand": "git config core.hooksPath hooks && if [ -f package-lock.json ]; then npm ci; fi && if [ -f pyproject.toml ]; then python -m pip install --upgrade pip && python -m pip install --group dev; fi",
"customizations": {
"vscode": {
"extensions": [
"editorconfig.editorconfig",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"ms-python.python",
"charliermarsh.ruff"
]
}
},
"remoteUser": "vscode"
}version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 10
cooldown:
default-days: 7
groups:
npm-minor-and-patch:
update-types: [minor, patch]
- package-ecosystem: pip
directory: /
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 10
cooldown:
default-days: 7
groups:
pip-minor-and-patch:
update-types: [minor, patch]
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: monday
groups:
actions:
patterns: ["*"]name: CodeQL
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: "27 4 * * 1"
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
analyze:
name: Analyze ${{ matrix.language }}
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [javascript-typescript, python, actions]
steps:
- name: Check out code
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Initialise CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: none
- name: Analyze
uses: github/codeql-action/analyze@v4
with:
category: "/language:${{ matrix.language }}"name: Dependency review
on:
pull_request:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
dependency-review:
name: Review dependency changes
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out code
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Review dependencies
uses: actions/dependency-review-action@v4
with:
fail-on-severity: highname: 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-presentname: Python 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: Python ${{ matrix.python-version }}
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12", "3.13", "3.14"]
steps:
- name: Check out code
uses: actions/checkout@v5
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: pyproject.toml
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --group dev
- name: Check formatting
run: ruff format --check .
- name: Lint
run: ruff check --output-format=github .
- name: Test
run: python -m pytest#!/bin/sh
set -eu
message_file=$1
subject=$(grep -v '^#' "$message_file" | sed -n '1p')
case "$subject" in
"Merge "* | "Revert \""* | "fixup! "* | "squash! "* | "amend! "*)
exit 0
;;
esac
types='feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert'
pattern="^($types)(\([A-Za-z0-9._/-]+\))?!?: [^ ].*[^ .]$"
if ! printf '%s\n' "$subject" | grep -Eq "$pattern"; then
printf 'commit-msg: the first line must follow Conventional Commits.\n' >&2
printf ' Format: type(scope): summary\n' >&2
printf ' Types: %s\n' "$(printf '%s' "$types" | tr '|' ' ')" >&2
printf ' Example: fix(auth): reject expired reset tokens\n' >&2
printf ' Got: %s\n' "$subject" >&2
exit 1
fi
length=${#subject}
if [ "$length" -gt 72 ]; then
printf 'commit-msg: the first line is %s characters. Keep it to 72 or fewer.\n' "$length" >&2
exit 1
fi
exit 0#!/bin/sh
set -eu
set -f
newline='
'
default_ifs=$IFS
fail=0
staged=$(git diff --cached --name-only --diff-filter=ACMR)
if [ -z "$staged" ]; then
exit 0
fi
blocked=""
IFS=$newline
for file in $staged; do
base=${file##*/}
case "$base" in
.env.example | .env.sample | .env.template) ;;
.env | .env.* | *.env | *.pem | *.key | *.p12 | *.pfx | *.jks | *.keystore | id_rsa | id_dsa | id_ecdsa | id_ed25519)
blocked="$blocked $file$newline"
;;
esac
done
IFS=$default_ifs
if [ -n "$blocked" ]; then
printf 'pre-commit: these files look like secrets or private keys and were not committed:\n%s' "$blocked" >&2
printf 'Unstage them with: git restore --staged <file>\n' >&2
fail=1
fi
if git diff --cached --diff-filter=ACMR -U0 --no-color | grep -Eq '^\+.*-----BEGIN ([A-Z0-9]+ )*PRIVATE KEY( BLOCK)?-----'; then
printf 'pre-commit: the staged changes contain a private key block.\n' >&2
fail=1
fi
run_check() {
label=$1
files=$2
shift 2
if [ -z "$files" ]; then
return 0
fi
IFS=$newline
set -- "$@" $files
IFS=$default_ifs
if ! "$@"; then
printf 'pre-commit: %s failed.\n' "$label" >&2
fail=1
fi
}
format_files=$(printf '%s\n' "$staged" | grep -E '\.(js|jsx|mjs|cjs|ts|tsx|mts|cts|json|css|scss|html|md|yml|yaml|vue|svelte)$' || true)
lint_files=$(printf '%s\n' "$staged" | grep -E '\.(js|jsx|mjs|cjs|ts|tsx|mts|cts)$' || true)
python_files=$(printf '%s\n' "$staged" | grep -E '\.py$' || true)
if [ -x node_modules/.bin/prettier ]; then
run_check "Prettier format check" "$format_files" node_modules/.bin/prettier --check --ignore-unknown
fi
if [ -x node_modules/.bin/eslint ]; then
run_check "ESLint" "$lint_files" node_modules/.bin/eslint --max-warnings=0 --no-warn-ignored
fi
if command -v ruff >/dev/null 2>&1; then
run_check "Ruff format check" "$python_files" ruff format --check
run_check "Ruff lint" "$python_files" ruff check
fi
if [ "$fail" -ne 0 ]; then
printf 'pre-commit: commit blocked. Fix the problems above and commit again.\n' >&2
exit 1
fi
exit 0