Skip to content

Commit 27ca920

Browse files
authored
Merge pull request #3759 from markmssd/buildkite-hooks-shell
Support PowerShell 7
2 parents e87fa42 + e2122dd commit 27ca920

12 files changed

Lines changed: 72 additions & 12 deletions

File tree

agent/agent_configuration.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ type AgentConfiguration struct {
6363
WriteJobLogsToStdout bool
6464
LogFormat string
6565
Shell string
66+
HooksShell string
6667
Profile string
6768
RedactedVars []string
6869
AcquireJob string

agent/job_runner.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ BUILDKITE_LOCAL_HOOKS_ENABLED
442442
BUILDKITE_PLUGINS_ENABLED
443443
BUILDKITE_REDACTED_VARS
444444
BUILDKITE_SHELL
445+
BUILDKITE_HOOKS_SHELL
445446
BUILDKITE_SIGNAL_GRACE_PERIOD_SECONDS
446447
BUILDKITE_SSH_KEYSCAN
447448
BUILDKITE_STRICT_SINGLE_HOOKS
@@ -577,6 +578,7 @@ BUILDKITE_AGENT_JWKS_KEY_ID`
577578
setEnv("BUILDKITE_GIT_SUBMODULE_CLONE_CONFIG", strings.Join(r.conf.AgentConfiguration.GitSubmoduleCloneConfig, ","))
578579

579580
setEnv("BUILDKITE_SHELL", r.conf.AgentConfiguration.Shell)
581+
setEnv("BUILDKITE_HOOKS_SHELL", r.conf.AgentConfiguration.HooksShell)
580582
setEnv("BUILDKITE_AGENT_EXPERIMENT", strings.Join(experiments.Enabled(ctx), ","))
581583
setEnv("BUILDKITE_REDACTED_VARS", strings.Join(r.conf.AgentConfiguration.RedactedVars, ","))
582584
setEnv("BUILDKITE_STRICT_SINGLE_HOOKS", fmt.Sprint(r.conf.AgentConfiguration.StrictSingleHooks))
@@ -745,7 +747,7 @@ func (r *JobRunner) executePreBootstrapHook(ctx context.Context, hook string) (b
745747
environ.Set("BUILDKITE_AGENT_DEBUG", fmt.Sprint(r.conf.Debug))
746748
environ.Set("BUILDKITE_AGENT_DEBUG_HTTP", fmt.Sprint(r.conf.DebugHTTP))
747749

748-
script, err := sh.Script(hook)
750+
script, err := sh.Script(hook, r.conf.AgentConfiguration.HooksShell)
749751
if err != nil {
750752
r.agentLogger.Error("Finished pre-bootstrap hook %q: script not runnable: %v", hook, err)
751753
return false, err

clicommand/agent_start.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ type AgentStartConfig struct {
134134
PluginsPath string `cli:"plugins-path" normalize:"filepath"`
135135

136136
Shell string `cli:"shell"`
137+
HooksShell string `cli:"hooks-shell"`
137138
BootstrapScript string `cli:"bootstrap-script" normalize:"commandpath"`
138139
NoPTY bool `cli:"no-pty"`
139140

@@ -306,7 +307,7 @@ func (asc AgentStartConfig) Features(ctx context.Context) []string {
306307
}
307308

308309
func DefaultShell() string {
309-
// https://github.com/golang/go/blob/master/src/go/build/syslist.go#L7
310+
// https://github.com/golang/go/blob/master/src/internal/syslist/syslist.go#L17
310311
switch runtime.GOOS {
311312
case "windows":
312313
return `C:\Windows\System32\CMD.exe /S /C`
@@ -434,6 +435,11 @@ var AgentStartCommand = cli.Command{
434435
Usage: "The shell command used to interpret build commands, e.g /bin/bash -e -c",
435436
EnvVar: "BUILDKITE_SHELL",
436437
},
438+
cli.StringFlag{
439+
Name: "hooks-shell",
440+
Usage: "The shell command used to interpret hooks commands, e.g pwsh -Command",
441+
EnvVar: "BUILDKITE_HOOKS_SHELL",
442+
},
437443
cli.StringFlag{
438444
Name: "queue",
439445
Usage: "The queue the agent will listen to for jobs. If not set, the agent will use the default queue. Overwrites the queue tag in the agent's tags",
@@ -1071,6 +1077,7 @@ var AgentStartCommand = cli.Command{
10711077
WriteJobLogsToStdout: cfg.WriteJobLogsToStdout,
10721078
LogFormat: cfg.LogFormat,
10731079
Shell: cfg.Shell,
1080+
HooksShell: cfg.HooksShell,
10741081
RedactedVars: cfg.RedactedVars,
10751082
AcquireJob: cfg.AcquireJob,
10761083
TracingBackend: cfg.TracingBackend,
@@ -1548,7 +1555,7 @@ func agentLifecycleHook(hookName string, log logger.Logger, cfg AgentStartConfig
15481555

15491556
// run hooks
15501557
for _, p = range hooks {
1551-
script, err := sh.Script(p)
1558+
script, err := sh.Script(p, cfg.HooksShell)
15521559
if err != nil {
15531560
log.Error("%q hook: %v", hookName, err)
15541561
return err

clicommand/bootstrap.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ type BootstrapConfig struct {
9797
LogLevel string `cli:"log-level"`
9898
Debug bool `cli:"debug"`
9999
Shell string `cli:"shell"`
100+
HooksShell string `cli:"hooks-shell"`
100101
Experiments []string `cli:"experiment" normalize:"list"`
101102
Phases []string `cli:"phases" normalize:"list"`
102103
Profile string `cli:"profile"`
@@ -306,6 +307,11 @@ var BootstrapCommand = cli.Command{
306307
EnvVar: "BUILDKITE_SHELL",
307308
Value: DefaultShell(),
308309
},
310+
cli.StringFlag{
311+
Name: "hooks-shell",
312+
Usage: "The shell to use to interpret hooks commands",
313+
EnvVar: "BUILDKITE_HOOKS_SHELL",
314+
},
309315
cli.StringSliceFlag{
310316
Name: "phases",
311317
Usage: "The specific phases to execute. The order they're defined is irrelevant.",
@@ -460,6 +466,7 @@ var BootstrapCommand = cli.Command{
460466
RunInPty: runInPty,
461467
SSHKeyscan: cfg.SSHKeyscan,
462468
Shell: cfg.Shell,
469+
HooksShell: cfg.HooksShell,
463470
StrictSingleHooks: cfg.StrictSingleHooks,
464471
Tag: cfg.Tag,
465472
TracingBackend: cfg.TracingBackend,

env/environment.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ var ProtectedEnv = map[string]struct{}{
363363
"BUILDKITE_PLUGINS_ENABLED": {},
364364
"BUILDKITE_PLUGINS_PATH": {},
365365
"BUILDKITE_SHELL": {},
366+
"BUILDKITE_HOOKS_SHELL": {},
366367
"BUILDKITE_SSH_KEYSCAN": {},
367368
}
368369

env/environment_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ func TestProtectedEnv(t *testing.T) {
303303
"BUILDKITE_PLUGINS_ENABLED",
304304
"BUILDKITE_PLUGINS_PATH",
305305
"BUILDKITE_SHELL",
306+
"BUILDKITE_HOOKS_SHELL",
306307
"BUILDKITE_SSH_KEYSCAN",
307308
}
308309

internal/job/config.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ type ExecutorConfig struct {
163163
// The shell used to execute commands
164164
Shell string
165165

166+
// The shell used to execute agent hooks
167+
HooksShell string
168+
166169
// Phases to execute, defaults to all phases
167170
Phases []string
168171

internal/job/executor.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ func (e *Executor) runWrappedShellScriptHook(ctx context.Context, hookName strin
549549
// (which acquires open file descriptors of the parent process) and
550550
// writing an executable (the script wrapper).
551551
// See https://github.com/golang/go/issues/22315.
552-
script, err := e.shell.Script(script.Path())
552+
script, err := e.shell.Script(script.Path(), e.HooksShell)
553553
if err != nil {
554554
r.Break()
555555
return err

internal/job/hook/hook.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,16 @@ func Find(root *os.Root, hookDir, name string) (string, error) {
2222

2323
// exts is a list of file extensions to check.
2424
var exts []string
25-
if runtime.GOOS == "windows" {
25+
switch runtime.GOOS {
26+
case "windows":
2627
// check for windows types first
27-
exts = []string{".bat", ".cmd", ".ps1", ".exe"}
28+
exts = []string{".bat", ".cmd", ".ps1", ".exe", ""}
29+
30+
default:
31+
// always check for an extensionless file
32+
// PowerShell 7 is cross-platform
33+
exts = []string{"", ".ps1"}
2834
}
29-
// always check for an extensionless file
30-
exts = append(exts, "")
3135

3236
// Check for a file named name+ext in hookDir.
3337
for _, ext := range exts {

internal/job/hook/wrapper_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ echo "hello world"
7878

7979
sh := shell.NewTestShell(t)
8080

81-
script, err := sh.Script(wrapper.Path())
81+
script, err := sh.Script(wrapper.Path(), "")
8282
if err != nil {
8383
t.Fatalf("sh.Script(%q) = %v", wrapper.Path(), err)
8484
}
@@ -177,7 +177,7 @@ echo hello world
177177
err = sh.Chdir(hookWorkingDir)
178178
assert.NilError(t, err, "sh.Chdir(%q) = %v", hookWorkingDir, err)
179179

180-
script, err := sh.Script(wrapper.Path())
180+
script, err := sh.Script(wrapper.Path(), "")
181181
if err != nil {
182182
t.Fatalf("sh.Script(%q) = %v", wrapper.Path(), err)
183183
}

0 commit comments

Comments
 (0)