| @@ -115,8 +115,14 @@ func (r *runner) runStepsPodman(j job, dir string, env []string, sink io.Writer, |
| 115 | 115 | // file outside the workspace holds them instead — outside because the |
| 116 | 116 | // workspace is bind mounted, and a file of secrets sitting in the |
| 117 | 117 | // checkout is one `cat` from a build's own log. |
| 118 | // |
| 119 | // A value with a newline in it — a private key — cannot go in the |
| 120 | // file, which has no escape for one. Those are named on the command |
| 121 | // line with --env NAME and valued in the podman process's own |
| 122 | // environment, which podman copies into the container. |
| 123 | fileEnv, inherit := splitEnv(env) |
| 118 | 124 | envFile := filepath.Join(r.workdir, fmt.Sprintf("env-%d", j.ID)) |
| 119 | | if err := writeEnvFile(envFile, env); err != nil { |
| 125 | if err := writeEnvFile(envFile, fileEnv); err != nil { |
| 120 | 126 | fmt.Fprintf(sink, "preparing the build environment: %v\n", err) |
| 121 | 127 | return false |
| 122 | 128 | } |
| @@ -144,7 +150,9 @@ func (r *runner) runStepsPodman(j job, dir string, env []string, sink io.Writer, |
| 144 | 150 | args := append(r.podmanGlobal(), "run", "--detach", "--rm", "--pull=never", "--cgroups=disabled") |
| 145 | 151 | args = append(args, |
| 146 | 152 | "--name", name, |
| 147 | | "--env-file", envFile, |
| 153 | "--env-file", envFile) |
| 154 | args = append(args, inheritArgs(inherit)...) |
| 155 | args = append(args, |
| 148 | 156 | "--volume", dir+":/workspace:rw", |
| 149 | 157 | // The build home holds the tool caches (Go modules, the sonar |
| 150 | 158 | // scanner) that must outlive a build; HOME in env points at it. |
| @@ -156,7 +164,7 @@ func (r *runner) runStepsPodman(j job, dir string, env []string, sink io.Writer, |
| 156 | 164 | "--entrypoint", "sh", |
| 157 | 165 | image, "-c", "sleep infinity") |
| 158 | 166 | start := exec.Command(podman, args...) |
| 159 | | start.Env = []string{"PATH=" + os.Getenv("PATH"), "HOME=" + r.podmanHome()} |
| 167 | start.Env = append([]string{"PATH=" + os.Getenv("PATH"), "HOME=" + r.podmanHome()}, inherit...) |
| 160 | 168 | intoCgroup(start, cgroupFD) |
| 161 | 169 | if out, err := start.CombinedOutput(); err != nil { |
| 162 | 170 | // A missing image lands here, and it is the common case worth |
| @@ -226,6 +234,31 @@ func (r *runner) podmanHome() string { |
| 226 | 234 | return "/var/lib/gitbay-runner" |
| 227 | 235 | } |
| 228 | 236 | |
| 237 | // splitEnv separates the entries an env file can carry from those whose |
| 238 | // value holds a newline, which podman must inherit from its environment. |
| 239 | func splitEnv(env []string) (file, inherit []string) { |
| 240 | for _, e := range env { |
| 241 | if strings.ContainsAny(e, "\n\r") { |
| 242 | inherit = append(inherit, e) |
| 243 | } else { |
| 244 | file = append(file, e) |
| 245 | } |
| 246 | } |
| 247 | return file, inherit |
| 248 | } |
| 249 | |
| 250 | // inheritArgs names each inherited variable for podman run: --env NAME |
| 251 | // with no value makes podman take it from its own environment, so the |
| 252 | // value never appears on a command line. |
| 253 | func inheritArgs(inherit []string) []string { |
| 254 | var args []string |
| 255 | for _, e := range inherit { |
| 256 | name, _, _ := strings.Cut(e, "=") |
| 257 | args = append(args, "--env", name) |
| 258 | } |
| 259 | return args |
| 260 | } |
| 261 | |
| 229 | 262 | // writeEnvFile writes KEY=VALUE lines for podman --env-file, readable |
| 230 | 263 | // only by this user. Values containing a newline are refused rather than |
| 231 | 264 | // silently truncated: the format has no escape for one, and a secret that |