Why manual only
Every pipeline here is declaredworkflow_dispatch, GitHub’s manually triggered workflow event. They never run on push and never on a schedule. The reason is cost safety: the interesting actions (apply, up, destroy) place real orders on a real Biznet GIO account, so they must never fire because of a commit. A push-triggered destroy is how you get an incident; a manual choice from the Actions tab is how you get a rehearsal.
The default action in each workflow is the safe one: validate for Terraform, build for Pulumi. Both need zero credentials.
The Terraform pipeline
The full file is ci.yml in biznetgio-example-terraform. From top to bottom:- Trigger and inputs. Two choice dropdowns:
example(a folder, orall) andaction(validate,plan,apply,destroy). The workflow file itself is the documentation; each input description says what needs credentials and what places real orders. - Permissions.
contents: read, the minimum a checkout needs. The workflow cannot write to the repo. - The
discoverjob. Turns theallchoice into a concrete list of the six folders and publishes it as a job output, which becomes the matrix in the next job. It is a small shell script, because GitHub matrices must be concrete lists. - The
runjob. A matrix over the chosen folders withfail-fast: false, so one broken example does not cancel the others. Steps:actions/checkout@v4gets the code.hashicorp/setup-terraform@v3installs Terraform.terraform fmt -checkfails the job if any file is not canonically formatted. This is the formatting gate from Conventions.terraform initdownloads the pinned provider from the registry.terraform validatetype checks the whole folder without any credentials or API calls.terraform planruns only when the action isplan,apply, ordestroy, with theBIZNETGIO_API_KEYsecret and the console password exposed as theTF_VAR_console_passwordenvironment variable. Fordestroyit plans with-destroy.terraform apply -auto-approveorterraform destroy -auto-approverun only when explicitly chosen.
BIZNETGIO_API_KEY for any action past validate, and EXAMPLE_CONSOLE_PASSWORD (mapped to TF_VAR_console_password, the Terraform convention for setting variables from the environment) for the folders that take a console password.
The Pulumi pipeline
The full file is ci.yml in biznetgio-example-pulumi. Same philosophy, one more dimension:- Trigger and inputs. Three dropdowns:
language,example, andaction(build,preview,up,destroy). - The
discoverjob. Cross-products the chosen language and example into a matrix of{language, example}pairs usingjq -nc. This is the sameallexpansion trick as the Terraform workflow, but the cross product meansallbyallfans out into 36 jobs. - The
runjob. A matrix over those pairs,fail-fast: false, with the working directory set to<language>/<example>. Steps:actions/checkout@v4.- Per-language toolchain and build, gated with
if:on the language:setup-node@v4with Node 20 plusnpm install && npx tsc --noEmitfor TypeScript;setup-python@v5with Python 3.11 pluspip install -r requirements.txt;setup-go@v5pinned to1.25.xplusgo build ./...;setup-dotnet@v4with .NET 8 plusdotnet build;setup-java@v4with Temurin 17 plusmvn -q compile; and a one-liner YAML parse for theyamllanguage. pulumi/actions@v7runs the chosen command (preview,up, ordestroy) against thedevstack in the example’s folder, only when the action is notbuild. It needsPULUMI_ACCESS_TOKEN(Pulumi Cloud auth) andBIZNETGIO_API_KEY(the provider reads it from the environment).
1.25.x exists because the Go SDK’s go.mod requires a recent toolchain; the comment in the workflow says to keep it in lockstep with the provider repo. If you bump Go in the examples, bump it there too.
Adding a new example or language
The matrix lists are hardcoded in the workflow files, so extending the matrix means editing the workflow in the same PR as the new folder:
Both workflows are plain YAML following the GitHub Actions workflow syntax. The
discover job pattern (compute a matrix in one job, consume it in the next via fromJson) is the official dynamic matrix recipe from the Actions documentation.
The docs site pipeline
Unlike the two example repos, the docs repo’s.github/workflows/ci.yml is not manual-dispatch-only - it runs automatically on every push to main and on every pull request, because there is nothing destructive to gate here, only a build check:
mint validate catches a broken docs.json or a page Mintlify can’t build; mint broken-links catches an internal link or anchor that doesn’t resolve, which is exactly the class of bug a [0]-heavy docs site full of cross-references is prone to. Neither step deploys anything - deployment is separate and handled by the Mintlify GitHub app, which builds and publishes to biznetgio.creations.ren whenever a commit lands on main, tracked in the repo’s deployments. Run both mint validate and mint broken-links locally before opening a PR - see Local setup - so CI is a confirmation, not the first time you find out something’s broken. If a deploy ever looks stale, the deployment history page shows the commit hash of the live site.