func()

in cli/azd/pkg/project/framework_service_docker.go [440:580]


func (p *dockerProject) packBuild(
	ctx context.Context,
	svc *ServiceConfig,
	dockerOptions DockerProjectOptions,
	imageName string) (*ServiceBuildResult, error) {
	packCli, err := pack.NewCli(ctx, p.console, p.commandRunner)
	if err != nil {
		return nil, err
	}
	builder := DefaultBuilderImage
	environ := []string{}
	userDefinedImage := false

	if os.Getenv("AZD_BUILDER_IMAGE") != "" {
		builder = os.Getenv("AZD_BUILDER_IMAGE")
		userDefinedImage = true
	}

	svcPath := svc.Path()
	buildContext := svcPath

	if svc.Docker.Context != "" {
		buildContext = svc.Docker.Context

		if !filepath.IsAbs(buildContext) {
			buildContext = filepath.Join(svcPath, buildContext)
		}
	}

	if !userDefinedImage {
		// Always default to port 80 for consistency across languages
		environ = append(environ, "ORYX_RUNTIME_PORT=80")

		if svc.Language == ServiceLanguageJava {
			environ = append(environ, "ORYX_RUNTIME_PORT=8080")

			if buildContext != svcPath {
				svcRelPath, err := filepath.Rel(buildContext, svcPath)
				if err != nil {
					return nil, fmt.Errorf("calculating relative context path: %w", err)
				}

				environ = append(environ, fmt.Sprintf("BP_MAVEN_BUILT_MODULE=%s", filepath.ToSlash(svcRelPath)))
			}
		}

		if svc.OutputPath != "" && (svc.Language == ServiceLanguageTypeScript || svc.Language == ServiceLanguageJavaScript) {
			inDockerOutputPath := path.Join("/workspace", svc.OutputPath)
			// A dist folder has been set.
			// We assume that the service is a front-end service, configuring a nginx web server to serve the static content
			// produced.
			environ = append(environ,
				"ORYX_RUNTIME_IMAGE=nginx:1.25.2-bookworm",
				fmt.Sprintf(
					//nolint:lll
					"ORYX_RUNTIME_SCRIPT=[ -d \"%s\" ] || { echo \"error: directory '%s' does not exist. ensure the 'dist' path in azure.yaml is specified correctly.\"; exit 1; } && "+
						"rm -rf /usr/share/nginx/html && ln -sT %s /usr/share/nginx/html && "+
						"nginx -g 'daemon off;'",
					inDockerOutputPath,
					svc.OutputPath,
					inDockerOutputPath,
				))
		}

		if svc.Language == ServiceLanguagePython {
			pyEnviron, err := getEnvironForPython(ctx, svc)
			if err != nil {
				return nil, err
			}
			if len(pyEnviron) > 0 {
				environ = append(environ, pyEnviron...)
			}
		}
	}

	previewer := p.console.ShowPreviewer(ctx,
		&input.ShowPreviewerOptions{
			Prefix:       "  ",
			MaxLineCount: 8,
			Title:        "Docker (pack) Output",
		})

	ctx, span := tracing.Start(
		ctx,
		events.PackBuildEvent,
		trace.WithAttributes(fields.ProjectServiceLanguageKey.String(string(svc.Language))))

	img, tag := docker.SplitDockerImage(builder)
	if userDefinedImage {
		span.SetAttributes(
			fields.StringHashed(fields.PackBuilderImage, img),
			fields.StringHashed(fields.PackBuilderTag, tag),
		)
	} else {
		span.SetAttributes(
			fields.PackBuilderImage.String(img),
			fields.PackBuilderTag.String(tag),
		)
	}

	err = packCli.Build(
		ctx,
		buildContext,
		builder,
		imageName,
		environ,
		previewer)
	p.console.StopPreviewer(ctx, false)
	if err != nil {
		span.EndWithStatus(err)

		var statusCodeErr *pack.StatusCodeError
		if errors.As(err, &statusCodeErr) && statusCodeErr.Code == pack.StatusCodeUndetectedNoError {
			return nil, &internal.ErrorWithSuggestion{
				Err: err,
				Suggestion: "No Dockerfile was found, and image could not be automatically built from source. " +
					fmt.Sprintf(
						"\nSuggested action: Author a Dockerfile and save it as %s",
						filepath.Join(svc.Path(), dockerOptions.Path)),
			}
		}

		return nil, err
	}

	span.End()

	imageId, err := p.docker.Inspect(ctx, imageName, "{{.Id}}")
	if err != nil {
		return nil, err
	}
	imageId = strings.TrimSpace(imageId)

	return &ServiceBuildResult{
		BuildOutputPath: imageId,
		Details: &dockerBuildResult{
			ImageId:   imageId,
			ImageName: imageName,
		},
	}, nil
}