Skip to content

Architecture

Components

.github/workflows/ci-quality.yml   GitHub Actions entry point (env + artifact)
        │
        ▼
ci/scripts/run-ci.mjs              runs 16 phases, parses output
        │  ├─ ci/reports/raw/<phase>.log   raw logs
        │  └─ ci/reports/results.json      machine-readable raw result
        ▼
ci/scripts/ci-policy.mjs           scoring + PASS/FAIL gates + findings
        ▼
ci/scripts/generate-ci-report.mjs  4 report files + $GITHUB_STEP_SUMMARY

The same chain runs inside Docker:

ci/docker-compose.ci.yml
  ├─ mongo-ci     (test MongoDB, tmpfs — not persistent)
  ├─ minio-ci     (test MinIO,   tmpfs — not persistent)
  ├─ minio-ci-init(test bucket: iqv-platform-ci)
  ├─ mailpit-ci   (test SMTP — no mail reaches a real recipient)
  └─ ci-runner    (ci/Dockerfile, Node 22)

Why this split?

  • The workflow stays thin. Phases live in run-ci.mjs, not in YAML, so the identical pipeline runs locally and in CI.
  • One source of policy. Score and PASS/FAIL live only in ci-policy.mjs; report files are just formatted views of that output.
  • Isolation from production. CI compose uses a separate project name (iqv-ci), separate service names and a separate database/bucket; the root docker-compose.yml is untouched.

Output contract

Each phase record inside results.json:

Field Meaning
id, name, group phase identity
command, cwd executed command
exitCode, durationMs, timedOut run outcome
counts {passed, failed, skipped, total} — parsed from output
evidence last 40 error lines
rawLog path to raw/<id>.log