{"id":45391431,"url":"https://github.com/daemonless/dbuild","last_synced_at":"2026-04-24T00:01:42.513Z","repository":{"id":338131541,"uuid":"1156633099","full_name":"daemonless/dbuild","owner":"daemonless","description":"Build, test, and push FreeBSD OCI container images.","archived":false,"fork":false,"pushed_at":"2026-04-17T17:57:11.000Z","size":368,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-17T19:35:20.505Z","etag":null,"topics":["build-tool","freebsd","oci-image","podman","python"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/daemonless.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":null,"security":null,"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-02-12T21:48:28.000Z","updated_at":"2026-04-17T17:57:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/daemonless/dbuild","commit_stats":null,"previous_names":["daemonless/dbuild"],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/daemonless/dbuild","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daemonless%2Fdbuild","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daemonless%2Fdbuild/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daemonless%2Fdbuild/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daemonless%2Fdbuild/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daemonless","download_url":"https://codeload.github.com/daemonless/dbuild/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daemonless%2Fdbuild/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32203362,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-23T20:19:26.138Z","status":"ssl_error","status_checked_at":"2026-04-23T20:19:23.520Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["build-tool","freebsd","oci-image","podman","python"],"created_at":"2026-02-21T18:39:41.089Z","updated_at":"2026-04-24T00:01:42.496Z","avatar_url":"https://github.com/daemonless.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# dbuild\n\nBuild, test, and push FreeBSD OCI container images.\n\ndbuild is a build tool for [daemonless](https://github.com/daemonless) container images. It reads a project directory containing `Containerfile` templates and an optional `.daemonless/config.yaml`, then handles the full image lifecycle: build, integration test, SBOM generation, and registry push.\n\nIt runs the same way locally and in CI. GitHub Actions and Woodpecker call dbuild — dbuild handles the FreeBSD-specific logic (variant detection, architecture mapping, OCI labels, registry auth, skip directives, SBOM generation) so CI workflows stay generic.\n\n```\nGitHub Actions / Woodpecker / local\n  └── dbuild detect     →  build matrix\n  └── dbuild build      →  podman build per variant\n  └── dbuild test       →  container integration tests\n  └── dbuild sbom       →  CycloneDX SBOM\n  └── dbuild push       →  push to ghcr.io\n  └── dbuild manifest   →  multi-arch manifest lists\n```\n\n## Requirements\n\n- Python 3.11+\n- PyYAML\n- Podman (with `doas` on FreeBSD when not root)\n- Buildah (for OCI label application and SBOM generation)\n\nOptional:\n\n| Feature | Packages |\n|---------|----------|\n| Multi-arch manifests | `skopeo` |\n| Compose testing | `podman-compose` |\n| SBOM generation | `trivy` |\n| Screenshot testing | `py311-selenium`, `py311-scikit-image`, `chromium`, `chromedriver` |\n\n## Install\n\n```sh\npip install .\n\n# With dev tools (pytest + ruff)\npip install \".[dev]\"\n```\n\nOr use without installing:\n\n```sh\nPYTHONPATH=/path/to/dbuild python3 -m dbuild info\n```\n\n## Quick Start\n\n```sh\n# Scaffold a new image project\nmkdir myapp \u0026\u0026 cd myapp\ndbuild init\n\n# From an existing image repo (e.g. radarr/)\ncd radarr\n\n# See what would be built\ndbuild info\n\n# Build all variants\ndbuild build\n\n# Build one variant\ndbuild build --variant pkg\n\n# Test built images\ndbuild test\n\n# Build and push in one step\ndbuild build --push\n```\n\n## Project Layout\n\ndbuild expects this directory structure in each image repo:\n\n```\nmyapp/\n  Containerfile           # upstream binary build (:latest tag)\n  Containerfile.pkg       # FreeBSD package build (:pkg tag) - optional\n  root/                   # files copied into the container\n  .daemonless/\n    config.yaml           # build + test configuration - optional\n    baseline.png          # screenshot baseline - optional\n    baselines/            # per-variant baselines (baseline-pkg.png) - optional\n    compose.yaml          # multi-service test stack - optional\n```\n\nIf no `config.yaml` exists, dbuild auto-detects variants from the Containerfiles present:\n\n| File | Variant tag |\n|------|------------|\n| `Containerfile` | `:latest` |\n| `Containerfile.\u003csuffix\u003e` | `:\u003csuffix\u003e` (e.g. `Containerfile.pkg` → `:pkg`) |\n\n## Configuration\n\n`.daemonless/config.yaml` (or `.dbuild.yaml`):\n\n```yaml\n# Image type: \"app\" (default) or \"base\"\ntype: app\n\n# Build configuration\nbuild:\n  auto_version: true                # extract version from built image\n  pkg_name: myapp                   # FreeBSD package name (for pkg variants)\n  architectures: [amd64, aarch64]   # target architectures (default: [amd64])\n  variants:\n    - tag: latest\n      containerfile: Containerfile\n      default: true\n    - tag: pkg\n      containerfile: Containerfile.pkg\n      args:\n        BASE_VERSION: \"15-quarterly\"\n      aliases: [pkg-quarterly]\n    - tag: pkg-latest\n      containerfile: Containerfile.pkg\n      args:\n        BASE_VERSION: \"15-latest\"\n\n# Container integration test configuration\ncit:\n  mode: health                      # shell | port | health | screenshot\n  port: 8080\n  health: /api/health               # health endpoint path\n  wait: 120                         # startup timeout (seconds)\n  ready: \"Server started\"           # log ready-pattern (regex)\n  https: false                      # use HTTPS for health checks\n  screenshot: /web                  # custom screenshot URL path\n  screenshot_wait: 5                # seconds to wait before screenshot\n  compose: false                    # use podman-compose for testing\n  annotations:                      # container annotations for testing\n    - \"org.freebsd.jail.allow.mlock=true\"\n```\n\n## Commands\n\n### `dbuild build`\n\nBuild container images for all (or selected) variants.\n\n```sh\ndbuild build                    # all variants\ndbuild build --variant latest   # one variant\ndbuild build --arch aarch64     # cross-architecture\ndbuild build --push             # build + push to registry\n```\n\nBuild output is tagged as `{registry}/{image}:build-{tag}` (e.g. `ghcr.io/daemonless/radarr:build-pkg`).\n\n### `dbuild test`\n\nRun container integration tests against built images.\n\n```sh\ndbuild test                         # test all variants\ndbuild test --variant pkg           # test one variant\ndbuild test --json result.json      # write JSON result\n```\n\nTest modes are cumulative — each includes all tests below it:\n\n| Mode | Tests |\n|------|-------|\n| `screenshot` | health + capture screenshot + visual verification |\n| `health` | port + HTTP endpoint responds (2xx/4xx = ok) |\n| `port` | shell + TCP port is listening |\n| `shell` | container starts, can exec into it |\n\n**Auto-detection**: If no mode is set in config, dbuild picks the highest applicable mode:\n- `baseline.png` exists and screenshot deps installed → `screenshot`\n- Health endpoint defined (config or OCI label) → `health`\n- Port defined (config or OCI label) → `port`\n- Otherwise → `shell`\n\nIf screenshot dependencies aren't installed, it downgrades automatically and tells you what's missing.\n\n**OCI labels**: dbuild reads `io.daemonless.port`, `io.daemonless.healthcheck-url`, and `org.freebsd.jail.*` labels from the built image. Config values override labels.\n\n### `dbuild push`\n\nTag and push built images to the configured registry.\n\n```sh\ndbuild push\ndbuild push --variant latest\n```\n\nSupports Docker Hub mirroring when `DOCKERHUB_USERNAME` and `DOCKERHUB_TOKEN` are set.\n\n### `dbuild sbom`\n\nGenerate a CycloneDX SBOM for built images. Uses trivy for application-level dependencies and `pkg query` for FreeBSD packages.\n\n```sh\ndbuild sbom\ndbuild sbom --variant pkg\n```\n\nOutput is written to `sbom-results/{image}-{tag}-sbom.json`.\n\n### `dbuild manifest`\n\nCreate and push multi-architecture manifest lists. Only useful when `architectures` has more than one entry.\n\n```sh\ndbuild manifest\n```\n\nFor each variant tag (plus aliases), creates a manifest referencing the architecture-specific images:\n- `latest` → `latest` (amd64) + `latest-arm64` (aarch64)\n- `pkg` → `pkg` (amd64) + `pkg-arm64` (aarch64)\n\n### `dbuild detect`\n\nOutput the build matrix as JSON for CI integration.\n\n```sh\ndbuild detect                       # plain JSON to stdout\ndbuild detect --format github       # write to $GITHUB_OUTPUT\ndbuild detect --format woodpecker   # JSON to stdout\n```\n\n### `dbuild info`\n\nHuman-readable overview of detected configuration.\n\n```sh\n$ dbuild info\n=== Image: ghcr.io/daemonless/radarr ===\n[info] Type: app\n[info] Architectures: amd64\n[info] Variants: 3\n[info]   latest (amd64) -\u003e Containerfile\n[info]   pkg (amd64) -\u003e Containerfile.pkg\n[info]     BASE_VERSION=15-quarterly\n[info]   pkg-latest (amd64) -\u003e Containerfile.pkg\n[info]     BASE_VERSION=15-latest\n[info] Test: mode= port=7878\n```\n\n### `dbuild init`\n\nScaffold a new dbuild project with starter files.\n\n```sh\ndbuild init                     # config.yaml + Containerfile\ndbuild init --github            # + GitHub Actions workflow\ndbuild init --woodpecker        # + Woodpecker CI pipeline\n```\n\n## Global Options\n\n```\n--variant TAG     filter to a single variant\n--arch ARCH       override target architecture (amd64, aarch64, riscv64)\n--registry URL    override the container registry\n--push            push images after building\n-v, --verbose     enable debug logging\n```\n\n## Skip Directives\n\nAdd `[skip \u003cstep\u003e]` to a commit message to skip CI steps:\n\n```\nfix: update config [skip test]              # skip testing\nfeat: bump version [skip push]              # skip all pushes\nchore: docs only [skip push:dockerhub]      # skip Docker Hub mirror only\nchore: update readme [skip sbom]            # skip SBOM generation\nfix: ci [skip test] [skip sbom]             # skip multiple steps\n```\n\n`[skip push]` also skips `push:dockerhub`. `[skip push:dockerhub]` only skips the Docker Hub mirror.\n\n## Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `DBUILD_REGISTRY` | `ghcr.io/daemonless` | Default container registry |\n| `GITHUB_TOKEN` | | Forwarded as build secret + registry auth |\n| `GITHUB_ACTOR` | | Registry login username |\n| `DOCKERHUB_USERNAME` | | Enable Docker Hub mirroring |\n| `DOCKERHUB_TOKEN` | | Docker Hub auth token |\n| `CHROME_BIN` | `/usr/local/bin/chrome` | Chrome binary for screenshots |\n| `CHROMEDRIVER_BIN` | `/usr/local/bin/chromedriver` | ChromeDriver binary |\n| `SCREENSHOT_SIZE` | `1920,1080` | Screenshot viewport size |\n| `VERIFY_SSIM_THRESHOLD` | `0.95` | SSIM threshold for baseline comparison |\n\n## CI Integration\n\n### GitHub Actions\n\n```sh\ndbuild init --github\n```\n\nThis generates `.github/workflows/build.yaml` which:\n1. Runs `dbuild detect --format github` to generate the build matrix\n2. Spins up a FreeBSD VM per matrix entry (via vmactions/freebsd-vm)\n3. Runs `dbuild build → test → sbom → push` inside the VM\n\n### Woodpecker CI\n\n```sh\ndbuild init --woodpecker\n```\n\nGenerates `.woodpecker.yaml`. The pipeline fetches `dbuild-ci.sh` which runs the full build pipeline on the native FreeBSD agent.\n\n### Local development\n\n```sh\n# Full pipeline (same as CI)\ndbuild build --variant latest\ndbuild test --variant latest\ndbuild sbom --variant latest\n\n# Transfer to another host\ndoas podman save ghcr.io/daemonless/myapp:build-latest | ssh jupiter doas podman load\n```\n\n## JSON Test Output\n\n`dbuild test --json result.json` writes:\n\n```json\n{\n  \"image\": \"ghcr.io/daemonless/radarr:build-pkg\",\n  \"mode\": \"health\",\n  \"timestamp\": \"2026-02-08T17:01:11Z\",\n  \"shell\": \"pass\",\n  \"port\": \"pass\",\n  \"health\": \"pass\",\n  \"screenshot\": \"skip\",\n  \"verify\": \"skip\",\n  \"result\": \"pass\"\n}\n```\n\nEach test is `\"pass\"`, `\"fail\"`, or `\"skip\"`.\n\n## Development\n\n```sh\npip install -e \".[dev]\"\n\n# Run tests\npytest\n\n# Lint\nruff check dbuild/ tests/\n```\n\n## License\n\nBSD-2-Clause\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaemonless%2Fdbuild","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaemonless%2Fdbuild","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaemonless%2Fdbuild/lists"}