perf(ci): parallelize e2e operator build prerequisites - #993
Conversation
bennyz
commented
Aug 10, 2026
- Parallelize independent Make prerequisites (docker-build, build-operator, cluster) with -j so kind cluster creation overlaps with Go compilation
- Add CI-optimized build path that compiles Go on the runner (with setup-go module cache) and packages binaries into minimal runtime-only containers, eliminating the ~1.5GB go-toolset image pull and in-container module downloads
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThe controller CI path now builds static binaries, packages prebuilt UBI-based images, deploys through CI-specific Make targets, and runs operator E2E tests with cached Go dependencies and parallel execution. ChangesCI Build and Deployment
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant ControllerKindWorkflow
participant ControllerMakefile
participant GoCompiler
participant PrebuiltContainer
participant OperatorE2E
ControllerKindWorkflow->>ControllerMakefile: Invoke CI deployment and E2E targets
ControllerMakefile->>GoCompiler: Compile static Linux binaries
GoCompiler->>PrebuiltContainer: Provide staged binaries
PrebuiltContainer->>ControllerMakefile: Produce CI images
ControllerMakefile->>OperatorE2E: Deploy and run operator E2E tests
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ 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 |
87e9e4d to
91ed779
Compare
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/controller-kind.yaml:
- Around line 18-26: Add a workflow-level or job-level permissions block for the
workflow containing the “Run make deploy” step, setting contents to read. Keep
permissions least-privilege and add no other scopes unless an existing workflow
step explicitly requires them.
In `@controller/Makefile`:
- Around line 221-227: Update the deploy-ci target prerequisites so
docker-build-ci and build-operator-ci are included only when SKIP_BUILD is
empty, alongside cluster and grpcurl, allowing Make to parallelize them. Remove
the corresponding recursive $(MAKE) commands from deploy-ci’s recipe, leaving
only ./hack/deploy_with_operator.sh.
🪄 Autofix
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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 02b84c70-f0b8-41ed-999d-5fe0ff5e1ef8
📒 Files selected for processing (6)
.github/workflows/controller-kind.yamlcontroller/Containerfile.cicontroller/Containerfile.exporter-set-controller.cicontroller/Containerfile.operator.cicontroller/Makefilecontroller/deploy/operator/Makefile
91ed779 to
6b604ba
Compare
6b604ba to
500b570
Compare
docker-build, docker-build-exporter-set-controller, build-operator, and cluster creation are independent targets. Running with -j3 lets them overlap, saving ~2-3 minutes of sequential container builds. Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
500b570 to
1b5442f
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
controller/Containerfile.prebuilt (1)
4-5: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRemove the unnecessary context copy.
controller/deploy/operator/MakefileLine 184 usesbin/ci-stageas the build context.COPY . .therefore copies the staged binary to/manager, and Line 5 copies it again to/entrypoint. This duplicates the executable in the image and conflicts with the minimal runtime-image objective.Proposed change
WORKDIR / -COPY . . COPY ${BIN} /entrypoint🤖 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 `@controller/Containerfile.prebuilt` around lines 4 - 5, Remove the COPY . . instruction from Containerfile.prebuilt, leaving the COPY ${BIN} /entrypoint instruction as the only artifact copy. Preserve the existing prebuilt-image build flow and entrypoint location.
🤖 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 `@controller/Containerfile.prebuilt`:
- Around line 5-7: Remove the redundant binary copy to /entrypoint in the
Containerfile, retain the existing ${BIN} path copied by COPY . ., and update
ENTRYPOINT and any related runtime references to consistently execute /${BIN}
(the deployed /manager path).
---
Nitpick comments:
In `@controller/Containerfile.prebuilt`:
- Around line 4-5: Remove the COPY . . instruction from Containerfile.prebuilt,
leaving the COPY ${BIN} /entrypoint instruction as the only artifact copy.
Preserve the existing prebuilt-image build flow and entrypoint location.
🪄 Autofix
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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e4b94c61-bacb-4e13-bf8a-575da2328666
📒 Files selected for processing (3)
controller/Containerfile.prebuiltcontroller/Makefilecontroller/deploy/operator/Makefile
| COPY ${BIN} /entrypoint | ||
| USER 65532:65532 | ||
| ENTRYPOINT ["/entrypoint"] |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n -C 3 '(/manager|/entrypoint)' controllerRepository: jumpstarter-dev/jumpstarter
Length of output: 7688
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- Containerfiles ---'
cat -n controller/Containerfile.prebuilt
cat -n controller/Containerfile.operator
printf '%s\n' '--- Operator manager manifest ---'
cat -n controller/deploy/operator/config/manager/manager.yaml
printf '%s\n' '--- Relevant build targets ---'
sed -n '135,152p' controller/Makefile
sed -n '176,187p' controller/deploy/operator/Makefile
printf '%s\n' '--- All operator command/entrypoint/probe references ---'
rg -n -C 4 'command:|/manager|/entrypoint|livenessProbe|readinessProbe|startupProbe' controller/deploy/operator controller/Containerfile.prebuilt controller/Containerfile.operatorRepository: jumpstarter-dev/jumpstarter
Length of output: 18058
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
prebuilt = Path("controller/Containerfile.prebuilt").read_text()
operator = Path("controller/Containerfile.operator").read_text()
manager = Path("controller/deploy/operator/config/manager/manager.yaml").read_text()
copy_all = bool(re.search(r"^COPY\s+\.\s+\.$", prebuilt, re.M))
copy_bin = re.search(r"^COPY\s+\$\{BIN\}\s+(\S+)$", prebuilt, re.M)
prebuilt_entrypoint = re.search(r"^ENTRYPOINT\s+(.+)$", prebuilt, re.M)
operator_entrypoint = re.search(r"^ENTRYPOINT\s+(.+)$", operator, re.M)
manager_command = re.search(r"^\s*-\s+(/(?:manager|entrypoint))\s*$", manager, re.M)
print({
"prebuilt_COPY_dot_dot": copy_all,
"prebuilt_binary_destination": copy_bin.group(1) if copy_bin else None,
"prebuilt_entrypoint": prebuilt_entrypoint.group(1) if prebuilt_entrypoint else None,
"operator_entrypoint": operator_entrypoint.group(1) if operator_entrypoint else None,
"generated_manager_command": manager_command.group(1) if manager_command else None,
"prebuilt_contains_manager_via_context_copy": copy_all,
})
PYRepository: jumpstarter-dev/jumpstarter
Length of output: 417
Remove the duplicate binary copy.
COPY . . already copies ${BIN} to /${BIN}, while the next instruction copies it again to /entrypoint. The deployment explicitly runs /manager, so keep one runtime path and update all consumers consistently.
🤖 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 `@controller/Containerfile.prebuilt` around lines 5 - 7, Remove the redundant
binary copy to /entrypoint in the Containerfile, retain the existing ${BIN} path
copied by COPY . ., and update ENTRYPOINT and any related runtime references to
consistently execute /${BIN} (the deployed /manager path).
1b5442f to
592c531
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
controller/Makefile (2)
147-153: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winResolve the
checkmakerecipe-length warning.
checkmakereports six recipe lines fordocker-build-ci, above its configured maximum of five. Split binary staging and compilation into a helper target, or update the rule if this target is intentionally exempt.Example target split
+.PHONY: docker-build-ci-binaries +docker-build-ci-binaries: + rm -rf bin/ci-stage && mkdir -p bin/ci-stage/controller bin/ci-stage/esc + CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o bin/ci-stage/controller/manager cmd/main.go + CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o bin/ci-stage/controller/router cmd/router/main.go + CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o bin/ci-stage/esc/exporter-set-controller cmd/exporter-set-controller/main.go + .PHONY: docker-build-ci -docker-build-ci: ## Build docker images from pre-compiled host binaries (fast CI path). - rm -rf bin/ci-stage && mkdir -p bin/ci-stage/controller bin/ci-stage/esc - CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o bin/ci-stage/controller/manager cmd/main.go - CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o bin/ci-stage/controller/router cmd/router/main.go - CGO_ENABLED=0 GOOS=linux GOARCH=$(GOARCH) go build -ldflags "$(LDFLAGS)" -o bin/ci-stage/esc/exporter-set-controller cmd/exporter-set-controller/main.go +docker-build-ci: docker-build-ci-binaries ## Build docker images from pre-compiled host binaries (fast CI path). $(CONTAINER_TOOL) build --build-arg BIN=manager -t $(IMG) -f Containerfile.prebuilt bin/ci-stage/controller $(CONTAINER_TOOL) build --build-arg BIN=exporter-set-controller -t $(EXPORTER_SET_CONTROLLER_IMG) -f Containerfile.prebuilt bin/ci-stage/escThe checkmake warning comes from the supplied static-analysis result. As per coding guidelines, use
make lint-fixrather than invoking the linter directly.🤖 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 `@controller/Makefile` around lines 147 - 153, Reduce the recipe count for docker-build-ci to five or fewer lines by moving the staging setup and Go build commands into a dedicated helper target, then make docker-build-ci depend on that target before running the container builds. Preserve the existing build outputs and arguments, and validate the change with make lint-fix.Sources: Coding guidelines, Linters/SAST tools
30-30: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAlign CI image platforms with
GOARCH. WhenGOARCHdiffers from the builder architecture,docker-build-ciembeds cross-compiled binaries in images built for the builder’s default platform. Pass--platform=linux/$(GOARCH)to both image builds, or reject cross-architecture overrides.🤖 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 `@controller/Makefile` at line 30, Update the docker-build-ci image build commands to pass --platform=linux/$(GOARCH) for both builds, ensuring image platforms match the configured GOARCH; alternatively, explicitly reject GOARCH values that differ from the builder architecture.
🤖 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 `@controller/deploy/operator/Makefile`:
- Around line 74-75: Update the docker-build-ci image build flow to pass the
target platform derived from GOARCH to the container build, rather than allowing
the host platform default; include GOARM or other supported architecture
variants in the mapping so the image platform matches the generated /entrypoint
architecture.
---
Nitpick comments:
In `@controller/Makefile`:
- Around line 147-153: Reduce the recipe count for docker-build-ci to five or
fewer lines by moving the staging setup and Go build commands into a dedicated
helper target, then make docker-build-ci depend on that target before running
the container builds. Preserve the existing build outputs and arguments, and
validate the change with make lint-fix.
- Line 30: Update the docker-build-ci image build commands to pass
--platform=linux/$(GOARCH) for both builds, ensuring image platforms match the
configured GOARCH; alternatively, explicitly reject GOARCH values that differ
from the builder architecture.
🪄 Autofix
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: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: f93aac18-b60b-4cc9-ba70-cafd616528c8
📒 Files selected for processing (3)
.github/workflows/controller-kind.yamlcontroller/Makefilecontroller/deploy/operator/Makefile
🚧 Files skipped from review as they are similar to previous changes (1)
- .github/workflows/controller-kind.yaml
| GOARCH ?= $(shell go env GOARCH) | ||
|
|
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- Makefile context ---'
sed -n '1,230p' controller/deploy/operator/Makefile
printf '%s\n' '--- Containerfile.prebuilt locations and contents ---'
fd -i 'Containerfile.prebuilt' .
for f in $(fd -i 'Containerfile.prebuilt' .); do
echo "### $f"
cat -n "$f"
done
printf '%s\n' '--- architecture-related references ---'
rg -n --glob 'Makefile' --glob 'Containerfile*' --glob '*.yaml' --glob '*.yml' \
'GOARCH|GOARM|TARGET_PLATFORM|platform=|Containerfile\.prebuilt|bin/ci-stage|entrypoint' .Repository: jumpstarter-dev/jumpstarter
Length of output: 15697
🏁 Script executed:
#!/bin/bash
set -eu
python3 - <<'PY'
from pathlib import Path
import re
makefile = Path("controller/deploy/operator/Makefile").read_text()
containerfile = Path("controller/Containerfile.prebuilt").read_text()
target = re.search(
r"docker-build-ci:.*?\n(?P<body>(?:\t.*\n)+)",
makefile,
)
assert target, "docker-build-ci target not found"
body = target.group("body")
assert "GOARCH=$(GOARCH)" in body, "docker-build-ci does not compile with GOARCH"
build = next(
line for line in body.splitlines()
if "$(CONTAINER_TOOL) build" in line
)
assert "--platform" not in build, "build command already specifies --platform"
assert re.search(r"^FROM\s+(?!--platform=)", containerfile, re.M), \
"Containerfile.prebuilt unexpectedly sets a FROM platform"
assert "COPY ${BIN} /entrypoint" in containerfile, \
"Containerfile.prebuilt does not copy the prebuilt binary"
assert "ENTRYPOINT [\"/entrypoint\"]" in containerfile, \
"Containerfile.prebuilt does not execute the copied binary"
print("docker-build-ci compiles with configurable GOARCH")
print("its container build has no --platform option")
print("Containerfile.prebuilt has no FROM --platform override")
print("the copied binary is the image entrypoint")
PY
printf '%s\n' '--- docker-build-ci invocations and GOARCH overrides ---'
rg -n -C 3 'docker-build-ci|GOARCH=|make .*docker-build-ci' \
.github controller --glob '*.yaml' --glob '*.yml' --glob 'Makefile' \
--glob '*.mk' || trueRepository: jumpstarter-dev/jumpstarter
Length of output: 3823
Set the image platform from GOARCH.
When GOARCH differs from the host, docker-build-ci creates a target-architecture /entrypoint, but the container build defaults to the host platform. The image can advertise the host architecture and fail with exec format error.
Proposed fix
GOARCH ?= $(shell go env GOARCH)
+TARGET_PLATFORM ?= linux/$(GOARCH)
...
- $(CONTAINER_TOOL) build --build-arg BIN=manager -t $(IMG) -f ../../Containerfile.prebuilt bin/ci-stage
+ $(CONTAINER_TOOL) build --platform=$(TARGET_PLATFORM) --build-arg BIN=manager -t $(IMG) -f ../../Containerfile.prebuilt bin/ci-stageMap variants such as GOARM if the repository supports them.
🤖 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 `@controller/deploy/operator/Makefile` around lines 74 - 75, Update the
docker-build-ci image build flow to pass the target platform derived from GOARCH
to the container build, rather than allowing the host platform default; include
GOARM or other supported architecture variants in the mapping so the image
platform matches the generated /entrypoint architecture.
592c531 to
5052dab
Compare
bkhizgiy
left a comment
There was a problem hiding this comment.
looks goo overall, just a small comment
| COPY . . | ||
| COPY ${BIN} /entrypoint |
There was a problem hiding this comment.
COPY . . copies everything from the build context, including the binaries, since ${BIN} is copied again as /entrypoint, we end up shipping duplicate manager/router binaries that aren't used.
So it probably can be simplified to just COPY ${BIN} /entrypoint
There was a problem hiding this comment.
coderabbit commented on this too,
the controller image has manager and router, and we have a third exporter-set-controller
so in order to get router too we copy it in COPY . .
at the expense of an extra 40-50 throwaway MBs
I'll add a comment to clarify
…ntainers Adds a CI-optimized build path that: - Compiles Go binaries directly on the runner (with setup-go module cache) - Packages them into minimal runtime-only containers (no Go toolchain) - Eliminates pulling the ~1.5GB go-toolset image for each build - Eliminates re-downloading modules inside containers without cache New targets: docker-build-ci, build-operator-ci, deploy-operator-ci, test-operator-e2e-ci. The CI workflow now uses setup-go with cache and the -ci targets. Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
Use setup-go with cached modules and the deploy-ci target for the deploy-kind job too (was only on e2e-test-operator). Run with -j to parallelize cluster creation with Go compilation. Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
5052dab to
64cd0e7
Compare