diff --git a/cli/azd/.vscode/cspell-azd-dictionary.txt b/cli/azd/.vscode/cspell-azd-dictionary.txt index 24dcab93eaa..515758f08e2 100644 --- a/cli/azd/.vscode/cspell-azd-dictionary.txt +++ b/cli/azd/.vscode/cspell-azd-dictionary.txt @@ -43,6 +43,6 @@ sstore staticwebapp structs swacli -unmarshaling +unmarshalling westus2 yacspin diff --git a/cli/azd/pkg/environment/azd_context.go b/cli/azd/pkg/environment/azd_context.go index 8882ec82cc4..5175eb19d90 100644 --- a/cli/azd/pkg/environment/azd_context.go +++ b/cli/azd/pkg/environment/azd_context.go @@ -78,7 +78,7 @@ func (c *AzdContext) BicepParameters(env string, module string) (map[string]inte var unmarshalled map[string]interface{} err = json.Unmarshal(byts, &unmarshalled) if err != nil { - return nil, fmt.Errorf("unmarshaling parameters file: %w", err) + return nil, fmt.Errorf("unmarshalling parameters file: %w", err) } ret := make(map[string]interface{}) diff --git a/cli/azd/pkg/project/framework_service_docker.go b/cli/azd/pkg/project/framework_service_docker.go index e92f3ab3bf9..7d3c1356aaf 100644 --- a/cli/azd/pkg/project/framework_service_docker.go +++ b/cli/azd/pkg/project/framework_service_docker.go @@ -12,6 +12,12 @@ import ( "github.com/azure/azure-dev/cli/azd/pkg/tools" ) +type DockerProjectOptions struct { + Path string `json:"path"` + Context string `json:"context"` + Platform string `json:"platform"` +} + type dockerProject struct { config *ServiceConfig env *environment.Environment @@ -24,13 +30,15 @@ func (p *dockerProject) RequiredExternalTools() []tools.ExternalTool { } func (p *dockerProject) Package(ctx context.Context, progress chan<- string) (string, error) { - log.Printf("building image for service %s (path: %s)", p.config.Name, p.config.Path()) + dockerOptions := getDockerOptionsWithDefaults(p.config.Docker) + + log.Printf("building image for service %s, cwd: %s, path: %s, context: %s)", p.config.Name, p.config.Path(), dockerOptions.Path, dockerOptions.Context) // Build the container progress <- "Building docker image" - imageId, err := p.docker.Build(ctx, "./Dockerfile", p.config.Path()) + imageId, err := p.docker.Build(ctx, p.config.Path(), dockerOptions.Path, dockerOptions.Platform, dockerOptions.Context) if err != nil { - return "", fmt.Errorf("building container: %s at %s: %w", p.config.Name, p.config.Path(), err) + return "", fmt.Errorf("building container: %s at %s: %w", p.config.Name, dockerOptions.Context, err) } log.Printf("built image %s for %s", imageId, p.config.Name) @@ -51,3 +59,19 @@ func NewDockerProject(config *ServiceConfig, env *environment.Environment, docke framework: framework, } } + +func getDockerOptionsWithDefaults(options DockerProjectOptions) DockerProjectOptions { + if options.Path == "" { + options.Path = "./Dockerfile" + } + + if options.Platform == "" { + options.Platform = "amd64" + } + + if options.Context == "" { + options.Context = "." + } + + return options +} diff --git a/cli/azd/pkg/project/framework_service_docker_test.go b/cli/azd/pkg/project/framework_service_docker_test.go new file mode 100644 index 00000000000..28dd146e005 --- /dev/null +++ b/cli/azd/pkg/project/framework_service_docker_test.go @@ -0,0 +1,141 @@ +package project + +import ( + "context" + "testing" + + "github.com/azure/azure-dev/cli/azd/pkg/environment" + "github.com/azure/azure-dev/cli/azd/pkg/executil" + "github.com/azure/azure-dev/cli/azd/pkg/tools" + "github.com/azure/azure-dev/cli/azd/test/helpers" + "github.com/stretchr/testify/require" +) + +func TestDefaultDockerOptions(t *testing.T) { + const testProj = ` +name: test-proj +metadata: + template: test-proj-template +services: + web: + project: src/web + language: js + host: containerapp +` + + ctx := helpers.CreateTestContext(context.Background(), gblCmdOptions, azCli, mockHttpClient) + env := environment.Environment{Values: make(map[string]string)} + env.SetEnvName("test-env") + + projectConfig, _ := ParseProjectConfig(testProj, &env) + prj, _ := projectConfig.GetProject(ctx, &env) + service := prj.Services[0] + ran := false + + dockerArgs := tools.DockerArgs{ + RunWithResultFn: func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) { + ran = true + + require.Equal(t, []string{ + "build", "-q", + "-f", "./Dockerfile", + "--platform", "amd64", + ".", + }, args.Args) + + return executil.RunResult{ + Stdout: "imageId", + Stderr: "", + ExitCode: 0, + }, nil + }, + } + + docker := tools.NewDocker(dockerArgs) + + progress := make(chan string) + defer close(progress) + + internalFramework := NewNpmProject(service.Config, &env) + status := "" + + go func() { + for value := range progress { + status = value + } + }() + + framework := NewDockerProject(service.Config, &env, docker, internalFramework) + res, err := framework.Package(ctx, progress) + + require.Equal(t, "imageId", res) + require.Nil(t, err) + require.Equal(t, "Building docker image", status) + require.Equal(t, true, ran) +} + +func TestCustomDockerOptions(t *testing.T) { + const testProj = ` +name: test-proj +metadata: + template: test-proj-template +services: + web: + project: src/web + language: js + host: containerapp + docker: + path: ./Dockerfile.dev + context: ../ +` + + ctx := helpers.CreateTestContext(context.Background(), gblCmdOptions, azCli, mockHttpClient) + env := environment.Environment{Values: make(map[string]string)} + env.SetEnvName("test-env") + + projectConfig, _ := ParseProjectConfig(testProj, &env) + prj, _ := projectConfig.GetProject(ctx, &env) + service := prj.Services[0] + ran := false + + dockerArgs := tools.DockerArgs{ + RunWithResultFn: func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) { + ran = true + + require.Equal(t, []string{ + "build", "-q", + "-f", "./Dockerfile.dev", + "--platform", "amd64", + "../", + }, args.Args) + + return executil.RunResult{ + Stdout: "imageId", + Stderr: "", + ExitCode: 0, + }, nil + }, + } + + docker := tools.NewDocker(dockerArgs) + + progress := make(chan string) + defer close(progress) + + internalFramework := NewNpmProject(service.Config, &env) + status := "" + + go func() { + for value := range progress { + status = value + } + }() + + framework := NewDockerProject(service.Config, &env, docker, internalFramework) + res, err := framework.Package(ctx, progress) + + require.Equal(t, "imageId", res) + require.Nil(t, err) + require.Equal(t, "Building docker image", status) + require.Equal(t, true, ran) +} diff --git a/cli/azd/pkg/project/project_config_test.go b/cli/azd/pkg/project/project_config_test.go index 39384e54dc4..7da65934047 100644 --- a/cli/azd/pkg/project/project_config_test.go +++ b/cli/azd/pkg/project/project_config_test.go @@ -109,3 +109,32 @@ services: require.NotNil(t, svc.Scope) } } + +func TestProjectWithCustomDockerOptions(t *testing.T) { + const testProj = ` +name: test-proj +metadata: + template: test-proj-template +services: + web: + project: src/web + language: js + host: containerapp + docker: + path: ./Dockerfile.dev + context: ../ +` + + e := environment.Environment{Values: make(map[string]string)} + e.SetEnvName("test-env") + + projectConfig, err := ParseProjectConfig(testProj, &e) + + require.NotNil(t, projectConfig) + require.Nil(t, err) + + service := projectConfig.Services["web"] + + require.Equal(t, "./Dockerfile.dev", service.Docker.Path) + require.Equal(t, "../", service.Docker.Context) +} diff --git a/cli/azd/pkg/project/service_config.go b/cli/azd/pkg/project/service_config.go index a71616ea65d..6f3403bc572 100644 --- a/cli/azd/pkg/project/service_config.go +++ b/cli/azd/pkg/project/service_config.go @@ -27,6 +27,8 @@ type ServiceConfig struct { OutputPath string `yaml:"dist"` // The infrastructure module name to use for this project ModuleName string `yaml:"moduleName"` + // The optional docker options + Docker DockerProjectOptions `yaml:"docker"` } // Path returns the fully qualified path to the project @@ -65,7 +67,7 @@ func (sc *ServiceConfig) GetServiceTarget(ctx context.Context, env *environment. case "", string(AppServiceTarget): target = NewAppServiceTarget(sc, env, scope, azCli) case string(ContainerAppTarget): - target = NewContainerAppTarget(sc, env, scope, azCli, tools.NewDocker()) + target = NewContainerAppTarget(sc, env, scope, azCli, tools.NewDocker(tools.DockerArgs{})) case string(AzureFunctionTarget): target = NewFunctionAppTarget(sc, env, scope, azCli) case string(StaticWebAppTarget): @@ -95,7 +97,7 @@ func (sc *ServiceConfig) GetFrameworkService(ctx context.Context, env *environme // For containerized applications we use a nested framework service if sc.Host == string(ContainerAppTarget) { sourceFramework := frameworkService - frameworkService = NewDockerProject(sc, env, tools.NewDocker(), sourceFramework) + frameworkService = NewDockerProject(sc, env, tools.NewDocker(tools.DockerArgs{}), sourceFramework) } return &frameworkService, nil diff --git a/cli/azd/pkg/project/service_target_containerapp.go b/cli/azd/pkg/project/service_target_containerapp.go index b3eefaa1866..35a1e80c959 100644 --- a/cli/azd/pkg/project/service_target_containerapp.go +++ b/cli/azd/pkg/project/service_target_containerapp.go @@ -58,7 +58,7 @@ func (at *containerAppTarget) Deploy(ctx context.Context, azdCtx *environment.Az // Tag image. log.Printf("tagging image %s as %s", path, fullTag) progress <- "Tagging image" - if err := at.docker.Tag(ctx, path, fullTag); err != nil { + if err := at.docker.Tag(ctx, at.config.Path(), path, fullTag); err != nil { return ServiceDeploymentResult{}, fmt.Errorf("tagging image: %w", err) } @@ -66,7 +66,7 @@ func (at *containerAppTarget) Deploy(ctx context.Context, azdCtx *environment.Az // Push image. progress <- "Pushing container image" - if err := at.docker.Push(ctx, fullTag); err != nil { + if err := at.docker.Push(ctx, at.config.Path(), fullTag); err != nil { return ServiceDeploymentResult{}, fmt.Errorf("pushing image: %w", err) } diff --git a/cli/azd/pkg/tools/azcli.go b/cli/azd/pkg/tools/azcli.go index f5b8e678370..f87c8acab4a 100644 --- a/cli/azd/pkg/tools/azcli.go +++ b/cli/azd/pkg/tools/azcli.go @@ -259,7 +259,7 @@ func (tok *AzCliAccessToken) UnmarshalJSON(data []byte) error { } if err := json.Unmarshal(data, &wire); err != nil { - return fmt.Errorf("unmarshaling json: %w", err) + return fmt.Errorf("unmarshalling json: %w", err) } tok.AccessToken = wire.AccessToken diff --git a/cli/azd/pkg/tools/docker.go b/cli/azd/pkg/tools/docker.go index 6223b1c62a4..bb0bfc96cee 100644 --- a/cli/azd/pkg/tools/docker.go +++ b/cli/azd/pkg/tools/docker.go @@ -8,17 +8,32 @@ import ( "github.com/azure/azure-dev/cli/azd/pkg/executil" ) -func NewDocker() *Docker { - return &Docker{} +func NewDocker(args DockerArgs) *Docker { + if args.RunWithResultFn == nil { + args.RunWithResultFn = executil.RunWithResult + } + + return &Docker{ + runWithResultFn: args.RunWithResultFn, + } +} + +type DockerArgs struct { + RunWithResultFn func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) } type Docker struct { + runWithResultFn func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) } -// Build runs a docker build for a given Dockerfile, forcing the amd64 platform. If successful, it +// Runs a Docker build for a given Dockerfile. If the platform is not specified (empty), it defaults to amd64. If the build is successful, the function // returns the image id of the built image. -func (d *Docker) Build(ctx context.Context, dockerFilePath string, buildContext string) (string, error) { - res, err := d.executeCommand(ctx, buildContext, "build", "-q", "-f", dockerFilePath, "--platform", "amd64", ".") +func (d *Docker) Build(ctx context.Context, cwd string, dockerFilePath string, platform string, buildContext string) (string, error) { + if strings.TrimSpace(platform) == "" { + platform = "amd64" + } + + res, err := d.executeCommand(ctx, cwd, "build", "-q", "-f", dockerFilePath, "--platform", platform, buildContext) if err != nil { return "", fmt.Errorf("building image: %s: %w", res.String(), err) } @@ -26,8 +41,8 @@ func (d *Docker) Build(ctx context.Context, dockerFilePath string, buildContext return strings.TrimSpace(res.Stdout), nil } -func (d *Docker) Tag(ctx context.Context, imageName string, tag string) error { - res, err := d.executeCommand(ctx, ".", "tag", imageName, tag) +func (d *Docker) Tag(ctx context.Context, cwd string, imageName string, tag string) error { + res, err := d.executeCommand(ctx, cwd, "tag", imageName, tag) if err != nil { return fmt.Errorf("tagging image: %s: %w", res.String(), err) } @@ -35,10 +50,10 @@ func (d *Docker) Tag(ctx context.Context, imageName string, tag string) error { return nil } -func (d *Docker) Push(ctx context.Context, tag string) error { - res, err := d.executeCommand(ctx, ".", "push", tag) +func (d *Docker) Push(ctx context.Context, cwd string, tag string) error { + res, err := d.executeCommand(ctx, cwd, "push", tag) if err != nil { - return fmt.Errorf("tagging image: %s: %w", res.String(), err) + return fmt.Errorf("pushing image: %s: %w", res.String(), err) } return nil @@ -57,9 +72,10 @@ func (d *Docker) Name() string { } func (d *Docker) executeCommand(ctx context.Context, cwd string, args ...string) (executil.RunResult, error) { - return executil.RunWithResult(ctx, executil.RunArgs{ - Cmd: "docker", - Args: args, - Cwd: cwd, + return d.runWithResultFn(ctx, executil.RunArgs{ + Cmd: "docker", + Args: args, + Cwd: cwd, + EnrichError: true, }) } diff --git a/cli/azd/pkg/tools/docker_test.go b/cli/azd/pkg/tools/docker_test.go new file mode 100644 index 00000000000..ac93d407e68 --- /dev/null +++ b/cli/azd/pkg/tools/docker_test.go @@ -0,0 +1,246 @@ +package tools + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/azure/azure-dev/cli/azd/pkg/executil" + "github.com/stretchr/testify/require" +) + +func Test_DockerBuild(t *testing.T) { + docker := NewDocker(DockerArgs{}) + + cwd := "." + dockerFile := "./Dockerfile" + dockerContext := "../" + platform := "amd64" + + t.Run("NoError", func(t *testing.T) { + ran := false + + docker.runWithResultFn = func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) { + ran = true + + require.Equal(t, "docker", args.Cmd) + require.Equal(t, cwd, args.Cwd) + require.Equal(t, []string{ + "build", + "-q", + "-f", dockerFile, + "--platform", platform, + dockerContext, + }, args.Args) + + return executil.RunResult{ + Stdout: "Docker build output", + Stderr: "", + ExitCode: 0, + }, nil + } + + result, err := docker.Build(context.Background(), cwd, dockerFile, platform, dockerContext) + + require.Equal(t, true, ran) + require.Nil(t, err) + require.Equal(t, "Docker build output", result) + }) + + t.Run("WithError", func(t *testing.T) { + ran := false + stdErr := "Error tagging DockerFile" + customErrorMessage := "example error message" + + docker.runWithResultFn = func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) { + ran = true + + require.Equal(t, "docker", args.Cmd) + require.Equal(t, cwd, args.Cwd) + require.Equal(t, []string{ + "build", + "-q", + "-f", dockerFile, + "--platform", platform, + dockerContext, + }, args.Args) + + return executil.RunResult{ + Stdout: "", + Stderr: stdErr, + ExitCode: 1, + }, errors.New(customErrorMessage) + } + + result, err := docker.Build(context.Background(), cwd, dockerFile, platform, dockerContext) + + require.Equal(t, true, ran) + require.NotNil(t, err) + require.Equal(t, fmt.Sprintf("building image: exit code: 1, stdout: , stderr: %s: %s", stdErr, customErrorMessage), err.Error()) + require.Equal(t, "", result) + }) +} + +func Test_DockerBuildEmptyPlatform(t *testing.T) { + docker := NewDocker(DockerArgs{}) + + ran := false + cwd := "." + dockerFile := "./Dockerfile" + dockerContext := "../" + platform := "amd64" + + docker.runWithResultFn = func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) { + ran = true + + require.Equal(t, "docker", args.Cmd) + require.Equal(t, cwd, args.Cwd) + require.Equal(t, []string{ + "build", + "-q", + "-f", dockerFile, + "--platform", platform, + dockerContext, + }, args.Args) + + return executil.RunResult{ + Stdout: "Docker build output", + Stderr: "", + ExitCode: 0, + }, nil + } + + result, err := docker.Build(context.Background(), cwd, dockerFile, "", dockerContext) + + require.Equal(t, true, ran) + require.Nil(t, err) + require.Equal(t, "Docker build output", result) +} + +func Test_DockerTag(t *testing.T) { + docker := NewDocker(DockerArgs{}) + + cwd := "." + imageName := "image-name" + tag := "customTag" + + t.Run("NoError", func(t *testing.T) { + ran := false + + docker.runWithResultFn = func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) { + ran = true + + require.Equal(t, "docker", args.Cmd) + require.Equal(t, cwd, args.Cwd) + require.Equal(t, []string{ + "tag", + imageName, + tag, + }, args.Args) + + return executil.RunResult{ + Stdout: "Docker build output", + Stderr: "", + ExitCode: 0, + }, nil + } + + err := docker.Tag(context.Background(), cwd, imageName, tag) + + require.Equal(t, true, ran) + require.Nil(t, err) + }) + + t.Run("WithError", func(t *testing.T) { + ran := false + stdErr := "Error tagging DockerFile" + customErrorMessage := "example error message" + + docker.runWithResultFn = func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) { + ran = true + + require.Equal(t, "docker", args.Cmd) + require.Equal(t, cwd, args.Cwd) + require.Equal(t, []string{ + "tag", + imageName, + tag, + }, args.Args) + + return executil.RunResult{ + Stdout: "", + Stderr: stdErr, + ExitCode: 1, + }, errors.New(customErrorMessage) + } + + err := docker.Tag(context.Background(), cwd, imageName, tag) + + require.Equal(t, true, ran) + require.NotNil(t, err) + require.Equal(t, fmt.Sprintf("tagging image: exit code: 1, stdout: , stderr: %s: %s", stdErr, customErrorMessage), err.Error()) + }) +} + +func Test_DockerPush(t *testing.T) { + docker := NewDocker(DockerArgs{}) + + cwd := "." + tag := "customTag" + + t.Run("NoError", func(t *testing.T) { + ran := false + + docker.runWithResultFn = func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) { + ran = true + + require.Equal(t, "docker", args.Cmd) + require.Equal(t, cwd, args.Cwd) + require.Equal(t, []string{ + "push", + tag, + }, args.Args) + + return executil.RunResult{ + Stdout: "Docker build output", + Stderr: "", + ExitCode: 0, + }, nil + } + + err := docker.Push(context.Background(), cwd, tag) + + require.Equal(t, true, ran) + require.Nil(t, err) + }) + + t.Run("WithError", func(t *testing.T) { + ran := false + stdErr := "Error pushing DockerFile" + customErrorMessage := "example error message" + + docker.runWithResultFn = func(ctx context.Context, args executil.RunArgs) (executil.RunResult, error) { + ran = true + + require.Equal(t, "docker", args.Cmd) + require.Equal(t, cwd, args.Cwd) + require.Equal(t, []string{ + "push", + tag, + }, args.Args) + + return executil.RunResult{ + Stdout: "", + Stderr: stdErr, + ExitCode: 1, + }, errors.New(customErrorMessage) + } + + err := docker.Push(context.Background(), cwd, tag) + + require.Equal(t, true, ran) + require.NotNil(t, err) + require.Equal(t, fmt.Sprintf("pushing image: exit code: 1, stdout: , stderr: %s: %s", stdErr, customErrorMessage), err.Error()) + }) +} diff --git a/schemas/v1.0/azure.yaml.json b/schemas/v1.0/azure.yaml.json index f0304b6f9e9..c066356b9df 100644 --- a/schemas/v1.0/azure.yaml.json +++ b/schemas/v1.0/azure.yaml.json @@ -1,25 +1,29 @@ { - "$schema": "http://json-schema.org/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json", - "type": "object", - "required": [ - "name", "services" + "name", + "services" ], - + "additionalProperties": false, "properties": { - "name": { - "type": "string", + "name": { + "type": "string", "minLength": 2, "title": "Name of the application" }, - + "resourceGroup": { + "type": "string", + "minLength": 3, + "maxLength": 64, + "title": "Name of the Azure resource group", + "description": "When specified will override the resource group name used for infrastructure provisioning." + }, "metadata": { "type": "object", - "properties": { - "template": { + "template": { "type": "string", "title": "Identifier of the template from which the application was created. Optional.", "examples": [ @@ -28,56 +32,108 @@ } } }, - "services": { "type": "object", "title": "Definition of services that comprise the application", - "additionalProperties": { - "type": "object", - - "properties": { - "resourceName": { - "type": "string", - "title": "Name of the Azure resource that implements the service", - "description": "Optional. If not specified, the resource name will be constructed from current environment name concatenated with service name (, for example 'prodapi')." - }, - - "project": { - "type": "string", - "title": "Path to the service source code directory" - }, - - "host": { - "type": "string", - "title": "Type of Azure resource used for service implementation", - "description": "If omitted, App Service will be assumed.", - "enum": [ "", "appservice", "containerapp", "function", "staticwebapp" ] - }, - - "language": { - "type": "string", - "title": "Service implementation language", - "description": "If omitted, .NET will be assumed.", - "enum": [ "", "dotnet", "csharp", "fsharp", "py", "python", "js", "ts" ] - }, - - "moduleName": { - "type": "string", - "title": "Name of the module used to deploy the service", - "description": "If omitted, the CLI will assume the module name is the same as the service name." - }, - - "dist": { - "type": "string", - "title": "Relative path to service deployment artifacts", - "description": "The CLI will use files under this path to create the deployment artifact (ZIP file). If omitted, all files under service project directory will be included." + "$ref": "#/$defs/service" + } + } + }, + "$defs": { + "service": { + "type": "object", + "additionalProperties": false, + "properties": { + "resourceName": { + "type": "string", + "title": "Name of the Azure resource that implements the service", + "description": "Optional. If not specified, the resource name will be constructed from current environment name concatenated with service name (, for example 'prodapi')." + }, + "project": { + "type": "string", + "title": "Path to the service source code directory" + }, + "host": { + "type": "string", + "title": "Type of Azure resource used for service implementation", + "description": "If omitted, App Service will be assumed.", + "enum": [ + "", + "appservice", + "containerapp", + "function", + "staticwebapp" + ] + }, + "language": { + "type": "string", + "title": "Service implementation language", + "description": "If omitted, .NET will be assumed.", + "enum": [ + "", + "dotnet", + "csharp", + "fsharp", + "py", + "python", + "js", + "ts" + ] + }, + "moduleName": { + "type": "string", + "title": "Name of the module used to deploy the service", + "description": "If omitted, the CLI will assume the module name is the same as the service name." + }, + "dist": { + "type": "string", + "title": "Relative path to service deployment artifacts", + "description": "The CLI will use files under this path to create the deployment artifact (ZIP file). If omitted, all files under service project directory will be included." + }, + "docker": { + "$ref": "#/$defs/dockerOptions" + } + }, + "if": { + "not": { + "properties": { + "host": { + "const": "containerapp" + } } + } + }, + "then": { + "properties": { + "docker": false + } + }, + "required": [ + "project" + ] + }, + "dockerOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string", + "title": "The path to the Dockerfile", + "description": "Path to the Dockerfile is relative to your service", + "default": "./Dockerfile" }, - - "required": [ - "project" - ] + "context": { + "type": "string", + "title": "The docker build context", + "description": "When specified overrides the default context", + "default": "." + }, + "platform": { + "type": "string", + "title": "The platform target", + "default": "amd64" + } } } }