Skip to content

refactor(project): share one xpkg client constructor; clamp build concurrency to 1 - #478

Merged
jcogilvie merged 2 commits into
crossplane:mainfrom
jcogilvie:jco-shared-xpkg-client
Oct 30, 2026
Merged

jcogilvie merged 2 commits into
crossplane:mainfrom
jcogilvie:jco-shared-xpkg-client

Conversation

@jcogilvie

@jcogilvie jcogilvie commented Oct 30, 2026 •

Copy link
Copy Markdown
Collaborator

Description of your changes

Summary

Adds project.NewXpkgClient and uses it in project build, project run, render op, render xr, dependency add, dependency update-cache and composition generate. These commands used to build the xpkg client and resolver inline, or in render xr's case through a private helper. BuildWithMaxConcurrency now treats 0 as 1. Before this change, render xr and render op hung with --max-concurrency=0 whenever the project had embedded functions.

Why

  • The commands built the same client and resolver in the same way: the cache dir defaulting to dependency.DefaultCacheDir(), the remote fetcher and optional ImageConfigs. Putting that in one place keeps them consistent.
  • project build and project run clamped max(1, MaxConcurrency) themselves, but render xr and render op passed the flag through unchanged. With 0 the builder's semaphore channel is unbuffered, so every embedded function build blocks forever, which hangs whenever the project had embedded functions. Clamping inside the option fixes this for every caller.

What changed

  • New internal/project/xpkg.go with NewXpkgClient(cacheDir, opts...), which returns the client and a resolver backed by it.
  • project build and project run call it with the project's ImageConfigs and drop their local max(1, ...).
  • render op calls it with the project's ImageConfigs.
  • render xr drops newClientAndResolver and calls the helper in its place: with the project's ImageConfigs for projects, and with none for Configurations, as before.
  • dependency add, dependency update-cache and composition generate call it with the project's ImageConfigs, replacing their inline client and resolver construction.
  • BuildWithMaxConcurrency(0) now means 1.

Everything else stays the same: registries, ImageConfigs, cache dirs and credentials match main for every command. Error wrapping changes only in project build, project run, dependency add, dependency update-cache and composition generate: a client construction failure is now wrapped as "cannot create xpkg client", as render op and render xr already did.

Testing

  • TestBuilderZeroConcurrency builds a project with BuildWithMaxConcurrency(0) and fails if the build doesn't finish within 30s. Without the clamp it times out; with the clamp it passes in about 1s.
  • ./nix.sh run .#test and ./nix.sh run .#lint pass.

Related

I have:

  • Read and followed Crossplane's contribution process.
  • Run ./nix.sh flake check to ensure this PR is ready for review. (./nix.sh run .#test and ./nix.sh run .#lint pass.)
  • Added or updated unit tests.
  • Linked a PR or a docs tracking issue to document this change. (no docs change)
  • Added backport release-x.y labels to auto-backport this PR.

Need help with this checklist? See the cheat sheet.

🤖 Generated with Claude Code

jcogilvie and others added 2 commits October 30, 2026 13:45
Add project.NewXpkgClient and use it from project build, project run,
render op and render xr instead of four inline copies.

BuildWithMaxConcurrency now clamps 0 to 1. Before, 0 made an unbuffered
semaphore and render xr and render op hung with --max-concurrency=0. The
max() in project build and run is no longer needed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
@coderabbitai

coderabbitai Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 57 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between bf5a02b and d94b09b.

Files selected for processing: 10
  • cmd/crossplane/composition/generate.go
  • cmd/crossplane/dependency/add.go
  • cmd/crossplane/dependency/cache.go
  • cmd/crossplane/project/build.go
  • cmd/crossplane/project/run.go
  • cmd/crossplane/render/op/cmd.go
  • cmd/crossplane/render/xr/cmd.go
  • internal/project/build.go
  • internal/project/build_test.go
  • internal/project/xpkg.go
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@adamwg adamwg 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 fact that we need both a Client and a Resolver in so many places makes me wonder whether we should have an abstraction that does both. E.g., we could have a Client in internal/xpkg that extends the upstream xpkg.Client with Resolve (since the Resolver uses an xpkg.Client underneath anyway).

That said, this is an improvement as-is and we can consider further refactors later if you prefer.

@jcogilvie
jcogilvie merged commit ba029f2 into crossplane:main Oct 30, 2026
10 of 12 checks passed
@jcogilvie
jcogilvie deleted the jco-shared-xpkg-client branch October 30, 2026 19:38
jcogilvie added a commit that referenced this pull request Oct 31, 2026
Add an internal/xpkg Client interface that extends the upstream
xpkg.Client with Resolve. NewClient and project.NewXpkgClient now
return it, so callers carry one value instead of a client plus a
Resolver. The dependency manager drops WithResolver and resolves
through its client; ResolveConfigurationFunctions takes the Client.
Resolution rules and error messages are unchanged.

Suggested by adamwg in review of #478.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Ogilvie <jonathan.ogilvie@sumologic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants