GitLab CI
Same pattern as GitHub Actions: provision with formae apply, capture a property from the new resource, deploy, verify. The whole pipeline is a GitLab::Project::Pipeline resource.
GitLab CI passes values between jobs with dotenv artifacts instead of $GITHUB_OUTPUT. The bridge mechanism differs; the pattern is identical.
The pipeline as a forma
import "@gitlab/project/projectpipeline.pkl" as pipeline
import "@gitlab/ci/pipeline.pkl" as CI
local deployPipeline = new pipeline.Pipeline {
label = "deploy"
path = ".gitlab-ci.yml"
stages = new { "provision"; "deploy"; "verify" }
jobs {
["provision"] = new CI.Job {
stage = "provision"
image = "ubuntu:24.04"
script = new {
#"curl -fsSL https://hub.platform.engineering/setup/formae.sh | bash -s -- -y"#
"export PATH=/opt/pel/formae/bin:$PATH"
"formae apply --mode reconcile --yes --watch infra/database.pkl"
#"""
DB_HOST=$(formae inventory resources \
--query='label:pg-server' \
--output-consumer=machine \
| jq -r '.Resources[0].ReadOnlyProperties.fullyQualifiedDomainName')
echo "DB_HOST=$DB_HOST" >> deploy.env
"""#
}
artifacts = new CI.Artifacts {
reports = new CI.Reports { dotenv = "deploy.env" }
}
}
["deploy"] = new CI.Job {
stage = "deploy"
needs = new { "provision" }
script = new {
#"deploy_cmd --db-host=$DB_HOST"#
}
}
["verify"] = new CI.Job {
stage = "verify"
needs = new { "deploy" }
script = new { "./run-smoke-tests.sh" }
}
}
}
The capture step writes DB_HOST=... to a deploy.env file, and the job exposes that file as a dotenv artifact report. Downstream jobs that needs this one get $DB_HOST injected into their environment automatically.
Apply
export GITLAB_TOKEN=glpat-...
formae apply --mode reconcile --watch main.pkl
The pipeline file lands at .gitlab-ci.yml. Trigger it from the GitLab UI.
Full working example
infra-to-app provisions Azure PostgreSQL, deploys Miniflux, and verifies feeds end to end. Same pattern, complete Pkl.
What's next
- Pipeline stages for the bridge primitive
- GitLab plugin for the full resource catalog
- GitHub Actions for the same pattern with the GHA plugin