1package shells 2 3import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io" 8 "strings" 9 10 "gitlab.com/gitlab-org/gitlab-runner/common" 11) 12 13// pwshTrapShellScript is used to wrap a shell script in a trap that makes sure the script always exits 14// with exit code of 0. This can be useful in container environments where exiting with an exit code different from 0 15// would kill the container. 16// At the same time it writes to a file the actual exit code of the script as well as the filename 17// At the same time it writes the actual exit code of the script as well as 18// the filename of the script (as json) to a file. 19// With powershell $? returns True if the last command was successful so the exit_code is set to 0 in that case 20const pwshTrapShellScript = ` 21function runner_script_trap() { 22 $lastExit = $? 23 $code = 1 24 If($lastExit -eq "True"){ $code = 0 } 25 26 $log_file=%q 27 $out_json= '{"command_exit_code": ' + $code + ', "script": "' + $MyInvocation.MyCommand.Name + '"}' 28 29 # Make sure the command status will always be printed on a new line 30 if ( $((Get-Content -Path $log_file | Measure-Object -Line).Lines) -gt 0 ) 31 { 32 Add-Content $log_file "$out_json" 33 } 34 else 35 { 36 Add-Content $log_file "" 37 Add-Content $log_file "$out_json" 38 } 39} 40 41trap {runner_script_trap} 42 43` 44 45type PwshTrapShellWriter struct { 46 *PsWriter 47 48 logFile string 49} 50 51func (b *PwshTrapShellWriter) Finish(trace bool) string { 52 var buffer bytes.Buffer 53 w := bufio.NewWriter(&buffer) 54 55 b.writeShebang(w) 56 b.writeTrap(w) 57 b.writeTrace(w, trace) 58 b.writeScript(w) 59 60 _ = w.Flush() 61 return buffer.String() 62} 63 64func (b *PwshTrapShellWriter) writeTrap(w io.Writer) { 65 // For code readability purpose, the pwshTrapShellScript is written with \n as EOL within the script 66 // However when written into the generated script for a job, the \n used within the trap script is 67 // replaced by the shell EOL to avoid having multiple EOL within it and to keep it consistent 68 _, _ = fmt.Fprintf(w, strings.ReplaceAll(pwshTrapShellScript, "\n", b.EOL), b.logFile) 69} 70 71type PwshTrapShell struct { 72 *PowerShell 73 74 LogFile string 75} 76 77func (b *PwshTrapShell) GenerateScript(buildStage common.BuildStage, info common.ShellScriptInfo) (string, error) { 78 w := &PwshTrapShellWriter{ 79 PsWriter: &PsWriter{ 80 TemporaryPath: info.Build.TmpProjectDir(), 81 Shell: b.Shell, 82 EOL: b.EOL, 83 }, 84 logFile: b.LogFile, 85 } 86 87 return b.generateScript(w, buildStage, info) 88} 89