Skip to content

ci: add Dockerfile and GitHub Actions workflow to publish to ghcr.io - #3

Merged
Andrei Kvapil (kvaps) merged 1 commit into
cozystack:mainfrom
mcanevet:feat/docker-image
Jun 1, 2026
Merged

ci: add Dockerfile and GitHub Actions workflow to publish to ghcr.io#3
Andrei Kvapil (kvaps) merged 1 commit into
cozystack:mainfrom
mcanevet:feat/docker-image

Conversation

@mcanevet

@mcanevet Mickaël Canévet (mcanevet) commented May 21, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Multi-architecture Docker images published to the registry with automated tagging (including latest) and conditional publishing.
  • Chores

    • Added CI pipeline to lint, test, build, and push images with build caching and gated jobs.
    • Introduced a multi-stage build producing a minimal runtime image and updated ignore rules to reduce build context.

Copilot AI review requested due to automatic review settings May 21, 2026 12:24
@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mcanevet, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 37 minutes and 26 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 221849fd-61f5-4d9b-b1bc-edcf6eb8c3a4

📥 Commits

Reviewing files that changed from the base of the PR and between e377a62 and f875556.

📒 Files selected for processing (5)
  • .dockerignore
  • .github/workflows/ci.yaml
  • .github/workflows/lint.yaml
  • .github/workflows/test.yaml
  • Dockerfile
📝 Walkthrough

Walkthrough

This PR adds a multi-stage Dockerfile plus a GitHub Actions workflow that runs tests and linting, builds a multi-arch Docker image with Buildx and metadata-based tags, and conditionally pushes the image to ghcr.io for non-pull-request events. A .dockerignore file reduces build context.

Changes

Docker Image Build and Publish Pipeline

Layer / File(s) Summary
Workflow triggers
.github/workflows/docker.yaml
Workflow triggers on pushes to main, version tags v*, and all pull requests.
Test and lint gating jobs
.github/workflows/docker.yaml
Adds test (go test ./...) and lint (golangci-lint) jobs that gate the docker job.
Docker job permissions and runtime
.github/workflows/docker.yaml
docker job depends on test and lint, runs on ubuntu-latest, and sets packages: write permission for publishing.
Buildx setup and conditional GHCR login
.github/workflows/docker.yaml
Checkout, initialize Docker Buildx, and login to GHCR only when the event is not a pull request.
Image metadata, build, and publish
.github/workflows/docker.yaml
docker/metadata-action generates semver + latest tags; docker/build-push-action builds for linux/amd64 and linux/arm64, uses GHA cache, and pushes only for non-PR events.
Dockerfile multi-stage build and context ignore
Dockerfile, .dockerignore
Builder stage (golang:1.26-alpine) compiles a static, stripped Go binary; runtime stage uses scratch with the binary as ENTRYPOINT. .dockerignore excludes VCS, workflows, docs, LICENSE, Go tests, and talos-meta-tool from build context.

Sequence Diagram

sequenceDiagram
  participant Developer
  participant GitHub as GitHub Events
  participant Actions as GitHub Actions
  participant Buildx as Docker Buildx
  participant GHCR as ghcr.io

  Developer->>GitHub: Push to main or create v* tag (or open PR)
  GitHub->>Actions: Trigger workflow
  Actions->>Actions: Run test and lint jobs
  Actions->>Buildx: Checkout repo & initialize Buildx
  Actions->>GHCR: Login to GHCR (if not PR)
  Actions->>Buildx: Generate tags via docker/metadata-action
  Buildx->>Buildx: Build multi-stage image (amd64, arm64) with cache
  Buildx->>GHCR: Push image with tags (if not PR)
  GHCR-->>Actions: Confirm image published
  Actions-->>GitHub: Workflow complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through Dockerfiles, tidy and spry,
Builder trims binaries, tiny as a sigh,
Buildx hums, tags bloom, caches keep time,
Push skips the PRs, releases chime,
A rabbit's small image—fast, lean, sublime.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: adding a Dockerfile and GitHub Actions workflow for publishing Docker images to ghcr.io, which aligns perfectly with the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a multi-stage Dockerfile for building the talos-meta-tool. The review identifies a critical issue where an invalid Go version (1.26) is specified, which will lead to build failures. Additionally, it is recommended to remove the hardcoded GOARCH=amd64 to support multiple architectures and to include the -trimpath flag for improved build reproducibility.

