{"id":35377860,"url":"https://github.com/anthony-spruyt/container-images","last_synced_at":"2026-07-05T07:01:06.428Z","repository":{"id":331823756,"uuid":"1126086323","full_name":"anthony-spruyt/container-images","owner":"anthony-spruyt","description":"Container images built from upstream sources or custom Dockerfiles, with automated security scanning, release management, and SLSA provenance","archived":false,"fork":false,"pushed_at":"2026-06-21T07:26:09.000Z","size":1344,"stargazers_count":1,"open_issues_count":3,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-21T08:20:38.661Z","etag":null,"topics":["chrony","container","container-image","containers","docker","docker-container","docker-image","docker-images","llm-guard","llm-guardrail","llm-guardrails","megalinter"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/anthony-spruyt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-01T04:17:52.000Z","updated_at":"2026-06-21T07:17:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"0981e1df-4953-4c1d-8758-60826a9b0c68","html_url":"https://github.com/anthony-spruyt/container-images","commit_stats":null,"previous_names":["anthony-spruyt/container-images"],"tags_count":500,"template":false,"template_full_name":null,"purl":"pkg:github/anthony-spruyt/container-images","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthony-spruyt%2Fcontainer-images","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthony-spruyt%2Fcontainer-images/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthony-spruyt%2Fcontainer-images/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthony-spruyt%2Fcontainer-images/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/anthony-spruyt","download_url":"https://codeload.github.com/anthony-spruyt/container-images/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/anthony-spruyt%2Fcontainer-images/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35145900,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-05T02:00:06.290Z","response_time":100,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chrony","container","container-image","containers","docker","docker-container","docker-image","docker-images","llm-guard","llm-guardrail","llm-guardrails","megalinter"],"created_at":"2026-01-02T04:53:10.026Z","updated_at":"2026-07-05T07:01:06.319Z","avatar_url":"https://github.com/anthony-spruyt.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Container Images\n\n[![License](https://img.shields.io/github/license/anthony-spruyt/container-images)](https://github.com/anthony-spruyt/container-images/blob/main/LICENSE) [![CI](https://github.com/anthony-spruyt/container-images/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/anthony-spruyt/container-images/actions/workflows/ci.yaml)\n[![Trivy Scan](https://github.com/anthony-spruyt/container-images/actions/workflows/trivy-scan.yaml/badge.svg?branch=main)](https://github.com/anthony-spruyt/container-images/actions/workflows/trivy-scan.yaml) [![Stars](https://img.shields.io/github/stars/anthony-spruyt/container-images)](https://github.com/anthony-spruyt/container-images/stargazers)\n[![Forks](https://img.shields.io/github/forks/anthony-spruyt/container-images)](https://github.com/anthony-spruyt/container-images/forks) [![Contributors](https://img.shields.io/github/contributors/anthony-spruyt/container-images)](https://github.com/anthony-spruyt/container-images/graphs/contributors)\n[![Issues](https://img.shields.io/github/issues/anthony-spruyt/container-images)](https://github.com/anthony-spruyt/container-images/issues)\n\nContainer images built from upstream sources or custom Dockerfiles, published to GitHub Container Registry with automated security scanning and SLSA provenance.\n\n## Development\n\nSee [DEVELOPMENT.md](DEVELOPMENT.md) for development environment setup.\n\n## Usage\n\nPull an image:\n\n```bash\ndocker pull ghcr.io/anthony-spruyt/chrony:latest\n```\n\n## Adding a New Image\n\n### Option 1: Build from Upstream Source\n\nUse this when building from an external repository (e.g., a GitHub project):\n\n1. Create a directory with the image name\n\n2. Add `metadata.yaml`:\n\n   ```yaml\n   upstream: owner/repo\n   version: \"1.0.0\"\n   ```\n\n3. Optionally add a custom `Dockerfile` to override the upstream's\n\n4. Push to main - the image will be built and published automatically\n\n### Option 2: Build from Local Dockerfile\n\nUse this for custom images with no upstream source:\n\n1. Create a directory with the image name\n\n2. Add your `Dockerfile` and any required files\n\n3. Add `metadata.yaml`:\n\n   ```yaml\n   version: \"1.0.0\"\n   ```\n\n4. Push to main - the image will be built and published automatically\n\n### Option 3: Auto-Patched Version\n\nFor images where CI should auto-increment the patch version on each build, specify only the base version and set `auto_patch: true`:\n\n```yaml\nversion: \"1.1\"\nauto_patch: true\n```\n\nCI appends `.N` automatically (e.g., `1.1.0`, `1.1.1`, `1.1.2`). Do **not** include the patch segment in `version` — writing `\"1.1.0\"` with `auto_patch: true` would produce `1.1.0.0`.\n\n### Automatic Configuration\n\n- **Upstream validation** - Uses each image's own `metadata.yaml` as source of truth\n- **Renovate tracking** - Add `# renovate:` annotations to `metadata.yaml` for automatic version updates\n\n## Adding a Custom MegaLinter Flavor\n\nCustom MegaLinter flavors extend official flavors with additional linters. The `megalinter-factory/` directory contains tooling to generate flavor files from a simple configuration.\n\n### Using Claude Code (Recommended)\n\nThe `/create-megalinter-flavor` command automates flavor creation with automatic base flavor selection:\n\n```bash\n# With specific linters\n/create-megalinter-flavor my-ci ACTION_ACTIONLINT,MARKDOWN_MARKDOWNLINT,BASH_SHELLCHECK\n\n# Interactive mode (prompts for linter selection)\n/create-megalinter-flavor my-ci\n```\n\nThe command will:\n\n1. Validate the flavor name and check for conflicts\n2. Look up linter configurations from the catalog\n3. Auto-select the optimal base flavor (minimizing custom installs)\n4. Generate `megalinter-\u003cname\u003e/flavor.yaml` with Renovate annotations\n\n### Manual Setup\n\n1. Create a directory for your flavor:\n\n   ```bash\n   mkdir megalinter-\u003cname\u003e/\n   ```\n\n2. Create `flavor.yaml` with your configuration:\n\n   ```yaml\n   name: my-flavor\n   description: \"MegaLinter for my use case\"\n\n   # renovate: datasource=docker depName=oxsecurity/megalinter-ci_light\n   upstream_image: \"oxsecurity/megalinter-ci_light:v9.3.0@sha256:...\"\n\n   custom_linters:\n     - ACTION_ACTIONLINT\n     - MARKDOWN_MARKDOWNLINT\n     - PYTHON_BANDIT\n   ```\n\n3. Commit `flavor.yaml` - CI generates Dockerfile, test.sh, and metadata.yaml, then builds automatically\n\n### Available Linters\n\nLinter information is extracted directly from MegaLinter's descriptors at build time. The generator automatically fetches the latest versions from \u003chttps://github.com/oxsecurity/megalinter/tree/main/megalinter/descriptors\u003e.\n\n### Version Updates\n\nLinter versions are automatically extracted from MegaLinter at build time - no manual tracking needed. The weekly scheduled rebuild workflow picks up any new linter versions.\n\nFor the base image, Renovate tracks the upstream MegaLinter version:\n\n```yaml\n# renovate: datasource=docker depName=oxsecurity/megalinter-ci_light\nupstream_image: \"oxsecurity/megalinter-ci_light:v9.3.0@sha256:...\"\n```\n\nWhen Renovate creates a PR updating `flavor.yaml`, CI regenerates the Dockerfile and builds.\n\n### Local Development\n\nTo test locally, generate files first:\n\n```bash\npip install pyyaml jinja2\npython megalinter-factory/generate.py megalinter-\u003cname\u003e/\n```\n\nGenerated files (`Dockerfile`, `test.sh`) are regenerated by CI at build time.\n\n## Build Triggers\n\n### Automatic\n\nPushing changes to `metadata.yaml`, `Dockerfile`, `flavor.yaml`, or `assets/` on main triggers a build using the version in metadata. Changes to `megalinter-factory/` trigger rebuilds of all flavors.\n\n### Manual / n8n Integration\n\nTrigger via GitHub API (workflow_dispatch):\n\n```bash\n# Dry run (default) - builds and scans but doesn't push or release\ncurl -X POST \\\n  -H \"Authorization: token $GITHUB_TOKEN\" \\\n  -H \"Accept: application/vnd.github.v3+json\" \\\n  https://api.github.com/repos/anthony-spruyt/container-images/actions/workflows/ci.yaml/dispatches \\\n  -d '{\"ref\":\"main\",\"inputs\":{\"image\":\"chrony\",\"version\":\"4.6.1\"}}'\n\n# Production build - pushes to GHCR and creates release\ncurl -X POST \\\n  -H \"Authorization: token $GITHUB_TOKEN\" \\\n  -H \"Accept: application/vnd.github.v3+json\" \\\n  https://api.github.com/repos/anthony-spruyt/container-images/actions/workflows/ci.yaml/dispatches \\\n  -d '{\"ref\":\"main\",\"inputs\":{\"image\":\"chrony\",\"version\":\"4.6.1\",\"dry_run\":\"false\"}}'\n```\n\nParameters:\n\n- **image** (required): Image directory name (e.g., `chrony`, `ssh-key-rotation`)\n- **version** (optional): Semver tag to build (e.g., `4.6.1`) - checks out this tag from upstream\n- **dry_run** (optional, default: `true`): When `true`, builds and scans the image but skips push to GHCR and release creation. Set to `false` for production builds.\n\n## Automatic Version Updates\n\n### Renovate (Recommended)\n\nAdd a Renovate annotation to `metadata.yaml` for automatic version tracking:\n\n```yaml\nupstream: owner/repo\n# renovate: datasource=github-tags depName=owner/repo\nversion: \"1.0.0\"\n```\n\nSupported datasources:\n\n- `github-tags` - GitHub repository tags\n- `github-releases` - GitHub releases\n- `docker` - Docker Hub or container registries\n\nWhen Renovate detects a new version, it creates a PR. Merging triggers the build automatically.\n\n### n8n Workflow (Special Cases)\n\nFor upstream sources Renovate cannot monitor (e.g., Alpine packages), use n8n workflows. See `chrony/n8n-release-watcher.json` for an example that monitors Alpine package versions and triggers builds via workflow_dispatch\n\n## Security\n\nSee [SECURITY.md](SECURITY.md) for security policy and controls.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanthony-spruyt%2Fcontainer-images","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fanthony-spruyt%2Fcontainer-images","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fanthony-spruyt%2Fcontainer-images/lists"}