Skip to content

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.

PromptHow to avoid it
Source nameAlways pass --source <name>
Tag selection (GitLab)Always pass --tag <ref>
Missing variablesPass every variable with --var, or give it a sidecar default
Deletion confirmationsPass --auto-approve on source remove, theme remove, cache clear, history clear

A fully non-interactive apply:

Terminal window
deckctl template apply go-service ./out \
--source work \
--tag v1.2.3 \
--var app_name=billing \
--var replicas=3 \
-o json

CI runners rarely have a useful $HOME. Every path deckctl reads is overridable, by flag or environment variable:

WhatFlagEnvironment variableDefault
Sources config--configDECKCTL_CONFIG~/.config/deckctl/config.yaml
Themes--themeDECKCTL_THEME~/.config/deckctl/themes.yaml
History DB--historyDECKCTL_HISTORY~/.config/deckctl/history.db
Cache directory--cache-dirDECKCTL_CACHE_DIR~/.cache/deckctl/

The flag always wins over the environment variable.

Commit a small config to the repo that runs the pipeline:

ci/deckctl.yaml
sources:
- name: work
type: gitlab
url: https://gitlab.com
group: platform/templates
token_env: DECKCTL_TOKEN
cache_ttl: 24h
default_source: work
Terminal window
export DECKCTL_CONFIG="$CI_PROJECT_DIR/ci/deckctl.yaml"
export DECKCTL_TOKEN="$TEMPLATES_READ_TOKEN"
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=3

Caching DECKCTL_CACHE_DIR between jobs means repeat runs revalidate with a conditional request instead of re-downloading the archive. See Caching.

deckctl validate exits non-zero when any check fails, which makes it a decent pipeline gate:

Terminal window
deckctl validate -o json

Two things that fail quietly and are worth guarding against:

  • A --tag that 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_env that 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:

Terminal window
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; }
Terminal window
# Every template in a source
deckctl 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 prompt
missing=$(deckctl template variables go-service --source work -o json \
| jq -r '.[] | select(.default == null) | .name')
[ -z "$missing" ] || { echo "would prompt for: $missing"; exit 1; }

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:

Terminal window
deckctl template apply ... --history /tmp/deckctl-history.db

Copier’s secret: true is not honoured by deckctl — see Copier compatibility.