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.