action/image-build

Composite Gitea Action that builds a container image with buildx and optionally runs a smoke test. Does not push — pair with action/image-push to publish.

Splitting build from push lets a PR workflow run image-build without push or deploy side effects while main runs the full build → push → deploy chain. A PR build that pulls a private base image still needs a registry token limited to the read:package capability; public-base builds need no token. The action logs in as ci-bot, so the token must be issued to that account.

Private Go module builds

tools/private-modules/ owns the stdlib-only offline generator and verifier for native Go file-proxy bundles. Consumers export exact committed source bytes so their first check needs no Git, network, module dependency, or credential. Changes belong here; consumers must not edit their generated copies.

From this checkout, export a reviewed full commit ID into a service checkout:

python3 tools/export-private-modules.py /path/to/service --revision FULL_COMMIT_ID
python3 tools/export-private-modules.py /path/to/service --revision FULL_COMMIT_ID --check

The export includes source revision/path/digest metadata and a SHA256 lock. Service Make and Docker builds run sha256sum -c tools/private-modules.sha256 before GO111MODULE=off GOTOOLCHAIN=local GOFLAGS= go run tools/private-modules.go. Reviewers can reproduce --check from that pinned source commit. The local hash check detects accidental drift; Git review establishes the trusted source pin.

Populate selected private versions through the configured authenticated Go client once, then run the generated command with -write to copy unchanged .info, .mod, and .zip cache artifacts into third_party/go-proxy. The generator itself enforces offline resolution and a local toolchain. It preserves the complete original archives, including any licenses and notices. Normal Go downloads still validate their content against the consumer's go.sum.

Consumer resolution uses GONOPROXY=none, GONOSUMDB=code.fritzlab.net, GOFLAGS=-mod=readonly, GOTOOLCHAIN=local, and GOPROXY=file:///absolute/service/third_party/go-proxy,https://proxy.golang.org. Run the checker before and after downloads; its exact go.mod/go.sum fingerprint rejects stale dependency closures. Public modules stay on the public proxy. Registry publication remains a separate authenticated operation.

Usage

- uses: actions/checkout@v4
- uses: https://code.fritzlab.net/action/image-build@v1
  with:
    image: code.fritzlab.net/fritzlab/chrony
    smoke-test: docker run --rm --entrypoint /usr/sbin/chronyd $IMAGE -v

The image is built and tagged as <image>:<github.run_number> in the runner's local Docker daemon. Subsequent steps (e.g. action/image-push) can reference the same tag.

Private bases in PRs

Pass token only when the PR head is trusted, and limit ci-bot package access to the private base images that the build requires. Never expose an organization-wide package reader to a contributor-controlled Dockerfile: it can pull and disclose any package that the account can read.

with:
  token: ${{ secrets.PACKAGE_READ_TOKEN }} # caller-chosen secret name

The token must be issued to ci-bot with read:package capability. Tokens from other accounts fail because the action's registry username is fixed. Omit the input for public bases.

Inputs

Name Required Default Description
image yes Full image name without tag (e.g. code.fritzlab.net/fritzlab/chrony).
context no . Docker build context.
dockerfile no Dockerfile (in context) Path to the Dockerfile, relative to the context (or absolute under $GITHUB_WORKSPACE). Use for monorepos where the build context is the repo root but the Dockerfile lives in a subdir, e.g. dockerfile: api/Dockerfile.
build-args no Multiline KEY=VALUE build args. Visible in docker history — never put secrets here.
secrets no Multiline id=VALUE BuildKit secrets (--secret). For tokens the build needs (e.g. a ci-bot token to go mod download a private module) that must not leak into layers. Reference with RUN --mount=type=secret,id=<id>.
smoke-test no Shell command run after build. $IMAGE is set to <image>:<run_number>. Non-zero exit fails the action.
token no ci-bot access token with read:package capability. Required to pull a private base image; omit for public bases.

Outputs

Name Description
tag Numeric tag assigned (= github.run_number).

Smoke test patterns

Override entrypoint for a binary that expects no args:

smoke-test: docker run --rm --entrypoint /usr/sbin/chronyd $IMAGE -v

Run a help command that returns non-zero:

smoke-test: docker run --rm $IMAGE --help || true

Multiple checks chained:

smoke-test: |
  docker run --rm $IMAGE --version
  docker run --rm --entrypoint /bin/sh $IMAGE -c 'test -x /usr/local/bin/myapp'
S
Description
fritzlab composite action: image-build
Readme
85 KiB
Languages
Go 67.3%
Python 31.1%
Dockerfile 1.6%