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
}