checkpoint laravel working

This commit is contained in:
Jake Runzer
2025-02-21 18:41:15 -05:00
parent 8696e88522
commit fcc33d45e6
22 changed files with 238 additions and 346 deletions
+7 -15
View File
@@ -17,7 +17,6 @@ import (
type BuildGraph struct {
graph *graph.Graph
BaseState *llb.State
CacheStore *BuildKitCacheStore
Plan *plan.BuildPlan
Platform *specs.Platform
@@ -32,7 +31,7 @@ type BuildGraphOutput struct {
GraphEnv BuildEnvironment
}
func NewBuildGraph(plan *plan.BuildPlan, baseState *llb.State, localState *llb.State, cacheStore *BuildKitCacheStore, secretsHash string, platform *specs.Platform) (*BuildGraph, error) {
func NewBuildGraph(plan *plan.BuildPlan, localState *llb.State, cacheStore *BuildKitCacheStore, secretsHash string, platform *specs.Platform) (*BuildGraph, error) {
var secretsFile *llb.State
if secretsHash != "" {
st := llb.Scratch().File(llb.Mkfile("/secrets-hash", 0644, []byte(secretsHash)), llb.WithCustomName("[railpack] secrets hash"))
@@ -42,7 +41,6 @@ func NewBuildGraph(plan *plan.BuildPlan, baseState *llb.State, localState *llb.S
g := &BuildGraph{
graph: graph.NewGraph(),
BaseState: baseState,
CacheStore: cacheStore,
Plan: plan,
Platform: platform,
@@ -83,16 +81,6 @@ func NewBuildGraph(plan *plan.BuildPlan, baseState *llb.State, localState *llb.S
children = append(children, llbNode)
depNode.SetChildren(children)
}
// if depNode, exists := g.graph.GetNode(depName); exists {
// parents := llbNode.GetParents()
// parents = append(parents, depNode)
// llbNode.SetParents(parents)
// children := depNode.GetChildren()
// children = append(children, node)
// depNode.SetChildren(children)
// }
}
}
@@ -103,7 +91,7 @@ func NewBuildGraph(plan *plan.BuildPlan, baseState *llb.State, localState *llb.S
return g, nil
}
func (g *BuildGraph) GetStateForInput(input plan.StepInput, baseState llb.State) llb.State {
func (g *BuildGraph) GetStateForInput(input plan.Input, baseState llb.State) llb.State {
var state llb.State
if input.Image != "" {
@@ -125,11 +113,15 @@ func (g *BuildGraph) GetStateForInput(input plan.StepInput, baseState llb.State)
return state
}
func (g *BuildGraph) GetFullStateFromInputs(inputs []plan.StepInput) llb.State {
func (g *BuildGraph) GetFullStateFromInputs(inputs []plan.Input) llb.State {
if len(inputs) == 0 {
return llb.Scratch()
}
if len(inputs[0].Include)+len(inputs[0].Exclude) > 0 {
panic("first input must not have include or exclude paths")
}
// Get the base state from the first input
state := g.GetStateForInput(inputs[0], llb.Scratch())
if len(inputs) == 1 {
+9 -59
View File
@@ -27,8 +27,6 @@ const (
func ConvertPlanToLLB(plan *p.BuildPlan, opts ConvertPlanOptions) (*llb.State, *Image, error) {
platform := opts.BuildPlatform.ToPlatform()
state := getBaseState(plan, platform)
localState := llb.Local("context",
llb.SharedKeyHint("local"),
llb.SessionID(opts.SessionID),
@@ -37,7 +35,7 @@ func ConvertPlanToLLB(plan *p.BuildPlan, opts ConvertPlanOptions) (*llb.State, *
)
cacheStore := build_llb.NewBuildKitCacheStore(opts.CacheKey)
graph, err := build_llb.NewBuildGraph(plan, &state, &localState, cacheStore, opts.SecretsHash, &platform)
graph, err := build_llb.NewBuildGraph(plan, &localState, cacheStore, opts.SecretsHash, &platform)
if err != nil {
return nil, nil, err
}
@@ -47,13 +45,11 @@ func ConvertPlanToLLB(plan *p.BuildPlan, opts ConvertPlanOptions) (*llb.State, *
return nil, nil, err
}
state = *graphOutput.State
state = getStartState(state, localState, plan, platform)
state := getStartState(*graphOutput.State)
imageEnv := getImageEnv(graphOutput, plan)
startCommand := plan.Start.Command
if plan.Start.Command == "" {
startCommand := plan.Deploy.StartCmd
if startCommand == "" {
startCommand = "/bin/bash"
}
@@ -79,58 +75,22 @@ func ConvertPlanToLLB(plan *p.BuildPlan, opts ConvertPlanOptions) (*llb.State, *
return &state, &image, nil
}
func getStartState(buildState llb.State, localState llb.State, plan *p.BuildPlan, platform specs.Platform) llb.State {
// If there is no custom start image, we just copy over the outputs from the local state
if plan.Start.BaseImage == "" {
startState := buildState.Dir(WorkingDir)
// TODO: This is confusing and needs to change
// If baseImage is provided, we are using `outputs` to copy files from the local state
// If not provided, we are using `outputs` to copy files from the build state
// Confusing and not good
for _, path := range plan.Start.Outputs {
startState = startState.File(llb.Copy(localState, path, path, &llb.CopyInfo{
CreateDestPath: true,
FollowSymlinks: true,
CopyDirContentsOnly: true,
}))
}
return startState
}
startState := llb.Image(plan.Start.BaseImage, llb.Platform(platform)).Dir(WorkingDir)
// This is all the user code + any modifications made by the providers
mergedState := buildState.File(llb.Copy(localState, ".", ".", &llb.CopyInfo{
CreateDestPath: true,
FollowSymlinks: true,
CopyDirContentsOnly: true,
}))
// Copy over necessary files to the start image
for _, path := range plan.Start.Outputs {
startState = startState.File(llb.Copy(mergedState, path, path, &llb.CopyInfo{
CreateDestPath: true,
FollowSymlinks: true,
CopyDirContentsOnly: true,
}))
}
func getStartState(buildState llb.State) llb.State {
startState := buildState.Dir(WorkingDir)
return startState
}
func getImageEnv(graphOutput *build_llb.BuildGraphOutput, plan *p.BuildPlan) []string {
paths := []string{}
paths = append(paths, plan.Deploy.Paths...)
paths = append(paths, graphOutput.GraphEnv.PathList...)
paths = append(paths, plan.Start.Paths...)
paths = append(paths, system.DefaultPathEnvUnix)
slices.Sort(paths)
pathString := strings.Join(paths, ":")
envMap := make(map[string]string, len(graphOutput.GraphEnv.EnvVars)+len(plan.Start.Variables)+1)
envMap := make(map[string]string, len(graphOutput.GraphEnv.EnvVars)+len(plan.Deploy.Variables)+1)
maps.Copy(envMap, graphOutput.GraphEnv.EnvVars)
maps.Copy(envMap, plan.Start.Variables)
maps.Copy(envMap, plan.Deploy.Variables)
envMap["PATH"] = pathString
@@ -142,13 +102,3 @@ func getImageEnv(graphOutput *build_llb.BuildGraphOutput, plan *p.BuildPlan) []s
return envVars
}
func getBaseState(plan *p.BuildPlan, platform specs.Platform) llb.State {
state := llb.Image(plan.BaseImage,
llb.Platform(platform),
)
state = state.AddEnv("DEBIAN_FRONTEND", "noninteractive").Dir(WorkingDir)
return state
}
+2 -5
View File
@@ -7,17 +7,14 @@ import (
)
type Config struct {
// The base image to use for the build
BaseImage string `json:"baseImage,omitempty" jsonschema:"description=The base image to use for the build"`
// List of providers to use
Providers *[]string `json:"providers,omitempty" jsonschema:"description=List of providers to use"`
// Map of step names to step definitions
Steps map[string]*plan.Step `json:"steps,omitempty" jsonschema:"description=Map of step names to step definitions"`
// Start configuration
Start plan.Start `json:"start,omitempty" jsonschema:"description=Start configuration"`
// Deploy configuration
Deploy plan.Deploy `json:"deploy,omitempty" jsonschema:"description=Deploy configuration"`
// Map of package name to package version
Packages map[string]string `json:"packages,omitempty" jsonschema:"description=Map of package name to package version"`
+9 -18
View File
@@ -153,24 +153,16 @@ func GenerateConfigFromEnvironment(app *app.App, env *app.Environment) *config.C
return config
}
// if installCmdVar, _ := env.GetConfigVariable("INSTALL_CMD"); installCmdVar != "" {
// installStep := config.GetOrCreateStep("install")
// installStep.Commands = []plan.Command{plan.NewExecShellCommand(installCmdVar, plan.ExecOptions{CustomName: installCmdVar})}
// installStep.DependsOn = append(installStep.DependsOn, "packages")
// }
// if buildCmdVar, _ := env.GetConfigVariable("BUILD_CMD"); buildCmdVar != "" {
// buildStep := config.GetOrCreateStep("build")
// buildStep.Commands = []plan.Command{
// // We want to run the build command with all the files in the current directory
// plan.NewCopyCommand("."),
// plan.NewExecShellCommand(buildCmdVar, plan.ExecOptions{CustomName: buildCmdVar}),
// }
// buildStep.DependsOn = append(buildStep.DependsOn, "install")
// }
if buildCmdVar, _ := env.GetConfigVariable("BUILD_CMD"); buildCmdVar != "" {
buildStep := config.GetOrCreateStep("build")
buildStep.Commands = []plan.Command{
plan.NewCopyCommand("."),
plan.NewExecShellCommand(buildCmdVar, plan.ExecOptions{CustomName: buildCmdVar}),
}
}
if startCmdVar, _ := env.GetConfigVariable("START_CMD"); startCmdVar != "" {
config.Start.Command = startCmdVar
config.Deploy.StartCmd = startCmdVar
}
if envPackages, _ := env.GetConfigVariable("PACKAGES"); envPackages != "" {
@@ -206,11 +198,10 @@ func GenerateConfigFromOptions(options *GenerateBuildPlanOptions) *config.Config
plan.NewCopyCommand("."),
plan.NewExecShellCommand(options.BuildCommand, plan.ExecOptions{CustomName: options.BuildCommand}),
}
// buildStep.DependsOn = append(buildStep.DependsOn, "install")
}
if options.StartCommand != "" {
config.Start.Command = options.StartCommand
config.Deploy.StartCmd = options.StartCommand
}
return config
+10 -6
View File
@@ -8,15 +8,15 @@ import (
type AptStepBuilder struct {
DisplayName string
// DependsOn []string
Packages []string
Packages []string
Inputs []plan.Input
}
func (c *GenerateContext) NewAptStepBuilder(name string) *AptStepBuilder {
step := &AptStepBuilder{
DisplayName: c.GetStepName(fmt.Sprintf("packages:apt:%s", name)),
// DependsOn: []string{},
Packages: []string{},
DisplayName: c.GetStepName(fmt.Sprintf("packages:%s", name)),
Packages: []string{},
Inputs: []plan.Input{},
}
c.Steps = append(c.Steps, step)
@@ -24,6 +24,10 @@ func (c *GenerateContext) NewAptStepBuilder(name string) *AptStepBuilder {
return step
}
func (b *AptStepBuilder) AddInput(input plan.Input) {
b.Inputs = append(b.Inputs, input)
}
func (b *AptStepBuilder) AddAptPackage(pkg string) {
b.Packages = append(b.Packages, pkg)
}
@@ -34,13 +38,13 @@ func (b *AptStepBuilder) Name() string {
func (b *AptStepBuilder) Build(options *BuildStepOptions) (*plan.Step, error) {
step := plan.NewStep(b.DisplayName)
// step.DependsOn = utils.RemoveDuplicates(b.DependsOn)
step.AddCommands([]plan.Command{
options.NewAptInstallCommand(b.Packages),
})
step.Caches = options.Caches.GetAptCaches()
step.Inputs = b.Inputs
// Does not use any secrets
step.Secrets = []string{}
+25 -26
View File
@@ -11,29 +11,34 @@ import (
type CommandStepBuilder struct {
DisplayName string
Commands []plan.Command
// DependsOn []string
// Outputs []string
Inputs []plan.StepInput
Assets map[string]string
Variables map[string]string
Caches []string
Secrets []string
app *a.App
env *a.Environment
Inputs []plan.Input
Assets map[string]string
Variables map[string]string
Caches []string
Secrets []string
app *a.App
env *a.Environment
}
func (c *GenerateContext) NewCommandStep(name string) *CommandStepBuilder {
step := &CommandStepBuilder{
DisplayName: c.GetStepName(name),
// DependsOn: []string{MisePackageStepName},
Inputs: []plan.StepInput{},
Commands: []plan.Command{},
Assets: map[string]string{},
Variables: map[string]string{},
Caches: []string{},
Secrets: []string{"*"},
app: c.App,
env: c.Env,
Inputs: []plan.Input{},
Commands: []plan.Command{},
Assets: map[string]string{},
Variables: map[string]string{},
Caches: []string{},
Secrets: []string{"*"},
app: c.App,
env: c.Env,
}
// Remove any existing step with the same name
for i, existingStep := range c.Steps {
if existingStep.Name() == step.Name() {
c.Steps = append(c.Steps[:i], c.Steps[i+1:]...)
break
}
}
c.Steps = append(c.Steps, step)
@@ -41,18 +46,14 @@ func (c *GenerateContext) NewCommandStep(name string) *CommandStepBuilder {
return step
}
func (b *CommandStepBuilder) AddInput(input plan.StepInput) {
func (b *CommandStepBuilder) AddInput(input plan.Input) {
b.Inputs = append(b.Inputs, input)
}
func (b *CommandStepBuilder) AddInputs(inputs []plan.StepInput) {
func (b *CommandStepBuilder) AddInputs(inputs []plan.Input) {
b.Inputs = append(b.Inputs, inputs...)
}
// func (b *CommandStepBuilder) DependOn(name string) {
// b.DependsOn = append(b.DependsOn, name)
// }
func (b *CommandStepBuilder) AddVariables(variables map[string]string) {
maps.Copy(b.Variables, variables)
}
@@ -113,8 +114,6 @@ func (b *CommandStepBuilder) Name() string {
func (b *CommandStepBuilder) Build(options *BuildStepOptions) (*plan.Step, error) {
step := plan.NewStep(b.DisplayName)
// step.DependsOn = utils.RemoveDuplicates(b.DependsOn)
// step.Outputs = b.Outputs
step.Inputs = b.Inputs
step.Commands = b.Commands
step.Assets = b.Assets
+21 -38
View File
@@ -16,10 +16,6 @@ import (
"github.com/railwayapp/railpack/core/utils"
)
const (
DefaultBaseImage = "ghcr.io/railwayapp/railpack-runtime-base:latest"
)
type BuildStepOptions struct {
ResolvedPackages map[string]*resolver.ResolvedPackage
Caches *CacheContext
@@ -37,7 +33,6 @@ type GenerateContext struct {
BaseImage string
Steps []StepBuilder
Deploy *DeployBuilder
Start StartContext
Caches *CacheContext
Secrets []string
@@ -58,16 +53,14 @@ func NewGenerateContext(app *a.App, env *a.Environment) (*GenerateContext, error
}
return &GenerateContext{
App: app,
Env: env,
BaseImage: DefaultBaseImage,
Steps: make([]StepBuilder, 0),
Start: *NewStartContext(),
Deploy: NewDeployBuilder(),
Caches: NewCacheContext(),
Secrets: []string{},
Metadata: NewMetadata(),
Resolver: resolver,
App: app,
Env: env,
Steps: make([]StepBuilder, 0),
Deploy: NewDeployBuilder(),
Caches: NewCacheContext(),
Secrets: []string{},
Metadata: NewMetadata(),
Resolver: resolver,
}, nil
}
@@ -118,9 +111,7 @@ func (c *GenerateContext) Generate() (*plan.BuildPlan, map[string]*resolver.Reso
}
// Generate the plan based on the context and resolved packages
buildPlan := plan.NewBuildPlan()
buildPlan.BaseImage = c.BaseImage
buildStepOptions := &BuildStepOptions{
ResolvedPackages: resolvedPackages,
@@ -138,17 +129,9 @@ func (c *GenerateContext) Generate() (*plan.BuildPlan, map[string]*resolver.Reso
}
buildPlan.Caches = c.Caches.Caches
buildPlan.Secrets = utils.RemoveDuplicates(c.Secrets)
buildPlan.Deploy = *c.Deploy.Build()
buildPlan.Start.BaseImage = c.Start.BaseImage
buildPlan.Start.Command = c.Start.Command
buildPlan.Start.Outputs = utils.RemoveDuplicates(c.Start.outputs)
buildPlan.Start.Paths = utils.RemoveDuplicates(c.Start.paths)
buildPlan.Start.Variables = c.Start.variables
return buildPlan, resolvedPackages, nil
}
@@ -237,23 +220,23 @@ func (c *GenerateContext) ApplyConfig(config *config.Config) error {
c.Secrets = append(c.Secrets, config.Secrets...)
// Start config
if config.Start.BaseImage != "" {
c.Start.BaseImage = config.Start.BaseImage
}
// if config.Start.BaseImage != "" {
// c.Start.BaseImage = config.Start.BaseImage
// }
if config.Start.Command != "" {
c.Start.Command = config.Start.Command
}
// if config.Start.Command != "" {
// c.Start.Command = config.Start.Command
// }
if config.Start.Variables != nil {
c.Start.AddEnvVars(config.Start.Variables)
}
// if config.Start.Variables != nil {
// c.Start.AddEnvVars(config.Start.Variables)
// }
c.Start.AddPaths(config.Start.Paths)
// c.Start.AddPaths(config.Start.Paths)
if len(config.Start.Outputs) > 0 {
c.Start.outputs = config.Start.Outputs
}
// if len(config.Start.Outputs) > 0 {
// c.Start.outputs = config.Start.Outputs
// }
return nil
}
+2 -2
View File
@@ -3,14 +3,14 @@ package generate
import "github.com/railwayapp/railpack/core/plan"
type DeployBuilder struct {
Inputs []plan.StepInput
Inputs []plan.Input
StartCmd string
Variables map[string]string
}
func NewDeployBuilder() *DeployBuilder {
return &DeployBuilder{
Inputs: []plan.StepInput{},
Inputs: []plan.Input{},
StartCmd: "",
Variables: map[string]string{},
}
+4 -7
View File
@@ -6,10 +6,9 @@ import (
)
type ImageStepBuilder struct {
DisplayName string
Resolver *resolver.Resolver
Packages []*resolver.PackageRef
// Outputs []string
DisplayName string
Resolver *resolver.Resolver
Packages []*resolver.PackageRef
ResolveStepImage func(options *BuildStepOptions) string
}
@@ -49,12 +48,10 @@ func (b *ImageStepBuilder) Build(options *BuildStepOptions) (*plan.Step, error)
step := plan.NewStep(b.DisplayName)
image := b.ResolveStepImage(options)
step.Inputs = []plan.StepInput{
step.Inputs = []plan.Input{
plan.NewImageInput(image),
}
// step.StartingImage = b.ResolveStepImage(options)
// step.Outputs = b.Outputs
step.Secrets = []string{}
return step, nil
+17 -13
View File
@@ -23,10 +23,10 @@ type MiseStepBuilder struct {
MisePackages []*resolver.PackageRef
SupportingMiseFiles []string
Assets map[string]string
// Outputs []string
Variables map[string]string
app *a.App
env *a.Environment
Inputs []plan.Input
Variables map[string]string
app *a.App
env *a.Environment
}
func (c *GenerateContext) newMiseStepBuilder() *MiseStepBuilder {
@@ -36,10 +36,10 @@ func (c *GenerateContext) newMiseStepBuilder() *MiseStepBuilder {
MisePackages: []*resolver.PackageRef{},
SupportingAptPackages: []string{},
Assets: map[string]string{},
// Outputs: []string{"/mise/shims", "/mise/installs", "/usr/local/bin/mise", "/etc/mise/config.toml", "/root/.local/state/mise"},
Variables: map[string]string{},
app: c.App,
env: c.Env,
Inputs: []plan.Input{},
Variables: map[string]string{},
app: c.App,
env: c.Env,
}
c.Steps = append(c.Steps, step)
@@ -51,6 +51,10 @@ func (b *MiseStepBuilder) AddSupportingAptPackage(name string) {
b.SupportingAptPackages = append(b.SupportingAptPackages, name)
}
func (b *MiseStepBuilder) AddInput(input plan.Input) {
b.Inputs = append(b.Inputs, input)
}
func (b *MiseStepBuilder) Default(name string, defaultVersion string) resolver.PackageRef {
for _, pkg := range b.MisePackages {
if pkg.Name == name {
@@ -72,7 +76,10 @@ func (b *MiseStepBuilder) Name() string {
}
func (b *MiseStepBuilder) GetOutputPaths() []string {
return []string{"/mise/shims", "/mise/installs", "/usr/local/bin/mise", "/etc/mise/config.toml", "/root/.local/state/mise"}
supportingMiseConfigFiles := b.GetSupportingMiseConfigFiles(b.app.Source)
files := []string{"/mise/shims", "/mise/installs", "/usr/local/bin/mise", "/etc/mise/config.toml", "/root/.local/state/mise"}
files = append(files, supportingMiseConfigFiles...)
return files
}
func (b *MiseStepBuilder) Build(options *BuildStepOptions) (*plan.Step, error) {
@@ -84,7 +91,7 @@ func (b *MiseStepBuilder) Build(options *BuildStepOptions) (*plan.Step, error) {
// step.StartingImage = BuilderBaseImage
step.Inputs = []plan.StepInput{
step.Inputs = []plan.Input{
plan.NewImageInput(plan.RAILPACK_BUILDER_IMAGE),
}
@@ -111,9 +118,6 @@ func (b *MiseStepBuilder) Build(options *BuildStepOptions) (*plan.Step, error) {
step.AddCommands([]plan.Command{
plan.NewCopyCommand(file, "/app/"+file),
})
// We want to make sure the file is copied into the next step
// b.Outputs = append(b.Outputs, "/app/"+file)
}
// Setup apt commands
-29
View File
@@ -1,29 +0,0 @@
package generate
type StartContext struct {
BaseImage string
Command string
outputs []string
paths []string
variables map[string]string
}
func NewStartContext() *StartContext {
return &StartContext{
variables: make(map[string]string),
}
}
func (s *StartContext) AddEnvVars(envVars map[string]string) {
for k, v := range envVars {
s.variables[k] = v
}
}
func (s *StartContext) AddPaths(paths []string) {
s.paths = append(s.paths, paths...)
}
func (s *StartContext) AddOutputs(outputs []string) {
s.outputs = append(s.outputs, outputs...)
}
+12 -23
View File
@@ -6,41 +6,30 @@ const (
)
type BuildPlan struct {
BaseImage string `json:"baseImage,omitempty"`
Steps []Step `json:"steps,omitempty"`
Start Start `json:"start,omitempty"`
Caches map[string]*Cache `json:"caches,omitempty"`
Secrets []string `json:"secrets,omitempty"`
Deploy Deploy `json:"deploy,omitempty"`
Steps []Step `json:"steps,omitempty"`
Caches map[string]*Cache `json:"caches,omitempty"`
Secrets []string `json:"secrets,omitempty"`
Deploy Deploy `json:"deploy,omitempty"`
}
type Deploy struct {
Inputs []StepInput `json:"inputs,omitempty"`
StartCmd string `json:"startCommand,omitempty"`
Variables map[string]string `json:"variables,omitempty"`
}
type Start struct {
// The image to use for the container runtime
BaseImage string `json:"baseImage,omitempty"`
// The inputs for the deploy step
Inputs []Input `json:"inputs,omitempty"`
// The command to run in the container
Command string `json:"cmd,omitempty"`
StartCmd string `json:"startCommand,omitempty"`
// Outputs to be copied from the container to the host
Outputs []string `json:"outputs,omitempty"`
// $PATHs to be prefixed to the container's base $PATH
Paths []string `json:"paths,omitempty"`
// Environment variables to set in the container. These are not available at build time.
// The variables available to this step. The key is the name of the variable that is referenced in a variable command
Variables map[string]string `json:"variables,omitempty"`
// The paths to prepend to the $PATH environment variable
Paths []string `json:"paths,omitempty"`
}
func NewBuildPlan() *BuildPlan {
return &BuildPlan{
Steps: []Step{},
Start: Start{},
Deploy: Deploy{},
Caches: make(map[string]*Cache),
Secrets: []string{},
}
+10 -22
View File
@@ -6,7 +6,7 @@ import (
"github.com/invopop/jsonschema"
)
type StepInput struct {
type Input struct {
Image string `json:"image,omitempty"`
Step string `json:"step,omitempty"`
Local bool `json:"local,omitempty"`
@@ -19,8 +19,8 @@ type InputOptions struct {
Exclude []string
}
func NewStepInput(stepName string, options ...InputOptions) StepInput {
input := StepInput{
func NewStepInput(stepName string, options ...InputOptions) Input {
input := Input{
Step: stepName,
}
@@ -32,8 +32,8 @@ func NewStepInput(stepName string, options ...InputOptions) StepInput {
return input
}
func NewImageInput(image string, options ...InputOptions) StepInput {
input := StepInput{
func NewImageInput(image string, options ...InputOptions) Input {
input := Input{
Image: image,
}
@@ -44,18 +44,18 @@ func NewImageInput(image string, options ...InputOptions) StepInput {
return input
}
func RuntimeImageInput() StepInput {
func RuntimeImageInput() Input {
return NewImageInput("ghcr.io/railwayapp/railpack-runtime-base:latest")
}
func NewLocalInput(path string) StepInput {
return StepInput{
func NewLocalInput(path string) Input {
return Input{
Local: true,
Include: []string{path},
}
}
func (i *StepInput) String() string {
func (i *Input) String() string {
bytes, _ := json.Marshal(i)
return string(bytes)
}
@@ -64,10 +64,7 @@ type Step struct {
// The name of the step
Name string `json:"name,omitempty" jsonschema:"description=The name of the step"`
Inputs []StepInput `json:"inputs,omitempty" jsonschema:"description=The inputs for this step"`
// The steps that this step depends on. The step will only run after all the steps in DependsOn have run
// DependsOn []string `json:"dependsOn,omitempty" jsonschema:"description=The steps that this step depends on. The step will only run after all the steps in DependsOn have run"`
Inputs []Input `json:"inputs,omitempty" jsonschema:"description=The inputs for this step"`
// The commands to run in this step
Commands []Command `json:"commands,omitempty" jsonschema:"description=The commands to run in this step"`
@@ -86,11 +83,6 @@ type Step struct {
// The caches available to all commands in this step. Each cache must refer to a cache at the top level of the plan
Caches []string `json:"caches,omitempty" jsonschema:"description=The caches available to all commands in this step. Each cache must refer to a cache at the top level of the plan"`
// The base image that will be used for this step
// If empty (default), the base image will be the one from the previous step
// Only set this if you don't want to reuse any part of the file system from the previous step
// StartingImage string `json:"startingImage,omitempty" jsonschema:"description=The base image that will be used for this step. If empty (default), the base image will be the one from the previous step. Only set this if you don't want to reuse any part of the file system from the previous step"`
}
func NewStep(name string) *Step {
@@ -102,10 +94,6 @@ func NewStep(name string) *Step {
}
}
// func (s *Step) DependOn(name string) {
// s.DependsOn = append(s.DependsOn, name)
// }
func (s *Step) AddCommands(commands []Command) {
if s.Commands == nil {
s.Commands = []Command{}
+3 -3
View File
@@ -144,10 +144,10 @@ func FormatBuildResult(br *BuildResult, options ...PrintOptions) string {
}
}
if br.Plan.Start.Command != "" {
output.WriteString(sectionHeaderStyle.MarginTop(1).Render("Start"))
if br.Plan.Deploy.StartCmd != "" {
output.WriteString(sectionHeaderStyle.MarginTop(1).Render("Deploy"))
output.WriteString("\n")
output.WriteString(fmt.Sprintf("%s %s", commandPrefixStyle.Render("$"), commandStyle.Render(br.Plan.Start.Command)))
output.WriteString(fmt.Sprintf("%s %s", commandPrefixStyle.Render("$"), commandStyle.Render(br.Plan.Deploy.StartCmd)))
}
if opts.Metadata && br.Metadata != nil && len(br.Metadata) > 0 {
+7 -7
View File
@@ -47,19 +47,19 @@ func (p *GoProvider) Plan(ctx *generate.GenerateContext) error {
}
if build != nil {
ctx.Start.Command = fmt.Sprintf("./%s", GO_BINARY_NAME)
ctx.Deploy.StartCmd = fmt.Sprintf("./%s", GO_BINARY_NAME)
if !p.hasCGOEnabled(ctx) {
ctx.Start.AddOutputs([]string{GO_BINARY_NAME})
// ctx.Deploy.Outputs = []string{GO_BINARY_NAME}
ctx.Start.BaseImage = START_IMAGE
if startImage, _ := ctx.Env.GetConfigVariable("START_IMAGE"); startImage != "" {
ctx.Start.BaseImage = startImage
}
// ctx.Start.BaseImage = START_IMAGE
// if startImage, _ := ctx.Env.GetConfigVariable("START_IMAGE"); startImage != "" {
// ctx.Start.BaseImage = startImage
// }
}
if p.isGin(ctx) {
ctx.Start.AddEnvVars(map[string]string{"GIN_MODE": "release"})
ctx.Deploy.Variables["GIN_MODE"] = "release"
}
}
+6 -2
View File
@@ -76,12 +76,11 @@ func (p *NodeProvider) Plan(ctx *generate.GenerateContext) error {
ctx.Deploy.StartCmd = p.GetStartCommand(ctx)
maps.Copy(ctx.Deploy.Variables, p.GetNodeEnvVars(ctx))
ctx.Deploy.Inputs = append(ctx.Deploy.Inputs, []plan.StepInput{
ctx.Deploy.Inputs = append(ctx.Deploy.Inputs, []plan.Input{
plan.NewImageInput(plan.RAILPACK_RUNTIME_IMAGE),
plan.NewStepInput(miseStep.Name(), plan.InputOptions{
Include: miseStep.GetOutputPaths(),
}),
plan.NewLocalInput("."),
plan.NewStepInput(build.Name(), plan.InputOptions{
Include: []string{"."},
Exclude: []string{"node_modules"},
@@ -89,6 +88,7 @@ func (p *NodeProvider) Plan(ctx *generate.GenerateContext) error {
plan.NewStepInput(prune.Name(), plan.InputOptions{
Include: []string{"/app/node_modules"}, // we only wanted the pruned node_modules
}),
plan.NewLocalInput("."),
}...)
return nil
@@ -130,6 +130,10 @@ func (p *NodeProvider) Build(ctx *generate.GenerateContext, build *generate.Comm
}
func (p *NodeProvider) PruneNodeDeps(ctx *generate.GenerateContext, prune *generate.CommandStepBuilder) {
if ctx.Env.IsConfigVariableTruthy("NO_PRUNE") {
return
}
prune.Variables["NPM_CONFIG_PRODUCTION"] = "true"
prune.Secrets = []string{}
p.packageManager.PruneDeps(ctx, prune)
+86 -63
View File
@@ -8,6 +8,7 @@ import (
"github.com/railwayapp/railpack/core/generate"
"github.com/railwayapp/railpack/core/plan"
"github.com/railwayapp/railpack/core/providers/node"
"github.com/stretchr/objx"
)
@@ -31,66 +32,19 @@ func (p *PhpProvider) Initialize(ctx *generate.GenerateContext) error {
}
func (p *PhpProvider) Plan(ctx *generate.GenerateContext) error {
_, err := p.phpImagePackage(ctx)
phpImageStep, err := p.phpImagePackage(ctx)
if err != nil {
return err
}
// Install nginx
// Nginx
nginxPackages := ctx.NewAptStepBuilder("nginx")
nginxPackages.AddInput(plan.NewStepInput(phpImageStep.Name()))
nginxPackages.Packages = []string{"nginx", "git", "zip", "unzip"}
// nginxPackages.DependsOn = []string{imageStep.DisplayName}
// Install composer
if _, err := p.readComposerJson(ctx); err == nil {
install := ctx.NewCommandStep("install")
install.AddCache(ctx.Caches.AddCache("composer", "/opt/cache/composer"))
install.AddEnvVars(map[string]string{"COMPOSER_CACHE_DIR": "/opt/cache/composer"})
install.AddCommands([]plan.Command{
// Copy composer from the composer image
plan.CopyCommand{Image: "composer:latest", Src: "/usr/bin/composer", Dest: "/usr/bin/composer"},
plan.NewCopyCommand("."),
plan.NewExecCommand("composer install --ignore-platform-reqs"),
})
// install.DependsOn = []string{nginxPackages.DisplayName}
}
// Install node
// nodeProvider := node.NodeProvider{}
// isNode := ctx.App.HasMatch("package.json")
// if packageJson, err := nodeProvider.GetPackageJson(ctx.App); isNode && err == nil && packageJson != nil {
// ctx.EnterSubContext("node")
// nodePackages, err := nodeProvider.InstallMisePackages(ctx)
// if err != nil {
// return err
// }
// // nodePackages.DependsOn = []string{imageStep.DisplayName}
// nodeInstall, err := nodeProvider.Install(ctx, nodePackages, packageJson)
// if err != nil {
// return err
// }
// nodeBuild, err := nodeProvider.Build(ctx, nodeInstall, packageJson)
// if err != nil {
// return err
// }
// fmt.Printf("nodeBuild: %+v\n", nodeBuild)
// // Only copy the changes to the app, not the entire rest of the file system
// // nodeBuild.Outputs = []string{"/app"}
// ctx.ExitSubContext()
// }
// Setup nginx
nginxSetup := ctx.NewCommandStep("nginx:setup")
// nginxSetup.DependsOn = []string{nginxPackages.DisplayName}
nginxSetup.AddCommands([]plan.Command{
nginx := ctx.NewCommandStep("nginx")
nginx.AddInput(plan.NewStepInput(nginxPackages.Name()))
nginx.AddCommands([]plan.Command{
plan.NewFileCommand("/etc/nginx/railpack.conf", "nginx.conf", plan.FileOptions{CustomName: "create nginx config"}),
plan.NewExecCommand("nginx -t -c /etc/nginx/railpack.conf"),
plan.NewFileCommand("/etc/php-fpm.conf", "php-fpm.conf", plan.FileOptions{CustomName: "create php-fpm config"}),
@@ -100,22 +54,91 @@ func (p *PhpProvider) Plan(ctx *generate.GenerateContext) error {
}),
})
if p.usesLaravel(ctx) {
nginxSetup.AddEnvVars(map[string]string{"IS_LARAVEL": "true"})
}
nginxSetup.Assets["start-nginx.sh"] = startNginxScriptAsset
nginx.Assets["start-nginx.sh"] = startNginxScriptAsset
configFiles, err := p.getConfigFiles(ctx)
if err != nil {
return fmt.Errorf("failed to get config files: %w", err)
}
nginxSetup.Assets["nginx.conf"] = configFiles.NginxConf
nginxSetup.Assets["nginx.conf"] = configFiles.NginxConf
nginxSetup.Assets["php-fpm.conf"] = configFiles.PhpFpmConf
nginx.Assets["nginx.conf"] = configFiles.NginxConf
nginx.Assets["nginx.conf"] = configFiles.NginxConf
nginx.Assets["php-fpm.conf"] = configFiles.PhpFpmConf
ctx.Start.Command = "bash /start-nginx.sh"
ctx.Start.AddOutputs([]string{"."})
// Composer
composer := ctx.NewCommandStep("install:composer")
composer.AddInput(plan.NewStepInput(nginx.Name()))
if _, err := p.readComposerJson(ctx); err == nil {
composer.AddCache(ctx.Caches.AddCache("composer", "/opt/cache/composer"))
composer.AddEnvVars(map[string]string{"COMPOSER_CACHE_DIR": "/opt/cache/composer"})
composer.AddCommands([]plan.Command{
// Copy composer from the composer image
plan.CopyCommand{Image: "composer:latest", Src: "/usr/bin/composer", Dest: "/usr/bin/composer"},
plan.NewCopyCommand("."),
plan.NewExecCommand("composer install --ignore-platform-reqs"),
})
}
// Node (if necessary)
nodeProvider := node.NodeProvider{}
isNode, err := nodeProvider.Detect(ctx)
if err != nil {
return err
}
if isNode {
nodeProvider.Initialize(ctx)
miseStep := ctx.GetMiseStepBuilder()
nodeProvider.InstallMisePackages(ctx, miseStep)
install := ctx.NewCommandStep("install:node")
install.AddInput(plan.NewStepInput(miseStep.Name()))
nodeProvider.InstallNodeDeps(ctx, install)
prune := ctx.NewCommandStep("prune:node")
prune.AddInput(plan.NewStepInput(install.Name()))
nodeProvider.PruneNodeDeps(ctx, prune)
build := ctx.NewCommandStep("build")
build.Inputs = []plan.Input{
plan.NewStepInput(composer.Name()),
// Include the app and mise packages (node, pnpm, etc.)
plan.NewStepInput(install.Name(), plan.InputOptions{
Include: append([]string{"."}, miseStep.GetOutputPaths()...),
}),
}
nodeProvider.Build(ctx, build)
ctx.Deploy.Inputs = []plan.Input{
plan.NewStepInput(composer.Name()),
plan.NewStepInput(build.Name(), plan.InputOptions{
Include: []string{"."},
Exclude: []string{"node_modules"},
}),
plan.NewStepInput(prune.Name(), plan.InputOptions{
Include: []string{"/app/node_modules"},
}),
plan.NewLocalInput("."),
}
} else {
// A manual build command will go here
build := ctx.NewCommandStep("build")
build.AddInput(plan.NewStepInput(composer.Name()))
ctx.Deploy.Inputs = []plan.Input{
// plan.NewStepInput(build.Name()),
plan.NewStepInput(composer.Name()),
plan.NewLocalInput("."),
}
}
ctx.Deploy.StartCmd = "bash /start-nginx.sh"
if p.usesLaravel(ctx) {
ctx.Deploy.Variables["IS_LARAVEL"] = "true"
}
return nil
}
+2 -2
View File
@@ -22,9 +22,9 @@ func (p *ProcfileProvider) Plan(ctx *generate.GenerateContext) (bool, error) {
workerCommand := parsedProcfile["worker"]
if webCommand != "" {
ctx.Start.Command = webCommand
ctx.Deploy.StartCmd = webCommand
} else if workerCommand != "" {
ctx.Start.Command = workerCommand
ctx.Deploy.StartCmd = workerCommand
}
return false, nil
+1 -1
View File
@@ -14,5 +14,5 @@ func TestProcfile(t *testing.T) {
_, err := provider.Plan(ctx)
require.NoError(t, err)
require.Equal(t, "gunicorn --bind 0.0.0.0:3333 main:app", ctx.Start.Command)
require.Equal(t, "gunicorn --bind 0.0.0.0:3333 main:app", ctx.Deploy.StartCmd)
}
+2 -3
View File
@@ -53,7 +53,7 @@ func (p *PythonProvider) Plan(ctx *generate.GenerateContext) error {
}
func (p *PythonProvider) start(ctx *generate.GenerateContext) error {
ctx.Start.AddOutputs([]string{"."})
// ctx.Start.AddOutputs([]string{"."})
var startCommand string
@@ -62,14 +62,13 @@ func (p *PythonProvider) start(ctx *generate.GenerateContext) error {
}
if startCommand != "" {
ctx.Start.Command = startCommand
// ctx.Start.Command = startCommand
}
return nil
}
func (p *PythonProvider) install(ctx *generate.GenerateContext) error {
hasRequirements := p.hasRequirements(ctx)
hasPyproject := p.hasPyproject(ctx)
hasPipfile := p.hasPipfile(ctx)
+2 -2
View File
@@ -57,8 +57,8 @@ func (p *StaticfileProvider) Plan(ctx *generate.GenerateContext) error {
// setupStep.DependsOn = append(setupStep.DependsOn, ctx.GetMiseStepBuilder().DisplayName)
ctx.Start.AddOutputs([]string{"."})
ctx.Start.Command = p.CaddyStartCommand(ctx, rootDir)
// ctx.Start.AddOutputs([]string{"."})
// ctx.Start.Command = p.CaddyStartCommand(ctx, rootDir)
return nil
}
+1
View File
@@ -37,6 +37,7 @@ ENV MISE_INSTALL_PATH=/usr/local/bin/mise \
MISE_DATA_DIR=/mise \
MISE_CONFIG_DIR=/mise \
MISE_CACHE_DIR=/mise/cache \
DEBIAN_FRONTEND=noninteractive \
PATH=/mise/shims:$PATH
# Copy mise binary from builder