Skip to content

fix(ci): adapted CI workflows for the new version#328

Open
Andrey Kolkov (androndo) wants to merge 3 commits into
cozystack:mainfrom
androndo:fix/actualize_ci
Open

fix(ci): adapted CI workflows for the new version#328
Andrey Kolkov (androndo) wants to merge 3 commits into
cozystack:mainfrom
androndo:fix/actualize_ci

Conversation

@androndo

@androndo Andrey Kolkov (androndo) commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Helm chart installer, configurable CRD/RBAC rendering, and packaged release manifests
    • Automated release artifacts: multi-arch container images, Helm chart package, and cross‑compiled CLI binaries
    • CLIs now include a build-stamped version command
    • Draft release notes generation automated
  • Chores

    • Moved deployment/packaging to Helm; removed legacy kustomize artifacts
    • Added CI/CD workflows for publishing, release-assets, Docker/Helm publish, and release smoke tests
  • Documentation

    • Updated installation, migration, and operations docs for Helm-based flows

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This 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.

Changes

Release & Publishing Automation Pipeline

Layer / File(s) Summary
Release draft and automation configuration
.github/release-drafter.yml, .github/workflows/ci.yml
Release-drafter config defines version tagging, changelog categorization, and autolabel rules; CI push target updated to main and codegen drift comments expanded.
Docker image building and GHCR publishing
.github/workflows/docker-publish.yml
New workflow runs on semver tag push, builds multi-arch images via Buildx, pushes to GHCR with pinned tag, and signs the digest using cosign (keyless).
Helm chart packaging and OCI registry publishing
.github/workflows/helm-publish.yml
New workflow packages the chart, sets chart/app versions from git tag, and publishes the chart to GHCR as an OCI artifact.
Release asset generation (manifests and CLI binaries)
.github/workflows/release-assets.yml
On release creation or manual dispatch, renders manifest YAMLs and cross-compiles CLI binaries then uploads them to the GitHub Release.
Release smoke testing infrastructure
.github/workflows/release-smoke.yml
New PR-triggered/manual workflow runs manifest and Helm-mode smoke tests via matrix and verifies operator availability.

Helm Chart Implementation

Layer / File(s) Summary
Chart metadata, helpers, and configuration
charts/etcd-operator/Chart.yaml, charts/etcd-operator/.helmignore, charts/etcd-operator/templates/_helpers.tpl, charts/etcd-operator/values.yaml
Helm chart metadata, helper templates for naming/labels/images, packaging ignores, and defaults for CRDs, namespace, metrics, image, service account, scheduling, and resources.
Service account and RBAC (roles, bindings, policies)
charts/etcd-operator/templates/serviceaccount.yaml, charts/etcd-operator/templates/rbac.yaml, charts/etcd-operator/files/manager-role-rules.yaml
ServiceAccount template, ClusterRole/ClusterRoleBinding wired from generated manager-role-rules, leader-election Role/RoleBinding, optional proxy RBAC, and an unbound metrics-reader role.
Deployment, namespace, and CRD templating
charts/etcd-operator/templates/deployment.yaml, charts/etcd-operator/templates/namespace.yaml, charts/etcd-operator/templates/crds.yaml
Deployment template with manager container and optional kube-rbac-proxy sidecar, conditional Namespace rendering, and CRD templating that can inject Helm keep annotation.
Metrics service and monitoring templates
charts/etcd-operator/templates/metrics-service.yaml, charts/etcd-operator/templates/servicemonitor.yaml
Conditional metrics Service and ServiceMonitor templates for Prometheus Operator with HTTPS scrape config and render-time validation.

Build System & Deployment Tooling

