func ManifestFromAppHost()

in cli/azd/pkg/apphost/manifest.go [216:335]


func ManifestFromAppHost(
	ctx context.Context, appHostProject string, dotnetCli *dotnet.Cli, dotnetEnv string,
) (*Manifest, error) {
	tempDir, err := os.MkdirTemp("", "azd-provision")
	if err != nil {
		return nil, fmt.Errorf("creating temp directory for apphost-manifest.json: %w", err)
	}
	defer os.RemoveAll(tempDir)

	manifestPath := filepath.Join(tempDir, "apphost-manifest.json")

	if err := dotnetCli.PublishAppHostManifest(ctx, appHostProject, manifestPath, dotnetEnv); err != nil {
		return nil, fmt.Errorf("generating app host manifest: %w", err)
	}

	manifestData, err := os.ReadFile(manifestPath)
	if err != nil {
		return nil, err
	}

	var manifest Manifest
	if err := json.Unmarshal(manifestData, &manifest); err != nil {
		return nil, fmt.Errorf("unmarshalling manifest: %w", err)
	}

	// Make all paths absolute, to simplify logic for consumers.
	// Note that since we created a temp dir, and `dotnet run --publisher` returns relative paths to the temp dir,
	// the resulting path may be a symlinked path that isn't safe for Rel comparisons with the azd root directory.
	manifestDir := filepath.Dir(manifestPath)

	// The manifest writer writes paths relative to the manifest file. When we use a fixed manifest, the manifest is
	// located SxS with the appHostProject.
	if enabled, err := strconv.ParseBool(os.Getenv("AZD_DEBUG_DOTNET_APPHOST_USE_FIXED_MANIFEST")); err == nil && enabled {
		manifestDir = filepath.Dir(appHostProject)
	}

	manifest.BicepFiles = memfs.New()

	for resourceName, res := range manifest.Resources {
		if res.Path != nil {
			if res.Type == "azure.bicep.v0" || res.Type == "azure.bicep.v1" {
				e := manifest.BicepFiles.MkdirAll(resourceName, osutil.PermissionDirectory)
				if e != nil {
					return nil, e
				}
				// try reading as a generated bicep adding the tem-manifest dir
				content, e := os.ReadFile(filepath.Join(manifestDir, *res.Path))
				if e != nil {
					// second try reading as relative (external bicep reference)
					content, e = os.ReadFile(*res.Path)
					if e != nil {
						return nil, fmt.Errorf("did not find bicep at generated path or at: %s. Error: %w", *res.Path, e)
					}
				}
				*res.Path = filepath.Join(resourceName, filepath.Base(*res.Path))
				e = manifest.BicepFiles.WriteFile(*res.Path, content, osutil.PermissionFile)
				if e != nil {
					return nil, e
				}
				// move on to the next resource
				continue
			}

			if !filepath.IsAbs(*res.Path) {
				*res.Path = filepath.Join(manifestDir, *res.Path)
			}
		}

		if res.Deployment != nil {
			if res.Deployment.Type != "azure.bicep.v0" && res.Deployment.Type != "azure.bicep.v1" {
				return nil, fmt.Errorf(
					"unexpected deployment type %q. Supported types: [azure.bicep.v0, azure.bicep.v1]", res.Deployment.Type)
			}
			// use a folder with the name of the resource
			e := manifest.BicepFiles.MkdirAll(resourceName, osutil.PermissionDirectory)
			if e != nil {
				return nil, e
			}
			content, e := os.ReadFile(filepath.Join(manifestDir, *res.Deployment.Path))
			if e != nil {
				return nil, fmt.Errorf("reading bicep file from deployment property: %w", e)
			}
			*res.Deployment.Path = filepath.Join(resourceName, filepath.Base(*res.Deployment.Path))
			e = manifest.BicepFiles.WriteFile(*res.Deployment.Path, content, osutil.PermissionFile)
			if e != nil {
				return nil, e
			}
		}

		if res.Type == "dockerfile.v0" {
			if !filepath.IsAbs(*res.Context) {
				*res.Context = filepath.Join(manifestDir, *res.Context)
			}
		}
		if res.BindMounts != nil {
			for _, bindMount := range res.BindMounts {
				if !filepath.IsAbs(bindMount.Source) {
					bindMount.Source = filepath.Join(manifestDir, bindMount.Source)
				}
			}
		}
		if res.Type == "container.v1" {
			if res.Build != nil {
				if !filepath.IsAbs(res.Build.Dockerfile) {
					res.Build.Dockerfile = filepath.Join(manifestDir, res.Build.Dockerfile)
				}
				if !filepath.IsAbs(res.Build.Context) {
					res.Build.Context = filepath.Join(manifestDir, res.Build.Context)
				}
				for _, secret := range res.Build.Secrets {
					if secret.Source != nil && !filepath.IsAbs(*secret.Source) {
						*secret.Source = filepath.Join(manifestDir, *secret.Source)
					}
				}
			}
		}
	}

	return &manifest, nil
}