@science-and-humans/science-and-humans-ci-config
v3.1.10
Published
Centralized CI quality standards, shared configs, and reusable GitHub Actions workflows for Science & Humans
Downloads
537
Readme
Science & Humans — CI Platform
Centralized CI quality standards, shared configs, and reusable GitHub Actions workflows.
Architecture
┌─────────────────────────────────────┐
│ policies/standards.yml │ Defines WHAT to enforce
│ Language-agnostic, tool-agnostic │
├─────────────────────────────────────┤
│ .github/workflows/ci-node.yml │ HOW to enforce (Node.js)
│ 9 mandatory checks, all blocking │
├─────────────────────────────────────┤
│ Consumer repo calls workflow │ WHERE it runs
│ ~5 lines of YAML │ GitHub Actions in caller's context
└─────────────────────────────────────┘Quick Start
Add .github/workflows/ci.yml to your repo:
name: CI Quality Gate
on:
push:
branches: [main, release]
pull_request:
branches: [main, release]
jobs:
quality:
uses: Science-and-Humans/science-and-humans-ci-configs/.github/workflows/ci-node.yml@v3
with:
node-version: '22'What It Checks
| # | Check | Tool | Default | Severity |
| --- | -------------- | -------------------- | ----------------------------------------- | ----------------------- |
| 1 | Commit message | commitlint | Conventional Commits + Linear ticket | error (blocks) |
| 2 | Format | Prettier | --check "**/*.{js,ts,json,md,yaml,yml}" | error (blocks) |
| 3 | Lint | ESLint | --max-warnings=0 | error (blocks) |
| 4 | Unit tests | Jest / Vitest | Auto-detects framework and coverage cmd | error (blocks) |
| 5 | Coverage | Jest / Vitest / lcov | >= 70% line coverage | error (blocks) |
| 6 | Security | npm audit | --production --audit-level=high | error (blocks) |
| 7 | License | license-checker | MIT, ISC, Apache-2.0, BSD, etc. | error (blocks) |
| 8 | Outdated | npm-check-updates | lists outdated packages in summary | warning (informational) |
| 9 | Trivy FS | Trivy | CRITICAL + HIGH severity, ignore unfixed | error (blocks) |
8 checks are error severity — pipeline fails if any check fails. Outdated packages is informational — lists what needs updating but does not block.
Inputs
All inputs are optional. Most repos only need node-version:
| Input | Type | Default | Description |
| -------------------------- | ------ | --------------------------------- | --------------------------------------- |
| node-version | string | '20' | Node.js version |
| working-directory | string | '.' | Directory with package.json |
| audit-level | string | 'high' | npm audit severity level |
| lint-max-warnings | number | 0 | Max ESLint warnings |
| format-file-patterns | string | '**/*.{js,ts,json,md,yaml,yml}' | Prettier glob |
| allowed-licenses | string | 'MIT;ISC;...' | Semicolon-separated SPDX IDs |
| coverage-threshold | number | 70 | Min line coverage % |
| trivy-severity | string | 'CRITICAL,HIGH' | Trivy scan severity levels |
| outdated-exclude-pattern | string | '/^(@types\|eslint\|prettier)/' | Packages to exclude from outdated check |
Outputs
Each check exposes its outcome (success, failure, or skipped):
| Output | Description |
| ------------------------ | ---------------------------- |
| commit-message-outcome | Commit message format result |
| format-outcome | Prettier result |
| lint-outcome | ESLint result |
| test-outcome | Unit test result |
| coverage-outcome | Coverage threshold result |
| security-outcome | npm audit result |
| license-outcome | License compliance result |
| outdated-outcome | Outdated packages result |
| trivy-fs-outcome | Trivy filesystem scan result |
Use outputs in downstream jobs:
jobs:
quality:
uses: Science-and-Humans/science-and-humans-ci-configs/.github/workflows/ci-node.yml@v3
with:
node-version: '22'
docker-build:
needs: [quality]
runs-on: ubuntu-latest
steps:
# Quality gate passed — safe to build and deployWhat Your Repo Needs
The workflow expects these to exist in your repo:
package.jsonwith alintscript (e.g.,"lint": "eslint .")package-lock.json(fornpm ciand caching)- A Prettier config (
.prettierrc,prettier.config.cjs, etc.) - An ESLint config (
eslint.config.mjs,.eslintrc.cjs, etc.) - A test script — one of these:
test:coveragein package.json (preferred)test:covin package.json- Jest or Vitest in devDependencies (auto-detected as fallback)
- commitlint available at
npm citime — easiest is to add@science-and-humans/science-and-humans-ci-configas a devDependency (it bundles@commitlint/cli+@commitlint/config-conventionaland ships the shared config). Alternatively, install@commitlint/cliand@commitlint/config-conventionaldirectly and add a localcommitlint.config.cjs.
The workflow uses your repo's configs — it just runs them consistently.
Commit Message Format
All commits must follow Conventional Commits plus a Linear ticket prefix on the subject for product-facing work:
type(scope): [TICKET-123] subjectAllowed types: feat, fix, hotfix, chore, docs, refactor, test, ci, style, perf, build, revert
Linear ticket prefix:
- Required for
feat,fix,hotfix,refactor,perf— these correspond to planned product work tracked in Linear. - Optional for
chore,docs,test,ci,style,build,revert— these are housekeeping and don't always map to a ticket.
Examples:
feat(auth): [PRO-1234] add JWT refresh token rotation
fix(scheduler): [PRO-987] handle timezone offset in booking
hotfix(api): [PRO-2001] patch null deref in /orders
chore(deps): update eslint to v9
docs(readme): add deployment instructions
ci(pipeline): update workflow to use Node 22Rules:
- Scope is optional:
feat: [PRO-1234] add loginis valid - Ticket prefix uses
[PROJECT-NUMBER]with uppercase project key - Max 100 characters for the subject line
- Merge commits are skipped automatically
Consumer setup (optional but recommended): if you want to run commitlint locally (e.g. via husky), add a commitlint.config.cjs that re-exports the shared config:
// commitlint.config.cjs
module.exports = require('@science-and-humans/science-and-humans-ci-config/commitlint');The CI workflow falls back to the shared config automatically if you don't add a local one.
Shared ESLint & Prettier Configs (npm package)
Install the package:
npm install -D @science-and-humans/science-and-humans-ci-configESLint — Flat Config (Recommended)
Backend repos (NestJS, Express, Node.js):
// eslint.config.mjs
import config from '@science-and-humans/science-and-humans-ci-config/eslint-flat';
export default config({ tsconfigRootDir: import.meta.dirname });Frontend repos (Next.js, React):
// eslint.config.mjs
import config from '@science-and-humans/science-and-humans-ci-config/eslint-flat-frontend';
export default config({ tsconfigRootDir: import.meta.dirname });With custom ignores or overrides:
// eslint.config.mjs
import config from '@science-and-humans/science-and-humans-ci-config/eslint-flat';
export default [
...config({ tsconfigRootDir: import.meta.dirname, ignores: ['generated/'] }),
{
rules: { '@typescript-eslint/no-explicit-any': 'error' },
},
];ESLint — Legacy Config (for repos still on .eslintrc)
// .eslintrc.cjs (backend)
module.exports = {
extends: [require.resolve('@science-and-humans/science-and-humans-ci-config/eslint')],
};
// .eslintrc.cjs (frontend)
module.exports = {
extends: [require.resolve('@science-and-humans/science-and-humans-ci-config/eslint-full')],
};Prettier (all repos)
// prettier.config.cjs
module.exports = require('@science-and-humans/science-and-humans-ci-config/prettier');Available Exports
| Export | Format | Use case |
| ------------------------ | ----------------- | ----------------------------------------- |
| ./eslint-flat | ESM flat config | Backend repos with eslint.config.mjs |
| ./eslint-flat-frontend | ESM flat config | Frontend repos with eslint.config.mjs |
| ./eslint | CJS legacy config | Backend repos with .eslintrc.cjs |
| ./eslint-full | CJS legacy config | Frontend repos with .eslintrc.cjs |
| ./prettier | CJS | All repos |
| ./commitlint | CJS | All repos (Conventional Commits + ticket) |
What the configs enforce
ESLint (backend):
eslint:recommended@typescript-eslint/recommendedTypeCheckedeslint-plugin-prettier/recommendedno-explicit-any: offno-floating-promises: warnno-unsafe-argument: warn- Globals: node, jest
ESLint (frontend): Everything above plus:
reactpluginreact-hooks/recommendedrulesreact-in-jsx-scope: off- Globals: browser, node, jest
Prettier (./prettier):
- Single quotes
- Semicolons
- Trailing commas (all)
- Print width: 100
- Tab width: 2
Integrating With Existing Deploy Workflows
Replace the old install, lint, test, security-audits, and trivy-fs-scan jobs with a single quality gate call:
# Before: 5 separate jobs (~190 lines)
# After: 1 reusable workflow call
jobs:
quality:
uses: Science-and-Humans/science-and-humans-ci-configs/.github/workflows/ci-node.yml@v3
with:
node-version: '22'
# docker-build now depends on quality instead of lint + test + security-audits
docker-build:
needs: [quality]
runs-on: ubuntu-latest
steps:
# ... existing docker build steps
# Everything downstream stays the same
deploy-to-gke:
needs: [docker-build]
# ...Release Tracking (Linear + Amplitude)
Reusable workflow that auto-fires on tag push and creates a Linear release and an Amplitude release marker for the same version. After this, "Done" in Linear means "shipped to prod" and every Amplitude chart annotates each deploy.
Consumer setup is one file — drop this into the consumer repo at .github/workflows/release-tracking.yml:
name: Release Tracking
on:
push:
tags: ['v*']
jobs:
track:
uses: Science-and-Humans/science-and-humans-ci-configs/.github/workflows/release-tracking.yml@v3
secrets: inheritThat's it. Push v1.2.3 and the release lands in Linear and Amplitude as v1.2.3.
What you get
- Linear — uses
linear/linear-release-action@v0to scan commits for issue identifiers (PRO-123), groups them under the release, and updates issue status when code reaches production. - Amplitude — POSTs to Amplitude's Releases API so every deploy shows up as a vertical line on time-series charts.
Versions are the calling tag, verbatim. The workflow forwards ${{ github.ref_name }} to Linear and Amplitude as-is — a v1.2.3 tag becomes v1.2.3 in both UIs. Each repo is expected to point at its own Amplitude project (configured via the AMPLITUDE_API_KEY / AMPLITUDE_SECRET_KEY secrets), so cross-repo version collisions don't apply. If you have a monorepo shipping multiple apps under one Amplitude project, encode the scope in the tag itself (e.g. web-v1.2.3).
Inputs (all optional)
| Input | Type | Default | Description |
| ------------------ | ------- | ------------------------ | ------------------------------------------------------------------------------------ |
| version | string | ${{ github.ref_name }} | Override the version. Default is the calling tag — leave unset for tag-push releases |
| environment | string | 'production' | Environment label surfaced in titles |
| release-notes | string | '' | Extra description for the Amplitude release |
| enable-linear | boolean | true | Toggle Linear release creation |
| enable-amplitude | boolean | true | Toggle Amplitude release marker |
Secrets
| Secret | Required when |
| ---------------------- | -------------------------- |
| LINEAR_ACCESS_KEY | enable-linear is true |
| AMPLITUDE_API_KEY | enable-amplitude is true |
| AMPLITUDE_SECRET_KEY | enable-amplitude is true |
Use secrets: inherit from the consumer workflow so you don't have to re-list each one. Configure these as repo secrets with the exact names above and they'll be picked up automatically.
Outputs
linear-outcome and amplitude-outcome (success / failure / skipped) for downstream notification jobs.
Failure handling
Both targets are best-effort — a Linear or Amplitude outage is logged in the step summary but does not fail the workflow. The two integrations are independent: one can fail without affecting the other.
See docs/CONSUMER_GUIDE.md for the one-time Linear and Amplitude setup steps.
Adding a New Language
Versioning
- Consumer repos pin to
@v3(gets minor/patch updates automatically) - Breaking changes bump to
@v4 - Same pattern as
actions/checkout@v6
Repo Structure
├── policies/
│ └── standards.yml # Quality standards (language-agnostic)
├── configs/
│ └── node/
│ ├── eslint.base.cjs # Shared ESLint base config
│ ├── eslint.config.cjs # Backend ESLint config (extends base)
│ ├── eslint.frontend.cjs # Frontend ESLint config (extends base)
│ └── prettier.config.cjs # Shared Prettier config
├── .github/
│ └── workflows/
│ ├── ci-node.yml # Reusable workflow for Node.js repos
│ ├── release-tracking.yml # Reusable workflow: Linear + Amplitude release tracking
│ ├── publish.yml # Auto-publish npm package on version tags
│ └── self-test.yml # Tests the platform itself
├── test-fixtures/
│ └── node/ # Minimal project for self-testing
├── docs/
│ ├── CONSUMER_GUIDE.md # Step-by-step onboarding guide
│ └── ADDING_LANGUAGE.md # How to add Java/Python/Go support
├── CODEOWNERS # Required reviewers for all changes
└── package.json # npm package config