func()

in cli/azd/pkg/project/service_manager.go [270:403]


func (sm *serviceManager) Package(
	ctx context.Context,
	serviceConfig *ServiceConfig,
	buildOutput *ServiceBuildResult,
	progress *async.Progress[ServiceProgress],
	options *PackageOptions,
) (*ServicePackageResult, error) {
	if options == nil {
		options = &PackageOptions{}
	}

	cachedResult, ok := sm.getOperationResult(serviceConfig, string(ServiceEventPackage))
	if ok && cachedResult != nil {
		return cachedResult.(*ServicePackageResult), nil
	}

	if buildOutput == nil {
		cachedResult, ok := sm.getOperationResult(serviceConfig, string(ServiceEventBuild))
		if ok && cachedResult != nil {
			buildOutput = cachedResult.(*ServiceBuildResult)
		}
	}

	frameworkService, err := sm.GetFrameworkService(ctx, serviceConfig)
	if err != nil {
		return nil, fmt.Errorf("getting framework service: %w", err)
	}

	serviceTarget, err := sm.GetServiceTarget(ctx, serviceConfig)
	if err != nil {
		return nil, fmt.Errorf("getting service target: %w", err)
	}

	eventArgs := ServiceLifecycleEventArgs{
		Project: serviceConfig.Project,
		Service: serviceConfig,
	}

	hasBuildOutput := buildOutput != nil
	restoreResult := &ServiceRestoreResult{}

	// Get the language / framework requirements
	frameworkRequirements := frameworkService.Requirements()

	// When a previous restore result was not provided, and we require it
	// Then we need to restore the dependencies
	if frameworkRequirements.Package.RequireRestore && (!hasBuildOutput || buildOutput.Restore == nil) {
		restoreTaskResult, err := sm.Restore(ctx, serviceConfig, progress)
		if err != nil {
			return nil, err
		}

		restoreResult = restoreTaskResult
	}

	buildResult := &ServiceBuildResult{}

	// When a previous build result was not provided, and we require it
	// Then we need to build the project
	if frameworkRequirements.Package.RequireBuild && !hasBuildOutput {
		buildTaskResult, err := sm.Build(ctx, serviceConfig, restoreResult, progress)
		if err != nil {
			return nil, err
		}

		buildResult = buildTaskResult
	}

	if !hasBuildOutput {
		buildOutput = buildResult
		buildOutput.Restore = restoreResult
	}

	var packageResult *ServicePackageResult

	err = serviceConfig.Invoke(ctx, ServiceEventPackage, eventArgs, func() error {
		frameworkPackageResult, err := frameworkService.Package(ctx, serviceConfig, buildOutput, progress)
		if err != nil {
			return err
		}

		serviceTargetPackageResult, err := serviceTarget.Package(ctx, serviceConfig, frameworkPackageResult, progress)
		if err != nil {
			return err
		}

		packageResult = serviceTargetPackageResult
		sm.setOperationResult(serviceConfig, string(ServiceEventPackage), packageResult)

		return nil
	})

	if err != nil {
		return nil, fmt.Errorf("failed packaging service '%s': %w", serviceConfig.Name, err)
	}

	// Package path can be a file path or a container image name
	// We only move to desired output path for file based packages
	_, err = os.Stat(packageResult.PackagePath)
	hasPackageFile := err == nil

	if hasPackageFile && options.OutputPath != "" {
		var destFilePath string
		var destDirectory string

		isFilePath := filepath.Ext(options.OutputPath) != ""
		if isFilePath {
			destFilePath = options.OutputPath
			destDirectory = filepath.Dir(options.OutputPath)
		} else {
			destFilePath = filepath.Join(options.OutputPath, filepath.Base(packageResult.PackagePath))
			destDirectory = options.OutputPath
		}

		_, err := os.Stat(destDirectory)
		if errors.Is(err, os.ErrNotExist) {
			// Create the desired output directory if it does not already exist
			if err := os.MkdirAll(destDirectory, osutil.PermissionDirectory); err != nil {
				return nil, fmt.Errorf("failed creating output directory '%s': %w", destDirectory, err)
			}
		}

		// Move the package file to the desired path
		// We can't use os.Rename here since that does not work across disks
		if err := moveFile(packageResult.PackagePath, destFilePath); err != nil {
			return nil, fmt.Errorf(
				"failed moving package file '%s' to '%s': %w", packageResult.PackagePath, destFilePath, err)
		}

		packageResult.PackagePath = destFilePath
	}

	return packageResult, nil
}