Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,22 @@ jobs:
cache-from: type=gha,scope=buildx-${{ matrix.platform }}
cache-to: type=gha,mode=min,scope=buildx-${{ matrix.platform }}

# The sandbox template shares the builder-linux stage, so it reuses the
# binary compiled by the step above.
- name: Build template
uses: docker/build-push-action@ee4ca427a2f43b6a16632044ca514c076267da23 # v6.19.0
with:
context: .
target: template
platforms: ${{ matrix.platform }}
push: false
sbom: false
provenance: false
build-args: |
GIT_TAG=pr
GIT_COMMIT=dev
cache-from: type=gha,scope=buildx-${{ matrix.platform }}

build-and-push-image:
if: github.event_name != 'pull_request' && !github.event.repository.fork
needs: [lint, build-and-test, license-check]
Expand Down Expand Up @@ -192,6 +208,39 @@ jobs:
if-no-files-found: error
retention-days: 1

# The sandbox template is a stage of the same Dockerfile with identical
# build args, so it reuses the builder-linux layer from the image build
# above and embeds the exact same binary.
- name: Build and push template by digest
id: build-template
uses: docker/build-push-action@ee4ca427a2f43b6a16632044ca514c076267da23 # v6.19.0
with:
context: .
target: template
platforms: ${{ matrix.platform }}
sbom: true
provenance: mode=max
outputs: type=image,name=docker/docker-agent-sbx-templates,push-by-digest=true,name-canonical=true,push=true
build-args: |
GIT_TAG=${{ github.ref_name }}
GIT_COMMIT=${{ github.sha }}
cache-from: type=gha,scope=buildx-${{ matrix.platform }}

- name: Export template digest
env:
DIGEST: ${{ steps.build-template.outputs.digest }}
run: |
mkdir -p "${RUNNER_TEMP}/template-digests"
touch "${RUNNER_TEMP}/template-digests/${DIGEST#sha256:}"

- name: Upload template digest
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: template-digests-${{ env.PLATFORM_PAIR }}
path: ${{ runner.temp }}/template-digests/*
if-no-files-found: error
retention-days: 1

merge-and-push-image:
if: github.event_name != 'pull_request' && !github.event.repository.fork
needs: [build-and-push-image]
Expand Down Expand Up @@ -236,3 +285,45 @@ jobs:
- name: Inspect image
run: |
docker buildx imagetools inspect docker/docker-agent:${{ steps.meta.outputs.version }}

merge-and-push-template:
if: github.event_name != 'pull_request' && !github.event.repository.fork
needs: [build-and-push-image]
runs-on: ubuntu-latest
steps:
- name: Download template digests
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
path: ${{ runner.temp }}/template-digests
pattern: template-digests-*
merge-multiple: true

- name: Hub login
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
with:
username: ${{ vars.DOCKERPUBLICBOT_USERNAME }}
password: ${{ secrets.DOCKERPUBLICBOT_WRITE_PAT }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0

# Assemble the multi-arch manifest list from the per-arch digests pushed
# by build-and-push-image. On main only the edge tag moves; on a v* tag
# both the version tag and the floating latest tag that sandboxes pulls
# by default.
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/template-digests
run: |
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
tags=(-t "docker/docker-agent-sbx-templates:${GITHUB_REF_NAME#v}" -t "docker/docker-agent-sbx-templates:latest")
else
tags=(-t "docker/docker-agent-sbx-templates:edge")
fi
args=()
for digest in *; do args+=("docker/docker-agent-sbx-templates@sha256:${digest}"); done

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] Glob for digest in * may pass literal * to imagetools create if directory is unexpectedly empty

When bash runs for digest in * in a directory and nullglob is not set (the default), an empty directory leaves * unexpanded — so args would contain docker/docker-agent-sbx-templates@sha256:* instead of real digests. This would cause docker buildx imagetools create to fail with a confusing error rather than a clean CI failure.

The if-no-files-found: error guard on the upload step provides strong protection in the happy path, but the download step (actions/download-artifact) has no equivalent guard. A defensive fix would add a check after the loop:

args=()
for digest in *; do args+=("docker/docker-agent-sbx-templates@sha256:${digest}"); done
if [[ ${#args[@]} -eq 0 ]]; then
  echo "ERROR: no template digests found" >&2
  exit 1
fi
docker buildx imagetools create "${tags[@]}" "${args[@]}"

Alternatively, shopt -s nullglob before the loop would make the glob expand to nothing (empty args), and the subsequent length check would catch it cleanly.

docker buildx imagetools create "${tags[@]}" "${args[@]}"

- name: Inspect template
run: |
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then tag="${GITHUB_REF_NAME#v}"; else tag="edge"; fi
docker buildx imagetools inspect "docker/docker-agent-sbx-templates:${tag}"
27 changes: 27 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,33 @@ COPY --from=builder-cross /binaries/docker-agent-$TARGETOS-$TARGETARCH* docker-a
FROM scratch AS cross
COPY --from=builder-cross /binaries .

# Sandbox template for docker/sandboxes, pushed as
# docker/docker-agent-sbx-templates: layers the binary built above onto the
# sandboxes shell-docker base (tools stack, tini entrypoint, and
# com.docker.sandboxes.* labels — including start-docker, which sbx keys DinD
# setup on), so the template ships the exact same binary as the docker-agent
# image.
FROM docker/sandbox-templates:shell-docker AS template
ARG TARGETOS TARGETARCH
# The sandboxes tools base ships no editor; agents and users expect vi.
USER root
RUN <<EOF
set -euxo pipefail
apt-get update
apt-get install -yy --no-install-recommends vim
apt-get clean
rm -rf /var/lib/apt/lists/*
EOF
USER agent
# Skip the first-run getting-started tour offer and welcome/telemetry banner
# in sandboxes; /getting-started and --tour still work on demand, and
# telemetry itself stays governed by TELEMETRY_ENABLED.
ENV DOCKER_AGENT_NO_TOUR=1 \
DOCKER_AGENT_HIDE_TELEMETRY_BANNER=1
COPY --from=builder-linux --chmod=0755 /binaries/docker-agent-$TARGETOS-$TARGETARCH /usr/local/bin/docker-agent
LABEL com.docker.sandboxes.flavor="docker-agent-docker"
CMD [ "docker-agent" ]

FROM alpine:${ALPINE_VERSION}
RUN apk add --no-cache ca-certificates docker-cli && \
addgroup -S docker-agent && adduser -S -G docker-agent docker-agent && \
Expand Down
5 changes: 5 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ tasks:
cmds:
- docker buildx build --push --platform linux/amd64,linux/arm64 -t docker/docker-agent --build-arg GIT_TAG --build-arg GIT_COMMIT .

template:
desc: Build the sandbox template image with the locally-built binary
cmds:
- docker buildx build --load --target=template --build-arg GIT_TAG --build-arg GIT_COMMIT -t docker/docker-agent-sbx-templates .

docs-serve:
desc: Preview the Jekyll documentation site locally
dir: docs
Expand Down
Loading