https://github.com/rtuszik/steelman
Docker Hardened Images chart migration reporter
https://github.com/rtuszik/steelman
Last synced: 3 months ago
JSON representation
Docker Hardened Images chart migration reporter
- Host: GitHub
- URL: https://github.com/rtuszik/steelman
- Owner: rtuszik
- License: apache-2.0
- Created: 2026-03-02T13:08:06.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-03-24T23:35:56.000Z (3 months ago)
- Last Synced: 2026-03-26T05:11:07.252Z (3 months ago)
- Language: Python
- Homepage:
- Size: 80.1 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# 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:
```bash
uvx steelman
```
Common variants:
```bash
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):
```bash
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:
```bash
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`.
```yaml
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`.
```yaml
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.
```yaml
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:
```bash
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
```bash
uv sync --group dev
uv run ruff check .
uv run ruff format --check .
uv run ty check
uv run pytest
```