Layer / File(s) Summary
Version variables and CLI ldflags
Makefile, cmd/etcd-migrate/main.go, cmd/kubectl-etcd/main.go, cmd/etcd-migrate/config.go
Added VERSION from git describe and CLI_LDFLAGS for stamping binaries; CLIs expose build version and etcd-migrate defaults split for legacy vs Helm controller refs.
Manifests target refactored for Helm chart generation
Makefile
manifests now emits CRDs and manager RBAC into chart paths and extracts manager-role rules with yq for chart consumption.
Distribution targets (dist-cli, build-dist-manifests, smoke tests)
Makefile
Added dist-cli for cross-compiled CLIs and checksums, build-dist-manifests to render split CRD/non-CRD YAMLs, and smoke test targets.
Helm-based deploy/undeploy targets replacing kustomize
Makefile
Replaced kustomize-based deploy/install/uninstall with Helm-driven deploy/undeploy and chart-valued IMG split.
Tool versions and installation (yq, helm)
Makefile
Introduced YQ_VERSION and local yq installer, HELM variable and require-helm guard; removed kustomize toolvars/targets.
Dockerfile multi-arch builder
Dockerfile
Builder stage uses --platform=$BUILDPLATFORM to support multi-arch builds; cross-compilation steps unchanged.

Documentation, Tests, and Configuration Updates