Comment thread Dockerfile Outdated
@@ -0,0 +1,11 @@
FROM golang:1.26-alpine AS builder

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The Go version 1.26 does not exist (the current stable version is 1.23). This will cause the Docker build to fail as the base image cannot be pulled. Please use a valid version like 1.23-alpine. Note that the go.mod file also specifies 1.26.3, which should be corrected to a valid version to avoid build errors.

FROM golang:1.23-alpine AS builder

Comment thread Dockerfile Outdated
Comment on lines +6 to +7
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-s -w" -o /talos-meta-tool .

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Hardcoding GOARCH=amd64 prevents the image from being built for other architectures (e.g., arm64), which is a common requirement for Talos-related tools. Removing the hardcoded architecture allows the build to adapt to the target platform. Additionally, adding -trimpath is recommended for more reproducible builds.

RUN CGO_ENABLED=0 GOOS=linux \
    go build -trimpath -ldflags="-s -w" -o /talos-meta-tool .

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
.github/workflows/docker.yaml (1)

36-43: ⚡ Quick win

Consider enabling multi-arch publishing now that buildx is wired up.

Pairs with the Dockerfile comment about the hardcoded GOARCH. If you adopt TARGETOS/TARGETARCH in the Dockerfile, you can publish a true multi-arch manifest by adding platforms: here. QEMU is needed to emulate non-native targets on ubuntu-latest.

♻️ Proposed addition
       - uses: docker/setup-buildx-action@v3
+
+      - uses: docker/setup-qemu-action@v3
@@
       - uses: docker/build-push-action@v6
         with:
           context: .
+          platforms: linux/amd64,linux/arm64
           push: ${{ github.event_name != 'pull_request' }}
           tags: ${{ steps.meta.outputs.tags }}
           labels: ${{ steps.meta.outputs.labels }}
           cache-from: type=gha
           cache-to: type=gha,mode=max
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker.yaml around lines 36 - 43, Add multi-arch
publishing to the docker/build-push-action@v6 step by specifying a platforms:
list (e.g. linux/amd64,linux/arm64) and ensure buildx and QEMU are initialized
beforehand (use docker/setup-buildx-action and docker/setup-qemu-action) so
non-native targets can be emulated; also update the Dockerfile to use
TARGETOS/TARGETARCH instead of hardcoded GOARCH so the published images in the
manifest match each architecture.
Dockerfile (1)

6-7: ⚡ Quick win

Hardcoded GOARCH=amd64 defeats multi-arch builds.

With docker/setup-buildx-action already configured in the workflow, this Dockerfile is ready to be driven for multiple platforms, but forcing GOARCH=amd64 will silently cross-compile every build to amd64 regardless of the requested target platform (and ignore arm64 runners or platforms: inputs).

Prefer letting BuildKit pass TARGETOS/TARGETARCH via the platform args:

♻️ Proposed fix
-FROM golang:1.26-alpine AS builder
+FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder
+ARG TARGETOS
+ARG TARGETARCH
 WORKDIR /src
 COPY go.mod go.sum ./
 RUN go mod download
 COPY . .
-RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
-    go build -ldflags="-s -w" -o /talos-meta-tool .
+RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
+    go build -ldflags="-s -w" -o /talos-meta-tool .

If you also want the workflow to actually publish multi-arch manifests, add platforms: linux/amd64,linux/arm64 to the docker/build-push-action step.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 6 - 7, The RUN line forces GOARCH=amd64 which breaks
multi-arch builds; update the build invocation that sets CGO_ENABLED/GOOS/GOARCH
and runs go build (the RUN ... go build -ldflags="-s -w" -o /talos-meta-tool .)
to accept BuildKit platform args instead: remove the hardcoded GOARCH=amd64 (and
avoid hardcoding GOOS if desired) and use the passed TARGETOS and TARGETARCH
environment variables (or their BuildKit equivalents) so the image builds for
the requested platform; keep CGO_ENABLED=0 if needed and ensure the go build
step still outputs /talos-meta-tool.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In @.github/workflows/docker.yaml:
- Around line 36-43: Add multi-arch publishing to the
docker/build-push-action@v6 step by specifying a platforms: list (e.g.
linux/amd64,linux/arm64) and ensure buildx and QEMU are initialized beforehand
(use docker/setup-buildx-action and docker/setup-qemu-action) so non-native
targets can be emulated; also update the Dockerfile to use TARGETOS/TARGETARCH
instead of hardcoded GOARCH so the published images in the manifest match each
architecture.

