in cli/azd/pkg/project/framework_service_docker.go [194:340]
func (p *dockerProject) Build(
ctx context.Context,
serviceConfig *ServiceConfig,
restoreOutput *ServiceRestoreResult,
progress *async.Progress[ServiceProgress],
) (*ServiceBuildResult, error) {
if serviceConfig.Docker.RemoteBuild || useDotnetPublishForDockerBuild(serviceConfig) {
return &ServiceBuildResult{Restore: restoreOutput}, nil
}
dockerOptions := getDockerOptionsWithDefaults(serviceConfig.Docker)
resolveParameters := func(source []string) ([]string, error) {
result := make([]string, len(source))
for i, arg := range source {
evaluatedString, err := apphost.EvalString(arg, func(match string) (string, error) {
path := match
value, has := p.env.Config.GetString(path)
if !has {
return "", fmt.Errorf("parameter %s not found", path)
}
return value, nil
})
if err != nil {
return nil, err
}
result[i] = evaluatedString
}
return result, nil
}
dockerBuildArgs := []string{}
for _, arg := range dockerOptions.BuildArgs {
buildArgValue, err := arg.Envsubst(p.env.Getenv)
if err != nil {
return nil, fmt.Errorf("substituting environment variables in build args: %w", err)
}
dockerBuildArgs = append(dockerBuildArgs, buildArgValue)
}
// resolve parameters for build args and secrets
resolvedBuildArgs, err := resolveParameters(dockerBuildArgs)
if err != nil {
return nil, err
}
resolvedBuildEnv, err := resolveParameters(dockerOptions.BuildEnv)
if err != nil {
return nil, err
}
dockerOptions.BuildEnv = resolvedBuildEnv
// For services that do not specify a project path and have not specified a language then
// there is nothing to build and we can return an empty build result
// Ex) A container app project that uses an external image path
if serviceConfig.RelativePath == "" &&
(serviceConfig.Language == ServiceLanguageNone || serviceConfig.Language == ServiceLanguageDocker) {
return &ServiceBuildResult{}, nil
}
buildArgs := []string{}
for _, arg := range resolvedBuildArgs {
buildArgs = append(buildArgs, exec.RedactSensitiveData(arg))
}
log.Printf(
"building image for service %s, cwd: %s, path: %s, context: %s, buildArgs: %s)",
serviceConfig.Name,
serviceConfig.Path(),
dockerOptions.Path,
dockerOptions.Context,
buildArgs,
)
imageName := fmt.Sprintf(
"%s-%s",
strings.ToLower(serviceConfig.Project.Name),
strings.ToLower(serviceConfig.Name),
)
dockerfilePath := dockerOptions.Path
if !filepath.IsAbs(dockerfilePath) {
dockerfilePath = filepath.Join(serviceConfig.Path(), dockerfilePath)
}
_, err = os.Stat(dockerfilePath)
if errors.Is(err, os.ErrNotExist) && serviceConfig.Docker.Path == "" {
// Build the container from source when:
// 1. No Dockerfile path is specified, and
// 2. <service directory>/Dockerfile doesn't exist
progress.SetProgress(NewServiceProgress("Building Docker image from source"))
res, err := p.packBuild(ctx, serviceConfig, dockerOptions, imageName)
if err != nil {
return nil, err
}
res.Restore = restoreOutput
return res, nil
}
// Include full environment variables for the docker build including:
// 1. Environment variables from the host
// 2. Environment variables from the service configuration
// 3. Environment variables from the docker configuration
dockerEnv := []string{}
dockerEnv = append(dockerEnv, os.Environ()...)
dockerEnv = append(dockerEnv, p.env.Environ()...)
dockerEnv = append(dockerEnv, dockerOptions.BuildEnv...)
// Build the container
progress.SetProgress(NewServiceProgress("Building Docker image"))
previewerWriter := p.console.ShowPreviewer(ctx,
&input.ShowPreviewerOptions{
Prefix: " ",
MaxLineCount: 8,
Title: "Docker Output",
})
imageId, err := p.docker.Build(
ctx,
serviceConfig.Path(),
dockerOptions.Path,
dockerOptions.Platform,
dockerOptions.Target,
dockerOptions.Context,
imageName,
resolvedBuildArgs,
dockerOptions.BuildSecrets,
dockerEnv,
previewerWriter,
)
p.console.StopPreviewer(ctx, false)
if err != nil {
return nil, fmt.Errorf("building container: %s at %s: %w", serviceConfig.Name, dockerOptions.Context, err)
}
log.Printf("built image %s for %s", imageId, serviceConfig.Name)
return &ServiceBuildResult{
Restore: restoreOutput,
BuildOutputPath: imageId,
Details: &dockerBuildResult{
ImageId: imageId,
ImageName: imageName,
},
}, nil
}