Development Tooling¶
MkDocs Material + Mermaid¶
What: This documentation site — Markdown in docs/, built by MkDocs with the
Material theme and Mermaid for diagrams, validated with mkdocs build --strict.
Why: The course requires a living, versioned report; docs-as-code in the same repo gives that for free (Git history is the changelog, broken links fail the build). Mermaid keeps every context map, sequence diagram and topology picture as reviewable text instead of binary images.
AWS (S3 + CloudFront)¶
What: Static hosting for the built SPA with the documentation site mounted under
/docs. scripts/build-aws.sh produces a single _site/ bundle (app shell at the
root, the strict MkDocs build under _site/docs) that is uploaded to an S3 bucket and
served through a CloudFront distribution. CloudFront rewrites unmatched routes to
index.html so the SPA handles its own client-side routing.
Why: S3 + CloudFront keeps fast, low-cost static hosting within the €500 project budget (well inside the AWS free tier at this traffic), and one build ships both the app shell and the report it documents.
Make¶
What: A root Makefile exposing the repo's task vocabulary: make web,
make docs, make up / make down (compose), make check, make smoke and
make contracts-gen.
Why: With two language toolchains, a docs pipeline and a compose stack in one
repository, a single self-documenting entry point (make help) is worth more than
remembering four tools' invocations. CI calls the same Make targets used locally, so a
check should not behave differently after it is pushed.
GitHub Actions CI¶
What: Seven small workflows under .github/workflows/, each responsible for one
kind of validation:
| Workflow | When it runs | What it checks |
|---|---|---|
| Source CI | Pull requests and pushes to main, only when application or source-tooling files change |
Linting, unit tests, type checks and the web build |
| Docs and Contracts | Pull requests and pushes to main, only when docs, contracts or Compose change |
JSON Schemas, generated contract types, strict MkDocs build and Compose configuration |
| Secret Scan | Every pull request and push to main |
Complete Git history with the license-free Gitleaks CLI |
| Docs Guard | Every pull request | A PR introducing a feature (feat: title or commit) must change documentation — docs/, a root *.md, or a service openapi.yaml; the docs-exempt label overrides deliberately (scripts/docs-guard.sh) |
| Container Smoke Test | Every Monday at 06:00 UTC or manually | Builds the full Compose stack, waits for every container to become healthy and checks the gateway |
| Dependency Vulnerability Scan | Every Monday at 06:00 UTC or manually | make vuln-check: govulncheck on every Go module and npm audit on every npm package |
| Semantic Release | Every push to main or manually |
Reads conventional commits, updates CHANGELOG.md, creates a version tag and publishes a GitHub release |
Why: Fast checks should protect normal development without rebuilding the entire platform after every documentation or frontend edit. Path filters avoid unrelated workflows, while concurrency cancellation stops an older run when a newer commit is pushed to the same branch.
The workflows reuse Go, npm, Python and installed-dependency caches. Cache keys include
the relevant lockfiles, so a dependency change creates a fresh cache instead of using
stale packages. The expensive container smoke test intentionally runs rarely; it can
still be started from the GitHub Actions Run workflow button or locally with
make smoke.
Semantic-release resumes from the latest reachable v* tag. A fix: commit produces
a patch release, feat: produces a minor release, and a breaking change produces a
major release. Other commit types are inspected but do not create a release. This
repository is not published to npm; the release artifact is the GitHub release, its
tag and the generated changelog.
Action pinning¶
What: Every third-party action is referenced by its full commit SHA instead of a version tag, with the tag kept as a trailing comment:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
Why: A tag like v4 is a mutable pointer: its maintainer can move it to any
commit, and the workflow runs whatever the tag points to at that moment. That is how
the March 2025 tj-actions/changed-files compromise leaked CI secrets from thousands
of repositories — the attackers retargeted existing version tags. A commit SHA is
immutable, so the executed code can never change silently. This matters most for the
release workflow, which holds a contents: write token. Dependabot reads the trailing
tag comment and keeps opening update PRs that bump the SHA and the comment together,
so pinning costs no maintenance — only the explicit approval of each update, which is
the point.
golangci-lint and architecture guardrails¶
What: make lint goes beyond formatting: golangci-lint (pinned in the Makefile,
configured in .golangci.yml) runs staticcheck, gosec, errorlint and misspell over
every Go module, and its depguard rules forbid internal/domain and
internal/application packages from importing outer layers. make check-boundaries
fails if any Go service imports another service's packages, and ESLint
no-restricted-imports rules enforce the same layer and service boundaries for the
TypeScript services.
Why: The hexagonal layout and contracts-only sharing rules (ADR-008, ADR-010) are
only real if a violation fails a build; with AI-assisted development, plausible-looking
code that quietly crosses a boundary is the main erosion risk. The summarized rules
live in the repository-root AGENTS.md, so coding agents read the constraints before
touching the code, and CI rejects what slips through anyway.
Semantic-release¶
What: Automated versioning configured in .releaserc.json and executed by
.github/workflows/release.yml.
Why: Conventional commits already describe the impact of a change, so version
selection and release notes should not be repeated manually. On main,
semantic-release analyzes commits since the latest remote v* tag, updates
CHANGELOG.md, creates the next tag and publishes a GitHub release. The generated
changelog commit includes [skip ci] to avoid starting another CI cycle.
Triggering a major release¶
A major release communicates an incompatible change. Mark the commit as breaking using either conventional-commit form:
feat!: replace the public scan response
or add a breaking-change footer to a normal commit:
feat: replace the public scan response
BREAKING CHANGE: clients must now read product data from the passport field.
After that commit reaches main, semantic-release increments the major version, for
example from v1.4.2 to v2.0.0. Use a major release only when existing consumers
must change to remain compatible.
Git conventions¶
What: Conventional-commit messages (feat:, fix:, docs:, chore:) on a
trunk-ish flow with short-lived branches.
Why: Commit types make the history scannable and machine-releasable, and the course examiner reads the Git log as part of the living-document requirement.