Skip to content

Commit 806cdab

Browse files
committed
fix: check for system installed node and php version before setting up tools
1 parent 595f27f commit 806cdab

5 files changed

Lines changed: 222 additions & 0 deletions

File tree

internal/system/node.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package system
2+
3+
import (
4+
"fmt"
5+
"os/exec"
6+
"strings"
7+
8+
"github.com/shyim/go-version"
9+
)
10+
11+
// GetInstalledNodeVersion checks the installed Node.js version on the system.
12+
func GetInstalledNodeVersion() (string, error) {
13+
// Check if Node.js is installed
14+
nodePath, err := exec.LookPath("node")
15+
if err != nil {
16+
return "", fmt.Errorf("Node.js is not installed: %w", err)
17+
}
18+
19+
// Get the Node.js version
20+
cmd := exec.Command(nodePath, "-v")
21+
output, err := cmd.Output()
22+
if err != nil {
23+
return "", fmt.Errorf("failed to get Node.js version: %w, output: %s", err, string(output))
24+
}
25+
26+
// Node.js outputs version in format "vX.Y.Z", we need to trim the "v" prefix
27+
version := strings.TrimSpace(string(output))
28+
if len(version) > 0 && version[0] == 'v' {
29+
version = version[1:]
30+
}
31+
32+
return version, nil
33+
}
34+
35+
// IsNodeVersionAtLeast checks if the installed Node.js version meets the minimum required version.
36+
func IsNodeVersionAtLeast(requiredVersion string) (bool, error) {
37+
installedVersion, err := GetInstalledNodeVersion()
38+
if err != nil {
39+
return false, err
40+
}
41+
42+
nodeVersion, err := version.NewVersion(installedVersion)
43+
if err != nil {
44+
return false, fmt.Errorf("failed to parse installed Node.js version: %w", err)
45+
}
46+
47+
constraint, err := version.NewConstraint(fmt.Sprintf(">= %s", requiredVersion))
48+
if err != nil {
49+
return false, fmt.Errorf("failed to parse required Node.js version constraint: %w", err)
50+
}
51+
52+
return constraint.Check(nodeVersion), nil
53+
}

internal/system/node_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package system
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestGetNodeVersionNotInstalled(t *testing.T) {
13+
t.Setenv("PATH", "")
14+
_, err := GetInstalledNodeVersion()
15+
assert.ErrorContains(t, err, "Node.js is not installed")
16+
}
17+
18+
func TestGetNodeVersion(t *testing.T) {
19+
tmpDir := t.TempDir()
20+
21+
setupFakeNode(t, tmpDir, "18.16.0")
22+
23+
nodeVersion, err := GetInstalledNodeVersion()
24+
assert.NoError(t, err)
25+
assert.Equal(t, "18.16.0", nodeVersion)
26+
}
27+
28+
func TestNodeVersionIsAtLeast(t *testing.T) {
29+
setupFakeNode(t, t.TempDir(), "18.16.0")
30+
hit, err := IsNodeVersionAtLeast("16.0.0")
31+
32+
assert.NoError(t, err)
33+
assert.True(t, hit, "Node.js version should be at least 16.0.0")
34+
}
35+
36+
func TestNodeVersionIsNotAtLeast(t *testing.T) {
37+
setupFakeNode(t, t.TempDir(), "14.17.0")
38+
hit, err := IsNodeVersionAtLeast("16.0.0")
39+
40+
assert.NoError(t, err)
41+
assert.False(t, hit, "Node.js version should not be at least 16.0.0")
42+
}
43+
44+
func setupFakeNode(t *testing.T, tmpDir string, version string) {
45+
t.Helper()
46+
shPath, err := exec.LookPath("sh")
47+
assert.NoError(t, err)
48+
49+
// Node returns version with 'v' prefix
50+
assert.NoError(t, os.WriteFile(tmpDir+"/node", []byte(fmt.Sprintf("#!%s\necho v%s", shPath, version)), 0755))
51+
t.Setenv("PATH", tmpDir)
52+
}

