Skip to content

feat: add multi-arch Docker image build to release pipeline#882

Merged
stack72 merged 1 commit intomainfrom
s-branch-2
Mar 26, 2026
Merged

feat: add multi-arch Docker image build to release pipeline#882
stack72 merged 1 commit intomainfrom
s-branch-2

Conversation

@stack72
Copy link
Copy Markdown
Contributor

@stack72 stack72 commented Mar 26, 2026

Build and push linux/amd64 and linux/arm64 Docker images to DockerHub
(systeminit/swamp) on each release, using pre-compiled binaries from
the GitHub Release. Based on denoland/deno for Deno runtime support.

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

Build and push linux/amd64 and linux/arm64 Docker images to DockerHub
(systeminit/swamp) on each release, using pre-compiled binaries from
the GitHub Release. Based on denoland/deno for Deno runtime support.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review

No blocking issues found. This PR adds Docker image building to the release pipeline with a minimal Dockerfile. No TypeScript source changes, so CLAUDE.md conventions around tests, license headers, and libswamp imports are not applicable.

Suggestions

  1. Pin the Deno base image version in the DockerfileFROM denoland/deno:latest means builds are not reproducible across time. Consider pinning to a specific version (e.g., denoland/deno:2.2.2) or at least a major version tag, and updating it deliberately.

  2. Pass the version from the release job via outputs instead of using gh release view — While the concurrency group prevents parallel workflow runs, gh release view returns the repo's latest release, which could theoretically be a manually-created release. Passing the version/tag as a job output from release to docker would be more robust:

    # In the release job:
    outputs:
      version: ${{ steps.version.outputs.version }}
    # In the docker job, reference:
    needs.release.outputs.version
  3. Consider adding a latest tag — Many Docker users expect a latest tag alongside versioned tags. Could be added to the manifest creation step.

  4. Consider running as non-root — The Dockerfile runs as root by default. Adding a USER directive would follow container security best practices, though this depends on whether swamp needs root-level access at runtime.

  5. RUN chmod +x is redundant — The workflow already runs chmod +x on the binary before the Docker build context is created (line 192). The COPY instruction preserves file permissions, so the RUN chmod +x in the Dockerfile is unnecessary (though harmless).

Copy link
Copy Markdown

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

CI Security Review

Critical / High

None.

Medium

  1. Unpinned Docker actions (release.yml:196-227): docker/setup-qemu-action@v3, docker/setup-buildx-action@v3, docker/login-action@v3, and docker/build-push-action@v6 are pinned to tags, not commit SHAs. Notably docker/login-action handles Docker Hub credentials — a compromised tag could exfiltrate DOCKERHUB_TOKEN. However, this is consistent with the existing repo pattern (e.g., softprops/action-gh-release@v2, peter-evans/repository-dispatch@v3), so flagging as advisory only. Fix: Pin to full commit SHAs.

  2. Unpinned base image in Dockerfile (Dockerfile:1): FROM denoland/deno:latest means every build pulls whatever is currently tagged latest. A compromised or breaking upstream image affects all future builds. Fix: Pin to a specific version or digest, e.g., FROM denoland/deno:2.2.2 or use a @sha256:... digest.

Low

  1. ${{ env.docker_tag }} interpolated in run: block (release.yml:231-234): The docker_tag value is derived from a tag this workflow created (date-based format), so exploitation risk is negligible. Using shell variable expansion ("${docker_tag}") instead of Actions expression syntax in run: blocks would be marginally safer as a general practice.

Verdict

PASS — No critical or high severity findings. The new docker job is well-structured: it uses needs: release to sequence correctly, narrows permissions to contents: read at job level, checks out ref: main (not PR head), and secrets are scoped appropriately. The medium findings are supply-chain hygiene improvements worth addressing but do not block merge.

@stack72 stack72 merged commit 5871ffe into main Mar 26, 2026
11 checks passed
@stack72 stack72 deleted the s-branch-2 branch March 26, 2026 18:19
stack72 added a commit that referenced this pull request Mar 26, 2026
Pin the Dockerfile base image to denoland/deno:2.7.5 instead of :latest
for reproducible builds. Replace gh release view in the docker job with
an explicit job output from the release job, eliminating the possibility
of picking up a different release tag.

Addresses review feedback from #882.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
stack72 added a commit that referenced this pull request Mar 26, 2026
…883)

## Summary

- **Pin Dockerfile base image** to `denoland/deno:2.7.5` instead of
`:latest` for reproducible, deterministic builds
- **Pass version via job outputs** from the `release` job to the
`docker` job instead of querying `gh release view`, which could
theoretically return a different release

## Impact

These changes make the release pipeline more deterministic:

1. **Reproducible Docker builds** — Pinning the Deno base image means
the same Dockerfile produces the same image regardless of when it's
built. Previously, `:latest` meant builds could silently pick up a new
Deno version with breaking changes or security issues.

2. **Correct version propagation** — The docker job now receives the
exact version from the release job that created it via
`needs.release.outputs.version`. Previously, `gh release view` returned
the repo's latest release, which could be wrong if a manual release was
created between jobs.

## Why this is correct

- The `release` job already computes the version in the `version` step —
we simply expose it as a job output
- The `docker` job already declares `needs: release`, so the output is
guaranteed to be available
- No behavioral change in the happy path; this only eliminates edge-case
failure modes

Addresses review feedback from #882.

## Test plan

- [ ] Verify CI passes on this PR
- [ ] Confirm release workflow still builds and pushes Docker images
correctly on next merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant