Using deckctl in CI
deckctl prompts whenever something is missing. In a pipeline that means a hung job, so the rule is simple: supply everything up front.
The checklist
Section titled “The checklist”| Prompt | How to avoid it |
|---|---|
| Source name | Always pass --source <name> |
| Tag selection (GitLab) | Always pass --tag <ref> |
| Missing variables | Pass every variable with --var, or give it a sidecar default |
| Deletion confirmations | Pass --auto-approve on source remove, theme remove, cache clear, history clear |
A fully non-interactive apply:
deckctl template apply go-service ./out \ --source work \ --tag v1.2.3 \ --var app_name=billing \ --var replicas=3 \ -o jsonConfig without a home directory
Section titled “Config without a home directory”CI runners rarely have a useful $HOME. Every path deckctl reads is
overridable, by flag or environment variable:
| What | Flag | Environment variable | Default |
|---|---|---|---|
| Sources config | --config | DECKCTL_CONFIG | ~/.config/deckctl/config.yaml |
| Themes | --theme | DECKCTL_THEME | ~/.config/deckctl/themes.yaml |
| History DB | --history | DECKCTL_HISTORY | ~/.config/deckctl/history.db |
| Cache directory | --cache-dir | DECKCTL_CACHE_DIR | ~/.cache/deckctl/ |
The flag always wins over the environment variable.
Commit a small config to the repo that runs the pipeline:
sources: - name: work type: gitlab url: https://gitlab.com group: platform/templates token_env: DECKCTL_TOKEN cache_ttl: 24hdefault_source: workexport DECKCTL_CONFIG="$CI_PROJECT_DIR/ci/deckctl.yaml"export DECKCTL_TOKEN="$TEMPLATES_READ_TOKEN"A GitLab CI example
Section titled “A GitLab CI example”scaffold: image: alpine:3 variables: DECKCTL_VERSION: v0.2.0 DECKCTL_CONFIG: "$CI_PROJECT_DIR/ci/deckctl.yaml" DECKCTL_CACHE_DIR: "$CI_PROJECT_DIR/.deckctl-cache" cache: key: deckctl-templates paths: - .deckctl-cache before_script: - apk add --no-cache curl tar - | curl -fsSLO "https://gitlab.com/api/v4/projects/80491491/packages/generic/deckctl/${DECKCTL_VERSION}/deckctl_Linux_x86_64.tar.gz" tar -xzf deckctl_Linux_x86_64.tar.gz -C /usr/local/bin deckctl script: - export DECKCTL_TOKEN="$TEMPLATES_READ_TOKEN" - deckctl validate - | deckctl template apply go-service ./generated \ --source work \ --tag v1.2.3 \ --var app_name="$CI_PROJECT_NAME" \ --var replicas=3Caching DECKCTL_CACHE_DIR between jobs means repeat runs revalidate with a
conditional request instead of re-downloading the archive. See
Caching.
Exit codes and failure modes
Section titled “Exit codes and failure modes”deckctl validate exits non-zero when any check fails, which makes it a decent
pipeline gate:
deckctl validate -o jsonTwo things that fail quietly and are worth guarding against:
-
A
--tagthat doesn’t exist falls back to the project’s default branch with only a stderr warning. Verify the tag first if the pin matters:Terminal window deckctl template list --source work -o json >/dev/null # forces auth + reachability -
A
token_envthat resolves empty downgrades to unauthenticated requests with a stderr warning rather than failing. Assert the variable is set:Terminal window : "${DECKCTL_TOKEN:?TEMPLATES_READ_TOKEN is not set}"
Capturing stderr and failing on warning: is a reasonable belt-and-braces
approach:
deckctl template apply go-service ./out --source work --tag v1.2.3 \ --var app_name=billing 2> >(tee /tmp/deckctl.err >&2)grep -q '^warning:' /tmp/deckctl.err && { echo "deckctl warned; failing"; exit 1; }Scripting with JSON
Section titled “Scripting with JSON”# Every template in a sourcedeckctl template list --source work -o json | jq -r '.[].name'
# Variables that have no default (i.e. would prompt)deckctl template variables go-service --source work -o json \ | jq -r '.[] | select(.default == null) | .name'
# Fail if any variable would promptmissing=$(deckctl template variables go-service --source work -o json \ | jq -r '.[] | select(.default == null) | .name')[ -z "$missing" ] || { echo "would prompt for: $missing"; exit 1; }Secrets
Section titled “Secrets”Don’t put secret values in --var: they end up in the shell history, the CI job
log, and the apply history database. Keep secrets out of templates and inject
them at deploy time instead. If a template genuinely needs one, point
--history at a throwaway path so the record doesn’t persist:
deckctl template apply ... --history /tmp/deckctl-history.dbCopier’s secret: true is not honoured by deckctl — see
Copier compatibility.