func()

in pkg/gcpbuildpack/exec.go [159:253]


func (ctx *Context) configuredExec(params execParams) (*ExecResult, error) {
	if len(params.cmd) < 1 {
		return nil, fmt.Errorf("no command provided")
	}
	if params.cmd[0] == "" {
		return nil, fmt.Errorf("empty command provided")
	}

	defaultShouldLog := true
	if !params.userAttribution && !ctx.debug {
		// For "system" commands, we will only log if the debug flag is present.
		defaultShouldLog = false
	}

	readableCmd := strings.Join(params.cmd, " ")
	if len(params.env) > 0 {
		env := strings.Join(params.env, " ")
		readableCmd = fmt.Sprintf("%s (%s)", readableCmd, env)
	}

	logCmd := defaultShouldLog
	if params.logCommandOverride != nil {
		logCmd = *params.logCommandOverride
	}
	if logCmd {
		ctx.Logf(divider)
		ctx.Logf("Running %q", readableCmd)
	}

	status := buildererror.StatusInternal
	defer func(start time.Time) {
		truncated := readableCmd
		if len(truncated) > 60 {
			truncated = truncated[:60] + "..."
		}

		if logCmd {
			ctx.Logf("Done %q (%v)", truncated, time.Since(start))
		}
		ctx.Span(ctx.createSpanName(params.cmd), start, status)
	}(time.Now())

	exitCode := 0
	ecmd := ctx.execCmd(params.cmd[0], params.cmd[1:]...)

	if params.dir != "" {
		ecmd.Dir = params.dir
	}

	if len(params.env) > 0 {
		ecmd.Env = append(append(ecmd.Env, os.Environ()...), params.env...)
	}

	logOutput := defaultShouldLog
	if params.logOutputOverride != nil {
		logOutput = *params.logOutputOverride
	}
	var outb, errb bytes.Buffer
	combinedb := lockingBuffer{ctx: ctx, log: logOutput}
	ecmd.Stdout = io.MultiWriter(&outb, &combinedb)
	ecmd.Stderr = io.MultiWriter(&errb, &combinedb)

	if err := ecmd.Run(); err != nil {
		if ee, ok := err.(*exec.ExitError); ok {
			// The command returned a non-zero result.
			exitCode = ee.ExitCode()
		} else if pe, ok := err.(*os.PathError); ok && pe.Err == unix.ENOENT {
			// ENOENT normally occurs if the command cannot
			// be found, but also occurs with scripts using
			// CR-LF line endings.  Unix uses LF as its line
			// ending, so a script with a shebang using CR-LF
			// will result in the kernel attempting to
			// resolve an executable name with the trailing
			// CR. This search will almost certainly fail and
			// otherwise results in an confusing ENOENT.
			return nil, fmt.Errorf("executing command %q: %v: if %q is a script, ensure that it has Unix-style LF line endings", readableCmd, err, params.cmd[0])
		} else {
			return nil, fmt.Errorf("executing command %q: %v", readableCmd, err)
		}
	}

	result := &ExecResult{
		ExitCode: exitCode,
		Stdout:   strings.TrimSpace(string(outb.Bytes())),
		Stderr:   strings.TrimSpace(string(errb.Bytes())),
		Combined: strings.TrimSpace(string(combinedb.Bytes())),
	}

	if exitCode != 0 {
		return result, fmt.Errorf("executing command %q: exit code %d", readableCmd, exitCode)
	}

	status = buildererror.StatusOk
	return result, nil
}