mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
feat: check for .ps1 dotfiles scripts on windows (#16785)
This commit is contained in:
+17
-18
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -41,16 +42,7 @@ func (r *RootCmd) dotfiles() *serpent.Command {
|
||||
dotfilesDir = filepath.Join(cfgDir, dotfilesRepoDir)
|
||||
// This follows the same pattern outlined by others in the market:
|
||||
// https://github.com/coder/coder/pull/1696#issue-1245742312
|
||||
installScriptSet = []string{
|
||||
"install.sh",
|
||||
"install",
|
||||
"bootstrap.sh",
|
||||
"bootstrap",
|
||||
"script/bootstrap",
|
||||
"setup.sh",
|
||||
"setup",
|
||||
"script/setup",
|
||||
}
|
||||
installScriptSet = installScriptFiles()
|
||||
)
|
||||
|
||||
if cfg == "" {
|
||||
@@ -195,21 +187,28 @@ func (r *RootCmd) dotfiles() *serpent.Command {
|
||||
|
||||
_, _ = fmt.Fprintf(inv.Stdout, "Running %s...\n", script)
|
||||
|
||||
// Check if the script is executable and notify on error
|
||||
scriptPath := filepath.Join(dotfilesDir, script)
|
||||
fi, err := os.Stat(scriptPath)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("stat %s: %w", scriptPath, err)
|
||||
}
|
||||
|
||||
if fi.Mode()&0o111 == 0 {
|
||||
return xerrors.Errorf("script %q does not have execute permissions", script)
|
||||
// Permissions checks will always fail on Windows, since it doesn't have
|
||||
// conventional Unix file system permissions.
|
||||
if runtime.GOOS != "windows" {
|
||||
// Check if the script is executable and notify on error
|
||||
fi, err := os.Stat(scriptPath)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("stat %s: %w", scriptPath, err)
|
||||
}
|
||||
if fi.Mode()&0o111 == 0 {
|
||||
return xerrors.Errorf("script %q does not have execute permissions", script)
|
||||
}
|
||||
}
|
||||
|
||||
// it is safe to use a variable command here because it's from
|
||||
// a filtered list of pre-approved install scripts
|
||||
// nolint:gosec
|
||||
scriptCmd := exec.CommandContext(inv.Context(), filepath.Join(dotfilesDir, script))
|
||||
scriptCmd := exec.CommandContext(inv.Context(), scriptPath)
|
||||
if runtime.GOOS == "windows" {
|
||||
scriptCmd = exec.CommandContext(inv.Context(), "powershell", "-NoLogo", scriptPath)
|
||||
}
|
||||
scriptCmd.Dir = dotfilesDir
|
||||
scriptCmd.Stdout = inv.Stdout
|
||||
scriptCmd.Stderr = inv.Stderr
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
//go:build !windows
|
||||
|
||||
package cli
|
||||
|
||||
func installScriptFiles() []string {
|
||||
return []string{
|
||||
"install.sh",
|
||||
"install",
|
||||
"bootstrap.sh",
|
||||
"bootstrap",
|
||||
"setup.sh",
|
||||
"setup",
|
||||
"script/install.sh",
|
||||
"script/install",
|
||||
"script/bootstrap.sh",
|
||||
"script/bootstrap",
|
||||
"script/setup.sh",
|
||||
"script/setup",
|
||||
}
|
||||
}
|
||||
+149
-111
@@ -116,117 +116,6 @@ func TestDotfiles(t *testing.T) {
|
||||
require.NoError(t, staterr)
|
||||
require.True(t, stat.IsDir())
|
||||
})
|
||||
t.Run("InstallScript", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("install scripts on windows require sh and aren't very practical")
|
||||
}
|
||||
_, root := clitest.New(t)
|
||||
testRepo := testGitRepo(t, root)
|
||||
|
||||
// nolint:gosec
|
||||
err := os.WriteFile(filepath.Join(testRepo, "install.sh"), []byte("#!/bin/bash\necho wow > "+filepath.Join(string(root), ".bashrc")), 0o750)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := exec.Command("git", "add", "install.sh")
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "commit", "-m", `"add install.sh"`)
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
inv, _ := clitest.New(t, "dotfiles", "--global-config", string(root), "--symlink-dir", string(root), "-y", testRepo)
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
b, err := os.ReadFile(filepath.Join(string(root), ".bashrc"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(b), "wow\n")
|
||||
})
|
||||
|
||||
t.Run("NestedInstallScript", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("install scripts on windows require sh and aren't very practical")
|
||||
}
|
||||
_, root := clitest.New(t)
|
||||
testRepo := testGitRepo(t, root)
|
||||
|
||||
scriptPath := filepath.Join("script", "setup")
|
||||
err := os.MkdirAll(filepath.Join(testRepo, "script"), 0o750)
|
||||
require.NoError(t, err)
|
||||
// nolint:gosec
|
||||
err = os.WriteFile(filepath.Join(testRepo, scriptPath), []byte("#!/bin/bash\necho wow > "+filepath.Join(string(root), ".bashrc")), 0o750)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := exec.Command("git", "add", scriptPath)
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "commit", "-m", `"add script"`)
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
inv, _ := clitest.New(t, "dotfiles", "--global-config", string(root), "--symlink-dir", string(root), "-y", testRepo)
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
b, err := os.ReadFile(filepath.Join(string(root), ".bashrc"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(b), "wow\n")
|
||||
})
|
||||
|
||||
t.Run("InstallScriptChangeBranch", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("install scripts on windows require sh and aren't very practical")
|
||||
}
|
||||
_, root := clitest.New(t)
|
||||
testRepo := testGitRepo(t, root)
|
||||
|
||||
// We need an initial commit to start the `main` branch
|
||||
c := exec.Command("git", "commit", "--allow-empty", "-m", `"initial commit"`)
|
||||
c.Dir = testRepo
|
||||
err := c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
// nolint:gosec
|
||||
err = os.WriteFile(filepath.Join(testRepo, "install.sh"), []byte("#!/bin/bash\necho wow > "+filepath.Join(string(root), ".bashrc")), 0o750)
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "checkout", "-b", "other_branch")
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "add", "install.sh")
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "commit", "-m", `"add install.sh"`)
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "checkout", "main")
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
inv, _ := clitest.New(t, "dotfiles", "--global-config", string(root), "--symlink-dir", string(root), "-y", testRepo, "-b", "other_branch")
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
b, err := os.ReadFile(filepath.Join(string(root), ".bashrc"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(b), "wow\n")
|
||||
})
|
||||
t.Run("SymlinkBackup", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, root := clitest.New(t)
|
||||
@@ -277,6 +166,155 @@ func TestDotfiles(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestDotfilesInstallScriptUnix(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
t.Run("InstallScript", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, root := clitest.New(t)
|
||||
testRepo := testGitRepo(t, root)
|
||||
|
||||
// nolint:gosec
|
||||
err := os.WriteFile(filepath.Join(testRepo, "install.sh"), []byte("#!/bin/bash\necho wow > "+filepath.Join(string(root), ".bashrc")), 0o750)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := exec.Command("git", "add", "install.sh")
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "commit", "-m", `"add install.sh"`)
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
inv, _ := clitest.New(t, "dotfiles", "--global-config", string(root), "--symlink-dir", string(root), "-y", testRepo)
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
b, err := os.ReadFile(filepath.Join(string(root), ".bashrc"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(b), "wow\n")
|
||||
})
|
||||
|
||||
t.Run("NestedInstallScript", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, root := clitest.New(t)
|
||||
testRepo := testGitRepo(t, root)
|
||||
|
||||
scriptPath := filepath.Join("script", "setup")
|
||||
err := os.MkdirAll(filepath.Join(testRepo, "script"), 0o750)
|
||||
require.NoError(t, err)
|
||||
// nolint:gosec
|
||||
err = os.WriteFile(filepath.Join(testRepo, scriptPath), []byte("#!/bin/bash\necho wow > "+filepath.Join(string(root), ".bashrc")), 0o750)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := exec.Command("git", "add", scriptPath)
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "commit", "-m", `"add script"`)
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
inv, _ := clitest.New(t, "dotfiles", "--global-config", string(root), "--symlink-dir", string(root), "-y", testRepo)
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
b, err := os.ReadFile(filepath.Join(string(root), ".bashrc"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(b), "wow\n")
|
||||
})
|
||||
|
||||
t.Run("InstallScriptChangeBranch", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, root := clitest.New(t)
|
||||
testRepo := testGitRepo(t, root)
|
||||
|
||||
// We need an initial commit to start the `main` branch
|
||||
c := exec.Command("git", "commit", "--allow-empty", "-m", `"initial commit"`)
|
||||
c.Dir = testRepo
|
||||
err := c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
// nolint:gosec
|
||||
err = os.WriteFile(filepath.Join(testRepo, "install.sh"), []byte("#!/bin/bash\necho wow > "+filepath.Join(string(root), ".bashrc")), 0o750)
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "checkout", "-b", "other_branch")
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "add", "install.sh")
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "commit", "-m", `"add install.sh"`)
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "checkout", "main")
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
inv, _ := clitest.New(t, "dotfiles", "--global-config", string(root), "--symlink-dir", string(root), "-y", testRepo, "-b", "other_branch")
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
b, err := os.ReadFile(filepath.Join(string(root), ".bashrc"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(b), "wow\n")
|
||||
})
|
||||
}
|
||||
|
||||
func TestDotfilesInstallScriptWindows(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
t.Skip()
|
||||
}
|
||||
|
||||
t.Run("InstallScript", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, root := clitest.New(t)
|
||||
testRepo := testGitRepo(t, root)
|
||||
|
||||
// nolint:gosec
|
||||
err := os.WriteFile(filepath.Join(testRepo, "install.ps1"), []byte("echo \"hello, computer!\" > "+filepath.Join(string(root), "greeting.txt")), 0o750)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := exec.Command("git", "add", "install.ps1")
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
c = exec.Command("git", "commit", "-m", `"add install.ps1"`)
|
||||
c.Dir = testRepo
|
||||
err = c.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
inv, _ := clitest.New(t, "dotfiles", "--global-config", string(root), "--symlink-dir", string(root), "-y", testRepo)
|
||||
err = inv.Run()
|
||||
require.NoError(t, err)
|
||||
|
||||
b, err := os.ReadFile(filepath.Join(string(root), "greeting.txt"))
|
||||
require.NoError(t, err)
|
||||
// If you squint, it does in fact say "hello, computer!" in here, but in
|
||||
// UTF-16 and with a byte-order-marker at the beginning. Windows!
|
||||
require.Equal(t, b, []byte("\xff\xfeh\x00e\x00l\x00l\x00o\x00,\x00 \x00c\x00o\x00m\x00p\x00u\x00t\x00e\x00r\x00!\x00\r\x00\n\x00"))
|
||||
})
|
||||
}
|
||||
|
||||
func testGitRepo(t *testing.T, root config.Root) string {
|
||||
r, err := cryptorand.String(8)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package cli
|
||||
|
||||
func installScriptFiles() []string {
|
||||
return []string{
|
||||
"install.ps1",
|
||||
"bootstrap.ps1",
|
||||
"setup.ps1",
|
||||
"script/install.ps1",
|
||||
"script/bootstrap.ps1",
|
||||
"script/setup.ps1",
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user