Skip to content

Commit

Permalink
Merge pull request #23 from Gelio/add-force-rebuild-flag
Browse files Browse the repository at this point in the history
Add `--force` flag
  • Loading branch information
Gelio authored Jun 10, 2024
2 parents 2229114 + 6880418 commit b288b46
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 15 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

## Unreleased

## Added

- A new `--force` flag (alias: `-f`)
<https://github.com/Gelio/go-global-update/pull/23>.

The `--force` flag forces reinstalling binaries that are already up-to-date
and would otherwise be skipped when upgrading the versions.

This can be used to reinstall all binaries after a new version of go is
installed, especially when it fixes security vulnerabilities.

Thanks to [@thejan2009](https://github.com/thejan2009) for suggesting this
feature.

## v0.2.3 (2023-09-19)

### Improvements
Expand Down
32 changes: 23 additions & 9 deletions internal/updater/updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type Options struct {
// List of binary names to update.
// If empty, will update all binaries in GOBIN
BinariesToUpdate []string
// Whether to force reinstalling/updating all binaries.
ForceReinstall bool
}

// UpdateBinaries updates binaries in GOBIN
Expand Down Expand Up @@ -57,7 +59,7 @@ func UpdateBinaries(
printBinariesSummary(introspectionResults, out, colorsFactory, options.Verbose)

if !options.DryRun {
return updateBinaries(introspectionResults, &goCLI, out, colorsFactory, options.Verbose)
return updateBinaries(introspectionResults, &goCLI, out, colorsFactory, options)
}

return nil
Expand Down Expand Up @@ -122,7 +124,7 @@ func updateBinaries(
goCLI *gocli.GoCLI,
out io.Writer,
colorsFactory *colors.DecoratorFactory,
verbose bool,
options Options,
) error {
var upgradeErrors []error
var binariesToUpdate []gobinaries.GoBinary
Expand All @@ -133,12 +135,19 @@ func updateBinaries(
faintFormatter := colorsFactory.NewDecorator(color.Faint)

for _, result := range introspectionResults {
if result.Error != nil || !result.Binary.UpgradePossible() {
if result.Error != nil {
continue
}
if !result.Binary.UpgradePossible() && !options.ForceReinstall {
continue
}

if binary := result.Binary; binary.BuiltFromSource() {
fmt.Fprintf(out, "Skipping upgrading %s\n ", binaryNameFormatter(binary.Name))
verb := "reinstalling"
if result.Binary.UpgradePossible() {
verb = "upgrading"
}
fmt.Fprintf(out, "Skipping %s %s\n ", verb, binaryNameFormatter(binary.Name))
if binary.BuiltWithGoBuild() {
fmt.Fprintf(out, "The binary was built from source (probably using \"%s\") and the binary path is unknown.\n",
faintFormatter("go build"))
Expand Down Expand Up @@ -169,18 +178,23 @@ func updateBinaries(
latestVersionFormatter := colorsFactory.NewDecorator(color.FgGreen)

for _, binary := range binariesToUpdate {
fmt.Fprintf(out, "Upgrading %s to %s ... ", binaryNameFormatter(binary.Name),
latestVersionFormatter(binary.LatestVersion))
if binary.UpgradePossible() {
fmt.Fprintf(out, "Upgrading %s to %s ... ", binaryNameFormatter(binary.Name),
latestVersionFormatter(binary.LatestVersion))
} else {
fmt.Fprintf(out, "Force-reinstalling %s %s ... ", binaryNameFormatter(binary.Name),
latestVersionFormatter(binary.LatestVersion))
}
upgradeOutput, err := goCLI.UpgradePackage(binary.PathURL)
if err != nil {
upgradeErrors = append(upgradeErrors, err)
fmt.Fprintln(out, "❌")
fmt.Fprintln(out, " Could not upgrade package")
fmt.Fprintln(out, " Could not install package")
} else {
fmt.Fprintln(out, "✅")
}

if len(upgradeOutput) > 0 && (verbose || err != nil) {
if len(upgradeOutput) > 0 && (options.Verbose || err != nil) {
fmt.Fprintln(out, upgradeOutput)

for _, problem := range FindCommonUpdateProblems(upgradeOutput) {
Expand All @@ -191,7 +205,7 @@ func updateBinaries(
}

if len(upgradeErrors) > 0 {
return fmt.Errorf("could not upgrade %s package(s)",
return fmt.Errorf("could not install %s package(s)",
colorsFactory.NewDecorator(color.FgRed, color.Bold)(len(upgradeErrors)))
}

Expand Down
43 changes: 43 additions & 0 deletions internal/updater/updater_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,46 @@ Skipping upgrading installed-from-source
`), strings.TrimSpace(output.String()))
}

func TestForceReinstallingBinaries(t *testing.T) {
gofumptMockBinary := gobinariestest.GetGofumptMockBinary()
shfmtMockBinary := gobinariestest.GetShfmtMockBinary()
shfmtMockBinary.Binary.LatestVersion = "v3.4.3"

logger := zap.NewNop()
options := Options{
ForceReinstall: true,
}
var output bytes.Buffer
lister := gobinariestest.TestSuccessDirectoryLister{
Entries: []string{gofumptMockBinary.Binary.Name, shfmtMockBinary.Binary.Name},
}
cmdRunner := goclitest.TestGoCmdRunner{
Responses: []goclitest.MockResponse{
gobinMockResponse(),
gobinariestest.GetLatestVersionMockResponse(gofumptMockBinary.Binary),
gobinariestest.GetModuleInfoMockResponse(gofumptMockBinary),
updateMockResponse(gofumptMockBinary.Binary, "", nil),
gobinariestest.GetLatestVersionMockResponse(shfmtMockBinary.Binary),
gobinariestest.GetModuleInfoMockResponse(shfmtMockBinary),
updateMockResponse(shfmtMockBinary.Binary, "", nil),
},
}

fsutils := mockFilesystemUtils{}
colorsFactory := colors.NewFactory(false)

err := UpdateBinaries(logger, options, &output, &colorsFactory, &cmdRunner, &lister, fsutils)

assert.Nil(t, err)
assert.Equal(t, strings.TrimSpace(`
Binary Current version Status
gofumpt v0.3.0 up-to-date
shfmt v3.4.2 can upgrade to v3.4.3
Force-reinstalling gofumpt v0.3.0 ... ✅
Upgrading shfmt to v3.4.3 ... ✅
`), strings.TrimSpace(output.String()))
}
22 changes: 17 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ func main() {
Name: "colors",
Usage: "Force using ANSI color codes in the output even if the output is not a TTY.\n\t\tSet the NO_COLOR environment variable if you want to force-disable colors (see https://no-color.org/).",
},
&cli.BoolFlag{
Name: "force",
Aliases: []string{"f"},
Usage: "Force reinstall all binaries, even if they do not need to be updated",
},
},
Action: func(c *cli.Context) error {
forceColors := c.Bool("colors")
Expand All @@ -73,13 +78,20 @@ func main() {

cmdRunner := gocli.NewCmdRunner(logger)

options := updater.Options{
DryRun: c.Bool("dry-run"),
Verbose: c.Bool("verbose"),
ForceReinstall: c.Bool("force"),
BinariesToUpdate: c.Args().Slice(),
}

if options.DryRun && options.ForceReinstall {
return fmt.Errorf("--dry-run and --force options cannot be used together")
}

err = updater.UpdateBinaries(
logger,
updater.Options{
DryRun: c.Bool("dry-run"),
Verbose: c.Bool("verbose"),
BinariesToUpdate: c.Args().Slice(),
},
options,
os.Stdout,
&colorsDecoratorFactory,
&cmdRunner,
Expand Down
2 changes: 1 addition & 1 deletion test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func TestIntegration(t *testing.T) {
name: gnosticBinaryToInstall.name,
pathAndVersion: gnosticBinaryToInstall.pathAndVersion,
beforeUpdate: gnosticBinaryToInstall.beforeUpdate,
// NOTE: the gofumpt binary should not be upgraded
// NOTE: the binary should not be upgraded
afterUpdate: gnosticBinaryToInstall.beforeUpdate,
},
},
Expand Down

0 comments on commit b288b46

Please sign in to comment.