Skip to content

Commit

Permalink
feat: add --project flag to ddev add-on commands, fixes ddev#6513 (
Browse files Browse the repository at this point in the history
…ddev#6517) [skip ci]
  • Loading branch information
GuySartorelli authored Sep 3, 2024
1 parent 608267b commit 1c07506
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 14 deletions.
15 changes: 7 additions & 8 deletions cmd/ddev/cmd/addon-get.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ import (

// AddonGetCmd is the "ddev add-on get" command
var AddonGetCmd = &cobra.Command{
Use: "get <addonOrURL> [project]",
Use: "get <addonOrURL>",
Aliases: []string{"install"},
Args: cobra.MinimumNArgs(1),
Args: cobra.ExactArgs(1),
Short: "Get/Download a 3rd party add-on (service, provider, etc.)",
Long: `Get/Download a 3rd party add-on (service, provider, etc.). This can be a GitHub repo, in which case the latest release will be used, or it can be a link to a .tar.gz in the correct format (like a particular release's .tar.gz) or it can be a local directory.`,
Example: `ddev add-on get ddev/ddev-redis
ddev add-on get ddev/ddev-redis --version v1.0.4
ddev add-on get ddev/ddev-redis --project my-project
ddev add-on get https://github.com/ddev/ddev-drupal-solr/archive/refs/tags/v1.2.3.tar.gz
ddev add-on get /path/to/package
ddev add-on get /path/to/tarball.tar.gz
Expand All @@ -48,14 +49,10 @@ ddev add-on get /path/to/tarball.tar.gz
verbose = true
}

apps, err := getRequestedProjects(args[1:], false)
app, err := ddevapp.GetActiveApp(cmd.Flag("project").Value.String())
if err != nil {
util.Failed("Unable to get project(s) %v: %v", args, err)
util.Failed("Unable to get project %v: %v", cmd.Flag("project").Value.String(), err)
}
if len(apps) == 0 {
util.Failed("No project(s) found")
}
app := apps[0]
err = os.Chdir(app.AppRoot)
if err != nil {
util.Failed("Unable to change directory to project root %s: %v", app.AppRoot, err)
Expand Down Expand Up @@ -349,6 +346,8 @@ func createManifestFile(app *ddevapp.DdevApp, addonName string, repository strin
func init() {
AddonGetCmd.Flags().String("version", "", `Specify a particular version of add-on to install`)
AddonGetCmd.Flags().BoolP("verbose", "v", false, "Extended/verbose output")
AddonGetCmd.Flags().String("project", "", "Name of the project to install the add-on in")
_ = AddonGetCmd.RegisterFlagCompletionFunc("project", ddevapp.GetProjectNamesFunc("all", 0))

AddonCmd.AddCommand(AddonGetCmd)
}
12 changes: 10 additions & 2 deletions cmd/ddev/cmd/addon-list.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,24 @@ import (
// AddonListCmd is the "ddev add-on list" command
var AddonListCmd = &cobra.Command{
Use: "list",
Args: cobra.NoArgs,
Short: "List available or installed DDEV add-ons",
Long: `List available or installed DDEV add-ons. Without '--all' it shows only official DDEV add-ons. To list installed add-ons, use '--installed'`,
Example: `ddev add-on list
ddev add-on list --all
ddev add-on list --installed
ddev add-on list --installed --project my-project
`,
Run: func(cmd *cobra.Command, _ []string) {
if cmd.Flags().Changed("project") && !cmd.Flags().Changed("installed") {
util.Failed("--project flag can only be used with --installed flag")
}

// List installed add-ons
if cmd.Flags().Changed("installed") {
app, err := ddevapp.GetActiveApp("")
app, err := ddevapp.GetActiveApp(cmd.Flag("project").Value.String())
if err != nil {
util.Failed("Unable to find active project: %v", err)
util.Failed("Unable to get project %v: %v", cmd.Flag("project").Value.String(), err)
}

ListInstalledAddons(app)
Expand Down Expand Up @@ -130,6 +136,8 @@ func renderRepositoryList(repos []*github.Repository) string {
func init() {
AddonListCmd.Flags().Bool("all", false, `List unofficial DDEV add-ons for in addition to the official ones`)
AddonListCmd.Flags().Bool("installed", false, `Show installed DDEV add-ons`)
AddonListCmd.Flags().String("project", "", "Name of the project to list the add-ons for. Can only be used with `--installed`")
_ = AddonListCmd.RegisterFlagCompletionFunc("project", ddevapp.GetProjectNamesFunc("all", 0))
// Can't do 'ddev add-on list --all --installed', because the "installed" flag already shows *all* installed add-ons
AddonListCmd.MarkFlagsMutuallyExclusive("all", "installed")

Expand Down
9 changes: 6 additions & 3 deletions cmd/ddev/cmd/addon-remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ var AddonRemoveCmd = &cobra.Command{
Short: "Remove a DDEV add-on which was previously installed in this project",
Example: `ddev add-on remove someaddonname,
ddev add-on remove someowner/ddev-someaddonname,
ddev add-on remove ddev-someaddonname
ddev add-on remove ddev-someaddonname,
ddev add-on remove ddev-someaddonname --project my-project
`,
Run: func(cmd *cobra.Command, args []string) {
app, err := ddevapp.GetActiveApp("")
app, err := ddevapp.GetActiveApp(cmd.Flag("project").Value.String())
if err != nil {
util.Failed("Unable to find active project: %v", err)
util.Failed("Unable to get project %v: %v", cmd.Flag("project").Value.String(), err)
}

origDir, _ := os.Getwd()
Expand Down Expand Up @@ -48,5 +49,7 @@ ddev add-on remove ddev-someaddonname

func init() {
AddonRemoveCmd.Flags().BoolP("verbose", "v", false, "Extended/verbose output")
AddonRemoveCmd.Flags().String("project", "", "Name of the project to remove the add-on from")
_ = AddonRemoveCmd.RegisterFlagCompletionFunc("project", ddevapp.GetProjectNamesFunc("all", 0))
AddonCmd.AddCommand(AddonRemoveCmd)
}
42 changes: 42 additions & 0 deletions cmd/ddev/cmd/addon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,48 @@ func TestCmdAddonInstalled(t *testing.T) {
assert.NoError(err, "unable to ddev add-on get redis: %v, output='%s'", err, out)
}

// TestCmdAddonProjectFlag tests the `--project` flag in `ddev add-on` subcommands
func TestCmdAddonProjectFlag(t *testing.T) {
if os.Getenv("DDEV_RUN_GET_TESTS") != "true" {
t.Skip("Skipping because DDEV_RUN_GET_TESTS is not set")
}
origDdevDebug := os.Getenv("DDEV_DEBUG")
_ = os.Unsetenv("DDEV_DEBUG")
assert := asrt.New(t)

site := TestSites[0]
// Explicitly don't chdir to the project

t.Cleanup(func() {
_, err := exec.RunHostCommand(DdevBin, "add-on", "remove", "redis", "--project", site.Name)
assert.NoError(err)
_ = os.RemoveAll(filepath.Join(globalconfig.GetGlobalDdevDir(), "commands/web/global-touched"))
_ = os.Setenv("DDEV_DEBUG", origDdevDebug)
})

// Install the add-on using the `--project` flag
out, err := exec.RunHostCommand(DdevBin, "add-on", "get", "ddev/ddev-redis", "--project", site.Name, "--json-output")
require.NoError(t, err, "failed ddev add-on get ddev/ddev-redis --project %s --json-output: %v (output='%s')", site.Name, err, out)

redisManifest := getManifestFromLogs(t, out)
require.NoError(t, err)

installedOutput, err := exec.RunHostCommand(DdevBin, "add-on", "list", "--installed", "--project", site.Name, "--json-output")
require.NoError(t, err, "failed ddev add-on list --installed --project %s --json-output: %v (output='%s')", site.Name, err, installedOutput)
installedManifests := getManifestMapFromLogs(t, installedOutput)

require.NotEmptyf(t, redisManifest["Version"], "redis manifest is empty: %v", redisManifest)
assert.Equal(redisManifest["Version"], installedManifests["redis"]["Version"])

// Remove the add-on using the `--project` flag
out, err = exec.RunHostCommand(DdevBin, "add-on", "remove", "ddev/ddev-redis", "--project", site.Name)
require.NoError(t, err, "unable to ddev add-on remove ddev/ddev-redis --project %s: %v, output='%s'", site.Name, err, out)

// Now make sure we put it back so it can be removed in cleanup
out, err = exec.RunHostCommand(DdevBin, "add-on", "get", "ddev/ddev-redis", "--project", site.Name)
assert.NoError(err, "unable to ddev add-on get ddev/ddev-redis --project %s: %v, output='%s'", site.Name, err, out)
}

// getManifestFromLogs returns the manifest built from 'raw' section of
// ddev add-on get <project> -j output
func getManifestFromLogs(t *testing.T, jsonOut string) map[string]interface{} {
Expand Down
16 changes: 15 additions & 1 deletion cmd/ddev/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,21 @@ ddev get --remove ddev-someaddonname
if len(args) < 1 {
util.Failed("You must specify an add-on to download")
}
if len(args) > 1 {
// Update --project flag if projects were passed as args.
apps, err := getRequestedProjects(args[1:], false)
if err != nil {
util.Failed("Unable to get project(s) %v: %v", args, err)
}
if len(apps) > 0 {
err = cmd.Flag("project").Value.Set(apps[0].GetName())
if err != nil {
util.Failed("Unable to set --project flag %v: %v", apps[0].GetName(), err)
}
}
}
// Hand off execution for ddev get <add-on>
AddonGetCmd.Run(cmd, args)
AddonGetCmd.Run(cmd, []string{args[0]})
},
}

Expand All @@ -78,5 +91,6 @@ func init() {
Get.Flags().String("remove", "", `Remove a ddev-get add-on`)
Get.Flags().String("version", "", `Specify a particular version of add-on to install`)
Get.Flags().BoolP("verbose", "v", false, "Extended/verbose output for ddev get")
Get.Flags().String("project", "", "Name of the project to install the add-on in")
RootCmd.AddCommand(Get)
}
10 changes: 10 additions & 0 deletions docs/content/users/usage/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,7 @@ Download an add-on (service, provider, etc.).

Flags:

* `--project <projectName>`: Specify a project to install the add-on into. Defaults to checking for a project in the current directory.
* `--version <version>`: Specify a version to download
* `--verbose`, `-v`: Output verbose error information with Bash `set -x` (default `false`)

Expand All @@ -656,6 +657,9 @@ ddev add-on get /path/to/package

# Copy an add-on from a tarball in another directory
ddev add-on get /path/to/tarball.tar.gz

# Download the official Redis add-on and install it into a project named "my-project"
ddev add-on get ddev/ddev-redis --project my-project
```

In general, you can run `ddev add-on get` multiple times without doing any damage. Updating an add-on can be done by running `ddev add-on get <add-on-name>`. If you have changed an add-on file and removed the `#ddev-generated` marker in the file, that file will not be touched and DDEV will let you know about it.
Expand All @@ -666,6 +670,7 @@ Remove an installed add-on. Accepts the full add-on name, the short name of the

Flags:

* `--project <projectName>`: Specify a project to remove the add-on from. Defaults to checking for a project in the current directory.
* `--verbose`, `-v`: Output verbose error information with Bash `set -x` (default `false`)

Example:
Expand All @@ -674,6 +679,7 @@ Example:
ddev add-on remove redis
ddev add-on remove ddev-redis
ddev add-on remove ddev/ddev-redis
ddev add-on remove ddev/ddev-redis --project my-project
```

### `add-on list`
Expand All @@ -684,6 +690,7 @@ Flags:

* `--all`: List unofficial *and* official add-ons. (default `true`)
* `--installed`: List installed add-ons
* `--project <projectName>`: Specify a project to remove the add-on from. Can only be used with the `--installed` flag. Defaults to checking for a project in the current directory.

Example:

Expand All @@ -696,6 +703,9 @@ ddev add-on list --all

# List installed add-ons
ddev add-on list --installed

# List installed add-ons for a specific project
ddev add-on list --installed --project my-project
```

## `heidisql`
Expand Down

0 comments on commit 1c07506

Please sign in to comment.