From 18ca466f2b82c3e2852f13bf574223cd6477a0de Mon Sep 17 00:00:00 2001 From: Bob Haddleton Date: Fri, 12 Jun 2026 12:20:32 -0500 Subject: [PATCH 1/4] Add repository option to project init. Make the directory name truly optional. Signed-off-by: Bob Haddleton --- cmd/crossplane/project/init.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/crossplane/project/init.go b/cmd/crossplane/project/init.go index 90436c97..caf2749d 100644 --- a/cmd/crossplane/project/init.go +++ b/cmd/crossplane/project/init.go @@ -38,8 +38,9 @@ const projectFileName = "crossplane-project.yaml" // initCmd initializes a new project. type initCmd struct { - Name string `arg:"" help:"The name of the new project."` - Directory string `help:"Directory to initialize. Defaults to project name." short:"d" type:"path"` + Name string `arg:"" help:"The name of the new project."` + Directory string `arg:"" help:"Directory to initialize. Defaults to project name." optional:"" short:"d" type:"path"` + Repository string `default:"example.com/my-org" help:"Override the repository in the project file." optional:""` } func (c *initCmd) Help() string { @@ -73,8 +74,8 @@ kind: Project metadata: name: %s spec: - repository: example.com/my-org/%s -`, c.Name, c.Name) + repository: %s/%s +`, c.Name, c.Repository, c.Name) if err := os.WriteFile(projFile, []byte(content), 0o600); err != nil { return errors.Wrapf(err, "failed to write %s", projectFileName) From 92b63fac6e9de6446f649ba27511eff2178781bd Mon Sep 17 00:00:00 2001 From: Bob Haddleton Date: Fri, 12 Jun 2026 13:38:43 -0500 Subject: [PATCH 2/4] Address review comments, revert directory name changes. Signed-off-by: Bob Haddleton --- cmd/crossplane/project/init.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cmd/crossplane/project/init.go b/cmd/crossplane/project/init.go index caf2749d..8d5b1924 100644 --- a/cmd/crossplane/project/init.go +++ b/cmd/crossplane/project/init.go @@ -38,9 +38,9 @@ const projectFileName = "crossplane-project.yaml" // initCmd initializes a new project. type initCmd struct { - Name string `arg:"" help:"The name of the new project."` - Directory string `arg:"" help:"Directory to initialize. Defaults to project name." optional:"" short:"d" type:"path"` - Repository string `default:"example.com/my-org" help:"Override the repository in the project file." optional:""` + Name string `arg:"" help:"The name of the new project."` + Directory string `help:"Directory to initialize. Defaults to project name." short:"d" type:"path"` + Repository string `default:"example.com/my-org" help:"Override the repository in the project file." optional:"" short:"r"` } func (c *initCmd) Help() string { @@ -62,6 +62,10 @@ func (c *initCmd) Run(sp terminal.SpinnerPrinter) error { return err } + repo := strings.TrimRight(c.Repository, "/") + if repo == "" { + return errors.New("repository cannot be empty; set --repository to an OCI repository prefix like 'xpkg.crossplane.io/my-org'") + } return sp.WrapWithSuccessSpinner("Initializing project", func() error { if err := os.MkdirAll(c.Directory, 0o750); err != nil { return errors.Wrapf(err, "failed to create directory %s", c.Directory) @@ -75,7 +79,7 @@ metadata: name: %s spec: repository: %s/%s -`, c.Name, c.Repository, c.Name) +`, c.Name, repo, c.Name) if err := os.WriteFile(projFile, []byte(content), 0o600); err != nil { return errors.Wrapf(err, "failed to write %s", projectFileName) From 4675ae802704b1c0e7cb0ddab6b63d7db73a7867 Mon Sep 17 00:00:00 2001 From: Bob Haddleton Date: Fri, 12 Jun 2026 13:50:29 -0500 Subject: [PATCH 3/4] Reject whitespace as repository name Signed-off-by: Bob Haddleton --- cmd/crossplane/project/init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/crossplane/project/init.go b/cmd/crossplane/project/init.go index 8d5b1924..b194a228 100644 --- a/cmd/crossplane/project/init.go +++ b/cmd/crossplane/project/init.go @@ -62,7 +62,7 @@ func (c *initCmd) Run(sp terminal.SpinnerPrinter) error { return err } - repo := strings.TrimRight(c.Repository, "/") + repo := strings.TrimRight(strings.TrimSpace(c.Repository), "/") if repo == "" { return errors.New("repository cannot be empty; set --repository to an OCI repository prefix like 'xpkg.crossplane.io/my-org'") } From 81a8e27a94970ed96c7079ae17b199c9860a87f9 Mon Sep 17 00:00:00 2001 From: Bob Haddleton Date: Fri, 19 Jun 2026 09:57:52 -0500 Subject: [PATCH 4/4] Refactor to provide --registry and --repository flags Signed-off-by: Bob Haddleton --- cmd/crossplane/project/init.go | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/cmd/crossplane/project/init.go b/cmd/crossplane/project/init.go index b194a228..92ef932e 100644 --- a/cmd/crossplane/project/init.go +++ b/cmd/crossplane/project/init.go @@ -22,6 +22,7 @@ import ( "path/filepath" "strings" + "github.com/google/go-containerregistry/pkg/name" "k8s.io/apimachinery/pkg/util/validation" "github.com/crossplane/crossplane-runtime/v2/pkg/errors" @@ -39,8 +40,9 @@ const projectFileName = "crossplane-project.yaml" // initCmd initializes a new project. type initCmd struct { Name string `arg:"" help:"The name of the new project."` - Directory string `help:"Directory to initialize. Defaults to project name." short:"d" type:"path"` - Repository string `default:"example.com/my-org" help:"Override the repository in the project file." optional:"" short:"r"` + Directory string `help:"Directory to initialize. Defaults to project name." short:"d" type:"path"` + Registry string `default:"example.com/my-org" help:"Override the registry in the project file." optional:"" short:"r"` + Repository string `help:"Override the repository name in the project file. Defaults to the project name." optional:""` } func (c *initCmd) Help() string { @@ -56,16 +58,24 @@ func (c *initCmd) Run(sp terminal.SpinnerPrinter) error { if c.Directory == "" { c.Directory = c.Name } - + if strings.TrimSpace(c.Repository) == "" { + c.Repository = c.Name + } // Check if the target directory is suitable. if err := c.checkTargetDirectory(); err != nil { return err } - repo := strings.TrimRight(strings.TrimSpace(c.Repository), "/") - if repo == "" { - return errors.New("repository cannot be empty; set --repository to an OCI repository prefix like 'xpkg.crossplane.io/my-org'") + rp := strings.TrimRight(strings.TrimSpace(c.Registry), "/") + if rp == "" { + return errors.New("registry cannot be empty; set --registryPath to an OCI registry prefix like 'xpkg.crossplane.io/my-org'") + } + + r, err := name.NewRepository(rp + "/" + c.Repository) + if err != nil { + return errors.Wrapf(err, "cannot build function reference from repository \"%s/%s\"", rp, c.Repository) } + return sp.WrapWithSuccessSpinner("Initializing project", func() error { if err := os.MkdirAll(c.Directory, 0o750); err != nil { return errors.Wrapf(err, "failed to create directory %s", c.Directory) @@ -78,8 +88,8 @@ kind: Project metadata: name: %s spec: - repository: %s/%s -`, c.Name, repo, c.Name) + repository: %s +`, c.Name, r.String()) if err := os.WriteFile(projFile, []byte(content), 0o600); err != nil { return errors.Wrapf(err, "failed to write %s", projectFileName)