diff --git a/cmd/composer-cli/projects/depsolve.go b/cmd/composer-cli/projects/depsolve.go index 2f213a5..5fe08e5 100644 --- a/cmd/composer-cli/projects/depsolve.go +++ b/cmd/composer-cli/projects/depsolve.go @@ -13,22 +13,28 @@ import ( "github.com/spf13/cobra" "github.com/osbuild/weldr-client/v2/cmd/composer-cli/root" + "github.com/osbuild/weldr-client/v2/internal/common" ) var ( depsolveCmd = &cobra.Command{ Use: "depsolve PROJECT,...", Short: "Show the dependencies of all of the listed projects", + Long: ` By default this uses the host's distribution type and architecture when + depsolving. These can be overridden using the --distro and --arch flags. Use + 'composer-cli distros list' to show the list of supported distributions.`, Example: ` composer-cli projects depsolve tmux composer-cli projects depsolve tmux --json - composer-cli projects depsolve tmux --distro fedora-38`, + composer-cli projects depsolve tmux --distro fedora-38 + composer-cli projects depsolve tmux --distro fedora-38 --arch aarch64`, RunE: depsolve, Args: cobra.MinimumNArgs(1), } ) func init() { - depsolveCmd.Flags().StringVarP(&distro, "distro", "", "", "Return results for distribution") + depsolveCmd.Flags().StringVarP(&distro, "distro", "", "", "Distribution") + depsolveCmd.Flags().StringVarP(&arch, "arch", "", "", "Architecture") projectsCmd.AddCommand(depsolveCmd) } @@ -50,31 +56,69 @@ func (p project) String() string { func depsolve(cmd *cobra.Command, args []string) (rcErr error) { names := root.GetCommaArgs(args) - deps, errors, err := root.Client.DepsolveProjects(names, distro) - if err != nil { - return root.ExecutionError(cmd, "Depsolve Error: %s", err) - } - if len(errors) > 0 { - rcErr = root.ExecutionErrors(cmd, errors) - } + var err error + if root.Cloud.Exists() { + if len(distro) == 0 { + distro, err = common.GetHostDistroName() + if err != nil { + return root.ExecutionError(cmd, "Error determining host distribution: %s", err) + } + } - // Encode it using json - data := new(bytes.Buffer) - if err := json.NewEncoder(data).Encode(deps); err != nil { - fmt.Fprintf(os.Stderr, "ERROR: converting deps: %s\n", err) - return root.ExecutionError(cmd, "") - } + if len(arch) == 0 { + arch = common.HostArch() + } - // Decode the dependencies - var projects []project - if err = json.Unmarshal(data.Bytes(), &projects); err != nil { - fmt.Fprintf(os.Stderr, "ERROR: decoding deps: %s\n", err) - return root.ExecutionError(cmd, "") - } + type pkg struct { + Name string `json:"name"` + Version string `json:"version,omitempty"` + } + blueprint := struct { + Name string `json:"name"` + Version string `json:"version"` + Packages []pkg `json:"packages"` + }{ + Name: "projects-depsolve", + Version: "0.0.0", + Packages: []pkg{}, + } + for _, name := range names { + blueprint.Packages = append(blueprint.Packages, pkg{Name: name}) + } - for _, p := range projects { - fmt.Printf(" %s\n", p) - } + deps, err := root.Cloud.DepsolveBlueprint(blueprint, distro, arch) + if err != nil { + return root.ExecutionError(cmd, "Depsolve Error: %s", err) + } + for _, d := range deps { + fmt.Printf(" %s\n", d) + } + } else { + deps, errors, err := root.Client.DepsolveProjects(names, distro) + if err != nil { + return root.ExecutionError(cmd, "Depsolve Error: %s", err) + } + if len(errors) > 0 { + rcErr = root.ExecutionErrors(cmd, errors) + } + + // Encode it using json + data := new(bytes.Buffer) + if err := json.NewEncoder(data).Encode(deps); err != nil { + fmt.Fprintf(os.Stderr, "ERROR: converting deps: %s\n", err) + return root.ExecutionError(cmd, "") + } + // Decode the dependencies + var projects []project + if err = json.Unmarshal(data.Bytes(), &projects); err != nil { + fmt.Fprintf(os.Stderr, "ERROR: decoding deps: %s\n", err) + return root.ExecutionError(cmd, "") + } + + for _, p := range projects { + fmt.Printf(" %s\n", p) + } + } return rcErr } diff --git a/cmd/composer-cli/projects/depsolve_test.go b/cmd/composer-cli/projects/depsolve_test.go index 2ca426b..eccf34b 100644 --- a/cmd/composer-cli/projects/depsolve_test.go +++ b/cmd/composer-cli/projects/depsolve_test.go @@ -379,3 +379,51 @@ func TestCmdProjectsDepsolveUnknownJSON(t *testing.T) { assert.Equal(t, []byte(""), stderr) assert.Equal(t, "GET", mc.Req.Method) } + +func TestCmdProjectsDepsolveCloud(t *testing.T) { + // Test the "blueprint depsolve" command with a local blueprint file + mcc := root.SetupCloudCmdTest(func(request *http.Request) (*http.Response, error) { + json := `{ + "packages": [ + { + "arch": "noarch", + "checksum": "sha256:930722d893b77edf204d16d9f9c6403ecefe339036b699bc445ad9ab87e0e323", + "name": "basesystem", + "release": "21.fc41", + "type": "rpm", + "version": "11" + }, + { + "arch": "x86_64", + "checksum": "sha256:b10f7b9039bd3079d27e9883cd412f66acdac73b530b336c8c33e105a26391e8", + "name": "bash", + "release": "1.fc41", + "type": "rpm", + "version": "5.2.32" + }] + }` + + return &http.Response{ + StatusCode: 200, + Body: io.NopCloser(bytes.NewReader([]byte(json))), + }, nil + }) + + cmd, out, err := root.ExecuteTest("projects", "depsolve", "bash") + require.NotNil(t, out) + defer out.Close() + require.Nil(t, err) + require.NotNil(t, out.Stdout) + require.NotNil(t, out.Stderr) + require.NotNil(t, cmd) + assert.Equal(t, cmd, depsolveCmd) + stdout, err := io.ReadAll(out.Stdout) + assert.Nil(t, err) + assert.NotContains(t, string(stdout), "{") + assert.Contains(t, string(stdout), "basesystem-11-21.fc41.noarch") + assert.Contains(t, string(stdout), "bash-5.2.32-1.fc41.x86_64") + stderr, err := io.ReadAll(out.Stderr) + assert.Nil(t, err) + assert.Equal(t, []byte(""), stderr) + assert.Equal(t, "POST", mcc.Req.Method) +}