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
valuesFromresolution
What It Checks
For each Flux release, the tool:
- identifies the current chart source
- checks whether a DHI chart exists
- if not, tries to resolve chart defaults with
helm show values - merges inline
HelmRelease.spec.values - extracts image references from the effective values
- checks whether matching DHI images exist
Chart replacement takes precedence over image replacement.
Requirements
- Python 3.12+
uvhelmif 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
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_chartsection from the Markdown report - writes:
./steelman.md./steelman.json./steelman-issue.md
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 statusissue 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 --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 --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, andreports/steelman-issue.mdare uploaded as artifacts- 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 --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
steelmanstep - 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 --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_issuejob is optional - set
GITLAB_TOKENas a masked CI variable if you want issue creation - if you only want artifacts, keep just the
steelmanjob
Cluster Mode In CI
If you want to scan live clusters instead of Git manifests, switch to:
uvx steelman --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
valuesFromis detected but not resolved- image analysis depends on
helm show values - some OCI chart sources may fail
helm show valuesdepending 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file steelman-0.3.0.tar.gz.
File metadata
- Download URL: steelman-0.3.0.tar.gz
- Upload date:
- Size: 20.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
332158619217ffdfc2c18648063f7c1860e82b90a01adddb4dec3031dcc01596
|
|
| MD5 |
d8d1f4b249aa8d1527f88c3a4002d7f4
|
|
| BLAKE2b-256 |
8fca7a4613f088e0b0cda336ae37e539ebe90d028d59192334a9c52fae1b0692
|
File details
Details for the file steelman-0.3.0-py3-none-any.whl.
File metadata
- Download URL: steelman-0.3.0-py3-none-any.whl
- Upload date:
- Size: 26.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6a1690dac37bd8089e09e3521e610dc4a5d6b60f786fecb4ddcc2f38bd7c5a53
|
|
| MD5 |
a3b7b5ead013c3b2212bfee802a1b34f
|
|
| BLAKE2b-256 |
be69885bf0ea00e3762b36e6527e0b21be90fd91c71148e8cc866f5925d5bef5
|