Skip to content

Guides

This page applies the model to a real workflow. Open the guides overview

CI

Guide

In CI, envctl should act as an explicit validation and projection layer. The goal is not to “fix things quietly”, but to fail clearly when the selected environment is not valid.

The wrong mental model for CI

The most dangerous CI setup is usually the one that looks convenient:

  • values appear from unclear places
  • missing state gets patched silently
  • pipelines become harder to trust
  • failures turn into guesswork

That is exactly what envctl should help you avoid.

In CI, prefer explicit validation and narrow projection. Do not use CI as a place to invent local state or repair missing values interactively.

The default CI path

For the most common pipeline shape, validate the environment directly:

ci check
$ envctl check

That gives you the fastest explicit answer:

  • the selected environment satisfies the contract
  • or it does not

Use an explicit CI profile when you have one

If the pipeline should validate a dedicated CI-local value set, select it intentionally:

ci profile
$ ENVCTL_PROFILE=ci envctl check

Or, if your workflow prefers CLI flags globally:

explicit profile
$ envctl --profile ci check

The key thing is not which syntax you choose.

It is that the choice is explicit and easy to read later.

When CI needs the runtime environment, not just validation

Sometimes CI does not only need pass/fail validation. Sometimes the next step needs the resolved environment itself.

At that point, choose the narrowest handoff that fits the downstream tool.

Option 1: run the command directly

If the next step can receive environment variables through a subprocess, use run:

run
$ envctl run -- make integration-test
$ envctl run -- pytest

This is usually the cleanest option, because nothing has to be written to disk.

Option 2: export a dotenv payload

If a downstream step expects dotenv-style content, export it explicitly:

export
$ envctl export --format dotenv > /tmp/app.env

Use this when another tool wants a dotenv file-like handoff.

Option 3: select only part of the contract

If the pipeline only needs a subset, use selectors instead of projecting everything:

scoped ci
$ envctl --group Runtime check
$ envctl --set docker_runtime export --format dotenv
$ envctl --var DATABASE_URL inspect

That keeps CI tighter and easier to reason about.

What not to do in CI

There are a few patterns worth avoiding.

Do not use fill

fill is interactive by design.

That is useful locally, but wrong for CI. A pipeline should not pause and ask for missing values.

Do not treat generated files as the source of truth

If you use sync or exported dotenv output in CI, remember what it is:

  • projection output
  • not the source of truth
  • not a replacement for the contract or vault model

Do not hide profile selection

If your pipeline depends on ci, say so explicitly. Silent assumptions here make failures harder to debug.

A good CI outcome

A good CI integration with envctl produces one of two outcomes:

  • the pipeline passes because the selected environment satisfies the contract
  • the pipeline fails with explicit diagnostics about what is missing or invalid

That is the right kind of strictness.

check

See the command that acts as the fast validation gate.

Open check reference

run

Use subprocess projection when the next CI step can consume env vars directly.

Open run reference

Projection

Understand the difference between validating, exporting, syncing, and running.

Read about projection