CI/CD#
The platform provides standardized CI/CD pipeline templates for all services. Templates handle the common stages — lint, test, build, push, deploy — so teams don’t write and maintain pipeline code themselves.
How it works#
When you push to your repository, GitHub Actions triggers the platform pipeline template via a reusable workflow. Your .platform.yml file controls what stages run and how.
flowchart TD
A([Push / PR]) --> B[Lint]
B --> C[Test]
C --> D[Build Docker image]
D --> E[Push to registry]
E --> F{Branch?}
F -->|main| G[Deploy to staging]
F -->|PR| H([PR check passes])
G --> I([Manual approval])
I --> J([Deploy to production])
style H fill:#2da44e,color:#fff
style J fill:#2da44e,color:#fff
The pipeline template lives in .github/workflows/platform-pipeline.yml in this repository. It is the single source of truth for all pipeline logic — when the platform team updates it, all services pick up the change automatically.
.platform.yml reference#
# .platform.yml — place at the root of your service repository
platform:
version: "1" # schema version, always "1" for now
pipeline:
language: go # go | python | java | node | rust
test: true # run tests (default: true)
lint: true # run linter (default: true)
test_command: "" # override default test command (optional)
image:
registry: registry.mycorp.internal
name: your-service # image name, usually matches service name
deploy:
staging:
auto: true # deploy on every merge to main
namespace: yourteam # override namespace from service YAML (optional)
production:
auto: false # production always requires manual approval
namespace: yourteam
Pipeline stages#
1. Lint#
Runs the language-specific linter. Failures block the merge.
Language |
Linter |
|---|---|
Go |
|
Python |
|
Node |
|
Java |
|
Rust |
|
To skip lint (not recommended): set pipeline.lint: false in .platform.yml.
2. Test#
Runs the test suite. The platform injects test database and cache services automatically. Your tests can connect to PostgreSQL at localhost:5432 (database test) and Redis at localhost:6379.
Default test commands by language:
go test ./... -race -coverprofile=coverage.out
pytest --tb=short -q
npm test
./gradlew test
Override the default with pipeline.test_command in .platform.yml. To skip injected services:
pipeline:
test_services:
postgres: false
redis: true
3. Build#
Builds a Docker image from your Dockerfile. The platform passes the following build args:
Arg |
Value |
|---|---|
|
RFC 3339 timestamp |
|
Short commit SHA |
|
Tag or |
Use them to embed version info:
ARG GIT_COMMIT
ARG VERSION
LABEL org.opencontainers.image.revision=$GIT_COMMIT \
org.opencontainers.image.version=$VERSION
4. Push#
On a successful build, the image is pushed to registry.mycorp.internal/{service-name}:{tag}.
Tags follow this convention:
Trigger |
Image tag |
|---|---|
Push to |
|
Git tag |
|
Pull request |
|
5. Deploy to staging#
Happens automatically after a successful push to main if deploy.staging.auto: true. The platform runs:
helm dependency update deploy/helm
helm upgrade --install {service-name} ./deploy/helm \
-n {namespace} \
-f deploy/helm/values.yaml \
-f deploy/helm/values.staging.yaml \
--set deployment.image.tag={tag}
helm dependency update pulls the latest OCI chart from registry.mycorp.internal/charts before each deploy.
Deployment status is reported back to the GitHub commit via a deployment status check.
6. Deploy to production#
Production deployments require a manual approval step in the GitHub Actions UI. After approval:
helm dependency update deploy/helm
helm upgrade --install {service-name} ./deploy/helm \
-n {namespace} \
-f deploy/helm/values.yaml \
--set deployment.image.tag={tag}
Production deployments trigger a notification to the service’s Slack channel.
Secrets#
Secrets are stored in Vault and injected into the pipeline at runtime. Do not put secrets in .platform.yml or in repository environment variables.
To add a secret:
Store it in Vault at
secret/cicd/{service-name}/{key}Reference it in your pipeline config:
pipeline:
secrets:
- name: DATABASE_URL
vault_path: secret/cicd/your-service/database-url
The secret is available as an environment variable during the test and deploy stages.
Caching#
Dependencies are cached between pipeline runs. Cache keys are based on the lock file for each language:
Language |
Lock file |
|---|---|
Go |
|
Python |
|
Node |
|
If you need to force a cache reset, add [no-cache] to your commit message.
Troubleshooting#
Pipeline does not trigger after pushing .platform.yml
Check that your repository has the platform GitHub App installed. See #platform-access.
Test stage fails with “connection refused” for the database
Verify that your test code connects to localhost and not a container name. The platform services are exposed on localhost inside the CI runner.
Image push fails with “unauthorized”
The pipeline uses a service account token rotated weekly. If your last pipeline run was more than a week ago, re-trigger the pipeline — the new token will be picked up automatically.
Deployment is stuck in “Pending”
Check kubectl get pods -n {namespace} for ImagePullBackOff or CrashLoopBackOff. The most common cause is a missing secret or a misconfigured health check. See Kubernetes health checks for health check requirements.