diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..1cf56cb --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# Every path has an owner (K22-REPO-08). Add narrower rules below for areas that +# need a specific reviewer once the project has more than one maintainer. +* @KofTwentyTwo diff --git a/CLAUDE.md b/CLAUDE.md index a7c5a6f..fca6bab 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,7 +5,7 @@ CircleCI orb that generates CI/CD pipeline configurations from a `.munitor.yml` ## Key Commands ```bash -make test # Run all tests (BATS) +make test-scripts # Script unit tests (bats tests for the .bats suites) make pack # Pack orb YAML from src/ make all # lint + pack + test make validate # Validate packed orb with CircleCI CLI diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8152d4d..7328439 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,13 @@ # Contributing to Munitor +## Workflow + +- **Branches.** Branch `feature/` or `hotfix/` from `develop` and open a pull request back to `develop`. Releases go through `release/X.Y.Z` into `main`; pushing the `vX.Y.Z` tag publishes `kof22/munitor@X.Y.Z`, and every push to `develop` or `feature/*` publishes `dev:snapshot`. Back-merge `main` into `develop` after a release. +- **Commits.** Conventional Commits (`feat(#N): ...`, `fix: ...`, `docs: ...`), signed (`git commit -S`), with a Developer Certificate of Origin sign-off line (`git commit -s`, which adds `Signed-off-by: Name `). Reference the GitHub issue (`Closes #N`) where there is one. +- **Checks.** Run `make all` (yamllint, shellcheck, pack plus `circleci orb validate`, script and template tests) and `bats tests` (the `.bats` suites) before pushing. CircleCI runs only the orb-tools lint, pack and review jobs; the script, template and BATS tests run locally, so a green CI run does not replace them. +- **Security.** Report vulnerabilities privately as described in [SECURITY.md](SECURITY.md), never in a public issue. +- **AI assistance.** Disclose AI-assisted changes in the pull request description. + ## Module Inventory Munitor organizes CI/CD pipelines into **modules** (called "pipeline types" in `.munitor.yml`). Each module is a self-contained set of templates, jobs, commands, and scripts that handle a specific application stack. diff --git a/README.md b/README.md index 46fcc76..5ae0022 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ Latin: *munitor* (fortifier, protector) -- KofTwentyTwo's unified CircleCI orb for building, testing, and deploying all repositories. Pipeline changes happen in one place instead of 80+ repos. +| | | +| --- | --- | +| **Tier** | Internal until classified -- listed under *Awaiting classification* in the KofTwentyTwo standards [MAINTAINERS.md](https://github.com/KofTwentyTwo/standards/blob/main/MAINTAINERS.md); Product-tier requirements are being adopted | +| **Standards** | Targets [KofTwentyTwo standards](https://github.com/KofTwentyTwo/standards) 1.0.0 | +| **Orb** | [`kof22/munitor`](https://circleci.com/developer/orbs/orb/kof22/munitor), latest stable `0.3.3` | +| **Security** | [SECURITY.md](SECURITY.md) | +| **Contributing** | [CONTRIBUTING.md](CONTRIBUTING.md) | + ## How It Works Munitor uses CircleCI's [dynamic configuration](https://circleci.com/docs/dynamic-config/) to generate your full CI/CD pipeline at runtime. Each repo has a minimal `.circleci/config.yml` that triggers Munitor, and a `.munitor.yml` that declares what your project needs. Munitor reads your config, selects the right template, and generates a complete CircleCI workflow. @@ -58,6 +66,93 @@ Munitor validates branch names and will fail on non-conforming names. | `sonarcloud` | `SONAR_TOKEN` | SonarCloud analysis | | `nvd` | `NVD_API_KEY` | OWASP dependency check (Java only, optional) | +## Adopting in another app + +Munitor is a public CircleCI orb: any CircleCI organization can use it without forking. This checklist covers what a new consumer needs, for example a Java/Maven service such as me-health-portal. + +**Version.** Use `kof22/munitor@0.3.3` (or `@0.3` to float on the 0.3 line) in `.circleci/config.yml`, and the same value as `orb_version` in `.munitor.yml`. Keep the setup orb and the generated pipeline on the same line. + +### 1. Repository files + +- `.circleci/config.yml`: the setup config from [Quick Start](#quick-start), pinned to `kof22/munitor@0.3.3`. +- `.munitor.yml`: pick a [pipeline type](#pipeline-types). A Java service is `java-webapp`. KofTwentyTwo's Website-Backend is a working example; a generalized version: + +```yaml +pipeline: java-webapp +orb_version: "0.3.3" +image_name: my-service +java_version: "21" +docker: + registry: ghcr.io/my-org # the org default is ghcr.io/KofTwentyTwo -- always set this +cd: + repo: my-org/my-gitops-repo # omit to skip GitOps write-back + format: kustomize # default is helm (image.tag in values.yaml) + path: apps/my-service/kustomization.yaml # optional explicit file in a consolidated repo + production_only: true # only main updates the GitOps repo +supplemental_images: # optional extra images built from the same commit and version + - name: migrations + dockerfile: docker/Dockerfile.migrations + cd: + image_name: ghcr.io/my-org/my-service-migrations +health: + path: /health + port: "8000" + db: "true" # start a PostgreSQL sidecar for the health gate + migrations: migrations # run the -migrations image against it first + smoke_paths: [/health] +sonar: + project_key: my-org_my-service # omit to skip SonarCloud +contexts: + registry: ghcr + github: github + sonar: sonarcloud # required only with sonar.project_key + nvd: nvd # optional +org: # organization defaults (see Known limitations) + ci_email: ci@example.com + ci_name: My CI +``` + +- A health endpoint that returns HTTP 200 within 30 seconds of container start (see [Health Check Requirement](#health-check-requirement)). +- A Dockerfile only for `node-webapp` (`docker.use_repo_dockerfile: true`) or for `supplemental_images`; `java-webapp` and `node-api` generate theirs. + +### 2. CircleCI project settings + +See [Prerequisites](#prerequisites): enable dynamic config (setup workflows) and allow uncertified orbs in the consuming organization. + +### 3. Contexts and secrets + +CircleCI organization contexts; the context names are set through `contexts.*`. + +| Context | Variables | Needed for | +| --- | --- | --- | +| registry (e.g. `ghcr`) | `GHCR_USERNAME`, `GHCR_TOKEN` (`write:packages`) | Pushing images and inspecting existing tags; the immutable-tag guard fails closed without registry access | +| github | `GITHUB_TOKEN` with contents write on the app repository and on the GitOps repository | Reserving `vX.Y.Z` tags on `main` (required by `production`), GitHub Releases, GitOps write-back | +| sonar (optional) | `SONAR_TOKEN`; SonarCloud organization from `SONAR_ORG` (default `KofTwentyTwo`) | SonarCloud analysis | +| nvd (optional) | `NVD_API_KEY` | OWASP dependency check; `owasp: false` disables the check | + +### 4. Branch model + +Branch names are enforced: `main`, `develop`, `staging`, `feature/*`, `release/*`, `hotfix/*`. A merge to `main` is a production release: it reserves the next `vX.Y.Z` tag, pushes the image, updates the GitOps repository and creates the GitHub Release. Protect `main` accordingly. + +### 5. Deployment infrastructure (outside Munitor) + +Munitor stops at the GitOps commit. The consumer provides: + +- a GitOps repository whose `kustomization.yaml` (an `images[]` entry named `/`) or Helm `values.yaml` (`image.tag`) Munitor edits; +- Argo CD (or another controller) syncing that repository, with pull access to the registry; +- the runtime database and its backups. KofTwentyTwo's website uses a CloudNativePG cluster with an object-store backup and a `ScheduledBackup`, declared in `KofTwentyTwo/cluster-gitops` (`tenants/kof22/website`). Munitor only starts a throwaway PostgreSQL sidecar for the CI health gate and never touches a real database; +- schema migrations. Either the application migrates at start-up, or the deployment runs the `migrations` supplemental image (the KofTwentyTwo website runs a pre-migration backup image and then the migrations image as init containers). `health.migrations` only exercises that image in CI. + +### 6. Known limitations + +- Organization defaults point at KofTwentyTwo: registry `ghcr.io/KofTwentyTwo`, npm scope `@koftwentytwo`, CI email `munitor-ci@koftwentytwo.com`, SonarCloud organization `KofTwentyTwo`. Set `docker.registry`, the `org.*` keys and `SONAR_ORG` explicitly. The `org` block is read, but the unknown-key check prints a harmless `Unknown key 'org'` warning. +- `production` builds on `main` skip the quality gates, which run on `release/*` and `hotfix/*` pull requests. Do not merge unreviewed branches straight to `main`. +- CircleCI only; there is no GitHub Actions equivalent. Docker jobs use machine executors. + +### 7. Upgrade notes + +`0.3.3` reserves release tags atomically on `main` (needs the github context on `build-and-test`, which generated `production` workflows include), refuses to overwrite a version image tag built from another commit, and checks tag ownership before the GitOps bump. A consumer moving from a Munitor-derived orb with a tag-driven production trigger should compare that model with Munitor's merge-to-`main` model before switching. + ## Pipeline Types ### `node-api` @@ -475,7 +570,7 @@ Tags follow the format `vX.Y.Z` (git tag) and `X.Y.Z` (Docker tag). Tags are imm | Field | Type | Default | Pipelines | Description | |-------|------|---------|-----------|-------------| | `pipeline` | string | *required* | all | Pipeline type: `node-api`, `node-webapp`, `java-webapp`, `terraform`, `sdk-distribution`, `validate-cd-repo`, `argocd-apps` | -| `orb_version` | string | *required* | all | Orb version for the generated pipeline (e.g., `1`, `dev:snapshot`) | +| `orb_version` | string | *required* | all | Orb version for the generated pipeline (e.g., `"0.3.3"`, `"0.3"`, `dev:snapshot`) | | `image_name` | string | *required** | node-api, node-webapp, java-webapp | Docker image name (without registry prefix) | | `node_version` | string | `20` | node-api, node-webapp | Node.js major version | | `java_version` | string | `21` | java-webapp | Java major version | @@ -486,6 +581,17 @@ Tags follow the format `vX.Y.Z` (git tag) and `X.Y.Z` (Docker tag). Tags are imm | `cd.path` | string | -- | node-api, node-webapp, java-webapp | Explicit repository-relative `kustomization.yaml` path for consolidated GitOps repositories | | `cd.production_only` | bool | `false` | node-api, node-webapp, java-webapp | Disable CD write-back from develop, staging, and release workflows | | `cd.env.release` | string | `staging` | node-api, node-webapp, java-webapp | CD target environment for release branches | +| `cd.env.develop` / `cd.env.staging` / `cd.env.prod` | string | `develop` / `staging` / `prod` | node-api, node-webapp, java-webapp | CD target environment names for the develop, staging and main workflows | +| `cd.format` | string | `helm` | node-api, node-webapp, java-webapp | `helm` (sets `image.tag` in `values.yaml`, per environment under `environments//` when present) or `kustomize` (sets `images[].newTag`) | +| `server_module` | string | -- | gradle-webapp | Gradle module whose `build/libs` JAR goes into the generated image (multi-module builds) | +| `owasp` | bool | `true` | java-webapp | Run the OWASP dependency check | +| `package_manager` | string | `npm` | node-api, node-webapp | `npm` or `pnpm` | +| `supplemental_images` | list | `[]` | node-api, node-webapp, java-webapp | Extra images (`name`, `dockerfile`, `cd.image_name`) built and tagged with the same version | +| `health.db` | bool | `false` | node-api, node-webapp, java-webapp | Start a PostgreSQL sidecar for the health check | +| `health.migrations` | string | -- | node-api, node-webapp, java-webapp | Supplemental image suffix run against the sidecar before the health check | +| `health.smoke_paths` | list | `[]` | node-api, node-webapp, java-webapp | Extra paths that must return HTTP 200 during the health check | +| `test.coverage.summary_path` | string | `coverage/coverage-summary.json` | node-api, node-webapp | Coverage summary file read by the coverage gate | +| `org.docker_registry` / `org.npm_scope` / `org.ci_email` / `org.ci_name` / `org.orb_slug` | string | KofTwentyTwo values | all | Organization defaults; see [Adopting in another app](#adopting-in-another-app) | | `coverage.min_instruction` | int | `70` | node-api, node-webapp, java-webapp | Minimum coverage percentage | | `e2e` | bool | `false` | node-api, node-webapp, java-webapp | Enable Playwright E2E tests | | `sbom` | bool | `false` | node-api, node-webapp, java-webapp | Enable SBOM generation | @@ -596,3 +702,15 @@ excepted version. Audit errors, unknown chains and expired exceptions fail. A ne upstream package release also fails and requires review. Trivy remains unchanged. This mechanism requires npm audit report version 2 and a package-path lockfile; unsupported reports fail. BATS and Node are required for the audit regression tests. + +## Security + +Report vulnerabilities privately; see [SECURITY.md](SECURITY.md). + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md). + +## License + +[GPL-3.0](LICENSE) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..c622d02 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,56 @@ +# Security Policy + +## Reporting a vulnerability + +**Do not open a public issue for a security problem.** + +Report it privately through GitHub's +[private vulnerability reporting](https://github.com/KofTwentyTwo/Munitor/security/advisories/new) +(the repository's **Security** tab, then **Report a vulnerability**). If that form is +unavailable, contact the maintainer privately through GitHub before disclosing anything. + +Security contact: **James Maes** ([@KofTwentyTwo](https://github.com/KofTwentyTwo)), +maintainer. + +Please include the affected orb version, the `.munitor.yml` pipeline type, how to +reproduce it, and the impact you see. Never include secrets or CircleCI context values. + +## What to expect + +KofTwentyTwo follows coordinated vulnerability disclosure. Timeframes are counted +from the day the report arrives and come from the +[KofTwentyTwo security program](https://github.com/KofTwentyTwo/standards/blob/main/policies/security-program.md): + +| Step | Target | +| --- | --- | +| Acknowledge the report | 7 days | +| Assess it: confirm or reject, with a severity | 14 days | +| Release a fix for a CRITICAL vulnerability | 7 days | +| Release a fix for a HIGH vulnerability | 30 days | +| Release a fix for a MEDIUM vulnerability | 90 days | +| Release a fix for a LOW vulnerability | The next release | +| Publish the advisory (GitHub Security Advisory, CVE where applicable) | When the fix ships, or 90 days after the report, whichever comes first | + +Reporters are credited in the advisory unless they ask not to be. + +## Supported versions + +| Version | Security fixes | +| --- | --- | +| 0.3.x (`kof22/munitor@0.3`) | Yes | +| Older | No; upgrade to the latest release | + +Only the latest minor line receives fixes; a line ends when the next minor version is +published to the CircleCI orb registry. + +## Scope notes + +Munitor runs inside the consumer's CircleCI project with the consumer's contexts +(registry, GitHub, SonarCloud, NVD). Issues that let a generated pipeline leak context +secrets, push an image or GitOps change for the wrong commit, or bypass a security +gate are in scope. + +## Published vulnerabilities + +Fixed vulnerabilities are published as +[GitHub Security Advisories](https://github.com/KofTwentyTwo/Munitor/security/advisories).