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
}