Skip to content

Reduce code duplication across rpkg CLI commands#541

Merged
liamfallon merged 4 commits into
kptdev:mainfrom
thc1006:fix/issue-1067-shared-helpers
May 21, 2026
Merged

Reduce code duplication across rpkg CLI commands#541
liamfallon merged 4 commits into
kptdev:mainfrom
thc1006:fix/issue-1067-shared-helpers

Conversation

@thc1006
Copy link
Copy Markdown
Contributor

@thc1006 thc1006 commented Apr 16, 2026

Title

Reduce code duplication across rpkg CLI commands


Description

  • What changed:
    Extract three shared helpers to pkg/cli/commands/rpkg/util/common.go:

    1. InitClient -- wraps CreateClientWithFlags with namespace flag validation (ported from get/command.go). Applied to approve, propose, proposedelete, reject, and del.
    2. CreateScheme -- consolidates the identical local createScheme() that was duplicated in both pull and push.
    3. RunForEachPackage -- extracts the retry-with-error-collection loop shared by approve, propose, proposedelete, and reject. Each command now provides only its lifecycle-specific logic as a callback.
  • Why it's needed:
    The five lifecycle commands (approve, propose, proposedelete, reject, del) had near-identical boilerplate: same runner struct, same preRunE with CreateClientWithFlags, same retry loop, same error collection pattern. Any bug fix (like the lastErr workaround from porch#489) had to be applied identically across all of them. Additionally, namespace validation was only present in the get command; the other commands silently accepted --namespace with no value.

  • How it works:
    InitClient checks the namespace flag first, then creates the client. RunForEachPackage takes a PackageAction callback that receives the fetched PackageRevision and returns a success message string. The helper handles retry, error collection, and output formatting. Commands that had side effects in the retry closure (propose and proposedelete printing "already proposed" to stderr) still do so inside the callback; they return ("", nil) to skip the standard success output.


Related Issue(s)


Type of Change

  • Bug fix
  • New feature
  • Enhancement
  • Refactor
  • Documentation
  • Tests
  • Other: ________

Checklist

  • Code follows project style guidelines
  • Self-reviewed changes
  • Tests added/updated
  • Documentation added/updated
  • All tests and gating checks pass

Testing Instructions

  1. go test ./pkg/cli/commands/rpkg/... -count=1 -- all 14 packages should pass
  2. golangci-lint run ./pkg/cli/commands/rpkg/... -- 0 issues
  3. Verify namespace validation: any rpkg subcommand with --namespace (no value) should now return an error

Additional Notes

  • del keeps its own runE (no retry, no Get, constructs object for Delete). Only adopts InitClient.
  • get is unchanged (already has namespace validation, uses resource builder pattern).
  • clone/copy/init/upgrade/pull/push runE are unchanged (unique patterns, minimal duplication).
  • The success-message inconsistency (propose/proposedelete printed inside retry closure; approve/reject printed outside) is resolved: all four now return the message from the callback.
  • kispaljr's NOTE comment in reject about UpdatePackageRevisionApproval has been resolved and removed: both the Proposed -> Draft and DeletionProposed -> Published paths now call cliutils.UpdatePackageRevisionApproval, which answers the question the NOTE raised. The reject test gains a SubResourceUpdate interceptor to cover the new code path.

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 16, 2026

Deploy Preview for porch ready!

Name Link
🔨 Latest commit 78708b3
🔍 Latest deploy log https://app.netlify.com/projects/porch/deploys/6a02bda93510090008d2a0ca
😎 Deploy Preview https://deploy-preview-541--porch.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@thc1006 thc1006 force-pushed the fix/issue-1067-shared-helpers branch 14 times, most recently from c075fbb to c8863a0 Compare April 20, 2026 17:37
Comment thread pkg/cli/commands/rpkg/util/common.go Outdated
Comment thread pkg/cli/commands/rpkg/reject/command.go Outdated
Comment thread pkg/cli/commands/rpkg/reject/command.go Outdated
@thc1006 thc1006 force-pushed the fix/issue-1067-shared-helpers branch 2 times, most recently from f9836dc to 8fad5c3 Compare April 20, 2026 18:55
@thc1006 thc1006 requested a review from efiacor April 20, 2026 18:55
@liamfallon
Copy link
Copy Markdown
Collaborator

I reran the e2e tests because one of the flaky e2e tests failed.

@mozesl-nokia
Copy link
Copy Markdown
Collaborator

This PR goes hand-in-hand with #592, I think we should get this in first and then I will rewrite #592

Copy link
Copy Markdown
Collaborator

@mozesl-nokia mozesl-nokia left a comment

Choose a reason for hiding this comment

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

Great work, just added my usual test-related comments

Comment thread pkg/cli/commands/rpkg/util/common.go Outdated
Comment thread pkg/cli/commands/rpkg/util/common.go Outdated
Comment thread pkg/cli/commands/rpkg/util/common_test.go Outdated
Comment thread pkg/cli/commands/rpkg/util/common_test.go Outdated
Comment thread pkg/cli/commands/rpkg/util/common_test.go
Comment thread pkg/cli/commands/rpkg/util/common_test.go Outdated
Comment thread pkg/cli/commands/rpkg/util/common_test.go Outdated
@thc1006 thc1006 force-pushed the fix/issue-1067-shared-helpers branch from 8fad5c3 to 42b2021 Compare April 27, 2026 19:35
@thc1006
Copy link
Copy Markdown
Contributor Author

thc1006 commented Apr 27, 2026

@mozesl-nokia review feedback addressed in the latest push, branch rebased on main. Highlights:

  • CreateScheme now registers corev1, configapi, and metav1 alongside porchapi, matching cliutils.createScheme.
  • RunForEachPackage takes a cmdName argument; callers pass their command const so op tags become cmdrpkg<verb>.runE, consistent with the rest of the package.
  • Test assertions cleaned up per the suggestions.

@liamfallon CI should be green again after the rebase.

@thc1006 thc1006 requested a review from mozesl-nokia April 27, 2026 19:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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

Comments suppressed due to low confidence (1)

pkg/cli/commands/rpkg/del/command.go:75

  • rpkg del still succeeds silently when no PACKAGE args are provided (the loop just doesn’t run). Other lifecycle commands now fail fast via RunForEachPackage’s positional-arg check, and the docs describe at least one package name. Consider adding a len(args)==0 validation (either in runE or a small wrapper PreRunE) so porchctl rpkg del returns a clear error instead of success.
func (r *runner) runE(_ *cobra.Command, args []string) error {
	const op errors.Op = command + ".runE"
	var messages []string

	for _, pkg := range args {
		pr := &porchapi.PackageRevision{

Comment thread pkg/cli/commands/rpkg/util/common.go Outdated
Comment thread pkg/cli/commands/rpkg/util/common.go Outdated
Copilot AI review requested due to automatic review settings May 12, 2026 05:41
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

@liamfallon
Copy link
Copy Markdown
Collaborator

Hi @thc1006,

Would you mind putting a comment after each of the copilot issues above that you have resolved explaining how you have resolved them and then park them resolved.

I think it's OK to take the creation of an "action" interface and also the v1alpha2 work as separate PRs. We'll be discussing the PRs during our community meeting tomorrow and I'll seek the community's opinion on that.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
10.6% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@thc1006
Copy link
Copy Markdown
Contributor Author

thc1006 commented May 12, 2026

Dear @liamfallon, thank you very much for the clear direction and for taking the v1alpha2 and action interface questions to the community meeting.

I have just gone through each unresolved Copilot thread above and added a per-thread comment describing how the issue was resolved, with the commit hash for traceability. The threads are now marked as resolved.

I will not make further code changes while the community meeting is pending. Once direction is set, I will open a tracking issue for the action interface work and another for the v1alpha2 mirroring, both as separate follow-up PRs. The duplication on new code is currently at 10.6%; I am very happy to follow your lead on a waiver once the meeting outcome is clear.

Thank you again for the thoughtful review.

@liamfallon
Copy link
Copy Markdown
Collaborator

The agenda for tomorrow's meeting is here. The meeting is at 13:00 UTC (which I know may not be convenient in your timezone). We have moved Porch meetings across to the kpt CNCF project as Porch is moving to kpt. The link to themeeting is at the bottomo of the kpt project page. The meeting is two hours long in the calendar, the first hour is for general community discussions and the second hour is the technical meeting. If you can, feel free to join both meetings but the second hour is the meeting where your PR will be discussed.

@liamfallon
Copy link
Copy Markdown
Collaborator

Community meeting discussion:

Thanks very much @thc1006 for the work.

  • Using multiple PRs to implement code improvements is fine
  • Once all the PRs are in, we should have consistent use of the same interfaces for all commands
  • v1alpaha2 should also be done at the end

@thc1006
Copy link
Copy Markdown
Contributor Author

thc1006 commented May 14, 2026

Dear @liamfallon, thanks for relaying the meeting outcome and for the kind call-out.

Understood: keep this PR focused on the v1alpha1 refactor, follow up PRs converge the other commands onto a shared interface, and v1alpha2 mirrors the pattern at the end. I'll open the tracking issues once this one is in.

I've just rebased onto the latest kptdev/porch main, so the #981 copyright changes are picked up and CI is back to green.

Thanks again to everyone at the meeting.

Extract shared helpers to pkg/cli/commands/rpkg/util/common.go and apply
them across the seven rpkg lifecycle commands (approve, propose,
proposedelete, reject, del, pull, push):

- InitClient: wraps cliutils.CreateClientWithFlags with namespace flag
  validation. Previously only the get command checked for an empty
  --namespace flag; this now applies to approve, propose,
  proposedelete, reject and del as well. Also rejects a nil cfg up
  front so callers cannot trigger a panic inside ToRESTConfig.

- CreateScheme: consolidates the identical local createScheme()
  function that was duplicated in both pull and push, registering
  porchapi, porchconfig, corev1 and metav1 so callers see the same
  kinds as cliutils.CreateClientWithFlags.

- MakePreRunE: returns a cobra PreRunE closure that validates
  namespace and creates the client. Eliminates duplicate preRunE
  methods from approve, propose, proposedelete and del.

- RunForEachPackage: extracts the retry-with-error-collection loop
  shared by approve, propose, proposedelete and reject. Each command
  now provides only its lifecycle-specific logic as a callback. The
  lastErr workaround for k8s retry library behaviour is included in
  the shared helper. Per-iteration body is further split into
  fetchAndAct and reportResult helpers for clarity. Behavioural
  options are grouped into RunForEachOpts; CmdName is required and
  rejected up front if empty.

- Runner struct + NewTestRunner: shared fields (Ctx, Cfg, Client,
  Command) embedded into each command's runner. NewTestRunner builds
  a Runner pre-wired for table-driven CLI tests. Ctx is intentionally
  stored on the struct, matching the existing per-command runner
  convention (see SonarQube godre:S8242 -- documented rather than
  refactored).

The seven lifecycle command files (approve, propose, proposedelete,
reject, del, pull, push) all embed rpkgutil.Runner. pull and push
retain their printer field on top of the embed.

Behavioural improvements that surfaced during the refactor:
- approve and propose-delete now reject empty argument invocations
  with "PACKAGE is a required positional argument", matching reject's
  existing behaviour. The reject error message itself is aligned to
  say PACKAGE rather than PACKAGE_REVISION, matching the Use string.
- reject's DeletionProposed -> Published path now uses
  UpdatePackageRevisionApproval like the Proposed -> Draft path,
  resolving the inconsistency kispaljr's NOTE comment had pointed at.
- pull, push and reject preRunE gain explicit unit tests that
  exercise the cfg.ToRESTConfig + CreateScheme + client.New wiring
  through a kubeconfig fixture, raising coverage on those files from
  0% to 76.9% (pull/push) and 87.5% (reject).

Fixes kptdev#870
Fixes kptdev#900

Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
@thc1006 thc1006 force-pushed the fix/issue-1067-shared-helpers branch from 28ba81b to 7a61c9a Compare May 14, 2026 14:41
liamfallon
liamfallon previously approved these changes May 14, 2026
mozesl-nokia
mozesl-nokia previously approved these changes May 20, 2026
Comment thread pkg/cli/commands/rpkg/approve/command_test.go Outdated
Comment thread pkg/cli/commands/rpkg/reject/command_test.go Outdated
Copilot AI review requested due to automatic review settings May 20, 2026 09:35
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 16 out of 16 changed files in this pull request and generated no new comments.

Two cosmetic items raised after @liamfallon's approval are addressed
in this commit.

1. approve/command_test.go used "nephio.org.Specializer.specialize"
   as the ReadinessGate fixture string, which lingered from the
   branch state before kptdev#981 swept the rest of the repository. The
   string is pure test data: the test only verifies that the
   ConditionType on a ReadinessGate matches a Condition.Type with
   status False. Aligned to "kpt.dev.Specializer.specialize" so it
   matches what kptdev#981 settled on for the rest of the codebase.

2. writeTempKubeconfig was duplicated across pull, push and reject
   test files. Extracted as the exported rpkgutil.WriteTempKubeconfig
   in a new pkg/cli/commands/rpkg/util/testhelpers.go, matching the
   existing testhelpers_v1alpha2.go pattern. The three test files
   now call the shared helper, and the now-unused "os" import is
   dropped from pull/command_test.go.

Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
@thc1006 thc1006 dismissed stale reviews from liamfallon and mozesl-nokia via f9f415d May 20, 2026 17:09
Sync with latest main to pick up kptdev#1001 (flaky TestConcurrentDeletes
fix) and trigger a fresh CI run after the flaky rpkg-get CLI E2E
timeout on the DB cache backend.

Signed-off-by: thc1006 <84045975+thc1006@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 21, 2026 02:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@liamfallon liamfallon merged commit ee763f8 into kptdev:main May 21, 2026
27 of 31 checks passed
@thc1006 thc1006 deleted the fix/issue-1067-shared-helpers branch May 21, 2026 05:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reduce code duplication across rpkg CLI commands [BUG]: Porchctl does not report an error when namespace flag specified without a value

6 participants