Skip to main content

Zero-config Flux to Docker Hardened Images chart migration reporter

Project description

Steelman

[!WARNING] This project is vibe-coded. Assume there are bugs, rough edges, missing validation, and incorrect assumptions. Review the output before relying on it.

steelman is a reporting CLI for Flux-managed Helm releases.

It scans Flux resources from:

  • live Kubernetes clusters
  • Git repositories containing Flux manifests
  • or both

It compares those releases against Docker Hardened Images catalog data and reports one of:

  • already using a DHI chart
  • a DHI chart replacement is available
  • DHI image replacements are available inside the chart values
  • no DHI replacement was found

Scope

Current scope:

  • Flux HelmRelease
  • Flux HelmRepository
  • Flux OCIRepository

Not currently supported:

  • plain Helm releases that are not represented as Flux resources
  • Argo CD applications
  • direct workload or pod inspection
  • valuesFrom resolution

What It Checks

For each Flux release, the tool:

  1. identifies the current chart source
  2. checks whether a DHI chart exists
  3. if not, tries to resolve chart defaults with helm show values
  4. merges inline HelmRelease.spec.values
  5. extracts image references from the effective values
  6. checks whether matching DHI images exist

Chart replacement takes precedence over image replacement.

Requirements

  • Python 3.12+
  • uv
  • helm if you want image replacement analysis
  • kubeconfig access if you want live cluster scanning

Usage

Using the published package:

uvx steelman

Common variants:

uvx steelman --mode cluster
uvx steelman --mode git --repo /path/to/gitops-repo
uvx steelman --contexts prod-eu,prod-us
uvx steelman --output-dir reports
uvx steelman --offline
uvx steelman --skip-image-analysis
uvx steelman --include-already-migrated
uvx steelman --helm-bin helm
uvx steelman --image-match-threshold 0.75
uvx steelman --aliases ./aliases.yaml
uvx steelman --verbose
uvx steelman --issue

Use the ci subcommand to run with CI defaults (generates steelman-issue.md by default):

uvx steelman ci
uvx steelman ci --mode git --repo . --output-dir reports
uvx steelman ci --no-issue

Defaults

If run without flags:

  • --mode both
  • scans . recursively for Flux manifests
  • reads kubeconfig from the default location
  • uses all contexts if kubeconfig contains 10 or fewer contexts
  • otherwise uses the current context only
  • omits the already_dhi_chart section from the Markdown report
  • writes:
    • ./steelman.md
    • ./steelman.json

[!NOTE] BREAKING CHANGE (from previous versions): steelman-issue.md is no longer written by default. Use --issue to generate it, or use the steelman ci subcommand which generates it by default.

The ci subcommand uses the same defaults with one difference:

  • writes ./steelman-issue.md in addition to the above (use --no-issue to suppress)

Output

The Markdown report contains:

  • summary
  • hardened chart available
  • hardened images available
  • no DHI replacement
  • scan notes

Use --include-already-migrated if you want the Markdown report to include releases that are already using DHI charts.

The JSON report contains:

  • catalog metadata
  • inventory summary
  • recommendation counts
  • per-release results
  • recorded errors

The issue report contains:

  • a CI-managed DHI implementation status issue body
  • pending chart and image migrations as checklist items
  • already-migrated DHI releases as checked items
  • scan errors and a compact summary for a single long-lived issue

CI Examples

These examples assume:

  • the repository is a Flux v2 GitOps repository
  • the scan should use Git manifests, not live cluster access
  • the generated report should be kept as an artifact or posted into an issue

For Git-only scans, use:

uvx steelman ci --mode git --repo . --output-dir reports

GitHub Actions

This example runs on a schedule and on manual dispatch, scans the current Flux repo, uploads the report artifacts, and creates or updates a single issue named DHI implementation status.

name: Steelman Report

on:
    schedule:
        - cron: "0 6 * * 1"
    workflow_dispatch:

permissions:
    contents: read
    issues: write

