Skip to content

Commit eb350f0

Browse files
authored
Merge pull request #27 from carolynvs/install-to-directory
Add EnsurePackageWith/InstallPackageWith
2 parents f0d0ccd + 54e4637 commit eb350f0

File tree

4 files changed

+241
-29
lines changed

4 files changed

+241
-29
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,16 @@ import (
1717
"github.com/carolynvs/magex/shx"
1818
)
1919

20-
// Install packr2 v2.8.0 if it's not available, and ensure it's in PATH.
20+
// Check if packr2 is in the bin/ directory and is at least v2.
21+
// If not, install packr@v2.8.0 into bin/
2122
func EnsurePackr2() error {
22-
return pkg.EnsurePackage("github.com/gobuffalo/packr/v2/packr2", "v2.8.0", "version")
23+
opts := pkg.EnsurePackageOptions{
24+
Name: "github.com/gobuffalo/packr/v2/packr2",
25+
DefaultVersion: "v2.8.0",
26+
VersionCommand: "version",
27+
Destination: "bin",
28+
}
29+
return pkg.EnsurePackageWith(opts)
2330
}
2431

2532
// Install mage if it's not available, and ensure it's in PATH. We don't care which version

pkg/install.go

Lines changed: 102 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import (
1010
"regexp"
1111
"strings"
1212

13+
"github.com/carolynvs/magex/pkg/gopath"
14+
1315
"github.com/Masterminds/semver/v3"
1416
"github.com/carolynvs/magex/pkg/downloads"
15-
"github.com/carolynvs/magex/pkg/gopath"
1617
"github.com/carolynvs/magex/shx"
1718
"github.com/carolynvs/magex/xplat"
1819
)
@@ -45,31 +46,77 @@ func EnsureMage(defaultVersion string) error {
4546
// is used as the minimum version and sets the allowed major version. For example,
4647
// a defaultVersion of 1.2.3 would result in a constraint of ^1.2.3.
4748
// When no defaultVersion is specified, the latest version is installed.
49+
//
50+
// Deprecated: Use EnsurePackageWith.
4851
func EnsurePackage(pkg string, defaultVersion string, versionArgs ...string) error {
49-
cmd := getCommandName(pkg)
52+
var versionCmd, allowedVersion string
5053

51-
// Apply optional arguments: versionCmd, versionConstraint
52-
versionCmd := ""
53-
versionConstraint := ""
5454
if len(versionArgs) > 0 {
5555
versionCmd = versionArgs[0]
5656
if len(versionArgs) > 1 {
57-
versionConstraint = versionArgs[1]
57+
allowedVersion = versionArgs[1]
5858
}
5959
}
6060

61+
return EnsurePackageWith(EnsurePackageOptions{
62+
Name: pkg,
63+
DefaultVersion: defaultVersion,
64+
AllowedVersion: allowedVersion,
65+
VersionCommand: versionCmd,
66+
})
67+
}
68+
69+
// EnsurePackageOptions are the set of options that can be passed to EnsurePackageWith.
70+
type EnsurePackageOptions struct {
71+
// Name of the Go package
72+
// Provide the name of the package that should be compiled into a cli,
73+
// such as github.com/gobuffalo/packr/v2/packr2
74+
Name string
75+
76+
// DefaultVersion is the version to install, if not found. When specified, and
77+
// AllowedVersion is not, DefaultVersion is used as the minimum version and sets
78+
// the allowed major version. For example, a DefaultVersion of 1.2.3 would result
79+
// in an AllowedVersion of ^1.2.3. When no DefaultVersion is specified, the
80+
// latest version is installed.
81+
DefaultVersion string
82+
83+
// AllowedVersion is a semver range that specifies which versions are acceptable
84+
// if found. For example, ^1.2.3 or 2.x. When unspecified, any installed version
85+
// is acceptable. See https://github.com/Masterminds/semver for further
86+
// documentation.
87+
AllowedVersion string
88+
89+
// Destination is the location where the CLI should be installed
90+
// Defaults to GOPATH/bin. Using ./bin is recommended to require build tools
91+
// without modifying the host environment.
92+
Destination string
93+
94+
// VersionCommand is the arguments to pass to the CLI to determine the installed version.
95+
// For example, "version" or "--version". When unspecified the CLI is called without any arguments.
96+
VersionCommand string
97+
}
98+
99+
// EnsurePackageWith checks if the package is installed and installs it if needed.
100+
func EnsurePackageWith(opts EnsurePackageOptions) error {
101+
cmd := getCommandName(opts.Name)
102+
61103
// Default the constraint to [defaultVersion - next major)
62-
if versionConstraint == "" {
63-
versionConstraint = makeDefaultVersionConstraint(defaultVersion)
104+
if opts.AllowedVersion == "" {
105+
opts.AllowedVersion = makeDefaultVersionConstraint(opts.DefaultVersion)
64106
}
65107

66-
found, err := IsCommandAvailable(cmd, versionCmd, versionConstraint)
108+
found, err := IsCommandAvailable(cmd, opts.VersionCommand, opts.AllowedVersion)
67109
if err != nil {
68110
return err
69111
}
70112

71113
if !found {
72-
return InstallPackage(pkg, defaultVersion)
114+
installOpts := InstallPackageOptions{
115+
Name: opts.Name,
116+
Destination: opts.Destination,
117+
Version: opts.DefaultVersion,
118+
}
119+
return InstallPackageWith(installOpts)
73120
}
74121
return nil
75122
}
@@ -91,26 +138,61 @@ func getCommandName(pkg string) string {
91138
return name
92139
}
93140

141+
// InstallPackageOptions are the set of options that can be passed to InstallPackageWith.
142+
type InstallPackageOptions struct {
143+
// Name of the Go package
144+
// Provide the name of the package that should be compiled into a cli,
145+
// such as github.com/gobuffalo/packr/v2/packr2
146+
Name string
147+
148+
// Destination is the location where the CLI should be installed
149+
// Defaults to GOPATH/bin. Using ./bin is recommended to require build tools
150+
// without modifying the host environment.
151+
Destination string
152+
153+
// Version of the package to install.
154+
Version string
155+
}
156+
94157
// InstallPackage installs the latest version of a package.
95158
//
96-
// When version is specified, install that version. Otherwise install the most
159+
// When version is specified, install that version. Otherwise, install the most
97160
// recent version.
161+
// Deprecated: Use InstallPackageWith instead.
98162
func InstallPackage(pkg string, version string) error {
99-
gopath.EnsureGopathBin()
163+
opts := InstallPackageOptions{
164+
Name: pkg,
165+
Version: version,
166+
}
167+
return InstallPackageWith(opts)
168+
}
100169

101-
cmd := getCommandName(pkg)
170+
// InstallPackageWith unconditionally installs a package
171+
func InstallPackageWith(opts InstallPackageOptions) error {
172+
cmd := getCommandName(opts.Name)
102173

103-
if version == "" {
104-
version = "latest"
174+
if opts.Version == "" {
175+
opts.Version = "latest"
105176
} else {
106-
if version != "latest" && !strings.HasPrefix(version, "v") {
107-
version = "v" + version
177+
if opts.Version != "latest" && !strings.HasPrefix(opts.Version, "v") {
178+
opts.Version = "v" + opts.Version
108179
}
109180
}
110181

111-
fmt.Printf("Installing %s@%s\n", cmd, version)
112-
return shx.Command("go", "install", pkg+"@"+version).
113-
Env("GO111MODULE=on").In(os.TempDir()).RunE()
182+
installCmd := shx.Command("go", "install", opts.Name+"@"+opts.Version).
183+
Env("GO111MODULE=on").In(os.TempDir())
184+
if opts.Destination == "" {
185+
gopath.EnsureGopathBin()
186+
fmt.Printf("Installing %s@%s into GOPATH/bin\n", cmd, opts.Version)
187+
} else {
188+
dest, err := filepath.Abs(opts.Destination)
189+
if err != nil {
190+
return fmt.Errorf("error converting %s to an absolute path", opts.Destination)
191+
}
192+
installCmd.Env("GOBIN=" + dest)
193+
fmt.Printf("Installing %s@%s into %s\n", cmd, opts.Version, dest)
194+
}
195+
return installCmd.RunE()
114196
}
115197

116198
// InstallMage mage into GOPATH and add GOPATH/bin to PATH if necessary.

pkg/install_example_test.go

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package pkg_test
22

33
import (
44
"log"
5+
"os"
56
"testing"
67

8+
"github.com/stretchr/testify/require"
9+
710
"github.com/carolynvs/magex/pkg"
811
"github.com/carolynvs/magex/pkg/gopath"
912
)
@@ -26,27 +29,81 @@ func TestExampleEnsurePackage(t *testing.T) {
2629
}
2730

2831
func ExampleEnsurePackage() {
29-
// Install packr2@v2.8.0 using the command `packr2 version` to detect if the
32+
// Install packr2@v2.8.3 using the command `packr2 version` to detect if the
3033
// correct version is installed.
3134
err := pkg.EnsurePackage("github.com/gobuffalo/packr/v2/packr2", "v2.8.3", "version")
3235
if err != nil {
3336
log.Fatal("could not install packr2")
3437
}
3538
}
3639

37-
func TestExampleEnsurePackage_WithVersionConstraint(t *testing.T) {
38-
ExampleEnsurePackage_WithVersionConstraint()
40+
func TestExampleEnsurePackageWith_LatestVersion(t *testing.T) {
41+
ExampleEnsurePackageWith_LatestVersion()
42+
}
43+
44+
func ExampleEnsurePackageWith_LatestVersion() {
45+
// Install packr2@latest into bin/ using the command `packr2 version` to detect if the
46+
// correct version is installed.
47+
err := pkg.EnsurePackageWith(pkg.EnsurePackageOptions{
48+
Name: "github.com/gobuffalo/packr/v2/packr2",
49+
VersionCommand: "version",
50+
Destination: "bin",
51+
})
52+
if err != nil {
53+
log.Fatal("could not install packr2")
54+
}
55+
}
56+
57+
func TestExampleEnsurePackageWith_DefaultVersion(t *testing.T) {
58+
ExampleEnsurePackageWith_DefaultVersion()
59+
}
60+
61+
func ExampleEnsurePackageWith_DefaultVersion() {
62+
// Install packr2@v2.8.3 into bin/ using the command `packr2 version` to detect if the
63+
// correct version is installed.
64+
err := pkg.EnsurePackageWith(pkg.EnsurePackageOptions{
65+
Name: "github.com/gobuffalo/packr/v2/packr2",
66+
DefaultVersion: "v2.8.3",
67+
VersionCommand: "version",
68+
Destination: "bin",
69+
})
70+
if err != nil {
71+
log.Fatal("could not install packr2")
72+
}
73+
}
74+
75+
func TestExampleEnsurePackage_VersionConstraint(t *testing.T) {
76+
ExampleEnsurePackage_VersionConstraint()
3977
}
4078

41-
func ExampleEnsurePackage_WithVersionConstraint() {
42-
// Install packr2@v2.8.0 using the command `packr2 version` to detect if
79+
func ExampleEnsurePackage_VersionConstraint() {
80+
// Install packr2@v2.8.3 using the command `packr2 version` to detect if
4381
// any v2 version is installed
4482
err := pkg.EnsurePackage("github.com/gobuffalo/packr/v2/packr2", "v2.8.3", "version", "2.x")
4583
if err != nil {
4684
log.Fatal("could not install packr2")
4785
}
4886
}
4987

88+
func TestExampleEnsurePackageWith_VersionConstraint(t *testing.T) {
89+
ExampleEnsurePackageWith_VersionConstraint()
90+
}
91+
92+
func ExampleEnsurePackageWith_VersionConstraint() {
93+
// Install packr2@v2.8.3 into bin/ using the command `packr2 version` to detect if
94+
// any v2 version is installed
95+
err := pkg.EnsurePackageWith(pkg.EnsurePackageOptions{
96+
Name: "github.com/gobuffalo/packr/v2/packr2",
97+
DefaultVersion: "v2.8.3",
98+
VersionCommand: "version",
99+
Destination: "bin",
100+
AllowedVersion: "2.x",
101+
})
102+
if err != nil {
103+
log.Fatal("could not install packr2")
104+
}
105+
}
106+
50107
func TestExampleInstallPackage(t *testing.T) {
51108
ExampleInstallPackage()
52109
}
@@ -59,6 +116,27 @@ func ExampleInstallPackage() {
59116
}
60117
}
61118

119+
func TestExampleInstallPackageWith(t *testing.T) {
120+
tmpBin, err := os.MkdirTemp("", "magex")
121+
require.NoError(t, err)
122+
defer os.RemoveAll(tmpBin)
123+
124+
ExampleInstallPackageWith()
125+
}
126+
127+
func ExampleInstallPackageWith() {
128+
// Install packr2@v2.8.3 into the bin/ directory
129+
opts := pkg.InstallPackageOptions{
130+
Name: "github.com/gobuffalo/packr/v2/packr2",
131+
Destination: "bin",
132+
Version: "v2.8.3",
133+
}
134+
err := pkg.InstallPackageWith(opts)
135+
if err != nil {
136+
log.Fatal("could not install packr2@v2.8.3 into bin")
137+
}
138+
}
139+
62140
func TestExampleDownloadToGopathBin(t *testing.T) {
63141
ExampleDownloadToGopathBin()
64142
}

pkg/install_test.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package pkg
33
import (
44
"os"
55
"os/exec"
6+
"path/filepath"
67
"testing"
78

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

71-
err = EnsurePackage("github.com/carolynvs/testpkg/v2", tc.defaultVersion, "--version", tc.versionConstraint)
72+
opts := EnsurePackageOptions{
73+
Name: "github.com/carolynvs/testpkg/v2",
74+
DefaultVersion: tc.defaultVersion,
75+
AllowedVersion: tc.versionConstraint,
76+
VersionCommand: "--version",
77+
}
78+
err = EnsurePackageWith(opts)
7279
require.NoError(t, err)
7380

7481
installedVersion, err := GetCommandVersion("testpkg", "")
82+
require.NoError(t, err, "GetCommandVersion failed")
7583
require.Equal(t, tc.wantVersion, installedVersion, "incorrect version was resolved")
7684
})
7785
}
7886
}
7987

88+
func TestEnsurePackage_IntoDirectory(t *testing.T) {
89+
os.Setenv(mg.VerboseEnv, "true")
90+
defer os.Unsetenv(mg.VerboseEnv)
91+
92+
// Make a temp GOPATH to avoid accidentally messing with the system GOPATH/bin if the test fails
93+
err, cleanup := gopath.UseTempGopath()
94+
require.NoError(t, err, "Failed to set up a temporary GOPATH")
95+
defer cleanup()
96+
97+
tmpBin, err := os.MkdirTemp("", "magex")
98+
require.NoError(t, err, "Failed to create temporary bin directory")
99+
defer os.RemoveAll(tmpBin)
100+
101+
opts := EnsurePackageOptions{
102+
Name: "github.com/carolynvs/testpkg/v2",
103+
DefaultVersion: "v2.0.2",
104+
Destination: tmpBin,
105+
}
106+
err = EnsurePackageWith(opts)
107+
require.NoError(t, err)
108+
109+
cmdPath := filepath.Join(tmpBin, "testpkg"+xplat.FileExt())
110+
require.FileExists(t, cmdPath, "The command was not installed into the bin directory")
111+
112+
installedVersion, err := GetCommandVersion(cmdPath, "")
113+
require.NoError(t, err, "GetCommandVersion failed")
114+
require.Equal(t, opts.DefaultVersion, installedVersion, "incorrect version was installed")
115+
116+
}
117+
80118
func TestEnsurePackage_Upgrade(t *testing.T) {
81119
os.Setenv(mg.VerboseEnv, "true")
82120
defer os.Unsetenv(mg.VerboseEnv)
@@ -104,10 +142,17 @@ func TestEnsurePackage_Upgrade(t *testing.T) {
104142
require.NoError(t, err)
105143

106144
// Ensure it's installed with a higher default version
107-
err = EnsurePackage("github.com/carolynvs/testpkg/v2", tc.defaultVersion, "--version", tc.versionConstraint)
145+
opts := EnsurePackageOptions{
146+
Name: "github.com/carolynvs/testpkg/v2",
147+
DefaultVersion: tc.defaultVersion,
148+
AllowedVersion: tc.versionConstraint,
149+
VersionCommand: "--version",
150+
}
151+
err = EnsurePackageWith(opts)
108152
require.NoError(t, err)
109153

110154
installedVersion, err := GetCommandVersion("testpkg", "")
155+
require.NoError(t, err, "GetCommandVersion failed")
111156
require.Equal(t, tc.wantVersion, installedVersion, "incorrect version was resolved")
112157
})
113158
}

0 commit comments

Comments
 (0)