fix(ci): adapted CI workflows for the new version#328
fix(ci): adapted CI workflows for the new version#328Andrey Kolkov (androndo) wants to merge 3 commits into
Conversation
📝 WalkthroughWalkthroughThis PR replaces kustomize-based deployment with a Helm chart, adds comprehensive release automation (Docker image, Helm chart, and CLI binary publishing with signing), introduces release smoke testing, updates build tooling for version stamping and cross-compilation, and revises docs/tests to reflect Helm-centric workflows. ChangesRelease & Publishing Automation Pipeline
Helm Chart Implementation
Build System & Deployment Tooling
Documentation, Tests, and Configuration Updates
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
577f552 to
40f1a9f
Compare
40f1a9f to
621eb68
Compare
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
621eb68 to
717af08
Compare
Timofei Larkin (lllamnyp)
left a comment
There was a problem hiding this comment.
The release-engineering setup is well-built — GHCR over the dead Docker Hub path, cosign signing, the OCI Helm chart, and a real PR-gating install smoke test. Two defects in the publish path before merge, plus one direction change you can take as a follow-up.
Blocking
1. Multi-arch image build will fail the arm64 leg
docker-publish.yml:71 builds linux/amd64,linux/arm64, but the job sets up only buildx — no setup-qemu-action — and builds the plain Dockerfile, whose builder stage is FROM golang:1.25.10 AS builder (Dockerfile:2), not pinned to $BUILDPLATFORM. So buildx instantiates the builder as the target arch: the arm64 leg runs go build as an emulated arm64 process, and with no QEMU registered it dies at the first RUN with exec format error. The arm64 image never publishes and cosign never runs.
The Dockerfile is already cross-compile-ready (CGO_ENABLED=0 GOARCH=${TARGETARCH}, Dockerfile:25) — it just needs the builder to run natively. Fix: FROM --platform=$BUILDPLATFORM golang:1.25.10 AS builder. Then Go cross-compiles on the amd64 runner — no emulation, no QEMU, faster. (This is exactly what make docker-buildx already injects into Dockerfile.cross; the plain Dockerfile is the single-arch host variant and shouldn't be fed to multi-arch buildx as-is.)
Test: release-smoke.sh can't catch this — it does a single host-arch docker build. Add a build-only multi-arch assertion on the existing trigger (Dockerfile/docker-publish.yml already in scope): docker buildx build --platform linux/amd64,linux/arm64 -t etcd-operator:buildtest . (no --push/--load). Red on arm64 today, green after the fix.
2. The user-facing CLIs aren't released — for any OS/arch
release-assets.yml uploads only install manifests. etcd-migrate and kubectl-etcd are never built or attached. These are client-side tools that run on operators' workstations (linux & darwin, amd64 & arm64) — exactly where multi-arch/multi-OS matters — yet users get nothing prebuilt and must make from source. For a kubectl plugin that's a poor distribution story, and etcd-migrate is reached for once, under migration pressure — "build it yourself first" is friction at the worst moment.
Both are CGO-off static binaries, so cross-compiling them on the amd64 runner is trivial (no emulation — the right way). Fix: add a GOOS/GOARCH matrix (linux/{amd64,arm64}, darwin/{amd64,arm64}) to release-assets.yml building each CLI and uploading via the same upload-release-action. A per-target go build in CI is the test.
Non-blocking — follow-up: make generation Helm-first, retire kustomize
Not required for this PR — fine as a fast-follow. The direction I'd like: generation targets the chart directly and the kustomize flow goes away, since there's little value keeping both. Concretely:
- CRDs straight into the chart. Point controller-gen at the chart (
output:crd:artifacts:config=charts/etcd-operator/crds), dropconfig/crd/basesand thehelm-synccopy step. RepointcrdBasesDir()inapi/v1alpha2/validation_envtest_test.go:57/80at the new location, or envtest breaks. Bonus: the existing codegen-drift CI gate (make manifests+git diff) then covers the chart CRDs for free — closing the unguarded chart-CRD-drift gap. - RBAC single-sourced into the chart. Today
templates/rbac.yamlhand-mirrors the generatedconfig/rbac/role.yaml(its own comment admits the smoke test is the only guard). controller-gen emits static YAML, so the bridge is: generate the rules to a chart-vendored file (e.g.charts/etcd-operator/files/manager-role-rules.yaml) and have the templated ClusterRole pull them in ({{- .Files.Get … | nindent }}), keeping Helm's release-scoped name/labels while the rules are generated-once. Removes the second source of truth. - Retire kustomize.
install/deploy/undeploy→helm upgrade --install/helm uninstall(the chart already renders image ==OPERATOR_IMAGE, so the kustomize image-replacement contract moves into the chart).build-dist-manifests→helm templatepiped through the existingyqsplit, so release-assets still ships plain CRDs/non-CRDs YAML for non-Helm users. Then deleteconfig/default,config/manager,config/rbac, theconfig/crdoverlay, and thekustomizetool dep (keepconfig/samples; foldconfig/prometheus's ServiceMonitor into the chart behind ametrics.serviceMonitor.enabledvalue).
One decision this forces: CRD upgrades. Helm's crds/ dir is install-only (helm upgrade won't touch it), whereas the kustomize/kubectl apply path did upgrade CRDs. If kustomize goes away, either move CRDs under templates/ with a crds.install/crds.keep toggle (templated, upgraded with the release — the cert-manager/prometheus-operator pattern) or document a manual CRD-apply step on upgrade. I'd lean to templated CRDs.
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
d85bb82 to
7b04604
Compare
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (3)
charts/etcd-operator/templates/deployment.yaml (2)
36-72: ⚖️ Poor tradeoffConsider enabling read-only root filesystem for the manager container.
Trivy flags that
securityContext.readOnlyRootFilesystemis not set. While the current security posture is already strong (drops all capabilities, prevents privilege escalation), setting the root filesystem to read-only provides additional defense-in-depth.This would require verifying that the manager container doesn't write to the filesystem outside of mounted volumes, and potentially mounting a writable
emptyDirfor any necessary temporary files.🔒 Suggested security hardening
securityContext: allowPrivilegeEscalation: false + readOnlyRootFilesystem: true capabilities: drop: - "ALL"If the container needs to write temporary files, add a writable tmpfs volume:
+ volumes: + - name: tmp + emptyDir: {} containers: - name: manager + volumeMounts: + - name: tmp + mountPath: /tmp🤖 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 `@charts/etcd-operator/templates/deployment.yaml` around lines 36 - 72, Enable a read-only root filesystem on the manager container by adding securityContext.readOnlyRootFilesystem: true under the manager container spec (in the same block that contains securityContext, capabilities, allowPrivilegeEscalation) and ensure any paths the process must write to are backed by mounted writable volumes (e.g., add an emptyDir volume and mount it into the manager container at the required path). Verify OPERATOR_IMAGE-related snapshot/restore init containers won’t write to root or adjust their mounts accordingly and update the Pod spec volumes to include the writable emptyDir if needed.Source: Linters/SAST tools
75-95: ⚖️ Poor tradeoffConsider enabling read-only root filesystem for the kube-rbac-proxy container.
The kube-rbac-proxy sidecar should also set
securityContext.readOnlyRootFilesystem: truefor consistency with security best practices.🔒 Suggested security hardening
securityContext: allowPrivilegeEscalation: false + readOnlyRootFilesystem: true capabilities: drop: - "ALL"🤖 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 `@charts/etcd-operator/templates/deployment.yaml` around lines 75 - 95, The kube-rbac-proxy container definition is missing the readOnlyRootFilesystem setting; update the deployment template for the container named "kube-rbac-proxy" to add securityContext.readOnlyRootFilesystem: true (alongside the existing allowPrivilegeEscalation and capabilities.drop) with matching YAML indentation so the container runs with a read-only root filesystem.Source: Linters/SAST tools
hack/release-smoke.sh (1)
69-72: 💤 Low valueConsider removing duplicate log collection.
Both label selectors likely target the same operator pods, resulting in duplicate log output in diagnostics. The Helm chart deployment uses the
control-plane=controller-managerlabel.♻️ Suggested simplification
- kubectl -n "$NAMESPACE" logs -l app.kubernetes.io/name=etcd-operator \ - --all-containers --tail=200 || true kubectl -n "$NAMESPACE" logs -l control-plane=controller-manager \ --all-containers --tail=200 || true🤖 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 `@hack/release-smoke.sh` around lines 69 - 72, The two kubectl log commands in hack/release-smoke.sh use overlapping label selectors (app.kubernetes.io/name=etcd-operator and control-plane=controller-manager) and produce duplicate output; remove or consolidate one of them (either drop the app.kubernetes.io/name=etcd-operator kubectl call or replace it with a non-overlapping selector) so only one logs the operator pods, keeping the remaining command that matches the Helm chart label (control-plane=controller-manager) to avoid duplicate diagnostics.
🤖 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/helm-publish.yml:
- Around line 53-57: The helm registry login command uses unquoted GitHub
Actions template expansions (the arguments to helm registry login) which can
allow shell injection if values contain special characters; update the run block
so each expansion is quoted (e.g., wrap the username, password and registry
expansions such as github.actor, secrets.GITHUB_TOKEN and env.REGISTRY in double
quotes) when passed as arguments to helm registry login to ensure they are
treated as single safe strings.
- Around line 46-50: The script exposes command injection and word-splitting
risks by directly expanding `${{ github.ref_name }}` into TAG and using unquoted
expansions for RELEASE_TAG and RELEASE_TAG_TRIMMED_V; fix by
validating/sanitizing the incoming ref before assignment (e.g., enforce a
semver/tag pattern and reject or normalize other inputs) and always quote
variable expansions (use "$TAG" and "${TAG#v}" when writing to GITHUB_ENV).
Update the lines that set TAG, RELEASE_TAG and RELEASE_TAG_TRIMMED_V to perform
validation of github.ref_name, assign a sanitized result to TAG, and then echo
quoted values to GITHUB_ENV so both template injection and word-splitting are
prevented.
- Around line 59-70: Quote all shell variable and GitHub env references in the
Helm commands to avoid word splitting and globbing: update the helm package call
to use "${{ env.CHART_NAME }}" and ensure the --version and --app-version args
remain quoted (e.g., "${RELEASE_TAG_TRIMMED_V}" and "${RELEASE_TAG}"), and
update the helm push invocation to quote the generated filename and registry
target as a single argument (e.g., "\"${{ env.CHART_NAME
}}-${RELEASE_TAG_TRIMMED_V}.tgz\"" and "\"oci://${{ env.REGISTRY }}/${{
env.CHARTS_REPOSITORY }}\""), so all occurrences of CHART_NAME,
RELEASE_TAG_TRIMMED_V, RELEASE_TAG, REGISTRY and CHARTS_REPOSITORY are wrapped
in quotes in the helm-publish.yml commands.
In @.github/workflows/release-assets.yml:
- Around line 38-42: Quote all shell variable and template expansions in the two
workflow steps: wrap "${{ github.ref_name }}" when writing RELEASE_TAG to
GITHUB_ENV and wrap the variables used in the make command (e.g. "${{
env.REGISTRY }}", "${{ env.IMAGE_NAME }}", and "${RELEASE_TAG}") so the echo and
make commands use quoted values and avoid word-splitting/injection risks; update
the step names "Resolve release tag" and "Render install manifests" invocations
to use the quoted template/variable forms.
- Around line 84-88: The shell variable expansion in the "Cross-compile CLIs"
step uses VERSION=${RELEASE_TAG} without quotes; update the run command so the
make invocation quotes the variable (e.g., VERSION="${RELEASE_TAG}") to prevent
word-splitting and globbing, leaving the "Resolve release tag" step intact;
target the step named "Cross-compile CLIs" and the make dist-cli invocation.
In @.github/workflows/release-drafter.yml:
- Line 30: Replace the mutable tag usage of
release-drafter/release-drafter@v6.0.0 with an immutable commit SHA: locate the
line containing "release-drafter/release-drafter@v6.0.0" and change it to use
the full commit SHA for the desired release-drafter revision (e.g.,
release-drafter/release-drafter@<full-commit-sha>), ensuring the workflow is
pinned to a specific immutable commit; obtain the commit SHA from the action's
GitHub repo releases or commits and substitute it into the uses field.
- Around line 20-22: The workflow currently grants contents: write at the job
level for the combined release-drafter job used by both push and
pull_request_target; split this into two event-specific jobs: keep the existing
release-drafter job (or create a release-draft job) that runs on push and
retains permissions: contents: write and pull-requests: write and preserves the
disable-releaser input, and add a separate release-drafter/autolabeler job that
runs on pull_request_target with permissions: pull-requests: write only; ensure
the autolabeler job uses the same action steps for labeling but without
contents: write so least privilege is enforced.
In `@charts/etcd-operator/templates/_helpers.tpl`:
- Around line 41-46: The template etctd-operator.serviceAccountName currently
returns the hardcoded "default" when .Values.serviceAccount.create is false;
change it to require an explicit ServiceAccount name by returning
.Values.serviceAccount.name (and fail render if it's empty) instead of
"default". Update the "etcd-operator.serviceAccountName" helper to use the
provided .Values.serviceAccount.name (or call required "serviceAccount.name is
required when serviceAccount.create=false" .Values.serviceAccount.name) so
templates/rbac.yaml will bind to an explicitly configured SA rather than the
namespace default.
---
Nitpick comments:
In `@charts/etcd-operator/templates/deployment.yaml`:
- Around line 36-72: Enable a read-only root filesystem on the manager container
by adding securityContext.readOnlyRootFilesystem: true under the manager
container spec (in the same block that contains securityContext, capabilities,
allowPrivilegeEscalation) and ensure any paths the process must write to are
backed by mounted writable volumes (e.g., add an emptyDir volume and mount it
into the manager container at the required path). Verify OPERATOR_IMAGE-related
snapshot/restore init containers won’t write to root or adjust their mounts
accordingly and update the Pod spec volumes to include the writable emptyDir if
needed.
- Around line 75-95: The kube-rbac-proxy container definition is missing the
readOnlyRootFilesystem setting; update the deployment template for the container
named "kube-rbac-proxy" to add securityContext.readOnlyRootFilesystem: true
(alongside the existing allowPrivilegeEscalation and capabilities.drop) with
matching YAML indentation so the container runs with a read-only root
filesystem.
In `@hack/release-smoke.sh`:
- Around line 69-72: The two kubectl log commands in hack/release-smoke.sh use
overlapping label selectors (app.kubernetes.io/name=etcd-operator and
control-plane=controller-manager) and produce duplicate output; remove or
consolidate one of them (either drop the app.kubernetes.io/name=etcd-operator
kubectl call or replace it with a non-overlapping selector) so only one logs the
operator pods, keeping the remaining command that matches the Helm chart label
(control-plane=controller-manager) to avoid duplicate diagnostics.
🪄 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: 89fd23a0-f8ef-4e21-95ff-95936f509cbb
📒 Files selected for processing (59)
.github/release-drafter.yml.github/workflows/ci.yml.github/workflows/docker-publish.yml.github/workflows/helm-publish.yml.github/workflows/publish.yml.github/workflows/release-assets.yml.github/workflows/release-drafter.yml.github/workflows/release-smoke.yml.gitignoreMakefileREADME.mdapi/v1alpha2/validation_envtest_test.gocharts/etcd-operator/.helmignorecharts/etcd-operator/Chart.yamlcharts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdclusters.yamlcharts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdmembers.yamlcharts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdsnapshots.yamlcharts/etcd-operator/files/manager-role-rules.yamlcharts/etcd-operator/templates/_helpers.tplcharts/etcd-operator/templates/crds.yamlcharts/etcd-operator/templates/deployment.yamlcharts/etcd-operator/templates/metrics-service.yamlcharts/etcd-operator/templates/namespace.yamlcharts/etcd-operator/templates/rbac.yamlcharts/etcd-operator/templates/serviceaccount.yamlcharts/etcd-operator/templates/servicemonitor.yamlcharts/etcd-operator/values.yamlcmd/etcd-migrate/config.gocmd/etcd-migrate/main.gocmd/kubectl-etcd/main.goconfig/crd/kustomization.yamlconfig/crd/kustomizeconfig.yamlconfig/crd/patches/cainjection_in_etcdclusters.yamlconfig/crd/patches/webhook_in_etcdclusters.yamlconfig/default/kustomization.yamlconfig/default/manager_auth_proxy_patch.yamlconfig/default/manager_config_patch.yamlconfig/manager/kustomization.yamlconfig/manager/manager.yamlconfig/prometheus/kustomization.yamlconfig/prometheus/monitor.yamlconfig/rbac/auth_proxy_client_clusterrole.yamlconfig/rbac/auth_proxy_role.yamlconfig/rbac/auth_proxy_role_binding.yamlconfig/rbac/auth_proxy_service.yamlconfig/rbac/etcdcluster_editor_role.yamlconfig/rbac/etcdcluster_viewer_role.yamlconfig/rbac/kustomization.yamlconfig/rbac/leader_election_role.yamlconfig/rbac/leader_election_role_binding.yamlconfig/rbac/role.yamlconfig/rbac/role_binding.yamlconfig/rbac/service_account.yamldocs/installation.mddocs/migration.mddocs/operations.mdhack/e2e.shhack/release-smoke.shmain.go
💤 Files with no reviewable changes (24)
- config/prometheus/kustomization.yaml
- config/default/manager_auth_proxy_patch.yaml
- config/crd/patches/webhook_in_etcdclusters.yaml
- config/rbac/etcdcluster_editor_role.yaml
- config/rbac/leader_election_role.yaml
- config/crd/kustomizeconfig.yaml
- config/rbac/auth_proxy_role.yaml
- config/rbac/etcdcluster_viewer_role.yaml
- config/rbac/role_binding.yaml
- config/rbac/leader_election_role_binding.yaml
- config/rbac/auth_proxy_role_binding.yaml
- config/rbac/service_account.yaml
- config/rbac/auth_proxy_service.yaml
- config/default/kustomization.yaml
- config/crd/patches/cainjection_in_etcdclusters.yaml
- config/rbac/kustomization.yaml
- config/prometheus/monitor.yaml
- config/crd/kustomization.yaml
- .github/workflows/publish.yml
- config/manager/kustomization.yaml
- config/default/manager_config_patch.yaml
- config/rbac/auth_proxy_client_clusterrole.yaml
- config/manager/manager.yaml
- config/rbac/role.yaml
| run: | | ||
| TAG=${{ github.ref_name }} | ||
| echo "RELEASE_TAG=${TAG}" >> $GITHUB_ENV | ||
| # Chart version is semver without the leading v; appVersion keeps it. | ||
| echo "RELEASE_TAG_TRIMMED_V=${TAG#v}" >> $GITHUB_ENV |
There was a problem hiding this comment.
Quote variables and consider input validation.
The shell script has two issues flagged by static analysis:
- Template injection risk:
${{ github.ref_name }}is expanded directly into shell without sanitization - Missing quotes: shellcheck correctly identifies unquoted variable expansions that enable word splitting
While the semver tag filter (v*.*.*) provides partial protection, it doesn't prevent all metacharacters. An attacker with tag creation privileges could potentially inject shell commands.
🔒 Proposed fix
- name: Resolve chart versions from tag
run: |
- TAG=${{ github.ref_name }}
- echo "RELEASE_TAG=${TAG}" >> $GITHUB_ENV
+ TAG="${{ github.ref_name }}"
+ echo "RELEASE_TAG=${TAG}" >> "$GITHUB_ENV"
# Chart version is semver without the leading v; appVersion keeps it.
- echo "RELEASE_TAG_TRIMMED_V=${TAG#v}" >> $GITHUB_ENV
+ echo "RELEASE_TAG_TRIMMED_V=${TAG#v}" >> "$GITHUB_ENV"🧰 Tools
🪛 actionlint (1.7.12)
[error] 46-46: shellcheck reported issue in this script: SC2086:info:2:30: Double quote to prevent globbing and word splitting
(shellcheck)
[error] 46-46: shellcheck reported issue in this script: SC2086:info:4:42: Double quote to prevent globbing and word splitting
(shellcheck)
🪛 zizmor (1.25.2)
[error] 47-47: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 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/helm-publish.yml around lines 46 - 50, The script exposes
command injection and word-splitting risks by directly expanding `${{
github.ref_name }}` into TAG and using unquoted expansions for RELEASE_TAG and
RELEASE_TAG_TRIMMED_V; fix by validating/sanitizing the incoming ref before
assignment (e.g., enforce a semver/tag pattern and reject or normalize other
inputs) and always quote variable expansions (use "$TAG" and "${TAG#v}" when
writing to GITHUB_ENV). Update the lines that set TAG, RELEASE_TAG and
RELEASE_TAG_TRIMMED_V to perform validation of github.ref_name, assign a
sanitized result to TAG, and then echo quoted values to GITHUB_ENV so both
template injection and word-splitting are prevented.
| run: | | ||
| helm registry login \ | ||
| --username ${{ github.actor }} \ | ||
| --password ${{ secrets.GITHUB_TOKEN }} \ | ||
| ${{ env.REGISTRY }} |
There was a problem hiding this comment.
Quote the template expansion in helm login command.
The github.actor context variable is expanded without quotes, creating a code injection risk if the username contains shell metacharacters.
🔒 Proposed fix
- name: Helm registry login
run: |
helm registry login \
- --username ${{ github.actor }} \
+ --username "${{ github.actor }}" \
--password ${{ secrets.GITHUB_TOKEN }} \
${{ env.REGISTRY }}📝 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.
| run: | | |
| helm registry login \ | |
| --username ${{ github.actor }} \ | |
| --password ${{ secrets.GITHUB_TOKEN }} \ | |
| ${{ env.REGISTRY }} | |
| run: | | |
| helm registry login \ | |
| --username "${{ github.actor }}" \ | |
| --password ${{ secrets.GITHUB_TOKEN }} \ | |
| ${{ env.REGISTRY }} |
🧰 Tools
🪛 zizmor (1.25.2)
[error] 55-55: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 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/helm-publish.yml around lines 53 - 57, The helm registry
login command uses unquoted GitHub Actions template expansions (the arguments to
helm registry login) which can allow shell injection if values contain special
characters; update the run block so each expansion is quoted (e.g., wrap the
username, password and registry expansions such as github.actor,
secrets.GITHUB_TOKEN and env.REGISTRY in double quotes) when passed as arguments
to helm registry login to ensure they are treated as single safe strings.
| - name: Package chart | ||
| working-directory: charts | ||
| run: | | ||
| helm package ${{ env.CHART_NAME }} \ | ||
| --version "${RELEASE_TAG_TRIMMED_V}" \ | ||
| --app-version "${RELEASE_TAG}" | ||
|
|
||
| - name: Push chart | ||
| working-directory: charts | ||
| run: | | ||
| helm push "${{ env.CHART_NAME }}-${RELEASE_TAG_TRIMMED_V}.tgz" \ | ||
| "oci://${{ env.REGISTRY }}/${{ env.CHARTS_REPOSITORY }}" |
There was a problem hiding this comment.
Quote environment variable references in shell commands.
Shell variables should be quoted to prevent word splitting and glob expansion, even when values are expected to be safe.
♻️ Proposed fix
- name: Package chart
working-directory: charts
run: |
- helm package ${{ env.CHART_NAME }} \
+ helm package "${{ env.CHART_NAME }}" \
--version "${RELEASE_TAG_TRIMMED_V}" \
--app-version "${RELEASE_TAG}"
- name: Push chart
working-directory: charts
run: |
- helm push "${{ env.CHART_NAME }}-${RELEASE_TAG_TRIMMED_V}.tgz" \
- "oci://${{ env.REGISTRY }}/${{ env.CHARTS_REPOSITORY }}"
+ helm push "${{ env.CHART_NAME }}-${RELEASE_TAG_TRIMMED_V}.tgz" \
+ "oci://${{ env.REGISTRY }}/${{ env.CHARTS_REPOSITORY }}"🧰 Tools
🪛 zizmor (1.25.2)
[warning] 70-70: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 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/helm-publish.yml around lines 59 - 70, Quote all shell
variable and GitHub env references in the Helm commands to avoid word splitting
and globbing: update the helm package call to use "${{ env.CHART_NAME }}" and
ensure the --version and --app-version args remain quoted (e.g.,
"${RELEASE_TAG_TRIMMED_V}" and "${RELEASE_TAG}"), and update the helm push
invocation to quote the generated filename and registry target as a single
argument (e.g., "\"${{ env.CHART_NAME }}-${RELEASE_TAG_TRIMMED_V}.tgz\"" and
"\"oci://${{ env.REGISTRY }}/${{ env.CHARTS_REPOSITORY }}\""), so all
occurrences of CHART_NAME, RELEASE_TAG_TRIMMED_V, RELEASE_TAG, REGISTRY and
CHARTS_REPOSITORY are wrapped in quotes in the helm-publish.yml commands.
| - name: Resolve release tag | ||
| run: echo "RELEASE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV | ||
|
|
||
| - name: Render install manifests | ||
| run: make build-dist-manifests IMG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${RELEASE_TAG} |
There was a problem hiding this comment.
Quote variables in shell commands.
Missing quotes around template expansions and variable references create potential injection risks and violate shell best practices.
🔒 Proposed fix
- name: Resolve release tag
- run: echo "RELEASE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
+ run: echo "RELEASE_TAG=${{ github.ref_name }}" >> "$GITHUB_ENV"
- name: Render install manifests
- run: make build-dist-manifests IMG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${RELEASE_TAG}
+ run: make build-dist-manifests IMG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${RELEASE_TAG}"📝 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.
| - name: Resolve release tag | |
| run: echo "RELEASE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV | |
| - name: Render install manifests | |
| run: make build-dist-manifests IMG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${RELEASE_TAG} | |
| - name: Resolve release tag | |
| run: echo "RELEASE_TAG=${{ github.ref_name }}" >> "$GITHUB_ENV" | |
| - name: Render install manifests | |
| run: make build-dist-manifests IMG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${RELEASE_TAG}" |
🧰 Tools
🪛 actionlint (1.7.12)
[error] 39-39: shellcheck reported issue in this script: SC2086:info:1:46: Double quote to prevent globbing and word splitting
(shellcheck)
[error] 42-42: shellcheck reported issue in this script: SC2086:info:1:73: Double quote to prevent globbing and word splitting
(shellcheck)
🪛 zizmor (1.25.2)
[error] 39-39: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
[warning] 42-42: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 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/release-assets.yml around lines 38 - 42, Quote all shell
variable and template expansions in the two workflow steps: wrap "${{
github.ref_name }}" when writing RELEASE_TAG to GITHUB_ENV and wrap the
variables used in the make command (e.g. "${{ env.REGISTRY }}", "${{
env.IMAGE_NAME }}", and "${RELEASE_TAG}") so the echo and make commands use
quoted values and avoid word-splitting/injection risks; update the step names
"Resolve release tag" and "Render install manifests" invocations to use the
quoted template/variable forms.
| - name: Resolve release tag | ||
| run: echo "RELEASE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV | ||
|
|
||
| - name: Cross-compile CLIs | ||
| run: make dist-cli VERSION=${RELEASE_TAG} |
There was a problem hiding this comment.
Quote variables in shell commands.
Same quoting issues as in the release-assets job above.
🔒 Proposed fix
- name: Resolve release tag
- run: echo "RELEASE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
+ run: echo "RELEASE_TAG=${{ github.ref_name }}" >> "$GITHUB_ENV"
- name: Cross-compile CLIs
- run: make dist-cli VERSION=${RELEASE_TAG}
+ run: make dist-cli VERSION="${RELEASE_TAG}"🧰 Tools
🪛 actionlint (1.7.12)
[error] 85-85: shellcheck reported issue in this script: SC2086:info:1:46: Double quote to prevent globbing and word splitting
(shellcheck)
[error] 88-88: shellcheck reported issue in this script: SC2086:info:1:23: Double quote to prevent globbing and word splitting
(shellcheck)
🪛 zizmor (1.25.2)
[error] 85-85: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 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/release-assets.yml around lines 84 - 88, The shell
variable expansion in the "Cross-compile CLIs" step uses VERSION=${RELEASE_TAG}
without quotes; update the run command so the make invocation quotes the
variable (e.g., VERSION="${RELEASE_TAG}") to prevent word-splitting and
globbing, leaving the "Resolve release tag" step intact; target the step named
"Cross-compile CLIs" and the make dist-cli invocation.
| permissions: | ||
| contents: write | ||
| pull-requests: write |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE=".github/workflows/release-drafter.yml"
echo "== File exists and line count =="
ls -l "$FILE"
wc -l "$FILE"
echo
echo "== 'on:' and permissions context =="
rg -n "on:|pull_request_target|permissions:" "$FILE" -n || true
echo
echo "== Lines 1-80 =="
sed -n '1,80p' "$FILE" | nl -baRepository: cozystack/etcd-operator
Length of output: 588
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE=".github/workflows/release-drafter.yml"
echo "== File exists and line count =="
ls -l "$FILE"
wc -l "$FILE"
echo
echo "== 'on:' and permissions context =="
rg -n "on:|pull_request_target|permissions:" "$FILE" || true
echo
echo "== Lines 1-120 =="
sed -n '1,120p' "$FILE" | nl -ba
echo
echo "== Any other permissions blocks =="
rg -n "permissions:" "$FILE"Repository: cozystack/etcd-operator
Length of output: 589
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE=".github/workflows/release-drafter.yml"
echo "== File =="
cat -n "$FILE"
echo
echo "== Extract lines 1-30 =="
sed -n '1,30p' "$FILE" | cat -n
echo
echo "== Extract lines 15-30 (focus on on/pull_request_target/permissions) =="
sed -n '12,30p' "$FILE" | cat -n
echo
echo "== Find pull_request_target job block =="
python3 - <<'PY'
import re
path=".github/workflows/release-drafter.yml"
txt=open(path,'r').read().splitlines()
for i,line in enumerate(txt, start=1):
if "pull_request_target:" in line:
start=max(1,i-5); end=min(len(txt), i+15)
for j in range(start,end+1):
print(f"{j:>3}: {txt[j-1]}")
break
PYRepository: cozystack/etcd-operator
Length of output: 5207
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE=".github/workflows/release-drafter.yml"
echo "== File =="
cat -n "$FILE"
echo
echo "== Extract lines 1-30 =="
sed -n '1,30p' "$FILE" | cat -n
echo
echo "== Extract lines 15-30 (focus on on/pull_request_target/permissions) =="
sed -n '12,30p' "$FILE" | cat -n
echo
echo "== Find pull_request_target job block =="
python3 - <<'PY'
import re
path=".github/workflows/release-drafter.yml"
txt=open(path,'r').read().splitlines()
for i,line in enumerate(txt, start=1):
if "pull_request_target:" in line:
start=max(1,i-5); end=min(len(txt), i+15)
for j in range(start,end+1):
print(f"{j:>3}: {txt[j-1]}")
break
PYRepository: cozystack/etcd-operator
Length of output: 5207
🌐 Web query:
release-drafter/release-drafter v6 disable-releaser permissions contents write pull_request_target
💡 Result:
Regarding your query on release-drafter configuration: 1. Status of disable-releaser: The input disable-releaser is no longer supported in modern versions of the release-drafter action (v7 and later) [1][2]. In earlier versions (including v6), it was sometimes used to skip the release-drafting process while running only the autolabeler, but this functionality has been removed in favor of splitting these into two distinct actions: the drafter and the autolabeler [1][2]. 2. Recommended Approach: If you wish to use only the autolabeler functionality, you should use the dedicated autolabeler action instead of the main release-drafter action [2]. Example usage: - uses: release-drafter/release-drafter/autolabeler@v7 3. Permissions: To function correctly, the release-drafter action requires specific permissions [3]. - Contents: write (required to create or update a GitHub release) [4][3] - Pull-requests: write (required if you are using the autolabeler functionality) [4][5][3] 4. pull_request_target: Using the pull_request_target event is a standard practice for workflows that require elevated permissions (such as contents: write or pull-requests: write) when dealing with pull requests, particularly those originating from forks [6]. When using pull_request_target, the workflow runs in the context of the base repository, which allows the use of secrets and higher permissions that are restricted in standard pull_request triggers [6]. Note: The disable-releaser input was marked as dead code in v7 because it did not actually stop the action from attempting to create releases in some configurations, leading to potential permission errors [7]. You should update your workflows to use the separate, dedicated actions [1][2].
Citations:
- 1: feat: new major version release-drafter/release-drafter#1475
- 2: fix: remove disable-releaser and disable-autolabeler from action.yaml release-drafter/release-drafter#1564
- 3: Provide instructions on how to setup an access token release-drafter/release-drafter#1418
- 4: https://github.com/release-drafter/release-drafter/blob/master/README.md
- 5: Resource not accessible by integration for the default token, the action is green nevertheless release-drafter/release-drafter#1350
- 6: What GitHub Actions
permissionsdoes Release Drafter Need release-drafter/release-drafter#869 - 7: Bug:
disable-releaseranddisable-autolabelerinputs are defined in action.yml but unimplemented in v7.x release-drafter/release-drafter#1562
Scope contents: write away from pull_request_target to enforce least privilege.
.github/workflows/release-drafter.yml uses a single release-drafter job for both push and pull_request_target, with job-level permissions that always include contents: write (lines 20-22). Since pull_request_target is gated from releasing via disable-releaser: ${{ github.ref != 'refs/heads/main' }} (step input), split into event-specific jobs: use release-drafter/autolabeler for pull_request_target with only pull-requests: write, and keep contents: write only for the push/release-draft behavior.
🤖 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/release-drafter.yml around lines 20 - 22, The workflow
currently grants contents: write at the job level for the combined
release-drafter job used by both push and pull_request_target; split this into
two event-specific jobs: keep the existing release-drafter job (or create a
release-draft job) that runs on push and retains permissions: contents: write
and pull-requests: write and preserves the disable-releaser input, and add a
separate release-drafter/autolabeler job that runs on pull_request_target with
permissions: pull-requests: write only; ensure the autolabeler job uses the same
action steps for labeling but without contents: write so least privilege is
enforced.
| # code under it is the canonical fork-to-RCE pattern. release-drafter | ||
| # touches no repo code, so this job stays safe as long as nothing here | ||
| # checks out untrusted refs. | ||
| - uses: release-drafter/release-drafter@v6.0.0 |
There was a problem hiding this comment.
Pin release-drafter/release-drafter to a full commit SHA (not @v6.0.0).
This workflow runs pull_request_target with write permissions, so using the mutable v6.0.0 tag for release-drafter/release-drafter increases supply-chain/retargeting risk; pin the action to an immutable commit SHA instead.
Suggested change
- - uses: release-drafter/release-drafter@v6.0.0
+ - uses: release-drafter/release-drafter@<full_commit_sha_for_v6.0.0>🧰 Tools
🪛 zizmor (1.25.2)
[error] 30-30: 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/release-drafter.yml at line 30, Replace the mutable tag
usage of release-drafter/release-drafter@v6.0.0 with an immutable commit SHA:
locate the line containing "release-drafter/release-drafter@v6.0.0" and change
it to use the full commit SHA for the desired release-drafter revision (e.g.,
release-drafter/release-drafter@<full-commit-sha>), ensuring the workflow is
pinned to a specific immutable commit; obtain the commit SHA from the action's
GitHub repo releases or commits and substitute it into the uses field.
| {{- define "etcd-operator.serviceAccountName" -}} | ||
| {{- if .Values.serviceAccount.create -}} | ||
| {{- include "etcd-operator.fullname" . -}} | ||
| {{- else -}} | ||
| default | ||
| {{- end -}} |
There was a problem hiding this comment.
Require an explicit ServiceAccount name when serviceAccount.create=false.
This branch hardcodes default, which makes templates/rbac.yaml bind broad operator ClusterRole permissions to the namespace default ServiceAccount. That is an avoidable privilege-escalation path for workloads using the default SA.
Suggested fix
--- a/charts/etcd-operator/templates/_helpers.tpl
+++ b/charts/etcd-operator/templates/_helpers.tpl
@@
{{- define "etcd-operator.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
-{{- include "etcd-operator.fullname" . -}}
+{{- default (include "etcd-operator.fullname" .) .Values.serviceAccount.name -}}
{{- else -}}
-default
+{{- required "serviceAccount.name must be set when serviceAccount.create=false" .Values.serviceAccount.name -}}
{{- end -}}
{{- end -}}--- a/charts/etcd-operator/values.yaml
+++ b/charts/etcd-operator/values.yaml
@@
serviceAccount:
# -- Create the operator ServiceAccount.
create: true
+ # -- Name of the ServiceAccount to use. Required when create=false.
+ name: ""
# -- Extra annotations for the ServiceAccount.
annotations: {}📝 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.
| {{- define "etcd-operator.serviceAccountName" -}} | |
| {{- if .Values.serviceAccount.create -}} | |
| {{- include "etcd-operator.fullname" . -}} | |
| {{- else -}} | |
| default | |
| {{- end -}} | |
| {{- define "etcd-operator.serviceAccountName" -}} | |
| {{- if .Values.serviceAccount.create -}} | |
| {{- default (include "etcd-operator.fullname" .) .Values.serviceAccount.name -}} | |
| {{- else -}} | |
| {{- required "serviceAccount.name must be set when serviceAccount.create=false" .Values.serviceAccount.name -}} | |
| {{- end -}} |
🤖 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 `@charts/etcd-operator/templates/_helpers.tpl` around lines 41 - 46, The
template etctd-operator.serviceAccountName currently returns the hardcoded
"default" when .Values.serviceAccount.create is false; change it to require an
explicit ServiceAccount name by returning .Values.serviceAccount.name (and fail
render if it's empty) instead of "default". Update the
"etcd-operator.serviceAccountName" helper to use the provided
.Values.serviceAccount.name (or call required "serviceAccount.name is required
when serviceAccount.create=false" .Values.serviceAccount.name) so
templates/rbac.yaml will bind to an explicitly configured SA rather than the
namespace default.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (5)
.github/workflows/helm-publish.yml (3)
53-57:⚠️ Potential issue | 🟠 Major | ⚡ Quick winQuote the template expansion in helm login command.
The
github.actortemplate expansion remains unquoted, creating a code injection risk if the username contains shell metacharacters.🔒 Proposed fix
- name: Helm registry login run: | helm registry login \ - --username ${{ github.actor }} \ + --username "${{ github.actor }}" \ --password ${{ secrets.GITHUB_TOKEN }} \ ${{ env.REGISTRY }}🤖 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/helm-publish.yml around lines 53 - 57, The helm registry login command passes an unquoted template expansion for github.actor which can lead to shell injection if the username contains metacharacters; update the helm registry login invocation to quote the username template expansion (i.e., change the --username argument to use "${{ github.actor }}" ), ensuring the template is safely expanded, and similarly ensure any other interpolated values like ${{ env.REGISTRY }} remain properly quoted where used in the helm registry login command.
46-50:⚠️ Potential issue | 🟠 Major | ⚡ Quick winTemplate injection and word-splitting risks remain unaddressed.
The unquoted template expansions and variable references flagged in previous reviews are still present. While the semver tag filter (
v*.*.*) provides partial protection, it doesn't prevent all shell metacharacters. An attacker with tag-creation privileges could potentially inject commands.🔒 Proposed fix
- name: Resolve chart versions from tag run: | - TAG=${{ github.ref_name }} - echo "RELEASE_TAG=${TAG}" >> $GITHUB_ENV + TAG="${{ github.ref_name }}" + echo "RELEASE_TAG=${TAG}" >> "$GITHUB_ENV" # Chart version is semver without the leading v; appVersion keeps it. - echo "RELEASE_TAG_TRIMMED_V=${TAG#v}" >> $GITHUB_ENV + echo "RELEASE_TAG_TRIMMED_V=${TAG#v}" >> "$GITHUB_ENV"🤖 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/helm-publish.yml around lines 46 - 50, The workflow exposes unquoted template expansions and variable uses (TAG, RELEASE_TAG, RELEASE_TAG_TRIMMED_V) to command injection and word-splitting; fix by validating/sanitizing TAG before exporting (e.g. enforce a strict semver pattern like v?X.Y.Z and fail the job on mismatch) and quote all expansions when writing to GITHUB_ENV (use quoted parameter expansions for RELEASE_TAG and RELEASE_TAG_TRIMMED_V), ensuring any trimming of the leading "v" uses safe parameter expansion only after validation.
59-70:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winQuote all template and environment variable expansions.
Shell variables and template expansions should be quoted to prevent word splitting and glob expansion, even when values are expected to be safe.
♻️ Proposed fix
- name: Package chart working-directory: charts run: | - helm package ${{ env.CHART_NAME }} \ + helm package "${{ env.CHART_NAME }}" \ --version "${RELEASE_TAG_TRIMMED_V}" \ --app-version "${RELEASE_TAG}" - name: Push chart working-directory: charts run: | helm push "${{ env.CHART_NAME }}-${RELEASE_TAG_TRIMMED_V}.tgz" \ "oci://${{ env.REGISTRY }}/${{ env.CHARTS_REPOSITORY }}"🤖 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/helm-publish.yml around lines 59 - 70, In the "Package chart" and "Push chart" steps, quote all GitHub template and shell variable expansions to avoid word-splitting and globbing: update the helm package invocation to use "${{ env.CHART_NAME }}" and ensure --version and --app-version use "${RELEASE_TAG_TRIMMED_V}" and "${RELEASE_TAG}"; update the helm push invocation to quote the chart filename and OCI target (e.g. "${{ env.CHART_NAME }}-${RELEASE_TAG_TRIMMED_V}.tgz" and "oci://${{ env.REGISTRY }}/${{ env.CHARTS_REPOSITORY }}") so all occurrences of ${{ ... }} and ${...} are wrapped in quotes in the helm package and helm push commands..github/workflows/release-assets.yml (2)
84-88:⚠️ Potential issue | 🟠 Major | ⚡ Quick winQuote variable expansions in shell commands.
The unquoted variable references flagged in previous reviews are still present, creating word-splitting and injection risks.
🔒 Proposed fix
- name: Resolve release tag - run: echo "RELEASE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV + run: echo "RELEASE_TAG=${{ github.ref_name }}" >> "$GITHUB_ENV" - name: Cross-compile CLIs - run: make dist-cli VERSION=${RELEASE_TAG} + run: make dist-cli VERSION="${RELEASE_TAG}"🤖 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/release-assets.yml around lines 84 - 88, The workflow uses unquoted shell variable expansions which can cause word-splitting/injection; update the "Resolve release tag" and "Cross-compile CLIs" steps to quote the variables: write the env assignment using "${{ github.ref_name }}" when echoing to GITHUB_ENV and invoke make with VERSION="${RELEASE_TAG}" (i.e., replace unquoted ${RELEASE_TAG} with a quoted expansion) so all references to RELEASE_TAG are safely quoted in shell contexts.
38-42:⚠️ Potential issue | 🟠 Major | ⚡ Quick winTemplate injection and word-splitting risks remain unaddressed.
The unquoted template expansions and variable references flagged in previous reviews are still present, creating potential shell injection vectors.
🔒 Proposed fix
- name: Resolve release tag - run: echo "RELEASE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV + run: echo "RELEASE_TAG=${{ github.ref_name }}" >> "$GITHUB_ENV" - name: Render install manifests - run: make build-dist-manifests IMG=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${RELEASE_TAG} + run: make build-dist-manifests IMG="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${RELEASE_TAG}"🤖 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/release-assets.yml around lines 38 - 42, The Resolve release tag and Render install manifests steps use unquoted template and shell expansions (RELEASE_TAG, github.ref_name, env.REGISTRY, env.IMAGE_NAME, and the make build-dist-manifests invocation), creating injection/word-splitting risk; fix by writing the env var with a safe, quoted assignment (e.g., use printf '%s=%s\n' for RELEASE_TAG with the github.ref_name expansion) and call make with the IMG argument quoted (pass IMG as a single double-quoted value combining env.REGISTRY, env.IMAGE_NAME and ${RELEASE_TAG}) so all expansions are double-quoted to prevent splitting or injection.
🧹 Nitpick comments (2)
.github/workflows/helm-publish.yml (1)
24-25: 💤 Low valueConsider setting
persist-credentials: falseon checkout.The checkout action doesn't explicitly disable credential persistence. While this workflow doesn't run untrusted code, setting
persist-credentials: falseis a defense-in-depth measure that prevents the GITHUB_TOKEN from being available to subsequent steps that don't need it.🔒 Proposed fix
- name: Checkout repository uses: actions/checkout@v4 + with: + persist-credentials: false🤖 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/helm-publish.yml around lines 24 - 25, Update the "Checkout repository" step that uses actions/checkout@v4 to explicitly disable credential persistence by adding persist-credentials: false to its step configuration; locate the step named "Checkout repository" and modify the checkout action invocation so the GITHUB_TOKEN is not persisted to subsequent steps..github/workflows/release-assets.yml (1)
25-25: 💤 Low valueConsider setting
persist-credentials: falseon checkout actions.Both checkout actions in this workflow don't explicitly disable credential persistence. While the workflow doesn't run untrusted code, setting
persist-credentials: falseis a defense-in-depth measure that prevents the GITHUB_TOKEN from being available to subsequent steps that don't need it.🔒 Proposed fix
- uses: actions/checkout@v4 + with: + persist-credentials: falseApply to both checkout steps (lines 25 and 77).
Also applies to: 77-77
🤖 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/release-assets.yml at line 25, The checkout steps using the actions/checkout@v4 action currently don't disable credential persistence; update both checkout steps that reference uses: actions/checkout@v4 to include the input persist-credentials: false so the GITHUB_TOKEN is not automatically propagated to later steps, ensuring you add this input to each checkout invocation (the two occurrences of actions/checkout@v4 in the workflow).
🤖 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 `@cmd/etcd-migrate/main.go`:
- Around line 73-82: The Run handler for the "version" cobra command uses
fmt.Fprintln(...) whose error return is ignored causing errcheck lint failure;
either explicitly discard the error by assigning it to the blank identifier
(e.g., _ = fmt.Fprintln(...)) inside the anonymous Run, or convert the command
to use RunE and return the fmt.Fprintln(...) error from the handler so the error
is propagated—update the cobra.Command creation (the block passed to
rootCmd.AddCommand with Use: "version") accordingly.
---
Duplicate comments:
In @.github/workflows/helm-publish.yml:
- Around line 53-57: The helm registry login command passes an unquoted template
expansion for github.actor which can lead to shell injection if the username
contains metacharacters; update the helm registry login invocation to quote the
username template expansion (i.e., change the --username argument to use "${{
github.actor }}" ), ensuring the template is safely expanded, and similarly
ensure any other interpolated values like ${{ env.REGISTRY }} remain properly
quoted where used in the helm registry login command.
- Around line 46-50: The workflow exposes unquoted template expansions and
variable uses (TAG, RELEASE_TAG, RELEASE_TAG_TRIMMED_V) to command injection and
word-splitting; fix by validating/sanitizing TAG before exporting (e.g. enforce
a strict semver pattern like v?X.Y.Z and fail the job on mismatch) and quote all
expansions when writing to GITHUB_ENV (use quoted parameter expansions for
RELEASE_TAG and RELEASE_TAG_TRIMMED_V), ensuring any trimming of the leading "v"
uses safe parameter expansion only after validation.
- Around line 59-70: In the "Package chart" and "Push chart" steps, quote all
GitHub template and shell variable expansions to avoid word-splitting and
globbing: update the helm package invocation to use "${{ env.CHART_NAME }}" and
ensure --version and --app-version use "${RELEASE_TAG_TRIMMED_V}" and
"${RELEASE_TAG}"; update the helm push invocation to quote the chart filename
and OCI target (e.g. "${{ env.CHART_NAME }}-${RELEASE_TAG_TRIMMED_V}.tgz" and
"oci://${{ env.REGISTRY }}/${{ env.CHARTS_REPOSITORY }}") so all occurrences of
${{ ... }} and ${...} are wrapped in quotes in the helm package and helm push
commands.
In @.github/workflows/release-assets.yml:
- Around line 84-88: The workflow uses unquoted shell variable expansions which
can cause word-splitting/injection; update the "Resolve release tag" and
"Cross-compile CLIs" steps to quote the variables: write the env assignment
using "${{ github.ref_name }}" when echoing to GITHUB_ENV and invoke make with
VERSION="${RELEASE_TAG}" (i.e., replace unquoted ${RELEASE_TAG} with a quoted
expansion) so all references to RELEASE_TAG are safely quoted in shell contexts.
- Around line 38-42: The Resolve release tag and Render install manifests steps
use unquoted template and shell expansions (RELEASE_TAG, github.ref_name,
env.REGISTRY, env.IMAGE_NAME, and the make build-dist-manifests invocation),
creating injection/word-splitting risk; fix by writing the env var with a safe,
quoted assignment (e.g., use printf '%s=%s\n' for RELEASE_TAG with the
github.ref_name expansion) and call make with the IMG argument quoted (pass IMG
as a single double-quoted value combining env.REGISTRY, env.IMAGE_NAME and
${RELEASE_TAG}) so all expansions are double-quoted to prevent splitting or
injection.
---
Nitpick comments:
In @.github/workflows/helm-publish.yml:
- Around line 24-25: Update the "Checkout repository" step that uses
actions/checkout@v4 to explicitly disable credential persistence by adding
persist-credentials: false to its step configuration; locate the step named
"Checkout repository" and modify the checkout action invocation so the
GITHUB_TOKEN is not persisted to subsequent steps.
In @.github/workflows/release-assets.yml:
- Line 25: The checkout steps using the actions/checkout@v4 action currently
don't disable credential persistence; update both checkout steps that reference
uses: actions/checkout@v4 to include the input persist-credentials: false so the
GITHUB_TOKEN is not automatically propagated to later steps, ensuring you add
this input to each checkout invocation (the two occurrences of
actions/checkout@v4 in the workflow).
🪄 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: d6ece272-c7fb-4122-a405-b3b5a99de149
📒 Files selected for processing (49)
.github/workflows/ci.yml.github/workflows/helm-publish.yml.github/workflows/release-assets.yml.github/workflows/release-smoke.ymlMakefileREADME.mdapi/v1alpha2/validation_envtest_test.gocharts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdclusters.yamlcharts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdmembers.yamlcharts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdsnapshots.yamlcharts/etcd-operator/files/manager-role-rules.yamlcharts/etcd-operator/templates/crds.yamlcharts/etcd-operator/templates/namespace.yamlcharts/etcd-operator/templates/rbac.yamlcharts/etcd-operator/templates/servicemonitor.yamlcharts/etcd-operator/values.yamlcmd/etcd-migrate/config.gocmd/etcd-migrate/main.gocmd/kubectl-etcd/main.goconfig/crd/kustomization.yamlconfig/crd/kustomizeconfig.yamlconfig/crd/patches/cainjection_in_etcdclusters.yamlconfig/crd/patches/webhook_in_etcdclusters.yamlconfig/default/kustomization.yamlconfig/default/manager_auth_proxy_patch.yamlconfig/default/manager_config_patch.yamlconfig/manager/kustomization.yamlconfig/manager/manager.yamlconfig/prometheus/kustomization.yamlconfig/prometheus/monitor.yamlconfig/rbac/auth_proxy_client_clusterrole.yamlconfig/rbac/auth_proxy_role.yamlconfig/rbac/auth_proxy_role_binding.yamlconfig/rbac/auth_proxy_service.yamlconfig/rbac/etcdcluster_editor_role.yamlconfig/rbac/etcdcluster_viewer_role.yamlconfig/rbac/kustomization.yamlconfig/rbac/leader_election_role.yamlconfig/rbac/leader_election_role_binding.yamlconfig/rbac/role.yamlconfig/rbac/role_binding.yamlconfig/rbac/service_account.yamlconfig/samples/kustomization.yamldocs/installation.mddocs/migration.mddocs/operations.mdhack/e2e.shhack/release-smoke.shmain.go
💤 Files with no reviewable changes (25)
- config/rbac/role_binding.yaml
- config/crd/patches/webhook_in_etcdclusters.yaml
- config/prometheus/kustomization.yaml
- .github/workflows/release-smoke.yml
- config/rbac/leader_election_role.yaml
- config/default/manager_auth_proxy_patch.yaml
- config/prometheus/monitor.yaml
- config/crd/kustomizeconfig.yaml
- config/crd/kustomization.yaml
- config/rbac/etcdcluster_editor_role.yaml
- config/rbac/etcdcluster_viewer_role.yaml
- config/rbac/auth_proxy_service.yaml
- config/crd/patches/cainjection_in_etcdclusters.yaml
- config/rbac/auth_proxy_client_clusterrole.yaml
- config/rbac/auth_proxy_role_binding.yaml
- config/rbac/leader_election_role_binding.yaml
- config/manager/kustomization.yaml
- config/rbac/service_account.yaml
- config/samples/kustomization.yaml
- config/rbac/kustomization.yaml
- config/default/manager_config_patch.yaml
- config/rbac/auth_proxy_role.yaml
- config/manager/manager.yaml
- config/rbac/role.yaml
- config/default/kustomization.yaml
✅ Files skipped from review due to trivial changes (5)
- api/v1alpha2/validation_envtest_test.go
- docs/migration.md
- docs/operations.md
- main.go
- README.md
🚧 Files skipped from review as they are similar to previous changes (6)
- hack/e2e.sh
- .github/workflows/ci.yml
- charts/etcd-operator/files/manager-role-rules.yaml
- cmd/etcd-migrate/config.go
- cmd/kubectl-etcd/main.go
- hack/release-smoke.sh
| // A `version` subcommand rather than a --version flag: --version is already | ||
| // taken by the etcd-version override (see bindFlags). | ||
| rootCmd.AddCommand(&cobra.Command{ | ||
| Use: "version", | ||
| Short: "Print the etcd-migrate binary version", | ||
| Args: cobra.NoArgs, | ||
| Run: func(cmd *cobra.Command, _ []string) { | ||
| fmt.Fprintln(cmd.OutOrStdout(), version) | ||
| }, | ||
| }) |
There was a problem hiding this comment.
Unchecked error from fmt.Fprintln may fail linting.
The errcheck linter flags the unchecked return value. While practically harmless for a version print, this may cause CI failures if errcheck is enabled.
🔧 Silence the linter or handle the error
Option 1 — Discard explicitly (silences errcheck):
Run: func(cmd *cobra.Command, _ []string) {
- fmt.Fprintln(cmd.OutOrStdout(), version)
+ _, _ = fmt.Fprintln(cmd.OutOrStdout(), version)
},Option 2 — Switch to RunE and propagate:
- Run: func(cmd *cobra.Command, _ []string) {
- fmt.Fprintln(cmd.OutOrStdout(), version)
+ RunE: func(cmd *cobra.Command, _ []string) error {
+ _, err := fmt.Fprintln(cmd.OutOrStdout(), version)
+ return err
},📝 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.
| // A `version` subcommand rather than a --version flag: --version is already | |
| // taken by the etcd-version override (see bindFlags). | |
| rootCmd.AddCommand(&cobra.Command{ | |
| Use: "version", | |
| Short: "Print the etcd-migrate binary version", | |
| Args: cobra.NoArgs, | |
| Run: func(cmd *cobra.Command, _ []string) { | |
| fmt.Fprintln(cmd.OutOrStdout(), version) | |
| }, | |
| }) | |
| // A `version` subcommand rather than a --version flag: --version is already | |
| // taken by the etcd-version override (see bindFlags). | |
| rootCmd.AddCommand(&cobra.Command{ | |
| Use: "version", | |
| Short: "Print the etcd-migrate binary version", | |
| Args: cobra.NoArgs, | |
| Run: func(cmd *cobra.Command, _ []string) { | |
| _, _ = fmt.Fprintln(cmd.OutOrStdout(), version) | |
| }, | |
| }) |
🧰 Tools
🪛 golangci-lint (2.12.2)
[error] 80-80: Error return value of fmt.Fprintln is not checked
(errcheck)
🤖 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 `@cmd/etcd-migrate/main.go` around lines 73 - 82, The Run handler for the
"version" cobra command uses fmt.Fprintln(...) whose error return is ignored
causing errcheck lint failure; either explicitly discard the error by assigning
it to the blank identifier (e.g., _ = fmt.Fprintln(...)) inside the anonymous
Run, or convert the command to use RunE and return the fmt.Fprintln(...) error
from the handler so the error is propagated—update the cobra.Command creation
(the block passed to rootCmd.AddCommand with Use: "version") accordingly.
Source: Linters/SAST tools
Pin the Dockerfile builder stage to $BUILDPLATFORM so a buildx --platform=linux/amd64,linux/arm64 build cross-compiles via GOARCH instead of running go build under emulation; without it the arm64 leg fails with exec format error on an amd64 runner with no QEMU. Add a no-QEMU multi-arch build-only CI job that fails closed if the pin regresses. Signed-off-by: Timofei Larkin <lllamnyp@gmail.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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/ci.yml:
- Around line 19-20: Update workflow steps that use actions/checkout@v4 to add
persist-credentials: false to the checkout step (look for the checkout step
using actions/checkout@v4) and replace mutable action tags like
docker/setup-buildx-action@v3.3.0 with the corresponding pinned commit SHA
(replace the tag reference in the step that uses
docker/setup-buildx-action@v3.3.0). Apply the same persist-credentials change to
all other workflows that call actions/checkout@v4 and pin
docker/setup-buildx-action@v3.3.0 occurrences (e.g., docker-publish.yml) to
their commit SHAs so the workflow uses immutable references.
- Around line 10-22: Add an explicit top-level GitHub Actions permissions block
to restrict GITHUB_TOKEN to the least privileges needed (for this build-only
workflow you can set at minimum contents: read); insert a permissions: map at
the workflow root (above jobs) and, if any job like image-multiarch needs
additional rights, grant only those specific scopes at job-level (e.g., override
to packages: read or id-token: write) rather than leaving defaults open.
🪄 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: a55b8536-6297-4219-a07d-ffaed8af5ba1
📒 Files selected for processing (2)
.github/workflows/ci.ymlDockerfile
✅ Files skipped from review due to trivial changes (1)
- Dockerfile
| image-multiarch: | ||
| # Build-only assertion that the operator image builds for every published | ||
| # platform. Deliberately sets up buildx WITHOUT QEMU: the Dockerfile builder | ||
| # is pinned to $BUILDPLATFORM and Go cross-compiles via GOARCH, so both legs | ||
| # must build natively on this amd64 runner with no emulation. If the | ||
| # --platform=$BUILDPLATFORM pin regresses, the arm64 leg fails here (exec | ||
| # format error) instead of silently breaking the tag-release publish. | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: docker/setup-buildx-action@v3.3.0 | ||
| - name: Build multi-arch image (no push, no QEMU) | ||
| run: docker buildx build --platform linux/amd64,linux/arm64 -t etcd-operator:buildtest . |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify workflow files missing explicit top-level permissions
fd -p ".github/workflows/*.yml" | while read -r f; do
if ! rg -n '^\s*permissions\s*:' "$f" >/dev/null; then
echo "missing permissions: $f"
fi
doneRepository: cozystack/etcd-operator
Length of output: 49
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE=".github/workflows/ci.yml"
echo "== matches in $FILE =="
rg -n '^\s*permissions\s*:' "$FILE" || true
echo
echo "== top of $FILE (first 60 lines) =="
sed -n '1,60p' "$FILE"Repository: cozystack/etcd-operator
Length of output: 2234
Add explicit least-privilege permissions to CI workflow
.github/workflows/ci.ymlhas no top-levelpermissions:, so the workflow uses GitHub’s defaultGITHUB_TOKENscopes.
Suggested patch
name: CI
on:
pull_request:
branches: [ main ]
push:
branches: [ main ]
+
+permissions:
+ contents: read🧰 Tools
🪛 zizmor (1.25.2)
[warning] 19-19: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[warning] 10-22: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
[error] 19-19: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 20-20: 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/ci.yml around lines 10 - 22, Add an explicit top-level
GitHub Actions permissions block to restrict GITHUB_TOKEN to the least
privileges needed (for this build-only workflow you can set at minimum contents:
read); insert a permissions: map at the workflow root (above jobs) and, if any
job like image-multiarch needs additional rights, grant only those specific
scopes at job-level (e.g., override to packages: read or id-token: write) rather
than leaving defaults open.
Source: Linters/SAST tools
| - uses: actions/checkout@v4 | ||
| - uses: docker/setup-buildx-action@v3.3.0 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify mutable action refs and checkout credential persistence settings
fd -p ".github/workflows/*.yml" | while read -r f; do
echo "== $f =="
rg -n 'uses:\s*[^@]+@([^0-9a-f]{40}|[0-9a-f]{1,39}$)' "$f" || true
rg -n 'uses:\s*actions/checkout@' "$f" -A3 | sed 's/^/ /'
doneRepository: cozystack/etcd-operator
Length of output: 49
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the workflow file the review comment targets
echo "== repo workflow files =="
ls -la .github/workflows || true
if [ -f .github/workflows/ci.yml ]; then
echo "== .github/workflows/ci.yml (lines 1-80) =="
nl -ba .github/workflows/ci.yml | sed -n '1,80p'
else
echo "Missing: .github/workflows/ci.yml"
fi
echo
echo "== checkout usage across workflows =="
rg -n --hidden --glob '.github/workflows/*.yml' 'uses:\s*actions/checkout@' .github/workflows/*.yml || true
echo
echo "== checkout persist-credentials settings across workflows =="
rg -n --hidden --glob '.github/workflows/*.yml' 'persist-credentials:\s*(false|true)' .github/workflows/*.yml || true
echo
echo "== setup-buildx usage across workflows =="
rg -n --hidden --glob '.github/workflows/*.yml' 'uses:\s*docker/setup-buildx-action@' .github/workflows/*.yml || trueRepository: cozystack/etcd-operator
Length of output: 762
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== .github/workflows/ci.yml (with line numbers) =="
if [ -f .github/workflows/ci.yml ]; then
# cat -n is available; use it instead of nl
cat -n .github/workflows/ci.yml | sed -n '1,120p'
else
echo "Missing: .github/workflows/ci.yml"
fi
echo
echo "== checkout usage across workflows =="
rg -n --glob '.github/workflows/*.yml' 'uses:\s*actions/checkout@' .github/workflows/*.yml || true
echo
echo "== checkout persist-credentials across workflows =="
rg -n --glob '.github/workflows/*.yml' 'persist-credentials:\s*(false|true)' .github/workflows/*.yml || true
echo
echo "== setup-buildx usage across workflows =="
rg -n --glob '.github/workflows/*.yml' 'uses:\s*docker/setup-buildx-action@' .github/workflows/*.yml || true
echo
echo "== workflows with mutable action refs (non-40 hex SHAs) =="
rg -n --glob '.github/workflows/*.yml' 'uses:\s*[^@]+@((?<![0-9a-fA-F])[^@]+)$' .github/workflows/*.yml || true
echo
echo "== targeted mutable refs for checkout and buildx =="
rg -n --glob '.github/workflows/*.yml' 'uses:\s*(actions/checkout|docker/setup-buildx-action)@' .github/workflows/*.yml || trueRepository: cozystack/etcd-operator
Length of output: 4525
Pin mutable GitHub Actions to commit SHAs and disable checkout credential persistence
In .github/workflows/ci.yml, actions/checkout@v4 is used (lines 19 and 27) without persist-credentials: false, and docker/setup-buildx-action@v3.3.0 is also referenced via a mutable tag (line 20). The same actions/checkout@v4 pattern appears in multiple other workflows, and docker/setup-buildx-action@v3.3.0 is used in docker-publish.yml, so this is broader than just CI.
Suggested patch
steps:
- - uses: actions/checkout@v4
- - uses: docker/setup-buildx-action@v3.3.0
+ - uses: actions/checkout@<FULL_COMMIT_SHA>
+ with:
+ persist-credentials: false
+ - uses: docker/setup-buildx-action@<FULL_COMMIT_SHA>🧰 Tools
🪛 zizmor (1.25.2)
[warning] 19-19: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 19-19: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 20-20: 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/ci.yml around lines 19 - 20, Update workflow steps that
use actions/checkout@v4 to add persist-credentials: false to the checkout step
(look for the checkout step using actions/checkout@v4) and replace mutable
action tags like docker/setup-buildx-action@v3.3.0 with the corresponding pinned
commit SHA (replace the tag reference in the step that uses
docker/setup-buildx-action@v3.3.0). Apply the same persist-credentials change to
all other workflows that call actions/checkout@v4 and pin
docker/setup-buildx-action@v3.3.0 occurrences (e.g., docker-publish.yml) to
their commit SHAs so the workflow uses immutable references.
Source: Linters/SAST tools
Summary by CodeRabbit
New Features
Chores
Documentation