jobs:
    steelman:
        runs-on: ubuntu-latest
        steps:
            - name: Checkout repository
              uses: actions/checkout@v6

            - name: Set up uv
              uses: astral-sh/setup-uv@v7

            - name: Run steelman
              run: uvx steelman ci --mode git --repo . --output-dir reports

            - name: Upload reports
              uses: actions/upload-artifact@v6
              with:
                  name: steelman-report
                  path: reports/*
                  if-no-files-found: error

            - name: Create or update issue
              env:
                  GH_TOKEN: ${{ github.token }}
              run: |
                  issue_number="$(gh issue list --state open --label steelman --search 'DHI implementation status in:title' --json number --jq '.[0].number')"
                  if [ -n "$issue_number" ]; then
                    gh issue edit "$issue_number" --title "DHI implementation status" --body-file reports/steelman-issue.md
                  else
                    gh issue create --title "DHI implementation status" --label steelman --body-file reports/steelman-issue.md
                  fi

Notes:

  • this scans desired state from Git only
  • reports/steelman.md, reports/steelman.json, and reports/steelman-issue.md are uploaded as artifacts
  • steelman ci generates steelman-issue.md by default; use steelman ci --no-issue to skip it
  • the issue update step requires issues: write

Woodpecker CI

This example runs the same Git-only scan and stores the generated files in the workspace. If your Woodpecker setup exposes a GitHub token, you can also open or update the same long-lived issue with gh.

steps:
    steelman:
        image: ghcr.io/astral-sh/uv:python3.13-bookworm
        commands:
            - uvx steelman ci --mode git --repo . --output-dir reports

    steelman-report:
        image: ghcr.io/astral-sh/uv:python3.13-bookworm
        environment:
            GITHUB_TOKEN:
                from_secret: github_token
        commands:
            - apt-get update
            - apt-get install -y gh
            - issue_number="$(gh issue list --repo "$CI_REPO" --state open --label steelman --search 'DHI implementation status in:title' --json number --jq '.[0].number')"
            - |
                if [ -n "$issue_number" ]; then
                  gh issue edit "$issue_number" --repo "$CI_REPO" --title "DHI implementation status" --body-file reports/steelman-issue.md
                else
                  gh issue create --repo "$CI_REPO" --title "DHI implementation status" --label steelman --body-file reports/steelman-issue.md
                fi

Notes:

  • the second step is optional
  • if you do not want issue creation, keep only the steelman step
  • artifact handling in Woodpecker depends on your runner and storage configuration, so this example leaves the report in reports/

GitLab CI

This example runs the same Git-only scan in a Flux repository, stores the generated reports as job artifacts, and optionally opens or updates a single GitLab issue for DHI implementation tracking.

stages:
    - report

steelman:
    stage: report
    image: ghcr.io/astral-sh/uv:python3.13-bookworm
    script:
        - uvx steelman ci --mode git --repo . --output-dir reports
    artifacts:
        when: always
        paths:
            - reports/steelman.md
            - reports/steelman.json
            - reports/steelman-issue.md
        expire_in: 7 days

steelman_issue:
    stage: report
    image: debian:bookworm-slim
    needs:
        - job: steelman
          artifacts: true
    rules:
        - if: $GITLAB_TOKEN
    script:
        - apt-get update
        - apt-get install -y curl jq
        - |
            issue_iid="$(curl --silent --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
              "$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues?state=opened&labels=steelman&search=DHI%20implementation%20status" | jq -r '.[0].iid // empty')"
        - |
            report_body="$(jq -Rs . < reports/steelman-issue.md)"
            if [ -n "$issue_iid" ]; then
              curl --silent --request PUT \
                --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
                --header "Content-Type: application/json" \
                --data "{\"title\":\"DHI implementation status\",\"description\":$report_body}" \
                "$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$issue_iid"
            else
              curl --silent --request POST \
                --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
                --header "Content-Type: application/json" \
                --data "{\"title\":\"DHI implementation status\",\"description\":$report_body,\"labels\":\"steelman\"}" \
                "$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues"
            fi

Notes:

  • the steelman_issue job is optional
  • set GITLAB_TOKEN as a masked CI variable if you want issue creation
  • if you only want artifacts, keep just the steelman job

Cluster Mode In CI

If you want to scan live clusters instead of Git manifests, switch to:

uvx steelman ci --mode cluster --contexts prod-eu,prod-us --output-dir reports

That requires kubeconfig access in the CI environment. For a Flux repository, Git mode is usually the simpler starting point because it only scans desired state from the repo.

Current Limitations

  • valuesFrom is detected but not resolved
  • image analysis depends on helm show values
  • some OCI chart sources may fail helm show values depending on how the chart is published
  • matching is heuristic and may still need alias tuning for edge cases

Development

uv sync --group dev
uv run ruff check .
uv run ruff format --check .
uv run ty check
uv run pytest

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

steelman-0.4.1.tar.gz (20.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

steelman-0.4.1-py3-none-any.whl (27.2 kB view details)

Uploaded Python 3

File details

Details for the file steelman-0.4.1.tar.gz.

File metadata

  • Download URL: steelman-0.4.1.tar.gz
  • Upload date:
  • Size: 20.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for steelman-0.4.1.tar.gz
Algorithm Hash digest
SHA256 612dbef5f521e605cdb5377ad069decdfa4d713542e8515ea7a9b064e6e5a2a5
MD5 ec89b0dfe925041ce50a428872fe3c2f
BLAKE2b-256 b6b08b6601243179fb526020003429a06a353130beed31a334641217a6cfc7dd

See more details on using hashes here.

File details

Details for the file steelman-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: steelman-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 27.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.11 {"installer":{"name":"uv","version":"0.10.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for steelman-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 39ec5f54429a9f89d08d0d362f0ba5a84443b1816d0fe0aa30fb877c51f0a1ee
MD5 e0e1f37f3e54bc79b52fa3a4a284ca13
BLAKE2b-256 4811431043a8bfcb18342b9e13ddde465c607551b953a1edd1a3aba32a27aea8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page