mirror of
https://github.com/hanzoai/pack.git
synced 2026-09-30 23:43:13 +00:00
* refactor: add io.Writer parameter to pretty print functions and JSON support Generated-by: aiautocommit * chore: promote tidwall/pretty from indirect to direct dependency Generated-by: aiautocommit * feat(cli): add show-plan flag to info command - Add `--show-plan` flag to optionally render the generated build plan in the info command output. - Update section header printing functions to accept an `io.Writer` destination for consistent output formatting. Generated-by: aiautocommit
154 lines
4.1 KiB
Go
154 lines
4.1 KiB
Go
// entrypoint to the `build` subcommand
|
|
// Primarily bundles CLI options into the structure that `BuildWithBuildkitClient` expects
|
|
|
|
package cli
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/railwayapp/railpack/buildkit"
|
|
"github.com/railwayapp/railpack/core"
|
|
"github.com/railwayapp/railpack/core/app"
|
|
"github.com/railwayapp/railpack/core/plan"
|
|
"github.com/urfave/cli/v3"
|
|
)
|
|
|
|
var BuildCommand = &cli.Command{
|
|
Name: "build",
|
|
Aliases: []string{"b"},
|
|
Usage: "build an image with BuildKit",
|
|
ArgsUsage: "DIRECTORY",
|
|
EnableShellCompletion: true,
|
|
Flags: append([]cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "name",
|
|
Usage: "name of the image to build",
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "output",
|
|
Usage: "output the final filesystem to a local directory",
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "platform",
|
|
Usage: "platform to build for (e.g. linux/amd64, linux/arm64)",
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "progress",
|
|
Usage: "buildkit progress output mode. Values: auto, plain, tty",
|
|
Value: "auto",
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "show-plan",
|
|
Usage: "Show the build plan before building. This is useful for development and debugging.",
|
|
Value: false,
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "cache-key",
|
|
Usage: "Unique id to prefix to cache keys",
|
|
},
|
|
&cli.StringSliceFlag{
|
|
Name: "cache-from",
|
|
Usage: "External cache sources",
|
|
},
|
|
&cli.StringSliceFlag{
|
|
Name: "cache-to",
|
|
Usage: "Cache export destinations",
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "no-cache",
|
|
Usage: "Do not use cache when building",
|
|
Value: false,
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "dump-llb",
|
|
Hidden: true,
|
|
Value: false,
|
|
},
|
|
}, commonPlanFlags()...),
|
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
|
buildResult, app, env, err := GenerateBuildResultForCommand(cmd)
|
|
if err != nil {
|
|
return cli.Exit(err, 1)
|
|
}
|
|
|
|
if !cmd.Bool("dump-llb") {
|
|
core.PrettyPrintBuildResult(buildResult, core.PrintOptions{Version: Version})
|
|
}
|
|
|
|
if !buildResult.Success {
|
|
os.Exit(1)
|
|
return nil
|
|
}
|
|
|
|
if cmd.Bool("show-plan") && !cmd.Bool("dump-llb") {
|
|
planMap, err := addSchemaToPlanMap(buildResult.Plan)
|
|
if err != nil {
|
|
return cli.Exit(err, 1)
|
|
}
|
|
|
|
serializedPlan, err := json.MarshalIndent(planMap, "", " ")
|
|
if err != nil {
|
|
return cli.Exit(err, 1)
|
|
}
|
|
|
|
core.PrettyPrintSectionHeader(os.Stdout, "Generated railpack-plan.json")
|
|
core.PrettyPrintJSON(os.Stdout, serializedPlan)
|
|
}
|
|
|
|
err = validateSecrets(buildResult.Plan, env)
|
|
if err != nil {
|
|
return cli.Exit(err, 1)
|
|
}
|
|
|
|
secretsHash := getSecretsHash(env)
|
|
|
|
platformStr := cmd.String("platform")
|
|
err = buildkit.BuildWithBuildkitClient(app.Source, buildResult.Plan, buildkit.BuildWithBuildkitClientOptions{
|
|
ImageName: cmd.String("name"),
|
|
DumpLLB: cmd.Bool("dump-llb"),
|
|
OutputDir: cmd.String("output"),
|
|
ProgressMode: cmd.String("progress"),
|
|
CacheKey: cmd.String("cache-key"),
|
|
// StringSlice to support multiple cache-from / cache-to entries, same shape as docker buildx
|
|
ImportCache: cmd.StringSlice("cache-from"),
|
|
ExportCache: cmd.StringSlice("cache-to"),
|
|
SecretsHash: secretsHash,
|
|
Secrets: env.Variables,
|
|
Platform: platformStr,
|
|
GitHubToken: os.Getenv("GITHUB_TOKEN"),
|
|
NoCache: cmd.Bool("no-cache"),
|
|
})
|
|
if err != nil {
|
|
return cli.Exit(err, 1)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
// make sure all secrets referenced in the build plan are present in the environment
|
|
func validateSecrets(plan *plan.BuildPlan, env *app.Environment) error {
|
|
for _, secret := range plan.Secrets {
|
|
if _, ok := env.Variables[secret]; !ok {
|
|
return fmt.Errorf("missing environment variable: %s. Please set using --env %s=%s", secret, secret, "...")
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// generate a hash all of build secrets to invalidate all caches when any secret changes
|
|
func getSecretsHash(env *app.Environment) string {
|
|
var secretsValue strings.Builder
|
|
for _, v := range env.Variables {
|
|
secretsValue.WriteString(v)
|
|
}
|
|
hasher := sha256.New()
|
|
hasher.Write([]byte(secretsValue.String()))
|
|
return fmt.Sprintf("%x", hasher.Sum(nil))
|
|
}
|