package cmd
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"github.com/spf13/cobra"
)
// Shared by every download subcommand. Only one command runs per
// process, so package-level flag vars are safe (same convention as
// flagForce).
var (
flagExec string
flagOptional bool
)
// addExecFlag attaches --exec. produces describes what the script is
// expected to leave behind, so each subcommand's help text is concrete.
func addExecFlag(c *cobra.Command, produces string) {
// The backquoted word sets the value placeholder cobra prints in
// --help ("--exec script"), so keep exactly one of them.
c.Flags().StringVar(&flagExec, "exec", "",
"on cache miss, run this `script` through sh -c (it must produce "+produces+") and seed the cache with the result")
}
// addOptionalFlag attaches --optional: a miss stops being an error, so
// `set -e` scripts don't need a trailing `|| echo "cache miss"`.
func addOptionalFlag(c *cobra.Command) {
c.Flags().BoolVar(&flagOptional, "optional", false,
"treat a cache miss as success (exit 0) instead of an error")
}
// runExecFallback runs the --exec script through `sh -c` with stdio
// inherited, so its output lands in the CI log in place, unbuffered and
// in order. A non-zero exit is propagated verbatim by exitCodeFor —
// the script's own status is more informative than cacher's.
func runExecFallback(ctx context.Context, w *cobra.Command, script string) error {
fmt.Fprintf(w.ErrOrStderr(), "Running — sh -c %q\n", script)
c := exec.CommandContext(ctx, "sh", "-c", script)
c.Stdin = os.Stdin
c.Stdout = os.Stdout
c.Stderr = os.Stderr
if err := c.Run(); err != nil {
var ee *exec.ExitError
if errors.As(err, &ee) {
return &execError{code: ee.ExitCode(), err: err}
}
return fmt.Errorf("--exec: %w", err)
}
return nil
}
// execError carries the --exec script's own exit status up to Execute.
type execError struct {
code int
err error
}
func (e *execError) Error() string { return fmt.Sprintf("--exec script failed: %v", e.err) }
func (e *execError) Unwrap() error { return e.err }
func (e *execError) ExitCode() int { return e.code }