internal/system/php.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package system
2+
3+
import (
4+
"fmt"
5+
"os/exec"
6+
"strings"
7+
8+
"github.com/shyim/go-version"
9+
)
10+
11+
// GetInstalledPHPVersion checks the installed PHP version on the system.
12+
func GetInstalledPHPVersion() (string, error) {
13+
// Check if PHP is installed
14+
phpPath, err := exec.LookPath("php")
15+
if err != nil {
16+
return "", fmt.Errorf("PHP is not installed: %w", err)
17+
}
18+
19+
// Get the PHP version
20+
cmd := exec.Command(phpPath, "-v")
21+
output, err := cmd.Output()
22+
if err != nil {
23+
return "", fmt.Errorf("failed to get PHP version: %w, output: %s", err, string(output))
24+
}
25+
26+
splitt := strings.Split(string(output), " ")
27+
28+
if len(splitt) < 2 {
29+
return "", fmt.Errorf("unexpected output format: %s", string(output))
30+
}
31+
32+
// Parse the version from the output
33+
version := splitt[1]
34+
return strings.TrimSpace(version), nil
35+
}
36+
37+
func IsPHPVersionAtLeast(requiredVersion string) (bool, error) {
38+
installedVersion, err := GetInstalledPHPVersion()
39+
if err != nil {
40+
return false, err
41+
}
42+
43+
phpVersion, err := version.NewVersion(installedVersion)
44+
if err != nil {
45+
return false, fmt.Errorf("failed to parse installed PHP version: %w", err)
46+
}
47+
48+
constraint, err := version.NewConstraint(fmt.Sprintf(">= %s", requiredVersion))
49+
if err != nil {
50+
return false, fmt.Errorf("failed to parse required PHP version constraint: %w", err)
51+
}
52+
53+
return constraint.Check(phpVersion), nil
54+
}

internal/system/php_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package system
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"os/exec"
7+
"testing"
8+
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestGetPHPVersionNotInstalled(t *testing.T) {
13+
t.Setenv("PATH", "")
14+
_, err := GetInstalledPHPVersion()
15+
assert.ErrorContains(t, err, "PHP is not installed")
16+
}
17+
18+
func TestGetPHPVersion(t *testing.T) {
19+
tmpDir := t.TempDir()
20+
21+
setupFakePHP(t, tmpDir, "8.0.0")
22+
23+
phpVersion, err := GetInstalledPHPVersion()
24+
assert.NoError(t, err)
25+
assert.Equal(t, "8.0.0", phpVersion)
26+
}
27+
28+
func TestPHPVersionIsAtLeast(t *testing.T) {
29+
setupFakePHP(t, t.TempDir(), "8.0.0")
30+
hit, err := IsPHPVersionAtLeast("8.0.0")
31+
32+
assert.NoError(t, err)
33+
assert.True(t, hit, "PHP version should be at least 8.0.0")
34+
}
35+
36+
func TestPHPVersionIsNotAtLeast(t *testing.T) {
37+
setupFakePHP(t, t.TempDir(), "7.4.0")
38+
hit, err := IsPHPVersionAtLeast("8.0.0")
39+
40+
assert.NoError(t, err)
41+
assert.False(t, hit, "PHP version should not be at least 8.0.0")
42+
}
43+
44+
func setupFakePHP(t *testing.T, tmpDir string, version string) {
45+
t.Helper()
46+
shPath, err := exec.LookPath("sh")
47+
assert.NoError(t, err)
48+
49+
assert.NoError(t, os.WriteFile(tmpDir+"/php", []byte(fmt.Sprintf("#!%s\necho PHP %s", shPath, version)), 0755))
50+
t.Setenv("PATH", tmpDir)
51+
}

internal/verifier/embed.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ func SetupTools(ctx context.Context, currentVersion string) error {
3333
return nil
3434
}
3535

36+
if ok, err := system.IsPHPVersionAtLeast("8.2.0"); err != nil {
37+
return fmt.Errorf("failed to check installed PHP version: %w", err)
38+
} else if !ok {
39+
return fmt.Errorf("PHP version must be at least 8.2.0 to use this. Update your PHP version or use the shopware-cli docker image")
40+
}
41+
42+
if ok, err := system.IsNodeVersionAtLeast("22.0.0"); err != nil {
43+
return fmt.Errorf("failed to check installed Node.js version: %w", err)
44+
} else if !ok {
45+
return fmt.Errorf("Node.js version must be at least 22.0.0 to use this. Update your Node.js version or use the shopware-cli docker image")
46+
}
47+
3648
logging.FromContext(ctx).Debugf("Using tool directory: %s", toolsDir)
3749
if err := os.MkdirAll(toolsDir, os.ModePerm); err != nil {
3850
return fmt.Errorf("failed to create directory %s: %w", toolsDir, err)

0 commit comments

Comments
 (0)