Skip to content

Commit

Permalink
Merge pull request #27 from carolynvs/install-to-directory
Browse files Browse the repository at this point in the history
Add EnsurePackageWith/InstallPackageWith
  • Loading branch information
carolynvs authored Jul 18, 2022
2 parents f0d0ccd + 54e4637 commit eb350f0
Show file tree
Hide file tree
Showing 4 changed files with 241 additions and 29 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,16 @@ import (
"github.com/carolynvs/magex/shx"
)

// Install packr2 v2.8.0 if it's not available, and ensure it's in PATH.
// Check if packr2 is in the bin/ directory and is at least v2.
// If not, install packr@v2.8.0 into bin/
func EnsurePackr2() error {
return pkg.EnsurePackage("github.com/gobuffalo/packr/v2/packr2", "v2.8.0", "version")
opts := pkg.EnsurePackageOptions{
Name: "github.com/gobuffalo/packr/v2/packr2",
DefaultVersion: "v2.8.0",
VersionCommand: "version",
Destination: "bin",
}
return pkg.EnsurePackageWith(opts)
}

// Install mage if it's not available, and ensure it's in PATH. We don't care which version
Expand Down
122 changes: 102 additions & 20 deletions pkg/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import (
"regexp"
"strings"

"github.com/carolynvs/magex/pkg/gopath"

"github.com/Masterminds/semver/v3"
"github.com/carolynvs/magex/pkg/downloads"
"github.com/carolynvs/magex/pkg/gopath"
"github.com/carolynvs/magex/shx"
"github.com/carolynvs/magex/xplat"
)
Expand Down Expand Up @@ -45,31 +46,77 @@ func EnsureMage(defaultVersion string) error {
// is used as the minimum version and sets the allowed major version. For example,
// a defaultVersion of 1.2.3 would result in a constraint of ^1.2.3.
// When no defaultVersion is specified, the latest version is installed.
//
// Deprecated: Use EnsurePackageWith.
func EnsurePackage(pkg string, defaultVersion string, versionArgs ...string) error {
cmd := getCommandName(pkg)
var versionCmd, allowedVersion string

// Apply optional arguments: versionCmd, versionConstraint
versionCmd := ""
versionConstraint := ""
if len(versionArgs) > 0 {
versionCmd = versionArgs[0]
if len(versionArgs) > 1 {
versionConstraint = versionArgs[1]
allowedVersion = versionArgs[1]
}
}

return EnsurePackageWith(EnsurePackageOptions{
Name: pkg,
DefaultVersion: defaultVersion,
AllowedVersion: allowedVersion,
VersionCommand: versionCmd,
})
}

// EnsurePackageOptions are the set of options that can be passed to EnsurePackageWith.
type EnsurePackageOptions struct {
// Name of the Go package
// Provide the name of the package that should be compiled into a cli,
// such as github.com/gobuffalo/packr/v2/packr2
Name string

// DefaultVersion is the version to install, if not found. When specified, and
// AllowedVersion is not, DefaultVersion is used as the minimum version and sets
// the allowed major version. For example, a DefaultVersion of 1.2.3 would result
// in an AllowedVersion of ^1.2.3. When no DefaultVersion is specified, the
// latest version is installed.
DefaultVersion string

// AllowedVersion is a semver range that specifies which versions are acceptable
// if found. For example, ^1.2.3 or 2.x. When unspecified, any installed version
// is acceptable. See https://github.com/Masterminds/semver for further
// documentation.
AllowedVersion string

// Destination is the location where the CLI should be installed
// Defaults to GOPATH/bin. Using ./bin is recommended to require build tools
// without modifying the host environment.
Destination string

// VersionCommand is the arguments to pass to the CLI to determine the installed version.
// For example, "version" or "--version". When unspecified the CLI is called without any arguments.
VersionCommand string
}

// EnsurePackageWith checks if the package is installed and installs it if needed.
func EnsurePackageWith(opts EnsurePackageOptions) error {
cmd := getCommandName(opts.Name)

// Default the constraint to [defaultVersion - next major)
if versionConstraint == "" {
versionConstraint = makeDefaultVersionConstraint(defaultVersion)
if opts.AllowedVersion == "" {
opts.AllowedVersion = makeDefaultVersionConstraint(opts.DefaultVersion)
}

found, err := IsCommandAvailable(cmd, versionCmd, versionConstraint)
found, err := IsCommandAvailable(cmd, opts.VersionCommand, opts.AllowedVersion)
if err != nil {
return err
}

if !found {
return InstallPackage(pkg, defaultVersion)
installOpts := InstallPackageOptions{
Name: opts.Name,
Destination: opts.Destination,
Version: opts.DefaultVersion,
}
return InstallPackageWith(installOpts)
}
return nil
}
Expand All @@ -91,26 +138,61 @@ func getCommandName(pkg string) string {
return name
}

// InstallPackageOptions are the set of options that can be passed to InstallPackageWith.
type InstallPackageOptions struct {
// Name of the Go package
// Provide the name of the package that should be compiled into a cli,
// such as github.com/gobuffalo/packr/v2/packr2
Name string

// Destination is the location where the CLI should be installed
// Defaults to GOPATH/bin. Using ./bin is recommended to require build tools
// without modifying the host environment.
Destination string

// Version of the package to install.
Version string
}

// InstallPackage installs the latest version of a package.
//
// When version is specified, install that version. Otherwise install the most
// When version is specified, install that version. Otherwise, install the most
// recent version.
// Deprecated: Use InstallPackageWith instead.
func InstallPackage(pkg string, version string) error {
gopath.EnsureGopathBin()
opts := InstallPackageOptions{
Name: pkg,
Version: version,
}
return InstallPackageWith(opts)
}

cmd := getCommandName(pkg)
// InstallPackageWith unconditionally installs a package
func InstallPackageWith(opts InstallPackageOptions) error {
cmd := getCommandName(opts.Name)

if version == "" {
version = "latest"
if opts.Version == "" {
opts.Version = "latest"
} else {
if version != "latest" && !strings.HasPrefix(version, "v") {
version = "v" + version
if opts.Version != "latest" && !strings.HasPrefix(opts.Version, "v") {
opts.Version = "v" + opts.Version
}
}

fmt.Printf("Installing %s@%s\n", cmd, version)
return shx.Command("go", "install", pkg+"@"+version).
Env("GO111MODULE=on").In(os.TempDir()).RunE()
installCmd := shx.Command("go", "install", opts.Name+"@"+opts.Version).
Env("GO111MODULE=on").In(os.TempDir())
if opts.Destination == "" {
gopath.EnsureGopathBin()
fmt.Printf("Installing %s@%s into GOPATH/bin\n", cmd, opts.Version)
} else {
dest, err := filepath.Abs(opts.Destination)
if err != nil {
return fmt.Errorf("error converting %s to an absolute path", opts.Destination)
}
installCmd.Env("GOBIN=" + dest)
fmt.Printf("Installing %s@%s into %s\n", cmd, opts.Version, dest)
}
return installCmd.RunE()
}

// InstallMage mage into GOPATH and add GOPATH/bin to PATH if necessary.
Expand Down
88 changes: 83 additions & 5 deletions pkg/install_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package pkg_test

import (
"log"
"os"
"testing"

"github.com/stretchr/testify/require"

"github.com/carolynvs/magex/pkg"
"github.com/carolynvs/magex/pkg/gopath"
)
Expand All @@ -26,27 +29,81 @@ func TestExampleEnsurePackage(t *testing.T) {
}

func ExampleEnsurePackage() {
// Install packr2@v2.8.0 using the command `packr2 version` to detect if the
// Install packr2@v2.8.3 using the command `packr2 version` to detect if the
// correct version is installed.
err := pkg.EnsurePackage("github.com/gobuffalo/packr/v2/packr2", "v2.8.3", "version")
if err != nil {
log.Fatal("could not install packr2")
}
}

func TestExampleEnsurePackage_WithVersionConstraint(t *testing.T) {
ExampleEnsurePackage_WithVersionConstraint()
func TestExampleEnsurePackageWith_LatestVersion(t *testing.T) {
ExampleEnsurePackageWith_LatestVersion()
}

func ExampleEnsurePackageWith_LatestVersion() {
// Install packr2@latest into bin/ using the command `packr2 version` to detect if the
// correct version is installed.
err := pkg.EnsurePackageWith(pkg.EnsurePackageOptions{
Name: "github.com/gobuffalo/packr/v2/packr2",
VersionCommand: "version",
Destination: "bin",
})
if err != nil {
log.Fatal("could not install packr2")
}
}

func TestExampleEnsurePackageWith_DefaultVersion(t *testing.T) {
ExampleEnsurePackageWith_DefaultVersion()
}

func ExampleEnsurePackageWith_DefaultVersion() {
// Install packr2@v2.8.3 into bin/ using the command `packr2 version` to detect if the
// correct version is installed.
err := pkg.EnsurePackageWith(pkg.EnsurePackageOptions{
Name: "github.com/gobuffalo/packr/v2/packr2",
DefaultVersion: "v2.8.3",
VersionCommand: "version",
Destination: "bin",
})
if err != nil {
log.Fatal("could not install packr2")
}
}

func TestExampleEnsurePackage_VersionConstraint(t *testing.T) {
ExampleEnsurePackage_VersionConstraint()
}

func ExampleEnsurePackage_WithVersionConstraint() {
// Install packr2@v2.8.0 using the command `packr2 version` to detect if
func ExampleEnsurePackage_VersionConstraint() {
// Install packr2@v2.8.3 using the command `packr2 version` to detect if
// any v2 version is installed
err := pkg.EnsurePackage("github.com/gobuffalo/packr/v2/packr2", "v2.8.3", "version", "2.x")
if err != nil {
log.Fatal("could not install packr2")
}
}

func TestExampleEnsurePackageWith_VersionConstraint(t *testing.T) {
ExampleEnsurePackageWith_VersionConstraint()
}

func ExampleEnsurePackageWith_VersionConstraint() {
// Install packr2@v2.8.3 into bin/ using the command `packr2 version` to detect if
// any v2 version is installed
err := pkg.EnsurePackageWith(pkg.EnsurePackageOptions{
Name: "github.com/gobuffalo/packr/v2/packr2",
DefaultVersion: "v2.8.3",
VersionCommand: "version",
Destination: "bin",
AllowedVersion: "2.x",
})
if err != nil {
log.Fatal("could not install packr2")
}
}

func TestExampleInstallPackage(t *testing.T) {
ExampleInstallPackage()
}
Expand All @@ -59,6 +116,27 @@ func ExampleInstallPackage() {
}
}

func TestExampleInstallPackageWith(t *testing.T) {
tmpBin, err := os.MkdirTemp("", "magex")
require.NoError(t, err)
defer os.RemoveAll(tmpBin)

ExampleInstallPackageWith()
}

func ExampleInstallPackageWith() {
// Install packr2@v2.8.3 into the bin/ directory
opts := pkg.InstallPackageOptions{
Name: "github.com/gobuffalo/packr/v2/packr2",
Destination: "bin",
Version: "v2.8.3",
}
err := pkg.InstallPackageWith(opts)
if err != nil {
log.Fatal("could not install packr2@v2.8.3 into bin")
}
}

func TestExampleDownloadToGopathBin(t *testing.T) {
ExampleDownloadToGopathBin()
}
Expand Down
49 changes: 47 additions & 2 deletions pkg/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pkg
import (
"os"
"os/exec"
"path/filepath"
"testing"

"github.com/carolynvs/magex/pkg/gopath"
Expand Down Expand Up @@ -68,15 +69,52 @@ func TestEnsurePackage_FreshInstall(t *testing.T) {
hasCmd, err := IsCommandAvailable("testpkg", "", "")
require.False(t, hasCmd)

err = EnsurePackage("github.com/carolynvs/testpkg/v2", tc.defaultVersion, "--version", tc.versionConstraint)
opts := EnsurePackageOptions{
Name: "github.com/carolynvs/testpkg/v2",
DefaultVersion: tc.defaultVersion,
AllowedVersion: tc.versionConstraint,
VersionCommand: "--version",
}
err = EnsurePackageWith(opts)
require.NoError(t, err)

installedVersion, err := GetCommandVersion("testpkg", "")
require.NoError(t, err, "GetCommandVersion failed")
require.Equal(t, tc.wantVersion, installedVersion, "incorrect version was resolved")
})
}
}

func TestEnsurePackage_IntoDirectory(t *testing.T) {
os.Setenv(mg.VerboseEnv, "true")
defer os.Unsetenv(mg.VerboseEnv)

// Make a temp GOPATH to avoid accidentally messing with the system GOPATH/bin if the test fails
err, cleanup := gopath.UseTempGopath()
require.NoError(t, err, "Failed to set up a temporary GOPATH")
defer cleanup()

tmpBin, err := os.MkdirTemp("", "magex")
require.NoError(t, err, "Failed to create temporary bin directory")
defer os.RemoveAll(tmpBin)

opts := EnsurePackageOptions{
Name: "github.com/carolynvs/testpkg/v2",
DefaultVersion: "v2.0.2",
Destination: tmpBin,
}
err = EnsurePackageWith(opts)
require.NoError(t, err)

cmdPath := filepath.Join(tmpBin, "testpkg"+xplat.FileExt())
require.FileExists(t, cmdPath, "The command was not installed into the bin directory")

installedVersion, err := GetCommandVersion(cmdPath, "")
require.NoError(t, err, "GetCommandVersion failed")
require.Equal(t, opts.DefaultVersion, installedVersion, "incorrect version was installed")

}

func TestEnsurePackage_Upgrade(t *testing.T) {
os.Setenv(mg.VerboseEnv, "true")
defer os.Unsetenv(mg.VerboseEnv)
Expand Down Expand Up @@ -104,10 +142,17 @@ func TestEnsurePackage_Upgrade(t *testing.T) {
require.NoError(t, err)

// Ensure it's installed with a higher default version
err = EnsurePackage("github.com/carolynvs/testpkg/v2", tc.defaultVersion, "--version", tc.versionConstraint)
opts := EnsurePackageOptions{
Name: "github.com/carolynvs/testpkg/v2",
DefaultVersion: tc.defaultVersion,
AllowedVersion: tc.versionConstraint,
VersionCommand: "--version",
}
err = EnsurePackageWith(opts)
require.NoError(t, err)

installedVersion, err := GetCommandVersion("testpkg", "")
require.NoError(t, err, "GetCommandVersion failed")
require.Equal(t, tc.wantVersion, installedVersion, "incorrect version was resolved")
})
}
Expand Down

0 comments on commit eb350f0

Please sign in to comment.