In `@Dockerfile`:
- Around line 6-7: The RUN line forces GOARCH=amd64 which breaks multi-arch
builds; update the build invocation that sets CGO_ENABLED/GOOS/GOARCH and runs
go build (the RUN ... go build -ldflags="-s -w" -o /talos-meta-tool .) to accept
BuildKit platform args instead: remove the hardcoded GOARCH=amd64 (and avoid
hardcoding GOOS if desired) and use the passed TARGETOS and TARGETARCH
environment variables (or their BuildKit equivalents) so the image builds for
the requested platform; keep CGO_ENABLED=0 if needed and ensure the go build
step still outputs /talos-meta-tool.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d9f3dd93-e8ad-464b-bdba-53b2d10912c3

📥 Commits

Reviewing files that changed from the base of the PR and between d388586 and 7506caa.

📒 Files selected for processing (2)
  • .github/workflows/docker.yaml
  • Dockerfile

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds container packaging and CI automation to build and publish talos-meta-tool images to GitHub Container Registry (ghcr.io).

Changes:

  • Introduces a multi-stage Dockerfile that builds a static Linux binary and ships it in a scratch image.
  • Adds a GitHub Actions workflow to build and (on main/tags) push the image to ghcr.io with metadata-driven tags and GHA caching.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
Dockerfile Builds talos-meta-tool as a static binary in a builder stage and copies it into a minimal runtime image.
.github/workflows/docker.yaml Builds and publishes the Docker image to GHCR on pushes/tags, using Docker metadata and build cache.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread Dockerfile
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
Comment thread .github/workflows/ci.yaml
Comment on lines +15 to +41
steps:
- uses: actions/checkout@v4

- uses: docker/setup-buildx-action@v3

- uses: docker/login-action@v3
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}

- uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Comment thread .github/workflows/ci.yaml
Comment on lines +10 to +15
docker:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:

@kvaps Andrei Kvapil (kvaps) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM — clean multi-stage build on scratch, and the workflow is well-scoped (packages:write only, login/push gated on non-PR events, semver tags, gha cache).

Two notes:

  • The DCO check is red — the commit needs a Signed-off-by line. git commit --amend -s + force-push will fix it.
  • Nice-to-have (not blocking): GOARCH=amd64 is hard-coded, so the image is amd64-only. Since bare-metal Talos/Tinkerbell also runs on arm64, consider building multi-arch via buildx TARGETARCH/TARGETOS and adding platforms: linux/amd64,linux/arm64 to the build-push step. Happy to take it as a follow-up.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
Dockerfile (2)

10-12: 💤 Low value

Run as a non-root user to satisfy the Trivy DS-0002 finding.

The container runs as root (uid 0) by default. Adding a numeric USER clears the static-analysis finding and follows least-privilege; a numeric UID works without /etc/passwd on scratch.

🔒 Proposed change
 FROM scratch
 COPY --from=builder /talos-meta-tool /talos-meta-tool
+USER 65534:65534
 ENTRYPOINT ["/talos-meta-tool"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 10 - 12, The image uses FROM scratch and currently
runs as root via ENTRYPOINT ["/talos-meta-tool"]; fix the Trivy DS-0002 finding
by adding a numeric non-root user (e.g., USER 65532) to the Dockerfile so the
container runs with least privilege—update the Dockerfile after the
COPY/ENTRYPOINT lines to include a USER <numeric-uid> instruction (referencing
the existing ENTRYPOINT and COPY from the diff) so no /etc/passwd entries are
required.

1-8: ⚡ Quick win

Pin the builder to $BUILDPLATFORM to avoid emulation on multi-arch builds

The workflow builds linux/amd64 and linux/arm64 via docker/build-push-action without specifying a --platform=$BUILDPLATFORM for the builder stage, so the builder stage will run under emulation for non-native platforms. Pinning the stage to $BUILDPLATFORM avoids that slowdown while keeping cross-compilation via GOARCH=${TARGETARCH}. (Base image tag golang:1.26-alpine exists.)

♻️ Proposed change
-FROM golang:1.26-alpine AS builder
+FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS builder
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 1 - 8, Update the builder stage in the Dockerfile to
pin it to the build platform so it doesn't run under emulation for non-native
architectures: modify the builder stage FROM instruction to include the
--platform=$BUILDPLATFORM flag (the stage named "builder" that uses
golang:1.26-alpine and relies on ARG TARGETARCH / GOARCH) so the stage runs on
the host build platform while continuing to cross-compile via CGO_ENABLED=0
GOOS=linux GOARCH=${TARGETARCH} in the go build step.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/docker.yaml:
- Line 15: Three occurrences of the checkout step use "uses:
actions/checkout@v4" and are persisting GITHUB_TOKEN into the runner git config;
update each "uses: actions/checkout@v4" step to include a "with:
persist-credentials: false" block so the checkout action does not persist
credentials (apply this to every checkout step in the workflow).
- Around line 16-19: The workflow uses actions/setup-go@v5 with only
go-version-file: go.mod which enables module caching for untrusted PRs; update
the test job to disable setup-go caching for pull_request events by adding the
setup-go input cache: false (or conditionally run a step that sets cache: false
when github.event_name == 'pull_request'), so replace the current
actions/setup-go@v5 step to include with: go-version-file: go.mod and cache:
false (or gate the step with an if check) to prevent cache poisoning on PRs.
- Around line 15-16: This workflow uses tag-pinned GitHub Actions and insecure
defaults; replace every uses: <action>@<tag> (e.g., actions/checkout@v4,
actions/setup-go@v5, docker/build-push-action@v3, actions/cache@v6,
actions/upload-artifact@v3, etc.) with the corresponding full commit SHA to
prevent tag movement, add with: persist-credentials: false to each
actions/checkout step when authenticated git access is not required, and change
actions/setup-go@v5 usage to include with: cache: false (or otherwise constrain
caching) and go-version-file: go.mod for untrusted pull_request runs; ensure you
update all instances across the file consistently.

---

Nitpick comments:
In `@Dockerfile`:
- Around line 10-12: The image uses FROM scratch and currently runs as root via
ENTRYPOINT ["/talos-meta-tool"]; fix the Trivy DS-0002 finding by adding a
numeric non-root user (e.g., USER 65532) to the Dockerfile so the container runs
with least privilege—update the Dockerfile after the COPY/ENTRYPOINT lines to
include a USER <numeric-uid> instruction (referencing the existing ENTRYPOINT
and COPY from the diff) so no /etc/passwd entries are required.
- Around line 1-8: Update the builder stage in the Dockerfile to pin it to the
build platform so it doesn't run under emulation for non-native architectures:
modify the builder stage FROM instruction to include the
--platform=$BUILDPLATFORM flag (the stage named "builder" that uses
golang:1.26-alpine and relies on ARG TARGETARCH / GOARCH) so the stage runs on
the host build platform while continuing to cross-compile via CGO_ENABLED=0
GOOS=linux GOARCH=${TARGETARCH} in the go build step.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6db6e4ad-88c5-4e30-a1b1-e52d64433eaa

📥 Commits

Reviewing files that changed from the base of the PR and between 7506caa and e377a62.

📒 Files selected for processing (3)
  • .dockerignore
  • .github/workflows/docker.yaml
  • Dockerfile

Comment thread .github/workflows/ci.yaml
permissions:
contents: read
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the workflow with line numbers around the referenced lines
nl -ba .github/workflows/docker.yaml | sed -n '1,200p'

# Check whether persist-credentials is already present anywhere in the workflow
rg -n "persist-credentials" .github/workflows/docker.yaml || true

# Show all checkout steps in the workflow for context
rg -n "uses:\s*actions/checkout@v" .github/workflows/docker.yaml -n
nl -ba .github/workflows/docker.yaml | rg -n "uses:\s*actions/checkout@v"

Repository: cozystack/talos-meta-tool

Length of output: 112


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path(".github/workflows/docker.yaml")
txt = p.read_text().splitlines()
for i, line in enumerate(txt, start=1):
    if i in {1,5,10,15,16,20,25,26,27,30,35,38,39,40,50} or "actions/checkout" in line or "persist-credentials" in line:
        print(f"{i:4d}: {line}")
PY

echo "---- persist-credentials matches ----"
rg -n "persist-credentials" .github/workflows/docker.yaml || true

echo "---- checkout@v4 matches ----"
rg -n "uses:\s*actions/checkout@v4" .github/workflows/docker.yaml || true

echo "---- checkout@v matches ----"
rg -n "uses:\s*actions/checkout@" .github/workflows/docker.yaml || true

echo "---- show file (truncated) ----"
python3 - <<'PY'
from pathlib import Path
p = Path(".github/workflows/docker.yaml")
lines = p.read_text().splitlines()
for i in range(min(len(lines),120)):
    print(f"{i+1:4d}: {lines[i]}")
PY

Repository: cozystack/talos-meta-tool

Length of output: 2829


Disable checkout credential persistence in each job.

Add with: persist-credentials: false to all actions/checkout@v4 steps in .github/workflows/docker.yaml (lines 15, 26, and 38) to avoid persisting GITHUB_TOKEN credentials into the runner’s git config.

Suggested patch
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v4
+        with:
+          persist-credentials: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
persist-credentials: false
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 15-15: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 15-15: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker.yaml at line 15, Three occurrences of the checkout
step use "uses: actions/checkout@v4" and are persisting GITHUB_TOKEN into the
runner git config; update each "uses: actions/checkout@v4" step to include a
"with: persist-credentials: false" block so the checkout action does not persist
credentials (apply this to every checkout step in the workflow).

Comment thread .github/workflows/ci.yaml
Comment on lines +15 to +16
- uses: actions/checkout@v4
- uses: actions/setup-go@v5

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/docker.yaml"

echo "== File head/tail for context =="
sed -n '1,120p' "$FILE" | nl -ba

echo
echo "== Exact uses: lines =="
nl -ba "$FILE" | rg -n "uses:\s*(actions/checkout@|actions/setup-go@)" -n || true

echo
echo "== persist-credentials occurrences =="
nl -ba "$FILE" | rg -n "persist-credentials" || true

echo
echo "== setup-go options block =="
# Print setup-go lines and nearby options
nl -ba "$FILE" | rg -n "uses:\s*actions/setup-go@v5" -n -C 6 || true

Repository: cozystack/talos-meta-tool

Length of output: 163


🌐 Web query:

actions/setup-go v5 default cache behavior cache option default value

💡 Result:

For actions/setup-go v5, the default cache behavior is that caching is enabled by default. 1) Cache option default value - The action input cache has a default value of true (so caching is on unless you set cache: false). [1] 2) “Default cache behavior” (what gets cached/restored) - The action includes built-in caching/restoration for Go modules and build outputs. [2] - It automatically computes the cache key from dependency files. [2] - For v5 specifically, the default dependency file used to form the Go modules cache key is go.sum in the repository root (you can change this via cache-dependency-path). [3] If you want to disable it explicitly: - uses: actions/setup-go@v5 with: cache: false Citations: default cache: true [1]; v5 caching enabled by default unless cache=false and uses go.sum in repo root for cache key [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/docker.yaml"

echo "== File =="
sed -n '1,120p' "$FILE"

echo
echo "== uses: (checkout/setup-go) with line numbers =="
awk '{print NR "\t" $0}' "$FILE" | rg -n "uses:\s*(actions/checkout@|actions/setup-go@)"

echo
echo "== persist-credentials occurrences =="
awk '{print NR "\t" $0}' "$FILE" | rg -n "persist-credentials"

echo
echo "== setup-go blocks (uses line + 8 lines after) =="
awk '{print NR "\t" $0}' "$FILE" | rg -n "uses:\s*actions/setup-go@" -n -C 8

Repository: cozystack/talos-meta-tool

Length of output: 1930


🌐 Web query:

actions/checkout@v4 persist-credentials default value persist-credentials false security guidance

💡 Result:

For actions/checkout@v4, the persist-credentials input’s default behavior is effectively true: the action treats a missing persist-credentials input as false in code, but the published input schema and security guidance indicate credentials are persisted unless you explicitly set persist-credentials: false. Default value (when you don’t pass persist-credentials): true (credentials are persisted unless opted out) [1][2]. What persist-credentials does: actions/checkout persists the auth token in the local git config so later steps can run authenticated git commands, then removes it during post-job cleanup; to opt out, set persist-credentials: false [1]. Security guidance: GitHub/security guidance recommends setting persist-credentials: false unless you need authenticated git operations, to reduce risk of credential exposure if the workflow is compromised [3]. Practical recommendation: - If you don’t need later steps to run authenticated git commands (push/fetch with repo-scoped credentials), use: uses: actions/checkout@v4 with: persist-credentials: false [3][1]

Citations:


🌐 Web query:

actions/setup-go v5 cache true what is cached go build cache go mod cache

💡 Result:

When cache is enabled (which is the default behavior) in actions/setup-go v5, it automatically manages both the Go module cache and the Go build cache [1][2]. Specifically, the action handles the following: - Go Module Cache: This is the directory where Go stores downloaded dependencies (typically retrieved via go mod download). The action identifies this path using the output of go env GOMODCACHE [3]. - Go Build Cache: This is the directory where Go stores build artifacts and test results to speed up subsequent builds and tests. The action identifies this path using the output of go env GOCACHE [3]. By setting cache: true, you do not need to manually configure the paths or cache keys for these directories [1][4]. The action automatically computes a cache key based on your dependency files (by default, it looks for go.mod in the repository root) [1][5]. You can customize which dependency files are used to generate the cache key by using the cache-dependency-path input [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/docker.yaml"
awk '{print NR "\t" $0}' "$FILE" | rg -n "^\s*-\s+uses:" -n

Repository: cozystack/talos-meta-tool

Length of output: 51


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE=".github/workflows/docker.yaml"

echo "== All uses: lines (with line numbers) =="
awk '{print NR ":" $0}' "$FILE" | rg -n "uses:"
echo
echo "== Specific checkout/setup-go uses =="
awk '{print NR ":" $0}' "$FILE" | rg -n "uses: actions/(checkout|setup-go)@"
echo
echo "== persist-credentials =="
awk '{print NR ":" $0}' "$FILE" | rg -n "persist-credentials"

Repository: cozystack/talos-meta-tool

Length of output: 737


Pin all GitHub Actions to full commit SHAs (and tighten checkout/setup-go security)

  • .github/workflows/docker.yaml uses tag-pinned actions (@v4/@v5/@v7/@v3/@v6) at lines 15, 16, 26, 27, 38, 40, 42, 49, and 58; replace each uses: <action>@<tag> with uses: <action>@<full-commit-sha> to prevent tag movement.
  • actions/checkout@v4 steps (lines 15/26/38) don’t set persist-credentials: false; add it if authenticated git operations aren’t needed.
  • actions/setup-go@v5 (line 16) has default cache: true (module + build cache); disable (cache: false) for untrusted pull_request runs or otherwise constrain caching.
Example changes
- uses: actions/checkout@v4
  with:
    persist-credentials: false
- uses: actions/setup-go@v5
  with:
    go-version-file: go.mod
    cache: false
🧰 Tools
🪛 zizmor (1.25.2)

[warning] 15-15: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[error] 15-15: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 16-16: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 16-16: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default

(cache-poisoning)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker.yaml around lines 15 - 16, This workflow uses
tag-pinned GitHub Actions and insecure defaults; replace every uses:
<action>@<tag> (e.g., actions/checkout@v4, actions/setup-go@v5,
docker/build-push-action@v3, actions/cache@v6, actions/upload-artifact@v3, etc.)
with the corresponding full commit SHA to prevent tag movement, add with:
persist-credentials: false to each actions/checkout step when authenticated git
access is not required, and change actions/setup-go@v5 usage to include with:
cache: false (or otherwise constrain caching) and go-version-file: go.mod for
untrusted pull_request runs; ensure you update all instances across the file
consistently.

Comment thread .github/workflows/ci.yaml
Comment on lines +16 to +19
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- run: go test ./...

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the referenced workflow and surrounding lines
if [ -f .github/workflows/docker.yaml ]; then
  nl -ba .github/workflows/docker.yaml | sed -n '1,120p'
else
  echo "Missing: .github/workflows/docker.yaml"
fi

echo "----"
# Check for any other setup-go usages
rg -n "actions/setup-go@v" .github/workflows || true

echo "----"
# Check workflow triggers for docker.yaml
rg -n "^(on:|jobs:|name:)" .github/workflows/docker.yaml || true

Repository: cozystack/talos-meta-tool

Length of output: 112


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "---- docker.yaml (first 160 lines, with line numbers) ----"
if [ -f .github/workflows/docker.yaml ]; then
  cat -n .github/workflows/docker.yaml | sed -n '1,160p'
else
  echo "Missing: .github/workflows/docker.yaml"
fi

echo "---- setup-go usage in workflows ----"
rg -n "actions/setup-go@v" .github/workflows || true

echo "---- triggers in docker.yaml ----"
if [ -f .github/workflows/docker.yaml ]; then
  rg -n "^(on:|jobs:|name:)" .github/workflows/docker.yaml || true
fi

Repository: cozystack/talos-meta-tool

Length of output: 2423


🌐 Web query:

actions/setup-go v5 cache false cache key go.sum go.mod cache-dependency-path

💡 Result:

When using actions/setup-go v5, if you set the cache input to false, the action will disable its built-in caching mechanism [1][2]. Because the caching feature is disabled, the cache-dependency-path input becomes irrelevant and will not be used by the action, as there is no cache to populate or restore [2]. The cache-dependency-path input is only functional when caching is enabled (the default state, or when cache: true is set) [1][2]. In that scenario, it allows you to specify the path to your dependency files (such as go.sum or go.mod) so the action can use their hashes to generate the cache key [1][2]. If you have multiple dependency files or they are located in subdirectories, you can use glob patterns or specify multiple paths using the pipe | syntax within the cache-dependency-path input [1]. If you need to implement custom caching logic—for instance, if you require more granular control than the default behavior provides—you can set cache: false and then manually implement caching using the actions/cache action (e.g., actions/cache/restore and actions/cache/save) [3][4]. In such a manual implementation, you would then be responsible for defining the cache keys and paths, effectively bypassing the setup-go action's built-in caching configuration [3][4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "---- .github/workflows/test.yaml (first 120 lines) ----"
if [ -f .github/workflows/test.yaml ]; then
  cat -n .github/workflows/test.yaml | sed -n '1,160p'
else
  echo "Missing test.yaml"
fi

Repository: cozystack/talos-meta-tool

Length of output: 494


Disable Go caching for pull_request workflows to avoid cache poisoning risk

Both .github/workflows/docker.yaml (job test) and .github/workflows/test.yaml run on pull_request with actions/setup-go@v5 configured only with go-version-file: go.mod (no cache: false), so the action’s default module cache behavior is enabled for untrusted PRs. Consider disabling setup-go caching for pull_request (or isolating trusted/untrusted workflows).

Suggested patch
       - uses: actions/setup-go@v5
         with:
           go-version-file: go.mod
+          cache: false
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
- run: go test ./...
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: false
- run: go test ./...
🧰 Tools
🪛 zizmor (1.25.2)

[error] 16-16: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 16-16: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): enables caching by default

(cache-poisoning)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/docker.yaml around lines 16 - 19, The workflow uses
actions/setup-go@v5 with only go-version-file: go.mod which enables module
caching for untrusted PRs; update the test job to disable setup-go caching for
pull_request events by adding the setup-go input cache: false (or conditionally
run a step that sets cache: false when github.event_name == 'pull_request'), so
replace the current actions/setup-go@v5 step to include with: go-version-file:
go.mod and cache: false (or gate the step with an if check) to prevent cache
poisoning on PRs.

Signed-off-by: Mickaël Canévet <mickael.canevet@proton.ch>

@kvaps Andrei Kvapil (kvaps) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Re-approving after the force-push. Nicely done — you addressed everything and more:

  • Multi-arch via TARGETARCH + platforms: linux/amd64,linux/arm64.
  • Workflows consolidated into a single ci.yaml with docker gated on needs: [test, lint] + main/tag refs.
  • Pinned golang:1.26.3-alpine, added -trimpath, and a tight .dockerignore.
  • DCO sign-off in place.

LGTM.

@kvaps
Andrei Kvapil (kvaps) merged commit 06717c5 into cozystack:main Jun 1, 2026
5 checks passed
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.

3 participants