Layer / File(s) Summary
Installation and deployment documentation
docs/installation.md
Rewrote installation to support release manifests, Helm installs, and building-from-source flows; documents Helm values, CRD keep semantics, and gitops rendering recommendations.
Migration, operations, and troubleshooting guidance
README.md, docs/migration.md, docs/operations.md
README quickstart now Helm-first; migration guide updated for release binaries and Helm deploy quiesce steps; operations logs use the etcd-operator deployment name.
E2E and smoke test scripts
hack/e2e.sh, hack/release-smoke.sh
Updated e2e diagnostics to use chart labels and helm deploy; new release-smoke.sh provides end-to-end smoke testing in kind for manifest/Helm modes.
Test asset path updates and cleanup
api/v1alpha2/validation_envtest_test.go, .gitignore, main.go
CRD test helper now points to charts/etcd-operator/crd-bases; .gitignore ignores /dist; main.go placeholder error message updated to Helm deploy guidance.
Legacy kustomize configuration removal
config/manager/*, config/default/*, config/rbac/*, config/crd/*, config/prometheus/*
Removed kustomize overlays/manifests and many standalone RBAC/prometheus artifacts now generated/managed by the chart.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • cozystack/etcd-operator#280: Adds manual workflow_dispatch trigger to release-assets workflow to support manual release artifact generation.

Suggested labels

chore, documentation

Suggested reviewers

  • kvaps
  • Kirill-Garbar
  • lllamnyp
  • BROngineer

Poem

🐰 From kustomize fields I hop to Helm’s neat art,

Release notes gather, images signed from the start,
CLIs wear versions, smoke tests leap and run,
Charts hold RBAC, CRDs, and deploys in one,
A tidy release trail — hop, ship, and done!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title 'fix(ci): adapted CI workflows for the new version' is overly vague and generic. While it mentions CI workflows, it doesn't clearly convey the scope of actual changes, which include migrating from kustomize-based to Helm-based deployment, adding new release automation workflows, and restructuring build processes. Use a more specific title that captures the primary architectural change, such as 'refactor(ci): migrate deployment from kustomize to Helm' or 'feat(release): add Helm and docker publish workflows' to accurately reflect the scale and nature of the modifications.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

@github-actions github-actions Bot added the bugfix label Jun 8, 2026
@androndo Andrey Kolkov (androndo) changed the title fix(ci): rename workflow branches fix(ci): adapt CI workflow for the new vesion Jun 9, 2026
Signed-off-by: Andrey Kolkov <androndo@gmail.com>
@androndo Andrey Kolkov (androndo) changed the title fix(ci): adapt CI workflow for the new vesion fix(ci): adapted CI workflows for the new vesion Jun 10, 2026
@lllamnyp Timofei Larkin (lllamnyp) changed the base branch from v1 to main June 10, 2026 07:04

@lllamnyp Timofei Larkin (lllamnyp) 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.

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), drop config/crd/bases and the helm-sync copy step. Repoint crdBasesDir() in api/v1alpha2/validation_envtest_test.go:57/80 at 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.yaml hand-mirrors the generated config/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/undeployhelm upgrade --install / helm uninstall (the chart already renders image == OPERATOR_IMAGE, so the kustomize image-replacement contract moves into the chart). build-dist-manifestshelm template piped through the existing yq split, so release-assets still ships plain CRDs/non-CRDs YAML for non-Helm users. Then delete config/default, config/manager, config/rbac, the config/crd overlay, and the kustomize tool dep (keep config/samples; fold config/prometheus's ServiceMonitor into the chart behind a metrics.serviceMonitor.enabled value).

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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (3)
charts/etcd-operator/templates/deployment.yaml (2)

36-72: ⚖️ Poor tradeoff

Consider enabling read-only root filesystem for the manager container.

Trivy flags that securityContext.readOnlyRootFilesystem is 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 emptyDir for 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 tradeoff

Consider enabling read-only root filesystem for the kube-rbac-proxy container.

The kube-rbac-proxy sidecar should also set securityContext.readOnlyRootFilesystem: true for 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 value

Consider 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-manager label.

♻️ 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

📥 Commits

Reviewing files that changed from the base of the PR and between 3cb469f and b80e013.

📒 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
  • .gitignore
  • Makefile
  • README.md
  • api/v1alpha2/validation_envtest_test.go
  • charts/etcd-operator/.helmignore
  • charts/etcd-operator/Chart.yaml
  • charts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdclusters.yaml
  • charts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdmembers.yaml
  • charts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdsnapshots.yaml
  • charts/etcd-operator/files/manager-role-rules.yaml
  • charts/etcd-operator/templates/_helpers.tpl
  • charts/etcd-operator/templates/crds.yaml
  • charts/etcd-operator/templates/deployment.yaml
  • charts/etcd-operator/templates/metrics-service.yaml
  • charts/etcd-operator/templates/namespace.yaml
  • charts/etcd-operator/templates/rbac.yaml
  • charts/etcd-operator/templates/serviceaccount.yaml
  • charts/etcd-operator/templates/servicemonitor.yaml
  • charts/etcd-operator/values.yaml
  • cmd/etcd-migrate/config.go
  • cmd/etcd-migrate/main.go
  • cmd/kubectl-etcd/main.go
  • config/crd/kustomization.yaml
  • config/crd/kustomizeconfig.yaml
  • config/crd/patches/cainjection_in_etcdclusters.yaml
  • config/crd/patches/webhook_in_etcdclusters.yaml
  • config/default/kustomization.yaml
  • config/default/manager_auth_proxy_patch.yaml
  • config/default/manager_config_patch.yaml
  • config/manager/kustomization.yaml
  • config/manager/manager.yaml
  • config/prometheus/kustomization.yaml
  • config/prometheus/monitor.yaml
  • config/rbac/auth_proxy_client_clusterrole.yaml
  • config/rbac/auth_proxy_role.yaml
  • config/rbac/auth_proxy_role_binding.yaml
  • config/rbac/auth_proxy_service.yaml
  • config/rbac/etcdcluster_editor_role.yaml
  • config/rbac/etcdcluster_viewer_role.yaml
  • config/rbac/kustomization.yaml
  • config/rbac/leader_election_role.yaml
  • config/rbac/leader_election_role_binding.yaml
  • config/rbac/role.yaml
  • config/rbac/role_binding.yaml
  • config/rbac/service_account.yaml
  • docs/installation.md
  • docs/migration.md
  • docs/operations.md
  • hack/e2e.sh
  • hack/release-smoke.sh
  • main.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

Comment on lines +46 to +50
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Quote variables and consider input validation.

The shell script has two issues flagged by static analysis:

  1. Template injection risk: ${{ github.ref_name }} is expanded directly into shell without sanitization
  2. 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.

Comment on lines +53 to +57
run: |
helm registry login \
--username ${{ github.actor }} \
--password ${{ secrets.GITHUB_TOKEN }} \
${{ env.REGISTRY }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

Comment on lines +59 to +70
- 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 }}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Comment on lines +38 to +42
- 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}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
- 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.

Comment on lines +84 to +88
- name: Resolve release tag
run: echo "RELEASE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV

- name: Cross-compile CLIs
run: make dist-cli VERSION=${RELEASE_TAG}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +20 to +22
permissions:
contents: write
pull-requests: write

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 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 -ba

Repository: 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
PY

Repository: 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
PY

Repository: 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:


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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

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.

Comment on lines +41 to +46
{{- define "etcd-operator.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{- include "etcd-operator.fullname" . -}}
{{- else -}}
default
{{- end -}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
{{- 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.

@androndo Andrey Kolkov (androndo) changed the title fix(ci): adapted CI workflows for the new vesion fix(ci): adapted CI workflows for the new version Jun 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (5)
.github/workflows/helm-publish.yml (3)

53-57: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Quote the template expansion in helm login command.

The github.actor template 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 win

Template 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 win

Quote 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 win

Quote 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 win

Template 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 value

Consider setting persist-credentials: false on checkout.

The checkout action doesn't explicitly disable credential persistence. While this workflow doesn't run untrusted code, setting persist-credentials: false is 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 value

Consider setting persist-credentials: false on 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: false is 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: false

Apply 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

📥 Commits

Reviewing files that changed from the base of the PR and between b80e013 and 7b04604.

📒 Files selected for processing (49)
  • .github/workflows/ci.yml
  • .github/workflows/helm-publish.yml
  • .github/workflows/release-assets.yml
  • .github/workflows/release-smoke.yml
  • Makefile
  • README.md
  • api/v1alpha2/validation_envtest_test.go
  • charts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdclusters.yaml
  • charts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdmembers.yaml
  • charts/etcd-operator/crd-bases/etcd-operator.cozystack.io_etcdsnapshots.yaml
  • charts/etcd-operator/files/manager-role-rules.yaml
  • charts/etcd-operator/templates/crds.yaml
  • charts/etcd-operator/templates/namespace.yaml
  • charts/etcd-operator/templates/rbac.yaml
  • charts/etcd-operator/templates/servicemonitor.yaml
  • charts/etcd-operator/values.yaml
  • cmd/etcd-migrate/config.go
  • cmd/etcd-migrate/main.go
  • cmd/kubectl-etcd/main.go
  • config/crd/kustomization.yaml
  • config/crd/kustomizeconfig.yaml
  • config/crd/patches/cainjection_in_etcdclusters.yaml
  • config/crd/patches/webhook_in_etcdclusters.yaml
  • config/default/kustomization.yaml
  • config/default/manager_auth_proxy_patch.yaml
  • config/default/manager_config_patch.yaml
  • config/manager/kustomization.yaml
  • config/manager/manager.yaml
  • config/prometheus/kustomization.yaml
  • config/prometheus/monitor.yaml
  • config/rbac/auth_proxy_client_clusterrole.yaml
  • config/rbac/auth_proxy_role.yaml
  • config/rbac/auth_proxy_role_binding.yaml
  • config/rbac/auth_proxy_service.yaml
  • config/rbac/etcdcluster_editor_role.yaml
  • config/rbac/etcdcluster_viewer_role.yaml
  • config/rbac/kustomization.yaml
  • config/rbac/leader_election_role.yaml
  • config/rbac/leader_election_role_binding.yaml
  • config/rbac/role.yaml
  • config/rbac/role_binding.yaml
  • config/rbac/service_account.yaml
  • config/samples/kustomization.yaml
  • docs/installation.md
  • docs/migration.md
  • docs/operations.md
  • hack/e2e.sh
  • hack/release-smoke.sh
  • main.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

Comment thread cmd/etcd-migrate/main.go
Comment on lines +73 to +82
// 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)
},
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
// 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>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 7b04604 and 27ff90d.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml
  • Dockerfile
✅ Files skipped from review due to trivial changes (1)
  • Dockerfile

Comment thread .github/workflows/ci.yml
Comment on lines +10 to +22
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 .

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 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
done

Repository: 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.yml has no top-level permissions:, so the workflow uses GitHub’s default GITHUB_TOKEN scopes.
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

Comment thread .github/workflows/ci.yml
Comment on lines +19 to +20
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3.3.0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 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/^/  /'
done

Repository: 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 || true

Repository: 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 || true

Repository: 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants