foundry

package
v1.27.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package foundry holds helpers shared across the Microsoft Foundry azd extensions (agents, projects, connections, toolboxes, skills, routines).

Index

Constants

View Source
const CodeInvalidFileRef = "invalid_file_ref"

CodeInvalidFileRef is the structured error code emitted when a $ref include cannot be resolved or written. Foundry extensions surface it through azd's structured error channel.

Variables

View Source
var ErrServiceNotFound = errors.New("service entry not found")

ErrServiceNotFound is returned when a named service entry is absent and creation was not requested. It mirrors yamlnode.ErrNodeNotFound so callers can branch on a sentinel.

Functions

func EntryRef

func EntryRef(entry *yaml.Node) (string, bool)

EntryRef reports the $ref target of a service entry and whether the entry is $ref-backed. It recognizes the same directive key as ResolveFileRefs, so a writer and the resolver agree on which entries are file includes.

func ExpandEnv

func ExpandEnv(value string, mapping func(string) string) (string, error)

ExpandEnv expands ${VAR} references in value against the azd environment (via mapping) while preserving Foundry server-side ${{...}} expressions verbatim. It supports default values (${VAR:-default}) and multiple expressions, matching drone/envsubst semantics for the ${VAR} portion. On expansion error the original value is returned unchanged alongside the error.

This is the single shared expander every Foundry field, in every Foundry extension, should route through so ${VAR} and ${{...}} are handled consistently. drone/envsubst cannot parse ${{...}}, so each span is masked with a sentinel placeholder, a single Eval expands the ${VAR} references, then the spans are restored. Masking rather than splitting preserves full ${VAR:-default} semantics even when a ${{...}} expression is the default value (e.g. ${MISSING:-${{event.body}}}). A ${VAR} inside a ${{...}} span is left as-is, since the span is reserved for Foundry.

func ResolveFileRefs

func ResolveFileRefs(cfg map[string]any, projectRoot string) (map[string]any, error)

ResolveFileRefs resolves $ref file includes within a Foundry resource service configuration.

In the separate-services azure.yaml shape every Foundry resource is its own service entry, so each owning extension calls ResolveFileRefs on its resource's inline map: the entry keys that reach the extension over gRPC, where they arrive as a structpb.Struct decoded to map[string]any. The core ServiceConfig fields (host, the service key, uses) are stripped by core and never appear here. cfg therefore takes any of these shapes:

  • A service-entry-level $ref. In azure.yaml the service entry can be authored with host: plus $ref (and optional overlay keys). Core removes ServiceConfig fields such as host before the extension sees the config, so the cfg map passed here has $ref at its top level.
  • A deployment array-item $ref. Deployments stay an array on the project service, so each item in deployments may be its own $ref (e.g. ./deployments/gpt-4o.yaml).
  • Any nested $ref reached while walking the entry (a $ref inside a loaded file, or a sibling value), since resolution is recursive over every map and sequence node.

projectRoot is the directory that holds azure.yaml; relative $ref targets at the top level resolve against it, and rebased project/instructions paths are anchored to it.

Any object that contains a "$ref" string is replaced by the referenced YAML or JSON file, with the object's remaining keys overlaid on top. The overlay is a shallow, top-level merge: sibling scalars, arrays, and objects each replace the loaded value wholesale. Nested $ref directives inside a loaded file resolve relative to that file's own directory, and that file's relative project, instructions, and $ref paths are rebased so the returned config has every path anchored to projectRoot in clean, forward-slash form. Inline path values authored directly in azure.yaml are left exactly as written.

$ref values that are URLs are rejected (remote includes are not supported yet); only local relative or absolute file paths are accepted. Cyclic and excessively deep include chains return an error rather than looping. $ref targets are treated as trusted input, the same trust level as azure.yaml itself.

Types

type EditTarget

type EditTarget int

EditTarget selects where a write to a service entry lands.

const (
	// EditInline writes the field on the service entry in the holding document, beside any
	// $ref. This is the spec §2.4 default (append inline): ResolveFileRefs reads such a key as
	// an overlay override layered on top of the referenced file, so the write is read back as
	// the winning value.
	EditInline EditTarget = iota

	// EditRefFile follows the entry's $ref and writes the field into the referenced split file
	// instead. If the entry is not $ref-backed it falls back to an inline write.
	EditRefFile
)

type YAMLDocument

type YAMLDocument struct {
	// contains filtered or unexported fields
}

YAMLDocument is an editable, comment-preserving azure.yaml (or a referenced split file). It wraps a braydonk yaml.Node tree so edits keep comments, key order, and block-scalar style, matching how azd core round-trips azure.yaml.

It is the $ref-aware write counterpart to ResolveFileRefs: EntryRef recognizes a $ref entry with the same key the resolver uses, and EditRefFile resolves the split-file path with the resolver's shared path logic, so reads and writes of $ref entries agree. It is also intended for the #8049 composition command write path.

Edits mutate the tree in memory; call Save to persist. Writes through EditRefFile lazily load the referenced file, and Save persists every file touched this way alongside the main one.

func LoadYAMLDocument

func LoadYAMLDocument(path string) (*YAMLDocument, error)

LoadYAMLDocument reads and parses the YAML file at path into an editable document.

func ParseYAMLDocument

func ParseYAMLDocument(path string, data []byte) (*YAMLDocument, error)

ParseYAMLDocument parses data as the YAML document located (logically) at path. path is used to resolve relative $ref split-file targets and as the destination for Save; it need not exist on disk yet. Empty input yields an empty document whose root is created on first edit.

func (*YAMLDocument) Bytes

func (d *YAMLDocument) Bytes() ([]byte, error)

Bytes serializes the document, preserving comments, key order, and block-scalar style with two-space indentation (the azure.yaml convention used by azd core).

func (*YAMLDocument) Save

func (d *YAMLDocument) Save() error

Save writes the document back to its path, then persists every referenced split file edited through EditRefFile.

func (*YAMLDocument) ServiceEntry

func (d *YAMLDocument) ServiceEntry(name string, create bool) (*yaml.Node, error)

ServiceEntry returns the mapping node for services.<name>. When create is true a missing services mapping and/or entry are added (and returned); when false a missing entry returns ErrServiceNotFound.

func (*YAMLDocument) SetServiceField

func (d *YAMLDocument) SetServiceField(name, key string, value any, target EditTarget) error

SetServiceField sets services.<name>.<key> to value, creating the service entry if missing. It is $ref-aware:

  • EditInline writes the key on the entry node beside any $ref. ResolveFileRefs reads it as an overlay override on top of the referenced file.
  • EditRefFile writes the key into the file named by the entry's $ref instead (resolved relative to this document with the resolver's shared path logic). If the entry has no $ref it falls back to an inline write.

value may be any value yamlnode.Encode accepts (scalars, sequences, mappings).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL