From 2688046af5a0bd3920e7f0b319c0c2c5b0e2170f Mon Sep 17 00:00:00 2001 From: Sergey Grebenshchikov Date: Sun, 27 Oct 2019 01:13:54 +0200 Subject: [PATCH] Release 1.0.0 --- Makefile | 9 +- README.md | 104 ++++++++++++-------- README.template.md | 114 +++++---------------- examples/Dockerfile.1 | 11 +++ examples/Dockerfile.2 | 8 ++ examples/Dockerfile.3 | 11 +++ main.go | 201 ++++++++++++++------------------------ pkg/dockerfile/analyze.go | 28 ++++++ pkg/dockerfile/expand.go | 61 ++++++++++++ pkg/dockerfile/json.go | 20 ++++ pkg/dockerfile/parse.go | 56 +++++++++++ pkg/dockerfile/types.go | 41 ++++++++ 12 files changed, 410 insertions(+), 254 deletions(-) create mode 100644 examples/Dockerfile.1 create mode 100644 examples/Dockerfile.2 create mode 100644 examples/Dockerfile.3 create mode 100644 pkg/dockerfile/analyze.go create mode 100644 pkg/dockerfile/expand.go create mode 100644 pkg/dockerfile/json.go create mode 100644 pkg/dockerfile/parse.go create mode 100644 pkg/dockerfile/types.go diff --git a/Makefile b/Makefile index 5732048..dd13603 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION = 0.3.5 +VERSION = 1.0.0 APP := dockerfile-json PACKAGES := $(shell go list -f {{.Dir}} ./...) @@ -21,6 +21,13 @@ release: README.md zip README.md: go get github.com/keilerkonzept/$(APP) && README.md zip: release/$(APP)_$(VERSION)_osx_x86_64.tar.gz release/$(APP)_$(VERSION)_windows_x86_64.zip release/$(APP)_$(VERSION)_linux_x86_64.tar.gz release/$(APP)_$(VERSION)_osx_x86_32.tar.gz release/$(APP)_$(VERSION)_windows_x86_32.zip release/$(APP)_$(VERSION)_linux_x86_32.tar.gz release/$(APP)_$(VERSION)_linux_arm64.tar.gz diff --git a/README.md b/README.md index 3a63840..b2380a6 100644 --- a/README.md +++ b/README.md @@ -31,13 +31,17 @@ Or [download the binary for your platform](https://github.com/keilerkonzept/dock ### CLI ```text -dockerfile-json +dockerfile-json [PATHS...] Usage of dockerfile-json: -build-arg value a key/value pair KEY[=VALUE] -expand-build-args expand build args (default true) + -jsonpath string + select parts of the output using JSONPath (https://goessner.net/articles/JsonPath) + -jsonpath-raw + when using JSONPath, output raw strings, not JSON values -quiet suppress log output (stderr) ``` @@ -69,6 +73,8 @@ $ dockerfile-json Dockerfile | jq . "MetaArgs": [ { "Key": "ALPINE_TAG", + "DefaultValue": "3.10", + "ProvidedValue": null, "Value": "3.10" } ], @@ -76,16 +82,19 @@ $ dockerfile-json Dockerfile | jq . { "Name": "build", "BaseName": "alpine:3.10", - "SourceCode": "FROM alpine:$ALPINE_TAG AS build", + "SourceCode": "FROM alpine:${ALPINE_TAG} AS build", + "Platform": "", + "As": "build", + "From": { + "Image": "alpine:3.10" + }, "Commands": [ { + "CmdLine": [ + "echo \"Hello world\" > abc" + ], "Name": "run", - "Command": { - "CmdLine": [ - "echo \"Hello world\" > abc" - ], - "PrependShell": true - } + "PrependShell": true } ] }, @@ -93,44 +102,48 @@ $ dockerfile-json Dockerfile | jq . "Name": "test", "BaseName": "build", "SourceCode": "FROM build AS test", - "FromStage": true, - "FromStageIndex": 0, + "Platform": "", + "As": "test", + "From": { + "Stage": { + "Named": "build", + "Index": 0 + } + }, "Commands": [ { + "CmdLine": [ + "echo \"foo\" > bar" + ], "Name": "run", - "Command": { - "CmdLine": [ - "echo \"foo\" > bar" - ], - "PrependShell": true - } + "PrependShell": true } ] }, { + "Name": "", "BaseName": "scratch", "SourceCode": "FROM scratch", - "FromScratch": true, + "Platform": "", + "From": { + "Scratch": true + }, "Commands": [ { + "Chown": "nobody:nobody", + "From": "build", "Name": "copy", - "Command": { - "SourcesAndDest": [ - "abc", - "." - ], - "From": "", - "Chown": "nobody:nobody" - } + "SourcesAndDest": [ + "abc", + "." + ] }, { + "CmdLine": [ + "echo" + ], "Name": "cmd", - "Command": { - "CmdLine": [ - "echo" - ], - "PrependShell": false - } + "PrependShell": false } ] } @@ -153,7 +166,7 @@ FROM openjdk:jre-alpine ``` ```sh -$ dockerfile-json Dockerfile | jq '.Stages[] | .Name | select(.)' +$ dockerfile-json --jsonpath=..As Dockerfile ``` ```json "build" @@ -179,19 +192,32 @@ FROM $APP_BASE #### Expand build args, omit stage aliases and `scratch` +Using `jq`: ```sh $ dockerfile-json Dockerfile | - jq '.Stages[] | select((.FromStage or .FromScratch)|not) | .BaseName' + jq '.Stages[] | select(.From | .Stage or .Scratch | not) | .BaseName' +``` +Using `--jsonpath`: +```sh + +$ dockerfile-json --jsonpath=..Image Dockerfile ``` ```json "alpine:3.10" ``` +Using `--jsonpath`, `--jsonpath-raw` output: +```sh +$ dockerfile-json --jsonpath=..Image --jsonpath-raw Dockerfile +``` +```json +alpine:3.10 +``` + #### Set build args, omit stage aliases and `scratch` ```sh -$ dockerfile-json --build-arg ALPINE_TAG=hello-world Dockerfile | - jq '.Stages[] | select((.FromStage or .FromScratch)|not) | .BaseName' +$ dockerfile-json --build-arg ALPINE_TAG=hello-world --jsonpath=..Image Dockerfile ``` ```json "alpine:hello-world" @@ -200,7 +226,7 @@ $ dockerfile-json --build-arg ALPINE_TAG=hello-world Dockerfile | #### Expand build args, include all base names ```sh -$ dockerfile-json Dockerfile | jq '.Stages[] | .BaseName' +$ dockerfile-json --jsonpath=..BaseName Dockerfile ``` ```json "alpine:3.10" @@ -211,10 +237,10 @@ $ dockerfile-json Dockerfile | jq '.Stages[] | .BaseName' #### Ignore build args, include all base names ```sh -$ dockerfile-json --expand-build-args=false Dockerfile | jq '.Stages[] | .BaseName' +$ dockerfile-json --expand-build-args=false --jsonpath=..BaseName Dockerfile ``` ```json -"alpine:$ALPINE_TAG" +"alpine:${ALPINE_TAG}" "build" -"$APP_BASE" +"${APP_BASE}" ``` diff --git a/README.template.md b/README.template.md index 1baee40..f462bda 100644 --- a/README.template.md +++ b/README.template.md @@ -31,7 +31,7 @@ Or [download the binary for your platform](https://github.com/keilerkonzept/${AP ### CLI ```text -${APP} +${APP} [PATHS...] ${USAGE} ``` @@ -59,77 +59,7 @@ CMD ["echo"] $ dockerfile-json Dockerfile | jq . ``` ```json -{ - "MetaArgs": [ - { - "Key": "ALPINE_TAG", - "Value": "3.10" - } - ], - "Stages": [ - { - "Name": "build", - "BaseName": "alpine:3.10", - "SourceCode": "FROM alpine:${ALPINE_TAG} AS build", - "Commands": [ - { - "Name": "run", - "Command": { - "CmdLine": [ - "echo \"Hello world\" > abc" - ], - "PrependShell": true - } - } - ] - }, - { - "Name": "test", - "BaseName": "build", - "SourceCode": "FROM build AS test", - "FromStage": true, - "FromStageIndex": 0, - "Commands": [ - { - "Name": "run", - "Command": { - "CmdLine": [ - "echo \"foo\" > bar" - ], - "PrependShell": true - } - } - ] - }, - { - "BaseName": "scratch", - "SourceCode": "FROM scratch", - "FromScratch": true, - "Commands": [ - { - "Name": "copy", - "Command": { - "SourcesAndDest": [ - "abc", - "." - ], - "From": "", - "Chown": "nobody:nobody" - } - }, - { - "Name": "cmd", - "Command": { - "CmdLine": [ - "echo" - ], - "PrependShell": false - } - } - ] - } - ] -} +${EXAMPLE_1} ``` ### Extract build stage names @@ -147,11 +77,10 @@ FROM openjdk:jre-alpine ``` ```sh -$ dockerfile-json Dockerfile | jq '.Stages[] | .Name | select(.)' +$ dockerfile-json --jsonpath=..As Dockerfile ``` ```json -"build" -"test" +${EXAMPLE_2} ``` ### Extract base images @@ -173,42 +102,51 @@ FROM ${APP_BASE} #### Expand build args, omit stage aliases and `scratch` +Using `jq`: ```sh $ dockerfile-json Dockerfile | - jq '.Stages[] | select((.FromStage or .FromScratch)|not) | .BaseName' + jq '.Stages[] | select(.From | .Stage or .Scratch | not) | .BaseName' +``` +Using `--jsonpath`: +```sh + +$ dockerfile-json --jsonpath=..Image Dockerfile +``` +```json +${EXAMPLE_3A} +``` + +Using `--jsonpath`, `--jsonpath-raw` output: +```sh +$ dockerfile-json --jsonpath=..Image --jsonpath-raw Dockerfile ``` ```json -"alpine:3.10" +${EXAMPLE_3B} ``` #### Set build args, omit stage aliases and `scratch` ```sh -$ dockerfile-json --build-arg ALPINE_TAG=hello-world Dockerfile | - jq '.Stages[] | select((.FromStage or .FromScratch)|not) | .BaseName' +$ dockerfile-json --build-arg ALPINE_TAG=hello-world --jsonpath=..Image Dockerfile ``` ```json -"alpine:hello-world" +${EXAMPLE_4} ``` #### Expand build args, include all base names ```sh -$ dockerfile-json Dockerfile | jq '.Stages[] | .BaseName' +$ dockerfile-json --jsonpath=..BaseName Dockerfile ``` ```json -"alpine:3.10" -"build" -"scratch" +${EXAMPLE_5} ``` #### Ignore build args, include all base names ```sh -$ dockerfile-json --expand-build-args=false Dockerfile | jq '.Stages[] | .BaseName' +$ dockerfile-json --expand-build-args=false --jsonpath=..BaseName Dockerfile ``` ```json -"alpine:${ALPINE_TAG}" -"build" -"${APP_BASE}" +${EXAMPLE_6} ``` diff --git a/examples/Dockerfile.1 b/examples/Dockerfile.1 new file mode 100644 index 0000000..9fdd68d --- /dev/null +++ b/examples/Dockerfile.1 @@ -0,0 +1,11 @@ +ARG ALPINE_TAG=3.10 + +FROM alpine:${ALPINE_TAG} AS build +RUN echo "Hello world" > abc + +FROM build AS test +RUN echo "foo" > bar + +FROM scratch +COPY --from=build --chown=nobody:nobody abc . +CMD ["echo"] diff --git a/examples/Dockerfile.2 b/examples/Dockerfile.2 new file mode 100644 index 0000000..0fd662f --- /dev/null +++ b/examples/Dockerfile.2 @@ -0,0 +1,8 @@ +FROM maven:alpine AS build +# ... + +FROM build AS test +# ... + +FROM openjdk:jre-alpine +# ... diff --git a/examples/Dockerfile.3 b/examples/Dockerfile.3 new file mode 100644 index 0000000..ea6db41 --- /dev/null +++ b/examples/Dockerfile.3 @@ -0,0 +1,11 @@ +ARG ALPINE_TAG=3.10 +ARG APP_BASE=scratch + +FROM alpine:${ALPINE_TAG} AS build +# ... + +FROM build +# ... + +FROM ${APP_BASE} +# ... diff --git a/main.go b/main.go index d4835f8..7ab559e 100644 --- a/main.go +++ b/main.go @@ -9,19 +9,18 @@ import ( "os" "path/filepath" - "github.com/moby/buildkit/frontend/dockerfile/instructions" - "github.com/moby/buildkit/frontend/dockerfile/parser" + "github.com/keilerkonzept/dockerfile-json/pkg/dockerfile" + "github.com/yalp/jsonpath" ) -type dockerfile struct { - MetaArgs []instructions.ArgCommand - Stages []instructions.Stage -} - var config struct { - Quiet bool - Expand bool - BuildArgs AssignmentsMap + Quiet bool + Expand bool + JSONPathString string + JSONPath jsonpath.FilterFunc + JSONPathRaw bool + BuildArgs AssignmentsMap + NonzeroExit bool } var name = "dockerfile-json" @@ -36,15 +35,32 @@ func init() { config.Expand = true flag.BoolVar(&config.Quiet, "quiet", config.Quiet, "suppress log output (stderr)") flag.BoolVar(&config.Expand, "expand-build-args", config.Expand, "expand build args") + flag.StringVar(&config.JSONPathString, "jsonpath", config.JSONPathString, "select parts of the output using JSONPath (https://goessner.net/articles/JsonPath)") + flag.BoolVar(&config.JSONPathRaw, "jsonpath-raw", config.JSONPathRaw, "when using JSONPath, output raw strings, not JSON values") flag.Var(&config.BuildArgs, "build-arg", config.BuildArgs.Help()) flag.Parse() if config.Quiet { - jsonOut = json.NewEncoder(ioutil.Discard) + log.SetOutput(ioutil.Discard) + } + + if flag.NArg() == 0 { + flag.Usage() + } + + if jsonPathString := config.JSONPathString; jsonPathString != "" { + if jsonPathString[0] != '$' { + jsonPathString = "$" + jsonPathString + } + jsonPath, err := jsonpath.Prepare(jsonPathString) + if err != nil { + log.Fatalf("parse jsonpath %s: %v", jsonPathString, err) + } + config.JSONPath = jsonPath } } -func buildArgEnvExpander() instructions.SingleWordExpander { +func buildArgEnvExpander() dockerfile.SingleWordExpander { env := make(map[string]string, len(config.BuildArgs.Values)) for key, value := range config.BuildArgs.Values { if value != nil { @@ -64,134 +80,67 @@ func buildArgEnvExpander() instructions.SingleWordExpander { } func main() { - var dockerfiles []dockerfile + var dockerfiles []*dockerfile.Dockerfile for _, path := range flag.Args() { - func() { - f, err := os.Open(path) - defer f.Close() - if err != nil { - log.Printf("error: %q: %v", path, err) - return - } - result, err := parser.Parse(f) - if err != nil { - log.Printf("error: parse %q: %v", path, err) - return - } - stages, metaArgs, err := instructions.Parse(result.AST) - if err != nil { - log.Printf("error: parse %q: %v", path, err) - return - } - dockerfile := dockerfile{ - MetaArgs: metaArgs, - Stages: stages, - } - dockerfiles = append(dockerfiles, dockerfile) - }() + dockerfile, err := dockerfile.Parse(path) + if err != nil { + log.Printf("error: parse %q: %v", path, err) + config.NonzeroExit = true + continue + } + dockerfiles = append(dockerfiles, dockerfile) } if config.Expand { + env := buildArgEnvExpander() for _, dockerfile := range dockerfiles { - dockerfile.expand(buildArgEnvExpander()) + dockerfile.Expand(env) } } - - type outCommand struct { - Name string - instructions.Command - } - - type outStage struct { - Name string `json:",omitempty"` - BaseName string - SourceCode string - Platform string `json:",omitempty"` - FromStage bool `json:",omitempty"` - FromStageIndex *int `json:",omitempty"` - FromScratch bool `json:",omitempty"` - Commands []outCommand - } - - type outDockerfile struct { - MetaArgs []instructions.ArgCommand - Stages []outStage - } - - for _, dockerfile := range dockerfiles { - var out outDockerfile - out.MetaArgs = dockerfile.MetaArgs - seenStageNames := make(map[string]int) - for i, stage := range dockerfile.Stages { - outStage := outStage{ - Name: stage.Name, - BaseName: stage.BaseName, - SourceCode: stage.SourceCode, - Platform: stage.Platform, - } - stageIndex, stageIndexOK := seenStageNames[stage.BaseName] - switch { - case stageIndexOK: - outStage.FromStage = true - outStage.FromStageIndex = &stageIndex - case stage.BaseName == "scratch": - outStage.FromScratch = true - } - if stage.Name != "" { - seenStageNames[stage.Name] = i + switch { + case config.JSONPath != nil: + for _, dockerfile := range dockerfiles { + rawJSON, err := json.Marshal(dockerfile) + if err != nil { + log.Printf("error: evaluate jsonpath: %v", err) + config.NonzeroExit = true + continue } - for _, command := range stage.Commands { - outStage.Commands = append(outStage.Commands, outCommand{ - Name: command.Name(), - Command: command, - }) + var data map[string]interface{} + if err := json.Unmarshal(rawJSON, &data); err != nil { + log.Printf("error: evaluate jsonpath: %v", err) + config.NonzeroExit = true + continue } - out.Stages = append(out.Stages, outStage) - } - jsonOut.Encode(out) - } -} - -func (d *dockerfile) expand(envExpander instructions.SingleWordExpander) { - metaArgsEnvExpander := d.metaArgsEnvExpander(envExpander) - for i, stage := range d.Stages { - d.Stages[i].BaseName = os.Expand(stage.BaseName, func(key string) string { - value, err := metaArgsEnvExpander(key) + result, err := config.JSONPath(data) if err != nil { - return "" + log.Printf("error: evaluate jsonpath: %v", err) + config.NonzeroExit = true + continue } - return value - }) - for i := range stage.Commands { - cmdExpander, ok := stage.Commands[i].(instructions.SupportsSingleWordExpansion) - if ok { - cmdExpander.Expand(metaArgsEnvExpander) + values, isArray := result.([]interface{}) + value, isString := result.(string) + switch { + case isString && config.JSONPathRaw: + fmt.Println(value) + case isArray && config.JSONPathRaw: + for _, value := range values { + fmt.Println(value) + } + case isArray && !config.JSONPathRaw: + for _, value := range values { + jsonOut.Encode(value) + } + default: + jsonOut.Encode(result) } - } - } -} -func (d *dockerfile) metaArgsEnvExpander(envExpander instructions.SingleWordExpander) instructions.SingleWordExpander { - metaArgsEnv := make(map[string]string, len(d.MetaArgs)) - for _, arg := range d.MetaArgs { - if arg.Value != nil { - metaArgsEnv[arg.Key] = *arg.Value - } - if value, err := envExpander(arg.Key); err == nil { - arg.Value = &value - metaArgsEnv[arg.Key] = value } - err := arg.Expand(envExpander) - if err != nil { - continue - } - if arg.Value != nil { - metaArgsEnv[arg.Key] = *arg.Value + default: + for _, dockerfile := range dockerfiles { + jsonOut.Encode(dockerfile) } } - return func(key string) (string, error) { - if value, ok := metaArgsEnv[key]; ok { - return value, nil - } - return "", fmt.Errorf("not defined: $%s", key) + if config.NonzeroExit { + os.Exit(1) } } diff --git a/pkg/dockerfile/analyze.go b/pkg/dockerfile/analyze.go new file mode 100644 index 0000000..015a621 --- /dev/null +++ b/pkg/dockerfile/analyze.go @@ -0,0 +1,28 @@ +package dockerfile + +func (d *Dockerfile) analyzeStages() { + seenStageNames := make(map[string]int) + for i, stage := range d.Stages { + stageIndex, stageIndexOK := seenStageNames[stage.BaseName] + switch { + case stageIndexOK: + stage.From.Stage = &FromStage{ + Named: &stage.BaseName, + Index: stageIndex, + } + case stage.BaseName == "scratch": + stage.From.Scratch = true + stage.From.Image = nil + default: + stage.From.Image = &stage.BaseName + } + if stage.Stage.Name != "" { + stage.Name = &stage.Stage.Name + seenStageNames[stage.Stage.Name] = i + } + for i, command := range stage.Commands { + stage.Commands[i].Name = command.Command.Name() + } + } + return +} diff --git a/pkg/dockerfile/expand.go b/pkg/dockerfile/expand.go new file mode 100644 index 0000000..e01691e --- /dev/null +++ b/pkg/dockerfile/expand.go @@ -0,0 +1,61 @@ +package dockerfile + +import ( + "fmt" + "os" + + "github.com/moby/buildkit/frontend/dockerfile/instructions" +) + +type SingleWordExpander instructions.SingleWordExpander + +func (d *Dockerfile) Expand(env SingleWordExpander) { + d.expand(instructions.SingleWordExpander(env)) + d.analyzeStages() +} + +func (d *Dockerfile) expand(env instructions.SingleWordExpander) { + metaArgsEnvExpander := d.metaArgsEnvExpander(env) + for i, stage := range d.Stages { + d.Stages[i].BaseName = os.Expand(stage.BaseName, func(key string) string { + value, err := metaArgsEnvExpander(key) + if err != nil { + return "" + } + return value + }) + for i := range stage.Commands { + cmdExpander, ok := stage.Commands[i].Command.(instructions.SupportsSingleWordExpansion) + if ok { + cmdExpander.Expand(metaArgsEnvExpander) + } + } + } +} + +func (d *Dockerfile) metaArgsEnvExpander(env instructions.SingleWordExpander) instructions.SingleWordExpander { + metaArgsEnv := make(map[string]string, len(d.MetaArgs)) + for _, arg := range d.MetaArgs { + if defaultValue := arg.DefaultValue; defaultValue != nil { + metaArgsEnv[arg.Key] = *defaultValue + } + if value, err := env(arg.Key); err == nil { + arg.ProvidedValue = &value + metaArgsEnv[arg.Key] = value + arg.Value = &value + } + err := arg.Expand(env) + if err != nil { + continue + } + if arg.Value != nil { + metaArgsEnv[arg.Key] = *arg.ArgCommand.Value + } + } + return func(key string) (string, error) { + if value, ok := metaArgsEnv[key]; ok { + return value, nil + } + return "", fmt.Errorf("not defined: $%s", key) + } +} diff --git a/pkg/dockerfile/json.go b/pkg/dockerfile/json.go new file mode 100644 index 0000000..6918763 --- /dev/null +++ b/pkg/dockerfile/json.go @@ -0,0 +1,20 @@ +package dockerfile + +import ( + "encoding/json" + "fmt" +) + +func (c *Command) MarshalJSON() ([]byte, error) { + rawJSON, err := json.Marshal(c.Command) + if err != nil { + return nil, fmt.Errorf("merge json fields: %v", err) + } + out := map[string]interface{}{ + "Name": c.Name, + } + if err := json.Unmarshal(rawJSON, &out); err != nil { + return nil, fmt.Errorf("merge json fields: %v", err) + } + return json.Marshal(out) +} diff --git a/pkg/dockerfile/parse.go b/pkg/dockerfile/parse.go new file mode 100644 index 0000000..c308296 --- /dev/null +++ b/pkg/dockerfile/parse.go @@ -0,0 +1,56 @@ +package dockerfile + +import ( + "fmt" + "io" + "os" + + "github.com/moby/buildkit/frontend/dockerfile/instructions" + "github.com/moby/buildkit/frontend/dockerfile/parser" +) + +func Parse(path string) (*Dockerfile, error) { + f, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("%q: %v", path, err) + } + defer f.Close() + return ParseReader(f) +} + +func ParseReader(r io.Reader) (*Dockerfile, error) { + result, err := parser.Parse(r) + if err != nil { + return nil, fmt.Errorf("dockerfile/parser.Parse %v", err) + } + stages, metaArgs, err := instructions.Parse(result.AST) + if err != nil { + return nil, fmt.Errorf("dockerfile/instructions.Parse %v", err) + } + var out Dockerfile + for _, metaArg := range metaArgs { + metaArgOut := &MetaArg{ArgCommand: metaArg} + metaArgOut.Key = metaArg.Key + if defaultValue := metaArg.Value; defaultValue != nil { + { + defaultValueCopy := *defaultValue + metaArgOut.DefaultValue = &defaultValueCopy + } + { + defaultValueCopy := *defaultValue + metaArgOut.Value = &defaultValueCopy + } + } + out.MetaArgs = append(out.MetaArgs, metaArgOut) + } + for _, stage := range stages { + outStage := &Stage{Stage: stage} + for _, command := range stage.Commands { + outCommand := &Command{Command: command} + outStage.Commands = append(outStage.Commands, outCommand) + } + out.Stages = append(out.Stages, outStage) + } + out.analyzeStages() + return &out, nil +} diff --git a/pkg/dockerfile/types.go b/pkg/dockerfile/types.go new file mode 100644 index 0000000..62d56f8 --- /dev/null +++ b/pkg/dockerfile/types.go @@ -0,0 +1,41 @@ +package dockerfile + +import ( + "github.com/moby/buildkit/frontend/dockerfile/instructions" +) + +type Dockerfile struct { + MetaArgs []*MetaArg + Stages []*Stage +} + +type MetaArg struct { + instructions.ArgCommand `json:"-"` + Key string + DefaultValue *string `json:","` + ProvidedValue *string `json:","` + Value *string `json:","` +} + +type From struct { + Stage *FromStage `json:",omitempty"` + Scratch bool `json:",omitempty"` + Image *string `json:",omitempty"` +} + +type FromStage struct { + Named *string `json:",omitempty"` + Index int +} + +type Command struct { + instructions.Command + Name string +} + +type Stage struct { + instructions.Stage + Name *string `json:"As,omitempty"` + From From + Commands []*Command +}