GitHub Actions
A typical workflow: provision infrastructure with formae apply, capture a property from the new resource, deploy an app that needs it, verify.
The whole workflow is itself a GHA::Repo::Workflow resource. One formae apply lays down the workflow file, the variables and secrets it depends on, and the environments it deploys into.
The workflow as a forma
import "@gha/repo/repoworkflow.pkl" as workflow
import "@com.github.actions/Workflow.pkl" as GHAWorkflow
local deployWorkflow = new workflow.Workflow {
label = "deploy"
path = ".github/workflows/deploy.yml"
name = "Deploy"
on = new GHAWorkflow.On { workflow_dispatch {} }
permissions = new GHAWorkflow.Permissions {
`id-token` = "write"
contents = "read"
}
jobs {
["provision"] {
`runs-on` = "ubuntu-latest"
outputs {
["db_host"] = "${{ steps.capture.outputs.db_host }}"
}
steps {
new { uses = "actions/checkout@v4" }
new { name = "Install formae"; run = "/bin/bash -c \"$(curl -fsSL https://hub.platform.engineering/setup/formae.sh)\"" }
new {
name = "Provision"
run = "formae apply --mode reconcile --yes --watch infra/database.pkl"
}
new {
id = "capture"
name = "Capture DB host"
run = #"""
DB_HOST=$(formae inventory resources \
--query='label:pg-server' \
--output-consumer=machine \
| jq -r '.Resources[0].ReadOnlyProperties.fullyQualifiedDomainName')
echo "db_host=$DB_HOST" >> $GITHUB_OUTPUT
"""#
}
}
}
["deploy"] {
needs { "provision" }
`runs-on` = "ubuntu-latest"
steps {
new {
name = "Deploy"
run = "deploy_cmd --db-host=${{ needs.provision.outputs.db_host }}"
}
}
}
["verify"] {
needs { "deploy" }
`runs-on` = "ubuntu-latest"
steps {
new { name = "Smoke test"; run = "./run-smoke-tests.sh" }
}
}
}
}
The provision job declares db_host in its outputs, points at a step output via ${{ steps.capture.outputs.db_host }}, and the capture step writes to $GITHUB_OUTPUT. The deploy job consumes via needs.provision.outputs.db_host.
Apply
export GHA_OWNER=my-org
export GHA_REPO=my-repo
formae apply --mode reconcile --watch main.pkl
The workflow file lands in the repo. Trigger it from the Actions tab.
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
- GitHub Actions plugin for the full resource catalog
- GitLab CI for the same pattern with the GitLab plugin