Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,19 @@ on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
# Deliberately NOT filtered by target branch. The retired pr-tests.yml ran on
# PRs into *any* branch; this workflow used to be limited to main/develop, so
# narrowing it here would silently leave PRs targeting release/feature
# branches with no test run at all.
types: [ opened, synchronize, reopened ]

# Inherited from the retired pr-tests.yml: without this, every push to a PR
# branch starts a full run and the superseded ones keep burning runners to
# completion.
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
test:
name: Test Suite
Expand Down Expand Up @@ -135,6 +145,35 @@ jobs:
echo "Checking server build..."
ls -la dist/server/

docs-build:
name: Documentation Build
runs-on: ubuntu-latest

# Absorbed from the retired pr-tests.yml, where it was the ONE job with
# coverage this workflow did not already have. It is not redundant with
# deploy-docs.yml: that only runs on push to main, i.e. after merge — so
# without this, a VitePress build break is caught by the deploy, not the PR.
#
# Run unconditionally rather than behind a dorny/paths-filter `docs` gate.
# The old gate keyed on `docs/**` and `**.md`, which missed the VitePress
# config and the docs' own dependencies, and the build is ~1 min. Dropping
# the gate is what let the `changed-files` job retire with pr-tests.yml.
steps:
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7

- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7
with:
node-version: '24.x'
cache: 'npm'

- name: Install dependencies
run: npm install --prefer-offline --no-audit --legacy-peer-deps

- name: Build documentation
run: npm run docs:build

docker:
name: Docker Build
runs-on: ubuntu-latest
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ jobs:
uses: lewagon/wait-on-check-action@2271c86c146b96545b4e871b855e10ffa6f50773 # v1.9.0
with:
ref: ${{ github.event.pull_request.head.sha }}
check-regexp: '(Build Check|Test Suite|Security Scan|Docker Build|Quick Tests).*'
# "Quick Tests" retired with pr-tests.yml (its coverage was a strict
# subset of "Test Suite"); "Documentation Build" moved into ci.yml.
check-regexp: '(Build Check|Test Suite|Security Scan|Docker Build|Documentation Build).*'
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 30
allowed-conclusions: success,skipped,neutral
Expand Down
225 changes: 0 additions & 225 deletions .github/workflows/pr-tests.yml

This file was deleted.

2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Three lint commands:
```bash
npm run lint:ci 2>&1 | grep '^FAIL' | grep -v '.claude/worktrees'
```
Empty output = the CI gate passes. The same applies to Vitest, which scans those worktrees and inflates the suite count.
Empty output = the CI gate passes. Vitest **no longer** has this problem — `vitest.config.ts` excludes `**/.claude/worktrees/**`. (It used to: two leftover worktrees tripled the local suite, adding ~11 min and ~22k phantom tests, and reported failures from the worktrees' own stale dependencies.) ESLint still walks them, so the `grep -v` above is still required for `lint:ci`.

**`npx eslint <file>` exiting 0 does not mean the ratchet passes.** The ratchet compares *per-file, per-rule counts* against the baseline, so adding one `react-hooks/exhaustive-deps` violation to an already-baselined file fails CI while plain ESLint reports nothing new. Always confirm with `lint:ci` before pushing.

Expand Down
21 changes: 11 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,18 +127,19 @@ Our CI/CD pipeline runs automatically on all PRs:

### GitHub Actions Workflows

1. **PR Tests** (`pr-tests.yml`)
- Runs on every PR
- Quick validation of changes
- Type checking and unit tests

2. **Full CI** (`ci.yml`)
- Comprehensive testing
- Multiple Node.js versions
- Docker build validation
1. **CI** (`ci.yml`)
- Runs on every PR, whatever branch it targets
- Lint ratchet, type checking, and the full unit suite on Node 22 / 24 / 25
(against real PostgreSQL and MySQL service containers)
- Frontend, server, documentation, and Docker build validation
- Security scanning

3. **Release Pipeline** (`release.yml`)
A separate `pr-tests.yml` used to run alongside this one, but every job in it
was a strict subset of a job here — it re-ran the same suite a fourth time on
Node 24 and then re-ran parts of it again. Its one unique job, the VitePress
documentation build, now lives in `ci.yml`.

2. **Release Pipeline** (`release.yml`)
- Runs on version tags
- Full test suite
- Multi-platform Docker builds
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# MeshMonitor

[![CI](https://github.com/Yeraze/meshmonitor/actions/workflows/ci.yml/badge.svg)](https://github.com/Yeraze/meshmonitor/actions/workflows/ci.yml)
[![PR Tests](https://github.com/Yeraze/meshmonitor/actions/workflows/pr-tests.yml/badge.svg)](https://github.com/Yeraze/meshmonitor/actions/workflows/pr-tests.yml)
[![Docker Image](https://ghcr-badge.egpl.dev/yeraze/meshmonitor/latest_tag?color=%235b4566&ignore=latest,main,dev&label=version&trim=)](https://github.com/Yeraze/meshmonitor/pkgs/container/meshmonitor)
[![Docker Pulls](https://ghcr-badge.egpl.dev/yeraze/meshmonitor/size?color=%235b4566&tag=latest&label=image%20size&trim=)](https://github.com/Yeraze/meshmonitor/pkgs/container/meshmonitor)
[![License](https://img.shields.io/github/license/Yeraze/meshmonitor)](https://github.com/Yeraze/meshmonitor/blob/main/LICENSE)
Expand Down
20 changes: 19 additions & 1 deletion src/server/test-helpers/userTestHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,25 @@ import bcrypt from 'bcrypt';
import { AuthRepository } from '../../db/repositories/auth.js';
import { User, CreateUserInput, UpdateUserInput } from '../../types/auth.js';

const SALT_ROUNDS = 12;
/**
* bcrypt cost factor for test fixtures ONLY.
*
* This is deliberately the bcrypt minimum (4), not production's 10
* (`localAuth.ts`). The cost factor is a brute-force hardening knob — nothing
* in the suite asserts on it, and every call here hashes a throwaway literal
* like 'password123' into an in-memory DB that dies with the process.
*
* It is worth this comment because the number is load-bearing for CI time:
* these helpers run inside `beforeEach`, so the cost is paid per test, not per
* file. At 12 a single hash costs ~209ms; at 4 it costs ~2ms. That one constant
* accounted for ~185s of the ~352s total suite execution time across the five
* files that use this helper (meshcoreRoutes, userRoutes, authRoutes,
* auditRoutes, packetRoutes).
*
* Do NOT raise this to "match production" — production hashing is covered by
* `localAuth.ts`/`mfa.ts`, which set their own rounds.
*/
const SALT_ROUNDS = 4;

/**
* Map a DbUser (authMethod field) to the legacy User shape (authProvider field).
Expand Down
Loading
Loading