Skip to content

enable in-repo ci-op config#5212

Draft
Prucek wants to merge 1 commit into
openshift:mainfrom
Prucek:in-repo-config-plugin
Draft

enable in-repo ci-op config#5212
Prucek wants to merge 1 commit into
openshift:mainfrom
Prucek:in-repo-config-plugin

Conversation

@Prucek
Copy link
Copy Markdown
Member

@Prucek Prucek commented May 28, 2026

Summary

  • Add a Prow external plugin (in-repo-config-plugin) that enables repos to manage CI configuration in-repo via .ci-operator.yaml files instead of the centralized openshift/release repository
  • /onboard command bootstraps a repo with config-checker presubmit and prowgen postsubmit jobs on EFS
  • /new-test command creates ProwJob CRs directly from the PR's .ci-operator/ configs for new tests not yet on EFS, with --unresolved-config so ci-operator uses the in-repo config
  • Add --from-file mode to ci-operator-prowgen for generating jobs from a single ci-operator config file
  • Add UnresolvedConfig podspec mutator and WriteBranchToDir for branch-scoped job writing

🤖 Generated with Claude Code

@openshift-merge-bot
Copy link
Copy Markdown
Contributor

Pipeline controller notification
This repo is configured to use the pipeline controller. Second-stage tests will be triggered either automatically or after lgtm label is added, depending on the repository configuration. The pipeline controller will automatically detect which contexts are required and will utilize /test Prow commands to trigger the second stage.

For optional jobs, comment /test ? to see a list of all defined jobs. To trigger manually all jobs from second stage use /pipeline required command.

This repository is configured in: automatic mode

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 28, 2026

📝 Walkthrough

Walkthrough

Adds an in-repo-config Prow webhook plugin (commands: /onboard, /new-test) with bootstrap job generation and ephemeral presubmit creation; extends prowgen to accept single-file inputs, propagate an unresolved-config path into job PodSpecs, and add utilities to write per-branch job shards.

Changes

In-repo-config Prow plugin

Layer / File(s) Summary
Bootstrap job generation
cmd/in-repo-config-plugin/bootstrap.go, cmd/in-repo-config-plugin/bootstrap_test.go
generateBootstrapJobs produces presubmit and postsubmit job templates scoped to an exact branch (escaped regex), with presubmit run-if-changed .ci-operator/, prowgen PVC mount for /etc/jobs, decoration enabled, and tests verifying names, branch regex, images, and volume/PVC wiring.
Server types and command routing
cmd/in-repo-config-plugin/server.go
Defines server struct, GitHub client and trust-checker interfaces, help provider, and issue-comment dispatcher that filters to PR-created comments and routes /onboard and /new-test.
Onboard command handler
cmd/in-repo-config-plugin/server.go
/onboard verifies trust, checks for pre-existing EFS or centralized configs, generates bootstrap jobs for the PR base branch, writes to EFS, and posts a success or error comment.
New-test command handler and helpers
cmd/in-repo-config-plugin/server.go, cmd/in-repo-config-plugin/server_test.go
/new-test verifies trust, fetches .ci-operator/ configs from the PR (directory or single-file fallback), sets UnresolvedConfigPath on configs, generates jobs via prowgen, suppresses duplicates against permanent jobs on EFS, creates presubmit ProwJobs, and comments results; includes metadata parsing, shortSHA, dirExists, and test coverage.
Plugin entrypoint and image
cmd/in-repo-config-plugin/main.go, images/in-repo-config-plugin/Dockerfile
Parses flags, validates options, starts secrets agent, wires GitHub and ProwJob clients, registers handlers and readiness, and packages the binary in a CentOS Stream 8 image.

prowgen single-file and job utilities

