diff --git a/CHANGELOG.md b/CHANGELOG.md
index bfea568..c8d6298 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Change Log (go-package-manager)
+## 0.30.0
+
+- feat: `gpm update` now supports update of specific modules as well
+- fix: using `TidyUp()` method from `AppContext` instead raw `go mod tidy`
+- refactor: `gpm doctor` now checks all dependencies for up-to-dateness
+- refactor: code cleanups and improvements
+
## 0.29.10
- feat: self-update by executing `gpm update --self`
diff --git a/README.md b/README.md
index 8b8fe19..d32535c 100644
--- a/README.md
+++ b/README.md
@@ -585,6 +585,14 @@ gpm update
is a short version of `go get -u ./... && go mod tidy` and will update all dependencies.
+On the other hand, you are able to run something like
+
+```bash
+gpm update github.com/alecthomas/chroma yaml
+```
+
+to update specific ones. Each argument can be a module URL or [alias](#add-alias-).
+
## Setup AI [↑]
If you would like to use AI feature, like suggestion of branch names, you can setup one of the following APIs:
diff --git a/commands/doctor.go b/commands/doctor.go
index ad0804e..8c60dc9 100644
--- a/commands/doctor.go
+++ b/commands/doctor.go
@@ -116,7 +116,6 @@ func Init_Doctor_Command(parentCmd *cobra.Command, app *types.AppContext) {
// cleanups and extract items as references
allItems := make([]*GoModFileRequireItem, 0)
- directItems := make([]*GoModFileRequireItem, 0)
for _, item := range goMod.Require {
refItem := &item
@@ -124,17 +123,14 @@ func Init_Doctor_Command(parentCmd *cobra.Command, app *types.AppContext) {
refItem.Version = strings.TrimSpace(refItem.Version)
allItems = append(allItems, refItem)
- if refItem.Indirect == nil || !*refItem.Indirect {
- directItems = append(directItems, refItem)
- }
}
- if len(directItems) > 0 {
+ if len(allItems) > 0 {
fmt.Println("Checking dependencies for up-to-dateness ...")
- for i, item := range directItems {
+ for i, item := range allItems {
s := spinner.New(spinner.CharSets[24], 100*time.Millisecond)
s.Prefix = "\t["
- s.Suffix = fmt.Sprintf("] Checking '%s' (%v/%v) ...", item.Path, i+1, len(directItems))
+ s.Suffix = fmt.Sprintf("] Checking '%s' (%v/%v) ...", item.Path, i+1, len(allItems))
s.Start()
thisVersion, err := version.NewVersion(strings.TrimSpace(item.Version))
diff --git a/commands/generate.go b/commands/generate.go
index 1d5dae1..dab3a48 100644
--- a/commands/generate.go
+++ b/commands/generate.go
@@ -418,12 +418,7 @@ require (
}
// cleanup project
- p = utils.CreateShellCommandByArgs("go", "mod", "tidy")
- p.Dir = outDir
- p.Stdout = nil
- p.Stderr = nil
- app.Debug(fmt.Sprintf("Cleanup project '%s' ...", projectUrl))
- utils.RunCommand(p)
+ app.TidyUp()
// output final summary
out, _ := glamour.Render(lastResponse.FinalSummary, "dark")
diff --git a/commands/update.go b/commands/update.go
index 1f3463a..6a8823b 100644
--- a/commands/update.go
+++ b/commands/update.go
@@ -23,20 +23,10 @@
package commands
import (
- "bufio"
- "bytes"
"fmt"
- "io"
- "net/http"
- "os"
- "os/exec"
- "runtime"
"strings"
- browser "github.com/EDDYCJY/fake-useragent"
- "github.com/alecthomas/chroma/quick"
"github.com/mkloubert/go-package-manager/types"
- "github.com/mkloubert/go-package-manager/utils"
"github.com/spf13/cobra"
)
@@ -51,215 +41,45 @@ func Init_Update_Command(parentCmd *cobra.Command, app *types.AppContext) {
var userAgent string
var updateCmd = &cobra.Command{
- Use: "update",
+ Use: "update ",
Aliases: []string{"upd"},
Short: "Update dependencies",
- Long: `Updates all dependencies in this project.`,
+ Long: `Updates all or only specific dependencies in this project.`,
Run: func(cmd *cobra.Command, args []string) {
if selfUpdate {
- app.Debug("Will start self-update ...")
-
- consoleFormatter := utils.GetBestChromaFormatterName()
- consoleStyle := utils.GetBestChromaStyleName()
-
- customUserAgent := strings.TrimSpace(userAgent)
- if customUserAgent == "" {
- customUserAgent = browser.Chrome()
- }
-
- customPowerShellBin := strings.TrimSpace(powerShellBin)
- if customPowerShellBin == "" {
- customPowerShellBin = "powershell"
- }
-
- customUpdateScript := strings.TrimSpace(updateScript)
- if customUpdateScript == "" {
- customUpdateScript = strings.TrimSpace(os.Getenv("GPM_UPDATE_SCRIPT"))
- }
-
- downloadScript := func(url string) ([]byte, error) {
- app.Debug(fmt.Sprintf("Download from '%s' ...", url))
- app.Debug(fmt.Sprintf("User agent: %s", customUserAgent))
-
- req, err := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
- if err != nil {
- return []byte{}, err
- }
-
- req.Header.Set("User-Agent", customUserAgent)
-
- client := &http.Client{}
- resp, err := client.Do(req)
- if err != nil {
- return []byte{}, err
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != 200 {
- return []byte{}, fmt.Errorf("unexpected response: %v", resp.StatusCode)
- }
-
- responseData, err := io.ReadAll(resp.Body)
-
- return responseData, err
- }
-
- showNewVersion := func() {
- if noVersionPrint {
- return
- }
+ run_self_update_command(
+ app,
+ force, noVersionPrint, powerShell, powerShellBin, updateScript, userAgent,
+ )
+ } else {
+ modulesToUpdate := make([]string, 0)
+ for _, moduleNameOrUrl := range args {
+ // maybe alias => module url(s)
+ moduleUrls := app.GetModuleUrls(moduleNameOrUrl)
- app.RunShellCommandByArgs("gpm", "--version")
+ modulesToUpdate = append(modulesToUpdate, moduleUrls...)
}
- if powerShell || utils.IsWindows() {
- // PowerShell
- app.Debug(fmt.Sprintf("Will use PowerShell '%s' ...", powerShellBin))
-
- scriptUrl := customUpdateScript
- if scriptUrl == "" {
- scriptUrl = "https://raw.githubusercontent.com/mkloubert/go-package-manager/main/sh.kloubert.dev/gpm.ps1"
- } else {
- su, err := utils.ToUrlForOpenHandler(scriptUrl)
- utils.CheckForError(err)
-
- scriptUrl = su
- }
-
- pwshScript, err := downloadScript(scriptUrl)
- utils.CheckForError(err)
-
- executeScript := func() {
- p := exec.Command(customPowerShellBin, "-NoProfile", "-Command", "-")
- p.Dir = app.Cwd
- p.Stderr = os.Stderr
- p.Stdout = os.Stdout
-
- stdinPipe, err := p.StdinPipe()
- utils.CheckForError(err)
-
- err = p.Start()
- utils.CheckForError(err)
-
- go func() {
- defer stdinPipe.Close()
- stdinPipe.Write([]byte(pwshScript))
- }()
+ additionalShellArgs := make([]string, 0)
+ additionalShellArgs = append(additionalShellArgs, modulesToUpdate...)
+ if len(modulesToUpdate) == 0 {
+ app.Debug("Will update all modules in project ...")
- err = p.Wait()
- utils.CheckForError(err)
-
- showNewVersion()
- os.Exit(0)
- }
-
- if force {
- executeScript()
- } else {
- // ask the user first
-
- err = quick.Highlight(os.Stdout, string(pwshScript), "powershell", consoleFormatter, consoleStyle)
- if err != nil {
- fmt.Print(string(pwshScript))
- }
-
- fmt.Println()
- fmt.Println()
-
- reader := bufio.NewReader(os.Stdin)
-
- for {
- fmt.Print("Do you really want to run this PowerShell script (Y/n)? ")
- userInput, _ := reader.ReadString('\n')
- userInput = strings.TrimSpace(strings.ToLower(userInput))
-
- switch userInput {
- case "", "y", "yes":
- executeScript()
- case "n", "no":
- os.Exit(0)
- }
- }
- }
- } else if utils.IsPOSIXLikeOS() {
- // if POSIX-like => sh
- app.Debug("Will use UNIX shell ...")
-
- scriptUrl := customUpdateScript
- if scriptUrl == "" {
- scriptUrl = "https://raw.githubusercontent.com/mkloubert/go-package-manager/main/sh.kloubert.dev/gpm.sh"
- } else {
- su, err := utils.ToUrlForOpenHandler(scriptUrl)
- utils.CheckForError(err)
-
- scriptUrl = su
- }
-
- bashScript, err := downloadScript(scriptUrl)
- utils.CheckForError(err)
-
- executeScript := func() {
- p := exec.Command("sh")
- p.Dir = app.Cwd
- p.Stderr = os.Stderr
- p.Stdout = os.Stdout
-
- stdinPipe, err := p.StdinPipe()
- utils.CheckForError(err)
-
- err = p.Start()
- utils.CheckForError(err)
-
- go func() {
- defer stdinPipe.Close()
- stdinPipe.Write([]byte(bashScript))
- }()
-
- err = p.Wait()
- utils.CheckForError(err)
-
- showNewVersion()
- os.Exit(0)
- }
-
- if force {
- executeScript()
- } else {
- // ask the user first
-
- err = quick.Highlight(os.Stdout, string(bashScript), "shell", consoleFormatter, consoleStyle)
- if err != nil {
- fmt.Print(string(bashScript))
- }
-
- fmt.Println()
- fmt.Println()
-
- reader := bufio.NewReader(os.Stdin)
-
- for {
- fmt.Print("Do you really want to run this bash script (Y/n)? ")
- userInput, _ := reader.ReadString('\n')
- userInput = strings.TrimSpace(strings.ToLower(userInput))
-
- switch userInput {
- case "", "y", "yes":
- executeScript()
- case "n", "no":
- os.Exit(0)
- }
- }
- }
+ // update all in this project instead specific onces
+ additionalShellArgs = append(additionalShellArgs, "./...")
} else {
- utils.CheckForError(fmt.Errorf("self-update for %s/%s is not supported yet", runtime.GOOS, runtime.GOARCH))
+ // update specific ones
+ app.Debug(fmt.Sprintf("Will update following modules in project: %s", strings.Join(modulesToUpdate, ",")))
}
- } else {
- app.Debug("Will start project dependencies ...")
- app.RunShellCommandByArgs("go", "get", "-u", "./...")
+ allShellArgs := make([]string, 0)
+ allShellArgs = append(allShellArgs, "get", "-u")
+ allShellArgs = append(allShellArgs, additionalShellArgs...)
+
+ app.RunShellCommandByArgs("go", allShellArgs...)
if !noCleanup {
- app.RunShellCommandByArgs("go", "mod", "tidy")
+ app.TidyUp()
}
}
},
diff --git a/commands/update.self_update.go b/commands/update.self_update.go
new file mode 100644
index 0000000..cf66448
--- /dev/null
+++ b/commands/update.self_update.go
@@ -0,0 +1,242 @@
+// MIT License
+//
+// Copyright (c) 2024 Marcel Joachim Kloubert (https://marcel.coffee)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+package commands
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "net/http"
+ "os"
+ "os/exec"
+ "runtime"
+ "strings"
+
+ browser "github.com/EDDYCJY/fake-useragent"
+ "github.com/alecthomas/chroma/quick"
+ "github.com/mkloubert/go-package-manager/types"
+ "github.com/mkloubert/go-package-manager/utils"
+)
+
+func run_self_update_command(
+ app *types.AppContext,
+ force bool, noVersionPrint bool, powerShell bool, powerShellBin string, updateScript string, userAgent string,
+) {
+ app.Debug("Will start self-update ...")
+
+ consoleFormatter := utils.GetBestChromaFormatterName()
+ consoleStyle := utils.GetBestChromaStyleName()
+
+ customUserAgent := strings.TrimSpace(userAgent)
+ if customUserAgent == "" {
+ customUserAgent = browser.Chrome()
+ }
+
+ customPowerShellBin := strings.TrimSpace(powerShellBin)
+ if customPowerShellBin == "" {
+ customPowerShellBin = "powershell"
+ }
+
+ customUpdateScript := strings.TrimSpace(updateScript)
+ if customUpdateScript == "" {
+ customUpdateScript = strings.TrimSpace(os.Getenv("GPM_UPDATE_SCRIPT"))
+ }
+
+ downloadScript := func(url string) ([]byte, error) {
+ app.Debug(fmt.Sprintf("Download from '%s' ...", url))
+ app.Debug(fmt.Sprintf("User agent: %s", customUserAgent))
+
+ req, err := http.NewRequest("GET", url, bytes.NewBuffer([]byte{}))
+ if err != nil {
+ return []byte{}, err
+ }
+
+ req.Header.Set("User-Agent", customUserAgent)
+
+ client := &http.Client{}
+ resp, err := client.Do(req)
+ if err != nil {
+ return []byte{}, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ return []byte{}, fmt.Errorf("unexpected response: %v", resp.StatusCode)
+ }
+
+ responseData, err := io.ReadAll(resp.Body)
+
+ return responseData, err
+ }
+
+ showNewVersion := func() {
+ if noVersionPrint {
+ return
+ }
+
+ app.RunShellCommandByArgs("gpm", "--version")
+ }
+
+ if powerShell || utils.IsWindows() {
+ // PowerShell
+ app.Debug(fmt.Sprintf("Will use PowerShell '%s' ...", powerShellBin))
+
+ scriptUrl := customUpdateScript
+ if scriptUrl == "" {
+ scriptUrl = "https://raw.githubusercontent.com/mkloubert/go-package-manager/main/sh.kloubert.dev/gpm.ps1"
+ } else {
+ su, err := utils.ToUrlForOpenHandler(scriptUrl)
+ utils.CheckForError(err)
+
+ scriptUrl = su
+ }
+
+ pwshScript, err := downloadScript(scriptUrl)
+ utils.CheckForError(err)
+
+ executeScript := func() {
+ p := exec.Command(customPowerShellBin, "-NoProfile", "-Command", "-")
+ p.Dir = app.Cwd
+ p.Stderr = os.Stderr
+ p.Stdout = os.Stdout
+
+ stdinPipe, err := p.StdinPipe()
+ utils.CheckForError(err)
+
+ err = p.Start()
+ utils.CheckForError(err)
+
+ go func() {
+ defer stdinPipe.Close()
+ stdinPipe.Write([]byte(pwshScript))
+ }()
+
+ err = p.Wait()
+ utils.CheckForError(err)
+
+ showNewVersion()
+ os.Exit(0)
+ }
+
+ if force {
+ executeScript()
+ } else {
+ // ask the user first
+
+ err = quick.Highlight(os.Stdout, string(pwshScript), "powershell", consoleFormatter, consoleStyle)
+ if err != nil {
+ fmt.Print(string(pwshScript))
+ }
+
+ fmt.Println()
+ fmt.Println()
+
+ reader := bufio.NewReader(os.Stdin)
+
+ for {
+ fmt.Print("Do you really want to run this PowerShell script (Y/n)? ")
+ userInput, _ := reader.ReadString('\n')
+ userInput = strings.TrimSpace(strings.ToLower(userInput))
+
+ switch userInput {
+ case "", "y", "yes":
+ executeScript()
+ case "n", "no":
+ os.Exit(0)
+ }
+ }
+ }
+ } else if utils.IsPOSIXLikeOS() {
+ // if POSIX-like => sh
+ app.Debug("Will use UNIX shell ...")
+
+ scriptUrl := customUpdateScript
+ if scriptUrl == "" {
+ scriptUrl = "https://raw.githubusercontent.com/mkloubert/go-package-manager/main/sh.kloubert.dev/gpm.sh"
+ } else {
+ su, err := utils.ToUrlForOpenHandler(scriptUrl)
+ utils.CheckForError(err)
+
+ scriptUrl = su
+ }
+
+ bashScript, err := downloadScript(scriptUrl)
+ utils.CheckForError(err)
+
+ executeScript := func() {
+ p := exec.Command("sh")
+ p.Dir = app.Cwd
+ p.Stderr = os.Stderr
+ p.Stdout = os.Stdout
+
+ stdinPipe, err := p.StdinPipe()
+ utils.CheckForError(err)
+
+ err = p.Start()
+ utils.CheckForError(err)
+
+ go func() {
+ defer stdinPipe.Close()
+ stdinPipe.Write([]byte(bashScript))
+ }()
+
+ err = p.Wait()
+ utils.CheckForError(err)
+
+ showNewVersion()
+ os.Exit(0)
+ }
+
+ if force {
+ executeScript()
+ } else {
+ // ask the user first
+
+ err = quick.Highlight(os.Stdout, string(bashScript), "shell", consoleFormatter, consoleStyle)
+ if err != nil {
+ fmt.Print(string(bashScript))
+ }
+
+ fmt.Println()
+ fmt.Println()
+
+ reader := bufio.NewReader(os.Stdin)
+
+ for {
+ fmt.Print("Do you really want to run this bash script (Y/n)? ")
+ userInput, _ := reader.ReadString('\n')
+ userInput = strings.TrimSpace(strings.ToLower(userInput))
+
+ switch userInput {
+ case "", "y", "yes":
+ executeScript()
+ case "n", "no":
+ os.Exit(0)
+ }
+ }
+ }
+ } else {
+ utils.CheckForError(fmt.Errorf("self-update for %s/%s is not supported yet", runtime.GOOS, runtime.GOARCH))
+ }
+}
diff --git a/go.mod b/go.mod
index a0734aa..7bbcf70 100644
--- a/go.mod
+++ b/go.mod
@@ -60,7 +60,7 @@ require (
github.com/yuin/goldmark-emoji v1.0.4 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/net v0.33.0 // indirect
- golang.org/x/sys v0.28.0 // indirect
+ golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.27.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
diff --git a/go.sum b/go.sum
index 80911b8..7bccc3a 100644
--- a/go.sum
+++ b/go.sum
@@ -203,8 +203,9 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
diff --git a/types/app_context.go b/types/app_context.go
index a800ee5..61ec8a0 100644
--- a/types/app_context.go
+++ b/types/app_context.go
@@ -745,7 +745,7 @@ func (app *AppContext) GetGpmFilePath() (string, error) {
}
// app.GetModuleUrls() - returns the list of module urls based on the
-// information from gpm.y(a)ml file
+// information from aliases.y(a)ml file if possible
func (app *AppContext) GetModuleUrls(moduleNameOrUrl string) []string {
moduleNameOrUrl = utils.CleanupModuleName(moduleNameOrUrl)