Skip to main content

CI/CD Automation

Every mutating CLI command (install, upgrade, uninstall, module enable, module disable) supports the same pair of automation flags: --yes to skip confirmation prompts and --dry-run to preview without executing. Combined with deterministic exit codes, this makes the CLI straightforward to integrate into CI/CD pipelines.

warning

Prerequisites in CI/CD. The CLI's automatic tool installation (see Installation - Prerequisites) only works in interactive terminals. In CI/CD pipelines, scripts, and non-interactive environments, you must pre-install helm, kubectl, and docker before invoking the CLI. If a required tool is missing, the CLI exits with error code ILUM-001 instead of offering to install it. See the GitHub Actions workflow below for a complete example that includes the prerequisite install step.

Non-Interactive Mode

--yes / -y skips all confirmation prompts and accepts defaults. Use it when running from a script, cron job, or CI pipeline where there is no human to type "y":

$ ilum install --yes
$ ilum upgrade --version 6.8.0 --yes
$ ilum module enable airflow --yes
$ ilum module disable sql --yes
$ ilum uninstall --yes

--dry-run previews the Helm command and values diff without executing anything. The command prints exactly what it would do and exits with code 0:

$ ilum install --dry-run
╭─ Install Summary ────────────────────────────────────────╮
│ Release ilum │
│ Namespace default │
│ Chart ilum/ilum │
│ Version latest │
│ Modules core, gitea, jupyter, minio, mongodb, │
│ postgresql, ui │
│ Atomic True │
╰──────────────────────────────────────────────────────────╯

Command: helm install ilum ilum/ilum \
--namespace default \
--timeout 10m \
--atomic \
--set ilum-core.enabled=true \
--set ilum-ui.enabled=true \
--set mongodb.enabled=true \
--set postgresql.enabled=true \
--set minio.enabled=true \
--set ilum-jupyter.enabled=true \
--set gitea.enabled=true

ℹ Dry-run mode — no changes applied.

The same --dry-run flag works on upgrade, uninstall, module enable, and module disable:

$ ilum upgrade --version 6.8.0 --dry-run
$ ilum uninstall --dry-run
$ ilum module enable sql --dry-run
$ ilum module disable airflow --dry-run
note

--dry-run and --yes can be combined, but --dry-run always takes precedence. When both are present the command previews the change and exits without executing, regardless of --yes.

Machine-Readable Output

Use --output json, --output yaml, or --output csv for CI/CD-friendly output:

# JSON output for status
ilum status --output json

# Module list as YAML
ilum module list --output yaml

# Doctor results as JSON (failures only)
ilum doctor --output json --failures-only

# Resource usage as CSV for spreadsheet import
ilum top --output csv

# Quiet mode (suppress informational messages, JSON still emits)
ilum --quiet --output json module list

Available on: status, module list, module show, module status, doctor, history, values, diff, rollback, top.

The --output flag emits structured data to stdout while suppressing Rich formatting. Combined with --quiet, only the data payload is written -- no banners, spinners, or informational messages. This makes the output safe to pipe directly into jq, yq, or other processing tools:

# Extract pod names from status JSON
ilum status --output json | jq '.pods[].name'

# Count failing doctor checks
ilum doctor --output json | jq '[.checks[] | select(.status == "FAIL")] | length'

CSV output with --no-headers and --field-selector. For shell scripts and CI pipelines that process tabular data with standard Unix tools, the CSV output format is often the most convenient. Combine it with --no-headers to omit the header row and --field-selector to select specific columns:

# CSV output with all columns
$ ilum top --output csv
module,pods,cpu_used,cpu_req,cpu_pct,mem_used,mem_req,mem_pct
core,1,250m,500m,50%,512Mi,1Gi,50%
mongodb,1,80m,250m,32%,192Mi,512Mi,38%
...

# No header row — useful for piping to awk/cut
$ ilum top --output csv --no-headers
core,1,250m,500m,50%,512Mi,1Gi,50%
mongodb,1,80m,250m,32%,192Mi,512Mi,38%
...

# Select only specific fields
$ ilum top --output csv --field-selector module,cpu_used,mem_used
module,cpu_used,mem_used
core,250m,512Mi
mongodb,80m,192Mi
...

# Combine for minimal output suitable for parsing
$ ilum status --output csv --no-headers --field-selector name,phase
ilum-core-6f8b4c7d9-xk2pl,Running
ilum-ui-5c9d3a1b8-mn4qr,Running
...

The --no-headers and --field-selector flags work with all output formats (csv, json, yaml) and across all commands that support --output.

Scripting Patterns

Exit codes. The CLI uses two exit codes:

Exit CodeMeaning
0Success (or dry-run preview completed)
1Failure (Helm error, validation error, user abort)

All error handling follows the same pattern: the CLI catches IlumError exceptions, prints the error message and suggestion, then exits with code 1. This makes it safe to use standard shell error handling:

#!/bin/bash
set -euo pipefail

# Deploy and fail the script if install fails
ilum install --yes

# Check health after install
ilum doctor --check pods
ilum doctor --check release

echo "Deployment verified."

Combining with ilum doctor for gating:

# Gate a deployment on health checks
if ! ilum doctor --check cluster; then
echo "Cluster unreachable, aborting deploy" >&2
exit 1
fi

ilum upgrade --version "$TARGET_VERSION" --yes

Piping logs to files or monitoring tools:

# Capture recent logs for a post-deploy report
ilum logs core --tail 200 > /tmp/core-deploy.log

# Stream logs to a monitoring pipeline
ilum logs core --follow | your-log-forwarder --source ilum-core

Reading config values in scripts:

# Get the active profile's namespace
NS=$(ilum config show profiles.$(ilum config show active_profile).cluster.namespace)
echo "Deploying to namespace: $NS"

# Back up the config before modifying
cp "$(ilum config path)" "$(ilum config path).bak"

GitHub Actions Example Workflow

The following workflow demonstrates a complete CI/CD pipeline: dry-run validation on pull requests, deployment on merge to main, and tag-based upgrades for releases.

name: Ilum Deploy

on:
pull_request:
branches: [main]
push:
branches: [main]
tags: ["v*"]

env:
ILUM_RELEASE: ilum
ILUM_NAMESPACE: ilum-system

jobs:
# ── Dry-run on PRs ──────────────────────────────────────
validate:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install prerequisites (helm, kubectl)
run: |
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
curl -fsSLo /tmp/kubectl "https://dl.k8s.io/release/$(curl -fsSL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x /tmp/kubectl && sudo mv /tmp/kubectl /usr/local/bin/kubectl

- name: Install Ilum CLI
run: pip install ilum

- name: Configure kubeconfig
uses: azure/k8s-set-context@v4
with:
kubeconfig: ${{ secrets.KUBECONFIG }}

- name: Dry-run install
run: |
ilum install \
--release $ILUM_RELEASE \
--namespace $ILUM_NAMESPACE \
--dry-run

- name: Run doctor checks
run: |
ilum doctor \
--release $ILUM_RELEASE \
--namespace $ILUM_NAMESPACE

# ── Deploy on merge to main ─────────────────────────────
deploy:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install prerequisites (helm, kubectl)
run: |
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
curl -fsSLo /tmp/kubectl "https://dl.k8s.io/release/$(curl -fsSL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x /tmp/kubectl && sudo mv /tmp/kubectl /usr/local/bin/kubectl

- name: Install Ilum CLI
run: pip install ilum

- name: Configure kubeconfig
uses: azure/k8s-set-context@v4
with:
kubeconfig: ${{ secrets.KUBECONFIG }}

- name: Deploy Ilum
run: |
ilum install \
--release $ILUM_RELEASE \
--namespace $ILUM_NAMESPACE \
--yes

- name: Verify deployment
run: |
ilum doctor \
--release $ILUM_RELEASE \
--namespace $ILUM_NAMESPACE \
--check release
ilum doctor \
--release $ILUM_RELEASE \
--namespace $ILUM_NAMESPACE \
--check pods

# ── Upgrade on tag push ─────────────────────────────────
upgrade:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install prerequisites (helm, kubectl)
run: |
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
curl -fsSLo /tmp/kubectl "https://dl.k8s.io/release/$(curl -fsSL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x /tmp/kubectl && sudo mv /tmp/kubectl /usr/local/bin/kubectl

- name: Install Ilum CLI
run: pip install ilum

- name: Configure kubeconfig
uses: azure/k8s-set-context@v4
with:
kubeconfig: ${{ secrets.KUBECONFIG }}

- name: Extract version from tag
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> "$GITHUB_OUTPUT"

- name: Upgrade Ilum
run: |
ilum upgrade \
--release $ILUM_RELEASE \
--namespace $ILUM_NAMESPACE \
--version ${{ steps.version.outputs.VERSION }} \
--force-rollback \
--yes

- name: Post-upgrade health check
run: |
ilum doctor \
--release $ILUM_RELEASE \
--namespace $ILUM_NAMESPACE

- name: Capture deployment logs
if: always()
run: |
ilum logs core --tail 100 > /tmp/core-logs.txt || true
ilum logs ui --tail 100 > /tmp/ui-logs.txt || true

- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
with:
name: deployment-logs
path: /tmp/*-logs.txt

Key patterns in this workflow:

  • --dry-run on PRs lets reviewers see the exact Helm command and values diff in the CI output before merging.
  • --yes on merge runs the deployment non-interactively.
  • --force-rollback on tag upgrades ensures recovery from any stuck release state before applying the new version.
  • ilum doctor after every deploy catches problems early. Because each check exits with code 1 on failure, a failing check fails the CI job.
  • ilum logs with if: always() captures pod logs as artifacts even when a prior step fails, giving you diagnostic data without SSHing into the cluster.

GitOps-Style Upgrades

For teams that manage infrastructure declaratively, the ilum upgrade command supports a tag-based deployment pattern where the desired Ilum version is stored in a Git repository and applied by a CI/CD pipeline on every push.

Store the desired version in a file:

# ilum-version.env
ILUM_VERSION=6.8.0
ILUM_MODULES=core,ui,mongodb,postgresql,minio,jupyter,gitea,sql,airflow

Apply it in CI:

#!/bin/bash
set -euo pipefail

source ilum-version.env

# Convert comma-separated modules to repeated -m flags
MODULE_FLAGS=""
IFS=',' read -ra MODS <<< "$ILUM_MODULES"
for mod in "${MODS[@]}"; do
MODULE_FLAGS="$MODULE_FLAGS -m $mod"
done

# Dry-run first to surface breaking changes
ilum upgrade \
--version "$ILUM_VERSION" \
$MODULE_FLAGS \
--dry-run

# Apply the upgrade
ilum upgrade \
--version "$ILUM_VERSION" \
$MODULE_FLAGS \
--force-rollback \
--yes

# Verify
ilum doctor --check release
ilum doctor --check pods

Tag-triggered flow. The pattern from the Scripting Patterns section can be adapted for GitOps: push a Git tag like v6.8.0 to trigger the upgrade job, which extracts the version from the tag and passes it to ilum upgrade --version. This gives you an auditable, version-controlled deployment history where every production change maps to a Git tag.

Multi-environment promotion. Combine profiles with CI stages to promote versions across environments:

# Stage 1: Deploy to dev
ilum config set active_profile dev
ilum upgrade --version "$ILUM_VERSION" --yes
ilum doctor

# Stage 2: Promote to staging (after dev passes)
ilum config set active_profile staging
ilum upgrade --version "$ILUM_VERSION" --yes
ilum doctor

# Stage 3: Promote to production (after staging passes)
ilum config set active_profile production
ilum upgrade --version "$ILUM_VERSION" --force-rollback --yes
ilum doctor

Each profile points to a different Kubernetes context and namespace, so the same CLI commands work identically across all environments. The --force-rollback on production adds an extra safety net for the most critical environment.