Layer / File(s) Summary
ci-operator-prowgen --from-file
cmd/ci-operator-prowgen/main.go
Adds --from-file mode: read (gz-aware) single ci-operator file, unmarshal/validate zz_generated_metadata, optionally resolve config, set UnresolvedConfigPath, generate jobs, and write outputs to the target org/repo directory.
Unresolved config propagation
pkg/api/types.go, pkg/prowgen/jobbase.go
Adds UnresolvedConfigPath to ReleaseBuildConfiguration (JSON ignored); sparse-checkout includes the unresolved config basename and job base builder appends UnresolvedConfig(...) PodSpec component when set.
PodSpec mutator and fixture
pkg/prowgen/podspec.go, pkg/prowgen/testdata/*
Adds UnresolvedConfig(configPath string) PodSpec mutator to inject --unresolved-config=<path> into the first container; includes fixture YAML asserting the argument in generated job PodSpec.
Job writing helper and tests
pkg/jobconfig/files.go, pkg/prowgen/jobbase_test.go
Adds WriteBranchToDir to shard JobConfig by branch and write deterministic files; updates builder tests to cover unresolved-config scenarios and ensure sparse-checkout/podspec behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes


Possibly related PRs

  • openshift/ci-tools#5207: overlaps in sparse-checkout/jobbase behavior (disable_sparse_checkout vs. unresolved-config sparse inclusion).

Suggested labels

lgtm


Suggested reviewers

  • droslean
  • danilo-gemoli

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 3 warnings, 1 inconclusive)

Check name Status Explanation Resolution
No-Sensitive-Data-In-Logs ❌ Error The commentError function renders raw backend errors into public GitHub comments, exposing infrastructure details like file paths and Kubernetes endpoints. Sanitize error messages before posting to GitHub; log full errors to logrus only and return user-friendly messages for PR comments.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Go Error Handling ⚠️ Warning PR violates error handling patterns: ignores ParseLevel error (line 86); dirExists() hides I/O errors by returning bool instead of (bool, error); UnresolvedConfig() lacks doc comment. Handle ParseLevel error at line 86; change dirExists to (bool, error); add doc comment for exported UnresolvedConfig; sanitize errors in commentError().
Test Coverage For New Features ⚠️ Warning WriteBranchToDir (pkg/jobconfig/files.go) is a new exported function with +57 lines that lacks any unit test coverage. Per guidelines, exported functions should have tests. Add unit tests for WriteBranchToDir covering file sharding, label generation, and per-branch YAML output.
Test Structure And Quality ❓ Inconclusive Custom check specifies "Review Ginkgo test code" but the PR adds only standard Go testing.T tests (no Ginkgo/onsi imports). The check instructions are inapplicable to testing.T-based tests. Clarify whether this check applies to standard Go tests or only Ginkgo-based tests, or repurpose criteria for testing.T pattern compliance.
✅ Passed checks (12 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: enabling in-repo CI operator configuration management through a new plugin.
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.
Stable And Deterministic Test Names ✅ Passed PR contains no Ginkgo tests; it uses standard Go testing with stable, deterministic names. All test case titles are static strings with no dynamic content.
Microshift Test Compatibility ✅ Passed No Ginkgo e2e tests are added in this PR; only standard Go unit tests using func TestXxx(t *testing.T) pattern.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests are added in this PR. All new tests are standard Go unit tests for CI tooling and use the testing package, not Ginkgo framework.
Topology-Aware Scheduling Compatibility ✅ Passed PR adds a Prow webhook plugin without scheduling constraints. Generated job configs contain no affinity, topology spread, node selectors, or topology-specific constraints.
Ote Binary Stdout Contract ✅ Passed The in-repo-config-plugin is a Prow webhook server, not an OTE binary. It has no JSON stdout contract and no direct stdout writes in main() or init(). Check is inapplicable.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed PR contains only standard Go unit tests (using testing.T), not Ginkgo e2e tests. The custom check for Ginkgo e2e test IPv6/disconnected network compatibility is not applicable.
No-Weak-Crypto ✅ Passed PR contains no weak crypto algorithms (MD5/SHA1/DES/RC4/etc), custom crypto implementations, or insecure secret comparisons. HMAC handling delegated to Prow libraries.
Container-Privileges ✅ Passed No container privilege-escalation flags found: no privileged: true, hostPID, hostNetwork, hostIPC, SYS_ADMIN, or allowPrivilegeEscalation settings in PR files.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@Prucek Prucek marked this pull request as draft May 28, 2026 13:31
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label May 28, 2026
@Prucek Prucek changed the title feat(prowgen): add in-repo-config Prow plugin prowgen: add in-repo-config Prow plugin May 28, 2026
@openshift-ci openshift-ci Bot requested review from bear-redhat and droslean May 28, 2026 13:31
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 28, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Prucek

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 28, 2026
@Prucek Prucek force-pushed the in-repo-config-plugin branch from d862c2b to 2e3de1a Compare May 28, 2026 13:37
@Prucek Prucek changed the title prowgen: add in-repo-config Prow plugin prowgen: enable in-repo ci-op config May 28, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (2)
images/in-repo-config-plugin/Dockerfile (1)

1-3: ⚡ Quick win

Add HEALTHCHECK for operational monitoring.

The Dockerfile lacks a HEALTHCHECK directive. Since this is a webhook server, adding a health endpoint check improves container orchestration and monitoring.

As per coding guidelines: "HEALTHCHECK defined".

💚 Proposed addition
 FROM quay.io/centos/centos:stream8
 ADD in-repo-config-plugin /usr/bin/in-repo-config-plugin
+HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
+  CMD ["/usr/bin/in-repo-config-plugin", "--health-check"]
 ENTRYPOINT ["/usr/bin/in-repo-config-plugin"]

Note: Verify whether the binary supports a --health-check flag or similar. If the webhook server exposes an HTTP health endpoint (common in Prow plugins), use a curl-based check instead:

HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:8888/healthz || exit 1
🤖 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 `@images/in-repo-config-plugin/Dockerfile` around lines 1 - 3, The Dockerfile
for the in-repo-config-plugin container has no HEALTHCHECK; add a HEALTHCHECK
directive so orchestration can monitor the webhook process started by ENTRYPOINT
(binary in-repo-config-plugin). If the binary supports a health flag (e.g.,
--health-check or --health-port) invoke it via the HEALTHCHECK CMD, otherwise
use an HTTP probe such as curling the plugin’s health endpoint (for example
http://localhost:8888/healthz) with sensible --interval and --timeout settings
and a non-zero exit on failure; place this HEALTHCHECK line after the ENTRYPOINT
so the liveness probe validates the actual running process.
cmd/in-repo-config-plugin/bootstrap.go (1)

81-82: ⚡ Quick win

Use canonical in-repo config filename constant

At Line 81, ".ci-operator.yaml" is hardcoded. Reuse cioperatorapi.CIOperatorInrepoConfigFileName to avoid drift between bootstrap and /new-test behavior.

Suggested diff
+	cioperatorapi "github.com/openshift/ci-tools/pkg/api"
...
-						fmt.Sprintf("--from-file=%s/.ci-operator.yaml", repoPath),
+						fmt.Sprintf("--from-file=%s/%s", repoPath, cioperatorapi.CIOperatorInrepoConfigFileName),
🤖 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/in-repo-config-plugin/bootstrap.go` around lines 81 - 82, The hardcoded
filename ".ci-operator.yaml" should be replaced with the canonical constant to
avoid drift; update the fmt.Sprintf call that builds the from-file path (the
expression currently using fmt.Sprintf("--from-file=%s/.ci-operator.yaml",
repoPath)) to use cioperatorapi.CIOperatorInrepoConfigFileName instead (e.g.,
fmt.Sprintf("--from-file=%s/%s", repoPath,
cioperatorapi.CIOperatorInrepoConfigFileName)), ensuring you import the
cioperatorapi package if not already imported.
🤖 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/in-repo-config-plugin/server_test.go`:
- Around line 324-415: Add a regression test case in
TestHandleIssueCommentDispatch to assert the `/new-testfoo` edge-case is
ignored: inside the testCases slice add an entry with name like "ignores
/new-testfoo suffix", body "/new-testfoo", action
github.IssueCommentActionCreated, isPR true, and expectComments 0 so that
handleIssueComment (and the fakeGithubClient used in the test) will fail the
test if the `/new-test` matcher wrongly triggers on `/new-testfoo`.

In `@cmd/in-repo-config-plugin/server.go`:
- Around line 122-123: The CreateComment calls on s.ghc
(s.ghc.CreateComment(...)) are ignoring returned errors; update each call
(including the other occurrences you flagged) to capture the error return and
log it (e.g., err := s.ghc.CreateComment(...); if err != nil {
s.logger.Errorf("CreateComment failed for %s/%s#%d: %v", org, repo, number, err)
}) or create a small helper like createCommentAndLog(org, repo, number, body)
that calls s.ghc.CreateComment, checks the error, and logs a descriptive
message; replace the raw calls with that helper to ensure consistent error
handling and logging across server.go.
- Around line 280-289: The success message is built from jobNames (which
includes Presubmits, Postsubmits, and Periodics) but the creation loop only
creates presubmit ProwJobs, causing misleading “ProwJobs created” messages when
nothing was created; update the code that builds the success message to use the
actual created list rather than jobNames (e.g., use the slice/variable you
append created presubmit names to in the creation loop, or filter jobNames to
only the presubmits from allJobs.PresubmitsStatic[orgrepo]) so the message only
reports jobs that were truly created; make the same fix for the corresponding
block covering the other region noted (around the other success message).
- Around line 99-100: The current command match using strings.HasPrefix(body,
newTestPrefix) is too loose and will match inputs like "/new-testfoo"; update
the match in the switch/case around handleNewTest (referencing newTestPrefix and
s.handleNewTest) to only trigger when body == "/new-test" or when body starts
with "/new-test " (slash-new-test followed by a space) so arguments remain
supported but accidental concatenations are rejected; implement the conditional
check accordingly and keep calling s.handleNewTest(l, ic) only when that
stricter match passes.

In `@images/in-repo-config-plugin/Dockerfile`:
- Line 2: Replace the Dockerfile ADD instruction with COPY to avoid ADD's extra
behavior; change the line that currently uses "ADD in-repo-config-plugin
/usr/bin/in-repo-config-plugin" to use "COPY" so the file in-repo-config-plugin
is explicitly copied into /usr/bin/in-repo-config-plugin without unintended
extraction or URL fetching.
- Around line 1-3: The Dockerfile currently runs the plugin as root; add a USER
directive after adding the binary and before ENTRYPOINT to switch to a non-root
UID:GID that matches the EFS and git-sync mounts (e.g., USER 1000:1000 or USER
65532:65532). Update the Dockerfile around the ADD in-repo-config-plugin and
ENTRYPOINT ["/usr/bin/in-repo-config-plugin"] lines to include the chosen USER
and ensure file ownership/permissions on /usr/bin/in-repo-config-plugin and any
expected runtime dirs (like --job-config-dir and --release-repo-dir) are set to
that UID:GID so the container can read/write those mounts.
- Line 1: Replace the non-compliant base image on the Dockerfile's FROM line
(currently "quay.io/centos/centos:stream8") with a UBI minimal or distroless
image sourced from catalog.redhat.com (use the appropriate image name/tag
provided by Red Hat); if you must keep a non-Red Hat base, pin it by digest
(e.g., change "quay.io/centos/centos:stream8" to
"quay.io/centos/centos@sha256:..."). Update the FROM instruction accordingly and
verify the new base meets the project's security rules.

---

Nitpick comments:
In `@cmd/in-repo-config-plugin/bootstrap.go`:
- Around line 81-82: The hardcoded filename ".ci-operator.yaml" should be
replaced with the canonical constant to avoid drift; update the fmt.Sprintf call
that builds the from-file path (the expression currently using
fmt.Sprintf("--from-file=%s/.ci-operator.yaml", repoPath)) to use
cioperatorapi.CIOperatorInrepoConfigFileName instead (e.g.,
fmt.Sprintf("--from-file=%s/%s", repoPath,
cioperatorapi.CIOperatorInrepoConfigFileName)), ensuring you import the
cioperatorapi package if not already imported.

In `@images/in-repo-config-plugin/Dockerfile`:
- Around line 1-3: The Dockerfile for the in-repo-config-plugin container has no
HEALTHCHECK; add a HEALTHCHECK directive so orchestration can monitor the
webhook process started by ENTRYPOINT (binary in-repo-config-plugin). If the
binary supports a health flag (e.g., --health-check or --health-port) invoke it
via the HEALTHCHECK CMD, otherwise use an HTTP probe such as curling the
plugin’s health endpoint (for example http://localhost:8888/healthz) with
sensible --interval and --timeout settings and a non-zero exit on failure; place
this HEALTHCHECK line after the ENTRYPOINT so the liveness probe validates the
actual running process.
🪄 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: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 3792b6e7-6ac0-4af1-a70e-c6449770ca46

📥 Commits

Reviewing files that changed from the base of the PR and between cbe040e and d862c2b.

📒 Files selected for processing (6)
  • cmd/in-repo-config-plugin/bootstrap.go
  • cmd/in-repo-config-plugin/bootstrap_test.go
  • cmd/in-repo-config-plugin/main.go
  • cmd/in-repo-config-plugin/server.go
  • cmd/in-repo-config-plugin/server_test.go
  • images/in-repo-config-plugin/Dockerfile

Comment on lines +324 to +415
func TestHandleIssueCommentDispatch(t *testing.T) {
testCases := []struct {
name string
body string
action github.IssueCommentEventAction
isPR bool
expectComments int
}{
{
name: "dispatches /onboard",
body: "/onboard",
action: github.IssueCommentActionCreated,
isPR: true,
expectComments: 1,
},
{
name: "dispatches /new-test",
body: "/new-test e2e",
action: github.IssueCommentActionCreated,
isPR: true,
expectComments: 1,
},
{
name: "ignores non-created actions",
body: "/onboard",
action: github.IssueCommentActionDeleted,
isPR: true,
expectComments: 0,
},
{
name: "ignores non-PR issues",
body: "/onboard",
action: github.IssueCommentActionCreated,
isPR: false,
expectComments: 0,
},
{
name: "ignores unrelated comments",
body: "LGTM",
action: github.IssueCommentActionCreated,
isPR: true,
expectComments: 0,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tmpDir := t.TempDir()
ghc := &fakeGithubClient{
prs: map[string]*github.PullRequest{
"org/repo#1": makePR("org", "repo", 1, "main", "abc1234567890"),
},
dirs: map[string][]github.DirectoryContent{},
files: map[string][]byte{},
}

pjc := fakectrlruntimeclient.NewClientBuilder().WithScheme(pjScheme()).Build()

s := &server{
ghc: ghc,
trustedChecker: &fakeTrustedChecker{trusted: true},
pjclient: pjc,
namespace: "test-ns",
jobConfigDir: tmpDir,
releaseRepoDir: tmpDir,
prowgenImage: "img",
checkconfigImage: "img",
}

ic := github.IssueCommentEvent{
Action: tc.action,
Repo: github.Repo{Owner: github.User{Login: "org"}, Name: "repo"},
Issue: github.Issue{
Number: 1,
},
Comment: github.IssueComment{
Body: tc.body,
User: github.User{Login: "testuser"},
},
}
if tc.isPR {
ic.Issue.PullRequest = &struct{}{}
}

s.handleIssueComment(logrus.NewEntry(logrus.StandardLogger()), ic)

if len(ghc.comments) != tc.expectComments {
t.Errorf("expected %d comments, got %d: %+v", tc.expectComments, len(ghc.comments), ghc.comments)
}
})
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add dispatch regression test for /new-test prefix edge case

Please add a case asserting /new-testfoo is ignored. This prevents accidental command triggering after the matcher fix in handleIssueComment.

As per coding guidelines, "New or modified functionality should include test coverage. Bug fixes should include a regression test that fails without the fix."

🤖 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/in-repo-config-plugin/server_test.go` around lines 324 - 415, Add a
regression test case in TestHandleIssueCommentDispatch to assert the
`/new-testfoo` edge-case is ignored: inside the testCases slice add an entry
with name like "ignores /new-testfoo suffix", body "/new-testfoo", action
github.IssueCommentActionCreated, isPR true, and expectComments 0 so that
handleIssueComment (and the fakeGithubClient used in the test) will fail the
test if the `/new-test` matcher wrongly triggers on `/new-testfoo`.

Comment on lines +99 to +100
case strings.HasPrefix(body, newTestPrefix):
s.handleNewTest(l, ic)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Tighten /new-test command matching

At Line 99, HasPrefix("/new-test") also matches unintended inputs (e.g. /new-testfoo). Gate on exact command or /new-test with args.

Suggested diff
-	case strings.HasPrefix(body, newTestPrefix):
+	case body == newTestPrefix || strings.HasPrefix(body, newTestPrefix+" "):
 		s.handleNewTest(l, ic)
📝 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
case strings.HasPrefix(body, newTestPrefix):
s.handleNewTest(l, ic)
case body == newTestPrefix || strings.HasPrefix(body, newTestPrefix+" "):
s.handleNewTest(l, ic)
🤖 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/in-repo-config-plugin/server.go` around lines 99 - 100, The current
command match using strings.HasPrefix(body, newTestPrefix) is too loose and will
match inputs like "/new-testfoo"; update the match in the switch/case around
handleNewTest (referencing newTestPrefix and s.handleNewTest) to only trigger
when body == "/new-test" or when body starts with "/new-test " (slash-new-test
followed by a space) so arguments remain supported but accidental concatenations
are rejected; implement the conditional check accordingly and keep calling
s.handleNewTest(l, ic) only when that stricter match passes.

Comment on lines +122 to +123
s.ghc.CreateComment(org, repo, number, fmt.Sprintf("@%s: you are not trusted to use `/onboard`.", user))
return
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle GitHub comment creation errors

These CreateComment(...) calls ignore returned errors, so user-facing status can be silently dropped. At minimum, log failures consistently (or route through a helper that logs on error).

As per coding guidelines, "Errors should be handled correctly - determine whether to ignore, log, wrap and raise up; use informative error messages".

Also applies to: 135-136, 141-142, 168-169, 190-191, 210-211, 292-293, 323-324

🤖 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/in-repo-config-plugin/server.go` around lines 122 - 123, The
CreateComment calls on s.ghc (s.ghc.CreateComment(...)) are ignoring returned
errors; update each call (including the other occurrences you flagged) to
capture the error return and log it (e.g., err := s.ghc.CreateComment(...); if
err != nil { s.logger.Errorf("CreateComment failed for %s/%s#%d: %v", org, repo,
number, err) }) or create a small helper like createCommentAndLog(org, repo,
number, body) that calls s.ghc.CreateComment, checks the error, and logs a
descriptive message; replace the raw calls with that helper to ensure consistent
error handling and logging across server.go.

Comment thread cmd/in-repo-config-plugin/server.go
@@ -0,0 +1,3 @@
FROM quay.io/centos/centos:stream8
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Base image must be UBI minimal or distroless, pinned by digest.

The Dockerfile uses quay.io/centos/centos:stream8, which violates two container security requirements:

  1. Base image must be UBI minimal or distroless from catalog.redhat.com (not CentOS Stream)
  2. Non-Red Hat images must be pinned by digest, not floating tags

As per coding guidelines: "Base image: UBI minimal or distroless from catalog.redhat.com" and "non-RH images: pin by digest".

🔒 Proposed fix
-FROM quay.io/centos/centos:stream8
+FROM registry.access.redhat.com/ubi8/ubi-minimal:latest

Note: Red Hat images can use floating tags since Red Hat manages updates. If a non-RH base is required, pin by digest:

FROM quay.io/centos/centos@sha256:...
🧰 Tools
🪛 Trivy (0.69.3)

[error] 1-1: Image user should not be 'root'

Specify at least 1 USER command in Dockerfile with non-root user as argument

Rule: DS-0002

Learn more

(IaC/Dockerfile)

🤖 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 `@images/in-repo-config-plugin/Dockerfile` at line 1, Replace the non-compliant
base image on the Dockerfile's FROM line (currently
"quay.io/centos/centos:stream8") with a UBI minimal or distroless image sourced
from catalog.redhat.com (use the appropriate image name/tag provided by Red
Hat); if you must keep a non-Red Hat base, pin it by digest (e.g., change
"quay.io/centos/centos:stream8" to "quay.io/centos/centos@sha256:..."). Update
the FROM instruction accordingly and verify the new base meets the project's
security rules.

Comment on lines +1 to +3
FROM quay.io/centos/centos:stream8
ADD in-repo-config-plugin /usr/bin/in-repo-config-plugin
ENTRYPOINT ["/usr/bin/in-repo-config-plugin"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Add USER directive to run as non-root.

The container runs as root by default, violating the security requirement that containers must run as non-root. The plugin writes to EFS-mounted --job-config-dir and reads from the git-sync'd --release-repo-dir, so the USER directive must align with the UID/GID of those volume mounts.

As per coding guidelines: "USER non-root; never run as root".

🛡️ Proposed fix
 FROM quay.io/centos/centos:stream8
 ADD in-repo-config-plugin /usr/bin/in-repo-config-plugin
+USER 1000:1000
 ENTRYPOINT ["/usr/bin/in-repo-config-plugin"]

Note: Verify the UID/GID (1000:1000 is an example) matches the permissions on the EFS and git-sync volume mounts in the deployment manifest. Common choices are 65532:65532 (nonroot) or a specific service account UID.

📝 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
FROM quay.io/centos/centos:stream8
ADD in-repo-config-plugin /usr/bin/in-repo-config-plugin
ENTRYPOINT ["/usr/bin/in-repo-config-plugin"]
FROM quay.io/centos/centos:stream8
ADD in-repo-config-plugin /usr/bin/in-repo-config-plugin
USER 1000:1000
ENTRYPOINT ["/usr/bin/in-repo-config-plugin"]
🧰 Tools
🪛 Hadolint (2.14.0)

[error] 2-2: Use COPY instead of ADD for files and folders

(DL3020)

🪛 Trivy (0.69.3)

[error] 1-1: Image user should not be 'root'

Specify at least 1 USER command in Dockerfile with non-root user as argument

Rule: DS-0002

Learn more

(IaC/Dockerfile)

🤖 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 `@images/in-repo-config-plugin/Dockerfile` around lines 1 - 3, The Dockerfile
currently runs the plugin as root; add a USER directive after adding the binary
and before ENTRYPOINT to switch to a non-root UID:GID that matches the EFS and
git-sync mounts (e.g., USER 1000:1000 or USER 65532:65532). Update the
Dockerfile around the ADD in-repo-config-plugin and ENTRYPOINT
["/usr/bin/in-repo-config-plugin"] lines to include the chosen USER and ensure
file ownership/permissions on /usr/bin/in-repo-config-plugin and any expected
runtime dirs (like --job-config-dir and --release-repo-dir) are set to that
UID:GID so the container can read/write those mounts.

@@ -0,0 +1,3 @@
FROM quay.io/centos/centos:stream8
ADD in-repo-config-plugin /usr/bin/in-repo-config-plugin
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use COPY instead of ADD for files.

ADD has additional features (auto-extraction, URL fetching) that aren't needed for simple file copying. COPY is more explicit and preferred.

As per coding guidelines: "COPY specific files, not entire context".

📝 Proposed fix
-ADD in-repo-config-plugin /usr/bin/in-repo-config-plugin
+COPY in-repo-config-plugin /usr/bin/in-repo-config-plugin
🧰 Tools
🪛 Hadolint (2.14.0)

[error] 2-2: Use COPY instead of ADD for files and folders

(DL3020)

🤖 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 `@images/in-repo-config-plugin/Dockerfile` at line 2, Replace the Dockerfile
ADD instruction with COPY to avoid ADD's extra behavior; change the line that
currently uses "ADD in-repo-config-plugin /usr/bin/in-repo-config-plugin" to use
"COPY" so the file in-repo-config-plugin is explicitly copied into
/usr/bin/in-repo-config-plugin without unintended extraction or URL fetching.

@Prucek Prucek changed the title prowgen: enable in-repo ci-op config enable in-repo ci-op config May 28, 2026
Add a Prow external plugin (`in-repo-config-plugin`) that enables repos
to manage CI configuration in-repo via `.ci-operator.yaml` files instead
of the centralized openshift/release repository.

The plugin handles two slash commands:
- `/onboard`: bootstraps a repo with config-checker presubmit and
  prowgen postsubmit jobs written to EFS
- `/new-test`: generates ProwJob CRs directly from the PR's
  `.ci-operator/` configs for new tests not yet on EFS, with
  `--unresolved-config` so ci-operator uses the in-repo config

Also adds `--from-file` mode to ci-operator-prowgen for generating jobs
from a single ci-operator config file, `UnresolvedConfig` podspec
mutator, and `WriteBranchToDir` for branch-scoped job writing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Prucek Prucek force-pushed the in-repo-config-plugin branch from 2e3de1a to a92eaa5 Compare May 28, 2026 13:40
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 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/ci-operator-prowgen/main.go`:
- Around line 135-137: The code currently assigns a host filesystem path
(o.fromFile) directly to configSpec.UnresolvedConfigPath which embeds an invalid
container path in generated jobs; change the assignment to a
repo/container-relative name by stripping host directories (e.g.,
configSpec.UnresolvedConfigPath = filepath.Base(o.fromFile) or otherwise compute
a repo-relative path) and ensure filepath import is added and empty/null cases
handled before calling prowgen.GenerateJobs(&configSpec, &info). This keeps the
generated job path valid inside the job container while still using the provided
file name.

In `@cmd/in-repo-config-plugin/server.go`:
- Around line 417-423: Change dirExists to return (bool, error) instead of
swallowing all stat errors: call os.Stat(path), if err == nil return
info.IsDir(), nil; if os.IsNotExist(err) return false, nil; otherwise return
false and propagate/wrap the error (e.g. fmt.Errorf("stat %s: %w", path, err)).
Update any callers of dirExists to handle the error path instead of assuming
false means "missing". This preserves permission/I/O errors while keeping
missing-file semantics.
- Around line 220-223: The loop that sets configSpec.UnresolvedConfigPath to the
constant cioperatorapi.CIOperatorInrepoConfigFileName causes every generated job
to point to ".ci-operator.yaml"; instead, carry the actual source path from
fetchConfigs into each configSpec (preserve the original filename or source path
returned by fetchConfigs), and set configSpec.UnresolvedConfigPath = <that
source path> inside the for filename, configSpec := range configs loop before
calling prowgen.GenerateJobs(info) so only the single-file fallback uses the
constant; update fetchConfigs to return the source path per config if it doesn't
already and use that value here (references: fetchConfigs, metadataFromFilename,
configSpec.UnresolvedConfigPath, prowgen.GenerateJobs).
- Around line 389-393: The commentError function currently posts the raw err
into a GitHub comment; change it to post a sanitized, generic user-facing
message instead (e.g., "@%s: command `%s` failed, please check plugin logs or
contact maintainers"), and send the full error details to the logger only.
Concretely, update commentError (and the call to s.ghc.CreateComment) to format
and post a redacted string without err, and call
l.WithError(err).Error("detailed error handling message") so the full error
stays in logrus; keep s.ghc.CreateComment and l references unchanged.

In `@pkg/jobconfig/files.go`:
- Around line 422-427: The current WriteBranchToDir path (loop over files ->
sortConfigFields(jc) -> WriteToFile(filepath.Join(jobDirForComponent, file),
jc)) blindly overwrites per-branch files and thus drops manually-managed fields;
update WriteBranchToDir to read the existing file (if present) for each target
path, merge/retain fields not managed by prowgen into the new jc (preserving
reporter_config and other unmanaged keys) before calling WriteToFile, using the
same file key from the files map and the jobDirForComponent path to locate the
existing content; implement the merge logic in a helper (e.g.,
mergePreservingUnmanaged) and call it prior to sortConfigFields/WriteToFile so
unmanaged fields remain intact.

In `@pkg/prowgen/jobbase.go`:
- Around line 42-44: The code is inserting only the basename of
UnresolvedConfigPath which truncates nested paths and causes sparse-checkout to
drop directories; update the insertion to use the full UnresolvedConfigPath
(i.e., pass configSpec.UnresolvedConfigPath to files.Insert rather than
path.Base(configSpec.UnresolvedConfigPath)) so the exact unresolved config path
is preserved; locate the occurrence around configSpec.UnresolvedConfigPath and
files.Insert to make this change.

In `@pkg/prowgen/podspec.go`:
- Around line 566-572: Add a Go doc comment immediately above the exported
UnresolvedConfig function describing its purpose, parameters, and return value:
explain that UnresolvedConfig(configPath string) returns a PodSpecMutator which
appends a command-line flag "--unresolved-config=<configPath>" to the first
container in a corev1.PodSpec; mention that it mutates the given PodSpec and
returns an error if mutation fails (or nil on success). Keep the comment concise
and follow Go doc style (start with "UnresolvedConfig").
🪄 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: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 159822a4-064e-4352-8f22-4ae28f85f17f

📥 Commits

Reviewing files that changed from the base of the PR and between d862c2b and a92eaa5.

📒 Files selected for processing (13)
  • cmd/ci-operator-prowgen/main.go
  • cmd/in-repo-config-plugin/bootstrap.go
  • cmd/in-repo-config-plugin/bootstrap_test.go
  • cmd/in-repo-config-plugin/main.go
  • cmd/in-repo-config-plugin/server.go
  • cmd/in-repo-config-plugin/server_test.go
  • images/in-repo-config-plugin/Dockerfile
  • pkg/api/types.go
  • pkg/jobconfig/files.go
  • pkg/prowgen/jobbase.go
  • pkg/prowgen/jobbase_test.go
  • pkg/prowgen/podspec.go
  • pkg/prowgen/testdata/zz_fixture_TestProwJobBaseBuilder_job_with_unresolved_config__including_podspec.yaml
✅ Files skipped from review due to trivial changes (1)
  • pkg/prowgen/testdata/zz_fixture_TestProwJobBaseBuilder_job_with_unresolved_config__including_podspec.yaml
🚧 Files skipped from review as they are similar to previous changes (4)
  • cmd/in-repo-config-plugin/bootstrap.go
  • cmd/in-repo-config-plugin/bootstrap_test.go
  • cmd/in-repo-config-plugin/server_test.go
  • cmd/in-repo-config-plugin/main.go

Comment on lines +135 to +137
configSpec.UnresolvedConfigPath = o.fromFile
generated, err := prowgen.GenerateJobs(&configSpec, &info)
if err != nil {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

--unresolved-config is populated with a host path, not a repo path.

On Line 135, UnresolvedConfigPath is set to o.fromFile directly. In --from-file usage this can be an absolute/local path, which is then embedded in generated jobs and won’t exist inside the job container.

Suggested fix
-	configSpec.UnresolvedConfigPath = o.fromFile
+	// ci-operator runs in the repo checkout; unresolved config path must be repo-relative.
+	configSpec.UnresolvedConfigPath = filepath.Base(o.fromFile)
📝 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
configSpec.UnresolvedConfigPath = o.fromFile
generated, err := prowgen.GenerateJobs(&configSpec, &info)
if err != nil {
// ci-operator runs in the repo checkout; unresolved config path must be repo-relative.
configSpec.UnresolvedConfigPath = filepath.Base(o.fromFile)
generated, err := prowgen.GenerateJobs(&configSpec, &info)
if err != nil {
🤖 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/ci-operator-prowgen/main.go` around lines 135 - 137, The code currently
assigns a host filesystem path (o.fromFile) directly to
configSpec.UnresolvedConfigPath which embeds an invalid container path in
generated jobs; change the assignment to a repo/container-relative name by
stripping host directories (e.g., configSpec.UnresolvedConfigPath =
filepath.Base(o.fromFile) or otherwise compute a repo-relative path) and ensure
filepath import is added and empty/null cases handled before calling
prowgen.GenerateJobs(&configSpec, &info). This keeps the generated job path
valid inside the job container while still using the provided file name.

Comment on lines +220 to +223
for filename, configSpec := range configs {
info := metadataFromFilename(filename, org, repo, branch)
configSpec.UnresolvedConfigPath = cioperatorapi.CIOperatorInrepoConfigFileName
generated, err := prowgen.GenerateJobs(configSpec, info)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use the source config path here.

Every generated job gets UnresolvedConfigPath = ".ci-operator.yaml", even when it came from .ci-operator/<file>.yaml. Those ProwJobs will point ci-operator at the wrong file and fail on repos that only use split configs. Carry the actual source path through fetchConfigs and set it per config; only the single-file fallback should use .ci-operator.yaml.

🤖 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/in-repo-config-plugin/server.go` around lines 220 - 223, The loop that
sets configSpec.UnresolvedConfigPath to the constant
cioperatorapi.CIOperatorInrepoConfigFileName causes every generated job to point
to ".ci-operator.yaml"; instead, carry the actual source path from fetchConfigs
into each configSpec (preserve the original filename or source path returned by
fetchConfigs), and set configSpec.UnresolvedConfigPath = <that source path>
inside the for filename, configSpec := range configs loop before calling
prowgen.GenerateJobs(info) so only the single-file fallback uses the constant;
update fetchConfigs to return the source path per config if it doesn't already
and use that value here (references: fetchConfigs, metadataFromFilename,
configSpec.UnresolvedConfigPath, prowgen.GenerateJobs).

Comment on lines +389 to +393
func (s *server) commentError(org, repo string, number int, user, command string, err error, l *logrus.Entry) {
comment := fmt.Sprintf("@%s: `%s` error:\n```\n%v\n```", user, command, err)
if commentErr := s.ghc.CreateComment(org, repo, number, comment); commentErr != nil {
l.WithError(commentErr).Error("failed to create error comment")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't publish raw backend errors to GitHub.

commentError renders err verbatim into a PR comment. Failures from filesystem, GitHub, or Kubernetes calls can include internal paths and cluster details that should stay in plugin logs, not on a public PR. Return a sanitized user message here and keep the full error in logrus. As per coding guidelines, "Flag logging that may expose passwords, tokens, API keys, PII (email, SSN, credit card), session IDs, internal hostnames, or customer data."

🤖 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/in-repo-config-plugin/server.go` around lines 389 - 393, The commentError
function currently posts the raw err into a GitHub comment; change it to post a
sanitized, generic user-facing message instead (e.g., "@%s: command `%s` failed,
please check plugin logs or contact maintainers"), and send the full error
details to the logger only. Concretely, update commentError (and the call to
s.ghc.CreateComment) to format and post a redacted string without err, and call
l.WithError(err).Error("detailed error handling message") so the full error
stays in logrus; keep s.ghc.CreateComment and l references unchanged.

Comment on lines +417 to +423
func dirExists(path string) bool {
info, err := os.Stat(path)
if err != nil {
return false
}
return info.IsDir()
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Distinguish "missing" from "stat failed".

dirExists collapses permission errors, broken mounts, and transient I/O failures into false. In this plugin that means onboarding and /new-test can proceed as if no jobs exist when EFS or the release repo is actually unhealthy. Return (bool, error) and only treat os.IsNotExist as absence. As per coding guidelines, "Errors should be handled correctly - determine whether to ignore, log, wrap and raise up; use informative error messages".

🤖 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/in-repo-config-plugin/server.go` around lines 417 - 423, Change dirExists
to return (bool, error) instead of swallowing all stat errors: call
os.Stat(path), if err == nil return info.IsDir(), nil; if os.IsNotExist(err)
return false, nil; otherwise return false and propagate/wrap the error (e.g.
fmt.Errorf("stat %s: %w", path, err)). Update any callers of dirExists to handle
the error path instead of assuming false means "missing". This preserves
permission/I/O errors while keeping missing-file semantics.

Comment thread pkg/jobconfig/files.go
Comment on lines +422 to +427
for file, jc := range files {
sortConfigFields(jc)
if err := WriteToFile(filepath.Join(jobDirForComponent, file), jc); err != nil {
return err
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

WriteBranchToDir overwrites branch files without preserving unmanaged fields.

The new write path replaces existing per-branch files directly, so manually maintained fields in those files can be lost (for example reporter config or cluster/max concurrency adjustments).

Suggested direction
-	for file, jc := range files {
-		sortConfigFields(jc)
-		if err := WriteToFile(filepath.Join(jobDirForComponent, file), jc); err != nil {
-			return err
-		}
-	}
+	for file, generated := range files {
+		target := filepath.Join(jobDirForComponent, file)
+		existing, err := readFromFile(target)
+		if err != nil && !os.IsNotExist(err) {
+			return err
+		}
+		if existing != nil {
+			allJobs := sets.New[string]()
+			for _, jobs := range generated.PresubmitsStatic {
+				for _, j := range jobs {
+					allJobs.Insert(j.Name)
+				}
+			}
+			for _, jobs := range generated.PostsubmitsStatic {
+				for _, j := range jobs {
+					allJobs.Insert(j.Name)
+				}
+			}
+			for _, j := range generated.Periodics {
+				allJobs.Insert(j.Name)
+			}
+			mergeJobConfig(existing, generated, allJobs)
+			generated = existing
+		}
+		sortConfigFields(generated)
+		if err := WriteToFile(target, generated); err != nil {
+			return err
+		}
+	}

As per coding guidelines, "pkg/jobconfig/**: ... Must preserve fields that are not managed by prowgen (e.g. manually-set reporter_config)."

📝 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
for file, jc := range files {
sortConfigFields(jc)
if err := WriteToFile(filepath.Join(jobDirForComponent, file), jc); err != nil {
return err
}
}
for file, generated := range files {
target := filepath.Join(jobDirForComponent, file)
existing, err := readFromFile(target)
if err != nil && !os.IsNotExist(err) {
return err
}
if existing != nil {
allJobs := sets.New[string]()
for _, jobs := range generated.PresubmitsStatic {
for _, j := range jobs {
allJobs.Insert(j.Name)
}
}
for _, jobs := range generated.PostsubmitsStatic {
for _, j := range jobs {
allJobs.Insert(j.Name)
}
}
for _, j := range generated.Periodics {
allJobs.Insert(j.Name)
}
mergeJobConfig(existing, generated, allJobs)
generated = existing
}
sortConfigFields(generated)
if err := WriteToFile(target, generated); err != nil {
return err
}
}
🤖 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 `@pkg/jobconfig/files.go` around lines 422 - 427, The current WriteBranchToDir
path (loop over files -> sortConfigFields(jc) ->
WriteToFile(filepath.Join(jobDirForComponent, file), jc)) blindly overwrites
per-branch files and thus drops manually-managed fields; update WriteBranchToDir
to read the existing file (if present) for each target path, merge/retain fields
not managed by prowgen into the new jc (preserving reporter_config and other
unmanaged keys) before calling WriteToFile, using the same file key from the
files map and the jobDirForComponent path to locate the existing content;
implement the merge logic in a helper (e.g., mergePreservingUnmanaged) and call
it prior to sortConfigFields/WriteToFile so unmanaged fields remain intact.

Comment thread pkg/prowgen/jobbase.go
Comment on lines +42 to +44
if configSpec.UnresolvedConfigPath != "" {
files.Insert(path.Base(configSpec.UnresolvedConfigPath))
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Sparse-checkout drops unresolved config directories.

Line 43 uses path.Base, so nested unresolved config paths are truncated. That can fetch the wrong file (or none) while --unresolved-config still references the full path.

Suggested fix
-	if configSpec.UnresolvedConfigPath != "" {
-		files.Insert(path.Base(configSpec.UnresolvedConfigPath))
-	}
+	if configSpec.UnresolvedConfigPath != "" {
+		files.Insert(path.Clean(configSpec.UnresolvedConfigPath))
+	}
📝 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
if configSpec.UnresolvedConfigPath != "" {
files.Insert(path.Base(configSpec.UnresolvedConfigPath))
}
if configSpec.UnresolvedConfigPath != "" {
files.Insert(path.Clean(configSpec.UnresolvedConfigPath))
}
🤖 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 `@pkg/prowgen/jobbase.go` around lines 42 - 44, The code is inserting only the
basename of UnresolvedConfigPath which truncates nested paths and causes
sparse-checkout to drop directories; update the insertion to use the full
UnresolvedConfigPath (i.e., pass configSpec.UnresolvedConfigPath to files.Insert
rather than path.Base(configSpec.UnresolvedConfigPath)) so the exact unresolved
config path is preserved; locate the occurrence around
configSpec.UnresolvedConfigPath and files.Insert to make this change.

Comment thread pkg/prowgen/podspec.go
Comment on lines +566 to +572
func UnresolvedConfig(configPath string) PodSpecMutator {
return func(spec *corev1.PodSpec) error {
container := &spec.Containers[0]
addUniqueParameter(container, fmt.Sprintf("--unresolved-config=%s", configPath))
return nil
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add a doc comment for exported UnresolvedConfig.

This exported function is missing a Go doc comment, which makes public API behavior less discoverable.

As per coding guidelines, "Go documentation on Classes/Functions/Fields should be written properly" and "Comment important exported functions with their purpose, parameters, and return values."

🤖 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 `@pkg/prowgen/podspec.go` around lines 566 - 572, Add a Go doc comment
immediately above the exported UnresolvedConfig function describing its purpose,
parameters, and return value: explain that UnresolvedConfig(configPath string)
returns a PodSpecMutator which appends a command-line flag
"--unresolved-config=<configPath>" to the first container in a corev1.PodSpec;
mention that it mutates the given PodSpec and returns an error if mutation fails
(or nil on success). Keep the comment concise and follow Go doc style (start
with "UnresolvedConfig").

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

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant