Scans every organisation in a GitHub Enterprise account, finds all Dockerfiles, and generates a report showing which base images (and versions) are in use. The goal is to identify common base images across the entire estate.
- Uses the GitHub Enterprise API to list all organisations.
- Uses the GitHub Code Search API (
filename:Dockerfile) to find every Dockerfile in each org. - Fetches each Dockerfile's raw content via the Contents API.
- Parses all
FROMinstructions, including multi-stage builds and--platformflags. - Produces three output files:
- detail CSV — one row per FROM instruction (org, repo, path, image, tag, stage, …)
- summary CSV — one row per unique
image:tag, sorted by frequency - summary TXT — human-readable top-30 table
curljqbase64(standard on Linux/macOS)- A GitHub Personal Access Token (classic) or a GitHub App token with at minimum:
read:org— list organisations and search coderepo— read file contents (orpublic_repofor public repos only)
Org discovery — 3-step strategy:
- REST enterprise endpoint (
/enterprises/{slug}/organizations) — requires an enterprise-owner token.- GraphQL enterprise query (
enterprise(slug).organizations) — works for enterprise members withread:org./user/orgsfallback — returns every org the token owner belongs to (may include non-enterprise orgs).If step 3 is reached and the list includes orgs outside your enterprise, use
ORG_FILTERto restrict the scan:export ORG_FILTER='^your-enterprise-prefix'
export GITHUB_TOKEN=ghp_yourtoken
export ENTERPRISE=my-enterprise # GitHub Enterprise slug
./github-dockerfile-discovery.shReports are written to ./reports/ by default.
| Variable | Required | Default | Description |
|---|---|---|---|
GITHUB_TOKEN |
Yes | — | GitHub PAT or App token |
ENTERPRISE |
No | — | GitHub Enterprise slug |
API_URL_PREFIX |
No | https://api.github.com |
Override for GHES instances |
REPORT_DIR |
No | ./reports |
Output directory for CSV/TXT reports |
ORGS |
No | — | Comma-separated org list; skips enterprise lookup |
ORG_FILTER |
No | — | ERE inclusion regex to keep only matching org names |
ORG_EXCLUDE |
No | — | ERE exclusion regex to drop matching org names |
SEARCH_SLEEP |
No | 2 |
Seconds to sleep between code-search requests |
CONTENT_SLEEP |
No | 1 |
Seconds to sleep between content-fetch requests |
export ORGS="org-one,org-two"
./github-dockerfile-discovery.sh| Column | Description |
|---|---|
org |
GitHub organisation name |
repo |
Repository name |
repo_full_name |
org/repo |
dockerfile_path |
Path to Dockerfile within the repo |
stage |
Build stage number (1 = first FROM, etc.) |
image |
Image name without tag |
tag |
Tag (defaults to latest when not specified) |
digest |
SHA256 digest if pinned (empty otherwise) |
base_image |
image:tag |
dockerfile_url |
Direct GitHub URL to the Dockerfile |
| Column | Description |
|---|---|
base_image |
image:tag |
image |
Image name without tag |
tag |
Tag |
count_dockerfiles |
Number of Dockerfiles using this image |
repos |
Semicolon-separated list of repos using it |
- Rate limits: The GitHub Code Search API allows 30 requests/minute for authenticated users. The script sleeps between requests to stay within limits. Large enterprises with many Dockerfiles may take several minutes to scan.
- Code search cap: GitHub code search returns a maximum of 1,000 results per query. Orgs with more than 1,000 Dockerfiles will be flagged with a warning.
- ARG resolution:
FROM $BASE_IMAGEreferences are resolved to their defaultARGvalues if defined. If no default exists the variable name is preserved. - Archived repos are excluded from org repo enumeration but may still appear via code search hits.