-
Notifications
You must be signed in to change notification settings - Fork 302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Experiment: Polyglot hooks #2040
Conversation
func (s *Shell) RunWithEnv(ctx context.Context, environ *env.Environment, command string, arg ...string) error { | ||
formatted := process.FormatCommand(command, arg) | ||
if s.stdin == nil { | ||
s.Promptf("%s", formatted) | ||
} else { | ||
// bash-syntax-compatible indication that input is coming from somewhere | ||
s.Promptf("%s < /dev/stdin", formatted) | ||
} | ||
|
||
cmd, err := s.buildCommand(command, arg...) | ||
if err != nil { | ||
s.Errorf("Error building command: %v", err) | ||
return err | ||
} | ||
|
||
cmd.Env = append(cmd.Env, environ.ToSlice()...) | ||
|
||
return s.executeCommand(ctx, cmd, s.Writer, executeFlags{ | ||
Stdout: true, | ||
Stderr: true, | ||
PTY: s.PTY, | ||
}) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this function is just the Run
method and the RunWithPrompt
methods garglemeshed together with an env-y thing going on in the middle. earlier on, i tried to make a nice refactor so that you could compose bits and bobs of the different Run*
methods together, but it wasn't super important to make this change. i'd like to clean it up at some point though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please
if e == nil { | ||
return New() | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gotta catch 'em all ('em being nil pointer dereference panics)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I sort of expect a copy of nil
to be nil
, but it's probably more pragmatic for it to be New()
.
hook/scriptwrapper.go
Outdated
if shebang != "" && !shellscript.IsPOSIXShell(shebang) { | ||
return nil, fmt.Errorf("hook starts with an unsupported shebang line %q", shebang) | ||
} | ||
// if shebang != "" && !shellscript.IsPOSIXShell(shebang) { | ||
// return nil, fmt.Errorf("hook starts with an unsupported shebang line %q", shebang) | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this doesn't technically have to be commented out anymore - it's still an error if we try to scriptwrap a non-shell hook, but we've kind of lifted this logic out to here. Maybe it makes sense to keep this in as a defensive programming thing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also defend ourselves with a test. I think that's more robust, so would prefer to delete the comments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh totally, if this gets out of WIP i'd either de-comment it or remove these lines entirely.
when you say we can defend ourselves with a test, do you mean adding a test that asserts that if we try to wrap a non-shell-script, it complains?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, exactly. I'd imagine, we would want to test a whole table of binaries and scripts and verify that they run. We should be able to tease out a test that fails when the scriptwrapper is run on the wrong type of binary from this.
You might have to think a bit about making an interface. I think that's a good thing to do, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, that sounds like a good idea. when we get to properly testing this, i'll do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we gotten to properly testing this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
very good call! when i said:
oh totally, if this gets out of WIP i'd either de-comment it or remove these lines entirely.
apparently i was lying :P
i'll bang out some tests for this now
shellscript/shellscript.go
Outdated
) | ||
|
||
// Magic numbers for various types of executable files | ||
var ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd like to thank the peanut gallery in the #serious-engineering channel for helping me with all of this binary nonsense
shellscript/shellscript.go
Outdated
ELFMagic = []byte{0x7F, 0x45, 0x4C, 0x46} | ||
|
||
// Windows uses MS-DOS MZ-style executables. This includes PE32 and PE32+ (which is 64-bit 🙃) binaries, | ||
// which a are what modern windowses tend to use. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It hates the windowses, precious.
Love this!
Yeah, this is my only concern. I wonder if this will be an unexpected sharp edge. I guess that folks assume that bash scripts are simply sourced (vs the magic of wrapped hooks) and they won't expect that behaviour for non-shell scripts. I assume the future is all Job API and no more magical environment capture? |
in an "as the limit tends to infinity" sense, i think so. i doubt that we'd ever fully remove the environment functionality anytime in the forseeable future; it's just too useful and easy to set up. i suspect that if we merged this, we'd try to make non-bash hooks the golden path, and try to build some nicer tooling around them, and encourage it that way, but keep env capture around as a simple mode? i'm not 100% sure, i may or may or may not have thought that far ahead 😅 |
2527108
to
81976a2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking pretty good 🥞
95775d0
to
25e8192
Compare
2b791bc
to
2f7e950
Compare
a73416c
to
6b534b1
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your service @moskyb 🫡
I think my only blocking comment is for the scriptwrapper tests. It may no longer be necessary to write them, though because of some combination of other tests. If so, I think you should delete the commented code. If not, you should still delete it. We are all pretty good at git archaeology.
// Potentially there's something that we could do with file extensions, but that's a bit of a minefield, and would | ||
// probably mean that we have to have a list of blessed hook runtimes on windows only... or something. | ||
|
||
// Regardless: not supported right now, or potentially ever. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯% reasonable to cut this from scope.
if e == nil { | ||
return New() | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I sort of expect a copy of nil
to be nil
, but it's probably more pragmatic for it to be New()
.
Env: map[string]string{ | ||
"OCEAN": "Pacífico", | ||
"MOUNTAIN": "chimborazo", | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good thinking to test the jobapi as well 🧠
func (s *Shell) RunWithEnv(ctx context.Context, environ *env.Environment, command string, arg ...string) error { | ||
formatted := process.FormatCommand(command, arg) | ||
if s.stdin == nil { | ||
s.Promptf("%s", formatted) | ||
} else { | ||
// bash-syntax-compatible indication that input is coming from somewhere | ||
s.Promptf("%s < /dev/stdin", formatted) | ||
} | ||
|
||
cmd, err := s.buildCommand(command, arg...) | ||
if err != nil { | ||
s.Errorf("Error building command: %v", err) | ||
return err | ||
} | ||
|
||
cmd.Env = append(cmd.Env, environ.ToSlice()...) | ||
|
||
return s.executeCommand(ctx, cmd, s.Writer, executeFlags{ | ||
Stdout: true, | ||
Stderr: true, | ||
PTY: s.PTY, | ||
}) | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please
hook/scriptwrapper.go
Outdated
if shebang != "" && !shellscript.IsPOSIXShell(shebang) { | ||
return nil, fmt.Errorf("hook starts with an unsupported shebang line %q", shebang) | ||
} | ||
// if shebang != "" && !shellscript.IsPOSIXShell(shebang) { | ||
// return nil, fmt.Errorf("hook starts with an unsupported shebang line %q", shebang) | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have we gotten to properly testing this?
The server needs to be able to handle JSON nulls as values, so it should use a map[string]*string, but the client library should just not allow anyone to send nulls, so it should use a map[string]string. This has the side-benefit of making the DX of actually using the library much better
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! Ship it 🚢
This PR: Removes the requirement that hooks for the buildkite-agent be written in a shell scripting language a la bash. With the use of this branch, hooks can also either be compiled executable binaries (ie those produced by languages like Golang or Rust), or any interpreted scripting language (ie python, ruby, lua), so long as an appropriate shebang is specified at the top of the file.
note that neither binary hooks nor non-shell script hooks have environment variable changes captured as bash script hooks do; they must use the Job API to modify the environment of the job.