Build and Deployment
This document covers the build pipeline for Kotlin-based components and the Helm-based deployment mechanism used to install components into Kubernetes environments.
Architectural Context
Section titled “Architectural Context”The build and deployment pipeline is an architectural element of the platform, not merely an operational convenience. It bridges the Source/Development viewpoint (repositories, branches, PRs) and the Runtime viewpoint (environments, pods, services) through a deterministic sequence of transformations.
Four Purposes
Each deployment target serves a distinct role in the development lifecycle:
| Purpose | Infrastructure | URL | Role |
|---|---|---|---|
| Personal | Local machine / Docker Desktop | localhost | Short development loop; no shared state |
| Development | Alpha002/dev | dev.app.arda.cards | Integration testing on production-like infrastructure |
| Stage | Alpha002/stage | stage.app.arda.cards | Regression testing before production promotion |
| Production | Alpha001/prod | app.arda.cards | Business operations |
Pipeline Guarantees
The pipeline enforces these invariants across all purposes:
- Reproducibility: Every artifact is built from a tagged commit; the same commit always produces the same artifact.
- Immutability: Published artifacts (Docker images, Helm charts) are never overwritten; versions are monotonically increasing.
- Traceability: Every deployed artifact can be traced back to its source commit, PR, and build run.
- Isolation: Each purpose has its own infrastructure partition; a failed dev deployment cannot affect production.
Promotion Path
Artifacts flow through purposes in order: Personal → Development → Stage → Production. Promotion between purposes is gated by automated checks (tests, linting) and manual approval (deployment request workflows).
These guarantees are what make the pipeline an architectural element: they define the system’s change propagation model and are as load-bearing as the module boundaries or the persistence layer.
Build Pipeline (Gradle)
Section titled “Build Pipeline (Gradle)”Each component repository uses Gradle with Kotlin DSL (build.gradle.kts).
Standard Build Targets
Section titled “Standard Build Targets”| Target | Description |
|---|---|
./gradlew clean build | Compile, run unit tests, assemble JARs |
./gradlew clean helmInstallToLocal | Deploy to local Kubernetes cluster (dev only) |
./gradlew clean dockerBuild | Build Docker image |
./gradlew clean dockerPush | Push image to the artifact registry |
Configuration Linting
Section titled “Configuration Linting”The lintValues map in build.gradle.kts provides placeholder values for all application.conf keys that are injected at deploy time. This allows ./gradlew build to succeed without a live environment:
val lintValues = mapOf( "path.to.key.extras.some.myKey" to "lint-value-placeholder")Helm Deployment
Section titled “Helm Deployment”Components are deployed as Helm charts. The helm-deploy-pipeline-action GitHub Action is the standard deployment mechanism.
Chart Structure
Section titled “Chart Structure”src/helm/├── Chart.yaml├── values.yaml├── read-cloudformation-values.cmd # Maps CFn exports to Helm values└── templates/ ├── deployment.yaml ├── service.yaml ├── ingress.yaml └── configmap.yamlResolving CloudFormation Outputs
Section titled “Resolving CloudFormation Outputs”At deploy time, read-cloudformation-values.cmd reads CloudFormation exports from the target Infrastructure and Partition and writes them into Helm values:
readExport path.to.key.extras.some.myKey ${PURPOSE}-API-MyKeyThe configmap.yaml template injects the resolved values into the component’s application.conf at container start:
override.properties: | path.to.key.extras.some.myKey={{ .Values.system.reference.item.extras.myKey | required "myKey" }}Helm Value Sources
Section titled “Helm Value Sources”| Source | Values |
|---|---|
values.yaml | Default / static configuration |
read-cloudformation-values.cmd | Environment-specific runtime values (DB endpoints, API URLs, Cognito config) |
| GitHub Actions secrets | Credentials and sensitive values |
Environment Configuration Pattern
Section titled “Environment Configuration Pattern”To add a new configuration key from a CloudFormation export:
- Add
readExportline insrc/helm/read-cloudformation-values.cmd - Add the key with empty default in
application.conf - Add the key injection in
configmap.yaml - Add the key with a placeholder to
lintValuesinbuild.gradle.kts - Run
./gradlew clean buildto verify lint passes - Run
./gradlew clean helmInstallToLocalto verify local deployment
Known Behaviors
Section titled “Known Behaviors”- First API test run flakiness: The first run against a freshly deployed pod may fail due to startup timing. Retry after 5 seconds before investigating.
- Upload test infrastructure: API tests for file upload features require S3/LocalStack. Pass
--ignore Uploadin environments without S3. - Mock cascade failures: Adding a method to a widely-mocked interface called from an
initblock causes widespread test failures. Fix by stubbing the new method in allmockk<ServiceType>()usages across the codebase.
Copyright: © Arda Systems 2025-2026, All rights reserved