package cli import ( "encoding/json" "fmt" "github.com/charmbracelet/log" "github.com/hanzoai/pack/core" a "github.com/hanzoai/pack/core/app" "github.com/hanzoai/pack/core/config" "github.com/hanzoai/pack/core/mise" "github.com/hanzoai/pack/core/plan" "github.com/hanzoai/pack/internal/utils" "github.com/urfave/cli/v3" ) var Version string // This will be set by main const ( // the command failed for a reason that will not change on a retry ExitCodeFailure = 1 // EX_TEMPFAIL: the command failed for a transient reason (e.g. a network error // downloading mise) and is worth retrying. Platforms that run Railpack in a // build pipeline key their retry logic on this. ExitCodeTransient = 75 ) func exitCodeForError(err error) int { if mise.IsTemporary(err) { return ExitCodeTransient } return ExitCodeFailure } func commonPlanFlags() []cli.Flag { return []cli.Flag{ &cli.StringSliceFlag{ Name: "env", Aliases: []string{"e"}, Usage: "environment variables to set", }, &cli.StringSliceFlag{ Name: "previous", Usage: "versions of packages used for previous builds (e.g. 'package@version')", }, &cli.StringFlag{ Name: "build-cmd", Usage: "build command to use", }, &cli.StringFlag{ Name: "start-cmd", Usage: "start command to use", }, &cli.StringFlag{ Name: "config-file", Usage: "relative path to railpack config file (default: railpack.json)", }, &cli.BoolFlag{ Name: "error-missing-start", Usage: "error if no start command is found", }, } } func GenerateBuildResultForCommand(cmd *cli.Command) (*core.BuildResult, *a.App, *a.Environment, error) { directory := cmd.Args().First() if directory == "" { return nil, nil, nil, cli.Exit("directory argument is required", ExitCodeFailure) } app, err := a.NewApp(directory) if err != nil { return nil, nil, nil, fmt.Errorf("error creating app: %w", err) } log.Debugf("Building %s", app.Source) envsArgs := cmd.StringSlice("env") env, err := a.FromEnvs(envsArgs) if err != nil { return nil, nil, nil, fmt.Errorf("error creating env: %w", err) } // if --verbose is passed as a CLI global argument, enable verbose mise logging so the user don't have to understand // the railpack build system deeply to get this debugging information. if cmd.Bool("verbose") && env.GetVariable("MISE_VERBOSE") == "" { env.SetVariable("MISE_VERBOSE", "1") } previousVersions := utils.ParsePackageWithVersion(cmd.StringSlice("previous")) generateOptions := &core.GenerateBuildPlanOptions{ RailpackVersion: Version, BuildCommand: cmd.String("build-cmd"), StartCommand: cmd.String("start-cmd"), PreviousVersions: previousVersions, ConfigFilePath: cmd.String("config-file"), ErrorMissingStartCommand: cmd.Bool("error-missing-start"), } buildResult, err := core.GenerateBuildPlan(app, env, generateOptions) if err != nil { return nil, nil, nil, err } return buildResult, app, env, nil } // add $schema link to resulting map JSON for improved IDE experience when manually editing func addSchemaToPlanMap(p *plan.BuildPlan) (map[string]any, error) { if p == nil { return map[string]any{"$schema": config.SchemaUrl}, nil } planBytes, err := json.Marshal(p) if err != nil { return nil, err } var planMap map[string]any if err := json.Unmarshal(planBytes, &planMap); err != nil { return nil, err } planMap["$schema"] = config.SchemaUrl return planMap, nil }