Files
support_backend/specs/001-ci-pipeline/research.md
T
saqib mirandClaude Sonnet 5 414f336704 docs: spec, plan, and design artifacts for CI pipeline feature
/speckit-specify + /speckit-plan output for 001-ci-pipeline: feature
spec with 2 user stories and 10 functional requirements, requirements
quality checklist, implementation plan with constitution gate check,
Phase 0 research (6 decisions incl. secrets-from-credentials-store),
Phase 1 data model, pipeline stage contract, and a 5-scenario
quickstart validation guide. No Jenkinsfile yet — that's the
implementation step after /speckit-tasks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 16:22:29 +05:30

6.6 KiB

Phase 0 Research: Continuous Integration Pipeline

No NEEDS CLARIFICATION markers remained in the Technical Context after /speckit-plan's Technical Context pass — this document records the decisions behind that context rather than resolving open unknowns.

Decision: CI system — Jenkins declarative pipeline

  • Decision: Use a single Jenkinsfile (declarative syntax) at the repo root.
  • Rationale: The constitution's Technology & Platform Constraints section and docs/09-testing-observability-cicd.md §3 both name Jenkins explicitly, with a defined stage order. This isn't a free choice — using anything else would need a constitution amendment.
  • Alternatives considered: GitHub Actions / GitLab CI — rejected only because the governing docs already commit to Jenkins; otherwise equally viable for this repo's needs.

Decision: Environment/secrets handling in the pipeline

  • Decision: The pipeline injects POSTGRES_PASSWORD, REDIS_PASSWORD, JWT_SECRET, and AWS credentials from Jenkins' credentials store as environment variables / a generated .env.* file written into the workspace at runtime — never read from a file committed to the repository.
  • Rationale: .env.development, .env.test, and .env.prod were found committed to git with real dev credentials in plain text (fixed separately: untracked, .env.example added, see repo commit 2093898). docker-compose.test.yml's app service still declares env_file: .env.test, so the pipeline's test stage must materialize a .env.test in the workspace from Jenkins credentials immediately before docker compose up, then discard it when the stage ends — the checked-in .env.test template must only ever contain non-secret placeholder values from here on, matching .env.prod's existing CHANGE_ME pattern. Production deploy correspondingly generates .env.prod the same way, from Jenkins prod credentials, never from the repo copy.
  • Alternatives considered: Docker secrets / mounted files instead of generated .env files — viable but a larger change to docker-compose.*.yml; deferred as out of scope since it doesn't change the pipeline's external behavior (FR-008 is satisfied either way).

Decision: Environment validation stage

  • Decision: The environment-validation stage runs the existing env.ts Zod schema (src/config/env.ts) against the materialized environment before any test stage starts, by invoking a lightweight script (e.g. node --env-file=.env.<target> -e "require('./dist/src/config/env.js')" post-build, or a dedicated tsx invocation pre-build) so a missing/malformed variable fails immediately with the schema's existing descriptive Zod error, satisfying FR-002.
  • Rationale: src/config/env.ts already throws a specific, actionable error (❌ Invalid environment variables: ...) on safeParse failure — no new validation logic is needed, just an early pipeline invocation of the existing one.
  • Alternatives considered: A separate shell script re-implementing required-var checks — rejected as duplicate logic that could drift from the real Zod schema.

Decision: Quality stage contents

  • Decision: Stage-to-script mapping is direct:
    • installnpm ci
    • typechecknpm run typecheck
    • lintnpm run lint (consider folding scripts/check-architecture.ts's module-boundary check into this stage, since it enforces constitution Principle III and already runs in .husky/pre-commit — confirmed as in-scope, see Assumptions below)
    • format checknpm run format:check
    • unit testnpm run test:unit
    • integration testnpm run test:integration (requires docker-compose.test.yml's postgres/redis services running first)
    • e2e testnpm run test:e2e (same dependency)
    • buildnpm run build:prod (or build:test/build:development depending on target, matching the BUILD_COMMAND pattern already used by each docker-compose.*.yml)
    • docker builddocker build using the existing root Dockerfile
  • Rationale: Every stage maps to a script that already exists and is already exercised locally/in the pre-commit hook — the pipeline's job is orchestration and environment isolation, not defining new checks (matches plan.md's Summary).
  • Alternatives considered: None — this mapping is essentially forced by "don't introduce new checks" (spec.md Assumptions).

Decision: Publish/deploy mechanism

  • Decision: publish pushes the built image to a container registry (registry choice left to implementation/tasks phase — no registry is currently configured in the repo); deploy runs docker compose --env-file <generated .env> -f docker-compose.<target>.yml up -d on the target host/agent, reusing the docker:up:* npm scripts' underlying compose invocation.
  • Rationale: The repo already models per-environment deployment as docker compose -f docker-compose.<env>.yml up -d (see docker:up:dev, docker:up:test, docker:up:prod in package.json) — the pipeline should drive the same mechanism an engineer would run by hand today, not invent a new one.
  • Alternatives considered: Kubernetes/Helm deploy — no k8s manifests exist in the repo today; out of scope unless a future feature introduces them.

Decision: Concurrent-run isolation (FR-009)

  • Decision: Rely on Jenkins' per-build workspace isolation (each pipeline run gets its own workspace directory and, for the Docker-dependent stages, project-scoped Compose project names e.g. -p support-test-${BUILD_NUMBER}) rather than building custom isolation logic.
  • Rationale: This is a built-in Jenkins guarantee once each build uses its own workspace and Compose project name; no additional application code is needed.
  • Alternatives considered: None needed — default Jenkins behavior already satisfies this when Compose project names are parameterized by build number.

Assumptions carried over from spec.md, confirmed against the codebase

  • package.json scripts (typecheck, lint, format:check, test:unit, test:integration, test:e2e, build*, docker:*) are confirmed present and are the source of truth for stage behavior.
  • docker-compose.development.yml / .test.yml / .prod.yml are confirmed present and already encode per-environment deploy shape.
  • .husky/pre-commit already runs lint-staged and scripts/check-architecture.ts locally — the CI lint stage should run the same architecture check server-side so a bypassed/missing local hook can't let a boundary violation merge.