core/plan/layer.go
core/plan/layer.goBrowse 1970 files
1,318 tokens
5,132 bytes
Token encoding: o200k_base
Snapshot 21a254f
← Back to SKILL.md
1package plan2 3import (4 "encoding/json"5 "fmt"6 "strings"7 8 "github.com/invopop/jsonschema"9)10 11type Layer struct {12 Image string `json:"image,omitempty" jsonschema:"description=The image to use as input"`13 Step string `json:"step,omitempty" jsonschema:"description=The step to use as input"`14 Local bool `json:"local,omitempty" jsonschema:"description=Whether to use local files as input"`15 Spread bool `json:"spread,omitempty" jsonschema:"description=Whether to spread the input"`16 17 Filter18}19 20func NewStepLayer(stepName string, filter ...Filter) Layer {21 input := Layer{22 Step: stepName,23 }24 25 if len(filter) > 0 {26 input.Include = filter[0].Include27 input.Exclude = filter[0].Exclude28 }29 30 return input31}32 33func NewImageLayer(image string, filter ...Filter) Layer {34 input := Layer{35 Image: image,36 }37 38 if len(filter) > 0 {39 input.Include = filter[0].Include40 input.Exclude = filter[0].Exclude41 }42 43 return input44}45 46func NewLocalLayer() Layer {47 return Layer{48 Local: true,49 Filter: NewIncludeFilter([]string{"."}),50 }51}52 53func (i Layer) IsEmpty() bool {54 return i.Step == "" && i.Image == "" && !i.Local && !i.Spread55}56 57func (i Layer) IsSpread() bool {58 return i.Spread59}60 61func (i *Layer) String() string {62 bytes, _ := json.Marshal(i)63 return string(bytes)64}65 66func (i *Layer) DisplayName() string {67 include := strings.Join(i.Include, ", ")68 69 if i.Local {70 return fmt.Sprintf("local %s", include)71 }72 73 if i.Spread {74 return fmt.Sprintf("spread %s", include)75 }76 77 if i.Step != "" {78 return fmt.Sprintf("$%s", i.Step)79 }80 81 if i.Image != "" {82 return i.Image83 }84 85 return fmt.Sprintf("input %s", include)86}87 88// Supports two types of inputs:89//90// Object Notation:91// - Step Layer: {"step": "build", "include": ["src/**/*.go"], "exclude": ["*_test.go"]}92// References the output of a named build step with optional file filtering93// - Image Layer: {"image": "golang:1.21", "include": ["."], "exclude": ["tmp"]}94// Uses a Docker image as input with optional file filtering95// - Local Layer: {"local": true, "include": ["src"], "exclude": ["node_modules"]}96// Uses local files from the build context with filtering97//98// String Shortcuts: ".", "...", "$stepname"99func (i *Layer) UnmarshalJSON(data []byte) error {100 // First try normal JSON unmarshal for object notation101 type Alias Layer102 aux := &struct {103 *Alias104 }{105 Alias: (*Alias)(i),106 }107 if err := json.Unmarshal(data, &aux); err == nil {108 return nil109 }110 111 // If object unmarshaling fails, try string shortcuts112 str := string(data)113 114 // Remove quotes from JSON string115 str = strings.Trim(str, "\"")116 switch str {117 case ".":118 // "." represents a local layer with current directory119 *i = NewLocalLayer()120 return nil121 case "...":122 // Creates a spread layer that expands to include all previous layers' files123 *i = Layer{Spread: true}124 return nil125 default:126 // "$stepname" represents a reference to another step127 if after, ok := strings.CutPrefix(str, "$"); ok {128 stepName := after129 *i = NewStepLayer(stepName)130 return nil131 }132 return fmt.Errorf("invalid input format: %s", str)133 }134}135 136func (Layer) JSONSchema() *jsonschema.Schema {137 // Create common schemas for include/exclude138 includeSchema := &jsonschema.Schema{139 Type: "array",140 Description: "Files or directories to include",141 Items: &jsonschema.Schema{142 Type: "string",143 },144 }145 excludeSchema := &jsonschema.Schema{146 Type: "array",147 Description: "Files or directories to exclude",148 Items: &jsonschema.Schema{149 Type: "string",150 },151 }152 153 // Step input schema154 stepSchema := &jsonschema.Schema{155 Type: "object",156 Properties: jsonschema.NewProperties(),157 }158 stepSchema.Properties.Set("step", &jsonschema.Schema{159 Type: "string",160 Description: "The step to use as input",161 })162 stepSchema.Properties.Set("include", includeSchema)163 stepSchema.Properties.Set("exclude", excludeSchema)164 stepSchema.Required = []string{"step"}165 166 // Image input schema167 imageSchema := &jsonschema.Schema{168 Type: "object",169 Properties: jsonschema.NewProperties(),170 }171 imageSchema.Properties.Set("image", &jsonschema.Schema{172 Type: "string",173 Description: "The image to use as input",174 })175 imageSchema.Properties.Set("include", includeSchema)176 imageSchema.Properties.Set("exclude", excludeSchema)177 imageSchema.Required = []string{"image"}178 179 // Local input schema180 localSchema := &jsonschema.Schema{181 Type: "object",182 Properties: jsonschema.NewProperties(),183 }184 localSchema.Properties.Set("local", &jsonschema.Schema{185 Type: "boolean",186 Description: "Whether to use local files as input",187 })188 localSchema.Properties.Set("include", includeSchema)189 localSchema.Properties.Set("exclude", excludeSchema)190 localSchema.Required = []string{"local"}191 192 // String input schema193 stringSchema := &jsonschema.Schema{194 Type: "string",195 Description: "Strings will be parsed and interpreted as an input. Valid formats are: '.', '...', or '$step'",196 Enum: []any{".", "..."},197 }198 199 availableInputs := []*jsonschema.Schema{stepSchema, imageSchema, localSchema, stringSchema}200 201 return &jsonschema.Schema{202 OneOf: availableInputs,203 }204}205