Stage 5 · Platform
Testing & Quality Gates
Linting and Formatting
Failing fast with ESLint, Prettier, golangci-lint, ShellCheck, and markdownlint checks.
Why Lint in CI?
Linting in CI catches style violations, potential bugs, and code quality issues before they reach the codebase. Even with IDE linters, not every developer runs them locally. CI linting is the safety net that enforces consistent standards across the team.
Linting should run first in the pipeline — it is fast (seconds) and catches obvious issues. If linting fails, there is no point running tests. This fail-fast approach saves time and resources.
ESLint and Prettier
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
- run: npm ci
- name: Run ESLint
run: npm run lint -- --format=json --output-file=eslint-results.json
continue-on-error: true
- name: Annotate ESLint results
if: always()
uses: atlassian/eslint-annotate@v3
with:
report: eslint-results.json
fail-on-error: true
- name: Check formatting
run: npx prettier --check .
- name: Auto-fix formatting
if: failure()
run: npx prettier --write .
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "chore: fix formatting with prettier"ESLint runs first to catch code quality issues. prettier --check verifies formatting without modifying files. If formatting fails, the auto-fix step runs and commits the changes automatically.
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- name: Lint with annotations
uses: reviewdog/action-eslint@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-review
eslint_flags: "src/ --ext .ts,.tsx"
filter_mode: addedReviewDog posts ESLint errors as inline review comments on the PR. filter_mode: added only comments on lines that changed, avoiding noise from pre-existing issues in the codebase.
golangci-lint
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.22"
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v4
with:
version: v1.57
args: --timeout=5m --out-format=json
- name: Run go vet
run: go vet ./...
- name: Check go mod tidy
run: |
go mod tidy
git diff --exit-code go.mod go.sumgolangci-lint runs 50+ linters in parallel. The timeout flag prevents hangs on large codebases. The go mod tidy check ensures the module file is clean. git diff --exit-code fails if go mod tidy produces changes.
ShellCheck
jobs:
shellcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install ShellCheck
run: sudo apt-get install -y shellcheck
- name: Run ShellCheck
run: |
find . -name "*.sh" -not -path "./node_modules/*" \
-not -path "./.git/*" \
-print0 | xargs -0 shellcheck --severity=warning
- name: Lint CI scripts
run: |
shellcheck scripts/*.shShellCheck catches common shell scripting errors: unquoted variables, missing error handling, deprecated syntax. --severity=warning focuses on real issues rather than style nits.
Multi-Language Linting
lint:javascript:
stage: test
image: node:20
script:
- npm ci
- npm run lint
lint:python:
stage: test
image: python:3.12
script:
- pip install ruff
- ruff check src/
lint:go:
stage: test
image: golangci/golangci-lint:v1.57
script:
- golangci-lint run
lint:yaml:
stage: test
image: alpine:3
before_script:
- apk add --no-cache yamllint
script:
- yamllint -c .yamllint.yml .
lint:markdown:
stage: test
image: node:20
script:
- npm install -g markdownlint-cli
- markdownlint docs/ README.mdGitLab CI runs linting for each language in parallel. Each job uses the appropriate language image. This catches language-specific issues across a polyglot codebase.
Formatting Enforcement
- Prettier for JavaScript/TypeScript — opinionated formatter with no configuration debates.
- gofmt/goimports for Go — built-in formatting that is non-negotiable.
- Black for Python — the uncompromising Python formatter.
- shfmt for shell scripts — consistent shell formatting across the team.
Configure pre-commit hooks with Husky (Node.js) or pre-commit (Python) to run linters and formatters before code is committed. This catches issues before they reach CI and reduces pipeline failures.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.