Documentation
¶
Overview ¶
Package deploy implements generic Fabric item deployment: it reads items from a repo's origin/main, applies parameter.yml substitution, compares against a target workspace, and publishes in dependency order. It mirrors the core of Microsoft's fabric-cicd, fronted by the interactive TUI in package cmd. All Fabric access goes through the narrow FabricClient interface so the logic is unit-testable with a fake.
Index ¶
- Constants
- func DefinitionFormat(item LocalItem) string
- func DeployedDescription(deployed *fabric.Definition) string
- func Execute(client FabricClient, token string, target fabric.Workspace, plan []PlannedItem, ...) ([]Result, []PendingReportRebind, error)
- func IsShellOnly(itemType string) bool
- func ListRemoteBranches(repoPath string) ([]string, error)
- func ReplaceLogicalIds(content []byte, idMap map[string]string) []byte
- func RepoItemNames(repoPath, itemType string) ([]string, error)
- func RepoItemNamesMulti(repoPaths []string, itemTypes ...string) ([]string, error)
- func RepoItemTypes(repoPath string) ([]string, error)
- func RepoItemTypesMulti(repoPaths []string) ([]string, error)
- func TopLevelFolders(items []LocalItem) []string
- func WorkspaceFolderPath(itemFolderPath, mappingRoot string) string
- type Action
- type Class
- type CompareRow
- type DeleteTarget
- type FabricClient
- type FindReplace
- type IndexedItem
- type LocalItem
- type LookupStatus
- type NameIndex
- type Override
- type Parameters
- type Part
- type PartDiff
- type PendingReportRebind
- type PlannedItem
- type RebindChange
- type RebindOutcome
- type Rebinder
- func (rb *Rebinder) ApplyCustomSubstitutions(item LocalItem, partPath string, content []byte) ([]byte, RebindOutcome)
- func (rb *Rebinder) RebindNotebookLakehouses(content []byte) ([]byte, RebindOutcome)
- func (rb *Rebinder) RebindPart(item LocalItem, partPath string, content []byte) ([]byte, RebindOutcome)
- func (rb *Rebinder) RebindReportConnection(item LocalItem, content []byte) ([]byte, RebindOutcome)
- func (rb *Rebinder) RebindSemanticModel(content []byte) ([]byte, RebindOutcome)
- func (rb *Rebinder) RebindShortcuts(content []byte) ([]byte, RebindOutcome)
- func (rb *Rebinder) RegisterTargetItem(name, itemType, guid, workspaceID string)
- func (rb *Rebinder) ResolveTargetAttr(itemType, itemName, attr string) (string, bool)
- func (rb *Rebinder) SetSubstitutions(subs []Substitution)
- type ReportBinding
- type ReportRebindOutcome
- type Resolver
- type Result
- type Source
- type StringOrSlice
- type Substitution
- type UnresolvedRef
Constants ¶
const ( ReasonNameUnknown = "name-unknown" // baseline GUID not in baseline index — no name to match by ReasonNotInTarget = "not-in-target" // name known but absent from every registered target workspace ReasonAmbiguous = "ambiguous" // name appears in 2+ target workspaces )
const LocationReportBinding = "report dataset binding"
LocationReportBinding is the UnresolvedRef.Location for a report's definition.pbir dataset binding. Exported because the cmd layer uses it to route ownership: binding refs are collected by the dedicated report-binding pass, everything else by the per-item substitution pass.
Variables ¶
This section is empty.
Functions ¶
func DefinitionFormat ¶ added in v0.8.0
DefinitionFormat returns the definition-envelope format flag for item types with several definition variants: an .ipynb notebook must declare "ipynb" (the API otherwise parses the payload as the .py git form), and SparkJobDefinition always uses "SparkJobDefinitionV2" (mirrors fabric-cicd's API_FORMAT_MAPPING). Empty for everything else. Exported because the compare must FETCH deployed definitions in the same format it publishes them — an ipynb repo diffed against the default .py form is a phantom full diff on every part.
func DeployedDescription ¶
func DeployedDescription(deployed *fabric.Definition) string
DeployedDescription returns the item description stored in the deployed definition's .platform part (empty if there is none or it can't be parsed). futils excludes .platform from the part-by-part diff, so description drift is surfaced separately as a field-level change.
func Execute ¶
func Execute(client FabricClient, token string, target fabric.Workspace, plan []PlannedItem, rb *Rebinder, modelsByWS map[string]map[string]string, done *int64) ([]Result, []PendingReportRebind, error)
Execute publishes a plan against the target workspace. For each item, in order, it applies logicalId substitution to every part; when rb is non-nil, it also auto-rebinds notebook lakehouse references by name. It then encodes parts to base64 and creates or updates the item.
modelsByWS is a caller-owned accumulator (targetWorkspaceID → model displayName → deployed GUID): Execute records every published SemanticModel into it so the SAME map, threaded through every group's Execute call, is complete by the time report rebinds run. Report rebinds are NOT done inline (that made them order-dependent); instead each published Report is returned as a PendingReportRebind for the post-deploy RebindReports pass.
A per-item error is captured in its Result (the run continues); Execute only returns a top-level error for a setup failure that aborts everything.
done, when non-nil, is incremented atomically once per plan item processed (success or failure) so a spinner can show live "Publishing X/Y" progress. The counter advances even for items that error out, matching the publish loop's "we're done with this item, on to the next" semantics.
func IsShellOnly ¶ added in v0.8.0
IsShellOnly reports whether an item type publishes as a shell — no definition through the item APIs; see shellOnlyPublish.
func ListRemoteBranches ¶ added in v0.8.0
ListRemoteBranches returns origin's branch names (without any origin/ prefix), sorted. It asks the remote directly (git ls-remote) so branches pushed from elsewhere — e.g. a Fabric workspace committing straight to DevOps — show up without a local fetch; when the remote is unreachable it falls back to the locally-known origin refs.
func ReplaceLogicalIds ¶
ReplaceLogicalIds rewrites every known source logicalId in content with its deployed target GUID. This is how cross-item references (e.g. a report pointing at a semantic model's logicalId) are repointed at the items that were just created in the target workspace. Requires that referenced items are published first (see order.go).
func RepoItemNames ¶
RepoItemNames scans repoPath (the working tree) for Fabric items of the given type and returns their display names, sorted and de-duplicated. Mirrors RepoItemTypes: unparseable .platform files are skipped, a missing path yields an empty result rather than an error.
func RepoItemNamesMulti ¶ added in v0.5.0
RepoItemNamesMulti unions RepoItemNames of the given types across several repos — one walk per repo regardless of how many types are asked for.
func RepoItemTypes ¶
RepoItemTypes scans a repo directory for Fabric item folders (those containing a .platform file) and returns the distinct item types found, sorted. Used by the edit-customer "exclude item types" picker — a cheap local scan, no API. A missing or empty repoPath yields an empty slice, not an error.
func RepoItemTypesMulti ¶ added in v0.5.0
RepoItemTypesMulti unions RepoItemTypes across several repos (empty paths and missing paths skipped), sorted and de-duplicated.
func TopLevelFolders ¶
TopLevelFolders returns the distinct first path segments of the items' FolderPaths (e.g. "FabricBackEnd" from "FabricBackEnd/NB_X.Notebook"), sorted. Items sitting directly at the repo root (no separator in FolderPath) have no grouping folder and are skipped. Used to offer a pick-list of mappable folders during deploy setup.
func WorkspaceFolderPath ¶ added in v0.8.0
WorkspaceFolderPath derives the target workspace folder an item should land in from its repo path and the mapping root it deploys under. The mapping root is stripped, then the item's own folder segment is dropped — what's left is the workspace folder path. Examples (root "Backend"):
Backend/X.Notebook → "" (workspace root) Backend/Sub/X.Notebook → "Sub" Backend/Notebooks/Config/X.Notebook → "Notebooks/Config"
An empty root (whole-repo mapping) treats the full path minus the item segment as the folder path. Slashes are normalized.
Types ¶
type Class ¶
type Class int
const ( ClassNew Class = iota // local item not present in target ClassExists // local item present in target (pre-content-diff / unverified) ClassOrphan // target item with no local counterpart ClassChanged // exists in target and content differs ClassUnchanged // exists in target and content matches )
type CompareRow ¶
CompareRow is one line in the compare view. Local is set for New/Exists; Deployed is set for Exists/Orphan.
func Compare ¶
Compare classifies local items against the deployed set by displayName+type. scope limits which deployed types can be flagged as orphans (so a workspace full of out-of-scope items isn't reported as orphaned).
func (CompareRow) ItemType ¶
func (r CompareRow) ItemType() string
ItemType returns the type regardless of side.
func (CompareRow) Name ¶
func (r CompareRow) Name() string
Name returns the displayName regardless of which side populated the row.
type DeleteTarget ¶
DeleteTarget is a target-only item (an orphan) the user chose to delete.
type FabricClient ¶
type FabricClient interface {
ListItems(token, workspaceID string) ([]fabric.Item, error)
ListItemsByType(token, workspaceID, itemType string) ([]fabric.Item, error)
ListWorkspaces(token string) ([]fabric.Workspace, error)
GetItemDefinition(token, workspaceID, itemID, format string) (*fabric.Definition, error)
CreateItem(token, workspaceID, displayName, itemType string, def *fabric.Definition, creationPayload json.RawMessage, folderID string) (fabric.Item, error)
ListFolders(token, workspaceID string) ([]fabric.Folder, error)
CreateFolder(token, workspaceID, displayName, parentFolderID string) (fabric.Folder, error)
UpdateItemDefinition(token, workspaceID, itemID string, def *fabric.Definition) error
UpdateItem(token, workspaceID, itemID, displayName, description string) error
DeleteItem(token, workspaceID, itemID string) error
RebindReport(token, workspaceID, reportID, datasetID string) error
BulkImportDefinitions(token, workspaceID string, parts []fabric.DefinitionPart, opts fabric.BulkImportOptions) (*fabric.BulkImportResult, error)
GetLakehouseSqlEndpoint(token, workspaceID, lakehouseID string) (host, id string, err error)
}
FabricClient is the narrow set of Fabric operations the deploy package needs. It is satisfied structurally by cmd.APIClient, so the TUI passes its existing client straight through without an adapter.
type FindReplace ¶
type FindReplace struct {
FindValue string `yaml:"find_value"`
ReplaceValue map[string]string `yaml:"replace_value"`
IsRegex string `yaml:"is_regex"`
IgnoreCase string `yaml:"ignore_case"`
ItemType StringOrSlice `yaml:"item_type"`
ItemName StringOrSlice `yaml:"item_name"`
FilePath StringOrSlice `yaml:"file_path"`
}
FindReplace is one literal/regex substitution rule.
type IndexedItem ¶
IndexedItem is one item located in an environment's name index.
type LocalItem ¶
type LocalItem struct {
Type string
DisplayName string
LogicalID string
Description string
FolderPath string
Parts []Part
Platform []byte // raw .platform bytes; retained for the bulk backend (not a Part)
// CreationPayload is .platform's raw metadata.creationPayload — one-time
// create settings for shell types (Warehouse collation, Lakehouse
// enableSchemas). Sent on create only; nil when .platform has none.
CreationPayload json.RawMessage
// ShellParts counts the definition files discovery dropped because the
// type publishes as a shell (see shellOnlyPublish) — surfaced as compare
// notes and publish warnings so a git-side schema change is never
// silently "deployed".
ShellParts int
}
func ItemsInFolder ¶
ItemsInFolder returns the items whose FolderPath is inside the given repo subfolder. An empty folder matches everything (whole-repo fallback). Slashes are normalized so "Backend", "/Backend", and "Backend/" behave identically. Matching is prefix-on-a-path-boundary: "Backend" matches "Backend/x" but not "BackendExtra/x".
func SortForPublish ¶
SortForPublish returns a new slice ordered by publish priority. Stable, so items of the same type keep their discovery order — except DataPipelines, which get a dependency-ordering pass within the type (see sortPipelinesByDependency).
func StripScheduleParts ¶ added in v0.7.0
StripScheduleParts returns items with any ".schedules" definition part removed. Schedules are optional in the definition APIs — omitting the part on update leaves the target item's schedules untouched — so filtering here lets schedules be managed per environment instead of overwritten from git.
type LookupStatus ¶
type LookupStatus int
LookupStatus distinguishes the three forward-lookup outcomes so callers can explain WHY a name didn't resolve.
const ( LookupFound LookupStatus = iota LookupAbsent LookupAmbiguous )
type NameIndex ¶
type NameIndex struct {
// contains filtered or unexported fields
}
NameIndex maps a single environment's items both ways: GUID->item (to learn the name of a baseline GUID) and name+type->item (to find that name's GUID in a target env). A name+type appearing in more than one of the env's workspaces is recorded as ambiguous and will not resolve forward — matching by name is only safe when names are unique within the env.
func BuildNameIndex ¶
func BuildNameIndex(client FabricClient, token string, workspaces []fabric.Workspace) (*NameIndex, error)
BuildNameIndex enumerates every workspace's items (one ListItems call each) and indexes them. Each item's WorkspaceID is set to the workspace it was listed from, since the items endpoint does not reliably echo it back.
func (*NameIndex) Add ¶ added in v0.8.0
func (i *NameIndex) Add(ent IndexedItem)
Add indexes one more item after the initial build — the publish path uses it to register items it just created so later rebinds in the same run resolve them. Mirrors BuildNameIndex's ambiguity rule: a name+type already indexed under a DIFFERENT GUID becomes ambiguous and stops resolving forward.
func (*NameIndex) ItemByGUID ¶
func (i *NameIndex) ItemByGUID(guid string) (IndexedItem, bool)
ItemByGUID returns the indexed item for a GUID (reverse lookup).
func (*NameIndex) ItemByName ¶
func (i *NameIndex) ItemByName(name, typ string) (IndexedItem, bool)
ItemByName returns the indexed item for a name+type (forward lookup). Returns false when the name is absent, or ambiguous across the env's workspaces.
func (*NameIndex) LookupName ¶
func (i *NameIndex) LookupName(name, typ string) (IndexedItem, LookupStatus)
LookupName resolves a name+type forward, reporting whether it was found, absent, or ambiguous across the env's workspaces.
type Override ¶
Override is a futils-native reference override resolved by name, keyed by the baseline GUID as it appears in git. It mirrors config.ReferenceOverride without the deploy package depending on config — the cmd layer converts.
type Parameters ¶
type Parameters struct {
FindReplace []FindReplace `yaml:"find_replace"`
KeyValueReplace []map[string]any `yaml:"key_value_replace"`
SparkPool []map[string]any `yaml:"spark_pool"`
}
Parameters is the parsed parameter.yml. KeyValueReplace and SparkPool are parsed (forward-compat) but not applied until Phase 3.
func ParseParameters ¶
func ParseParameters(raw []byte) (Parameters, error)
ParseParameters parses parameter.yml bytes. Empty input yields an empty (no-op) Parameters, since a repo may legitimately have no parameter file.
func (Parameters) ApplyFindReplace ¶
func (p Parameters) ApplyFindReplace(env string, item LocalItem, filePath string, content []byte, resolve func(string) (string, error)) ([]byte, error)
ApplyFindReplace runs every matching find_replace rule against one file's content for the chosen environment. resolve expands dynamic variables (e.g. "$items.Lakehouse.X.$id") in replacement values; pass an identity function when there are none. filePath is the item-relative path of the file.
type PartDiff ¶
PartDiff is the normalized old (deployed) vs new (substituted-local) text of one item part that differs. Old is empty when the part is new locally; New is empty when the part exists only in the deployed definition.
func DiffParts ¶
func DiffParts(localParts map[string][]byte, deployed *fabric.Definition) []PartDiff
DiffParts returns, for each part whose normalized content differs between the substituted local parts and the deployed definition, the normalized old (deployed) and new (local) text. It is the single source of the content verdict (len == 0 means unchanged).
type PendingReportRebind ¶
type PendingReportRebind struct {
WorkspaceID string
ReportID string // the report's deployed GUID
ReportName string
Ref datasetRef
}
PendingReportRebind defers a single report's rebind to the post-deploy pass. It carries everything RebindReports needs without re-reading the plan: which workspace the report landed in, its deployed GUID + name, and the parsed dataset reference. Because the rebind runs AFTER every group has deployed (and against a model map accumulated across all of them), it no longer depends on whether the model's group ran before or after the report's.
type PlannedItem ¶
type PlannedItem struct {
Item LocalItem
Action Action
ExistingID string // set when Action == ActionUpdate
// WorkspaceFolder is the target workspace folder path a newly-created item
// should land in, derived from its repo path under the mapping root ("" =
// workspace root). Only consulted for ActionCreate — existing items keep
// their current placement.
WorkspaceFolder string
}
PlannedItem is one item to publish, with its resolved create/update action.
func BuildPlan ¶
func BuildPlan(selected []LocalItem, deployed []fabric.Item, mappingRoot string) []PlannedItem
BuildPlan turns the user-selected local items into an ordered publish plan. An item already present in the target (matched by type+name) becomes an Update against its existing ID; otherwise a Create. The plan is sorted by publish priority so dependencies (e.g. semantic models) go first. mappingRoot is the repo folder this group deploys from; it's stripped when deriving each new item's target workspace folder (empty = whole-repo mapping).
type RebindChange ¶
RebindChange records one applied baseline→target rewrite, for the deploy summary. Kind is "Lakehouse", "Workspace", or "SQL endpoint". Name is the resolved item/workspace display name, shown alongside the GUIDs so the user can tell which reference is which.
type RebindOutcome ¶
type RebindOutcome struct {
Changes []RebindChange
Unresolved []UnresolvedRef
ReportBindings []ReportBinding
}
RebindOutcome bundles what a rebind pass produced: the applied changes (for the summary, deduped by Old within a pass) and the references it could not resolve (surfaced to the user).
func SubstituteParts ¶
func SubstituteParts(item LocalItem, idMap map[string]string, resolver *Resolver, rb *Rebinder) (map[string][]byte, RebindOutcome, error)
SubstituteParts applies logicalId substitution, then (when rb is non-nil) auto-rebind of notebook lakehouse references, to each of an item's parts. Returns path -> substituted raw bytes (not base64), plus a RebindOutcome with any changes applied and references the rebinder could not resolve (tagged with the item name). Shared by the publish path (which base64-encodes the result) and the content-diff. A nil rb skips rebinding entirely.
func (*RebindOutcome) AddUnresolved ¶ added in v0.6.0
func (o *RebindOutcome) AddUnresolved(ref UnresolvedRef)
AddUnresolved records an unresolved reference on the outcome, deduplicated on (GUID, ItemType, Location): a model carrying the same broken reference in 80 table expressions reports it once with Count=80, not as 80 identical lines. Merging pre-counted refs (outcome aggregation) sums the counts.
type Rebinder ¶
type Rebinder struct {
// contains filtered or unexported fields
}
Rebinder translates baseline-environment GUIDs to a target env by item name.
The lazy SQL-endpoint cache (targetEndpoint) is guarded by mu so the rebinder is safe to share across the concurrent per-item compare workers. Every other field is set in NewRebinder / SetSubstitutions and never mutated during the CONCURRENT phase (the compare): substitutions is installed once before any concurrent use, so only the endpoint cache needs the lock there. The target NameIndex additionally grows during the SEQUENTIAL publish loop — RegisterTargetItem adds each just-created item so later rebinds in the same run can resolve it; publish never runs concurrently with a compare.
func NewRebinder ¶
func NewRebinder(client FabricClient, token string, baselineWS, targetWS []fabric.Workspace, overrides map[string]Override) (*Rebinder, error)
NewRebinder builds the baseline and target name indices and returns a Rebinder. baselineWS / targetWS are each env's full workspace set (deploy targets plus reference-only workspaces). A nil overrides map is treated as empty.
func (*Rebinder) ApplyCustomSubstitutions ¶
func (rb *Rebinder) ApplyCustomSubstitutions(item LocalItem, partPath string, content []byte) ([]byte, RebindOutcome)
ApplyCustomSubstitutions runs the customer's find→replace rules over one part. Each rule whose optional item/file filters match is applied: the replacement is a literal (Literal) or the resolved target attribute (by name in the target env). Applied rewrites are recorded as RebindChange{Kind:"Substitution"}; rules whose target can't be resolved are left unapplied and surfaced as UnresolvedRef. Runs in the explicit tier (before auto-rebind).
func (*Rebinder) RebindNotebookLakehouses ¶
func (rb *Rebinder) RebindNotebookLakehouses(content []byte) ([]byte, RebindOutcome)
RebindNotebookLakehouses rewrites the lakehouse dependency GUIDs in a Fabric notebook part from baseline to target, by name. It only touches GUIDs found in the dependencies.lakehouse metadata block (never arbitrary UUIDs in code), records each applied rewrite as a RebindChange, and string-replaces those exact GUIDs throughout the content. GUIDs it cannot resolve become UnresolvedRef and are left unchanged. Content with no lakehouse block is returned unchanged with an empty outcome.
func (*Rebinder) RebindPart ¶
func (rb *Rebinder) RebindPart(item LocalItem, partPath string, content []byte) ([]byte, RebindOutcome)
RebindPart dispatches a single item part to the right rebind pass by item type and part name, returning the rewritten bytes and the outcome. Parts with no recognized reference location are returned unchanged.
func (*Rebinder) RebindReportConnection ¶
func (rb *Rebinder) RebindReportConnection(item LocalItem, content []byte) ([]byte, RebindOutcome)
func (*Rebinder) RebindSemanticModel ¶
func (rb *Rebinder) RebindSemanticModel(content []byte) ([]byte, RebindOutcome)
RebindSemanticModel rewrites a Direct Lake semantic model part's data-source connection from baseline to target. It handles both on-disk shapes: Direct Lake on OneLake (workspace+lakehouse GUID URL) and Direct Lake on SQL (Sql.Database host+endpoint-id). Only the values inside a matched connection expression are rewritten. Content with neither shape is returned unchanged.
func (*Rebinder) RebindShortcuts ¶ added in v0.8.0
func (rb *Rebinder) RebindShortcuts(content []byte) ([]byte, RebindOutcome)
RebindShortcuts rewrites the OneLake target GUIDs in a lakehouse's shortcuts.metadata.json from baseline to target, by name. shortcuts.metadata.json is a Lakehouse definition part, so it already deploys with the item — but the items API stores the targets verbatim (unlike Fabric's own deployment pipelines, it does NOT auto-remap internal shortcuts), so a shortcut pointing at a baseline lakehouse would keep pointing there after deploy. For each OneLake target we resolve itemId (a baseline GUID) to the same-named item in the target env and rewrite that shortcut's itemId and workspaceId in place. Self-references (empty/zero itemId GUIDs, which Fabric maps to the current lakehouse), external targets (ADLS/S3/etc.), and unparseable content are left untouched; an unresolvable OneLake target is surfaced as UnresolvedRef and left as-is.
The rewrite edits the parsed JSON per shortcut instead of string-replacing the whole file: a whole-file replace cannot express one baseline workspace GUID resolving to two different target workspaces (two shortcuts sharing a baseline workspace whose items deploy to different target workspaces), and a zero-GUID workspaceId used as a replace key would also rewrite every OTHER self-reference's zero GUIDs in the file. Content is re-marshalled only when something actually changed — an untouched file stays byte-identical — and the diff view normalizes JSON before comparing, so the formatting round-trip never reads as a change.
func (*Rebinder) RegisterTargetItem ¶ added in v0.8.0
RegisterTargetItem adds a just-created item to the target name index so later rebinds in the same publish run resolve it. Without this, an item created earlier in the run (e.g. the lakehouse a later lakehouse's shortcut points at) stays unresolvable, because the index was built before the run started — and the shortcut would silently keep its baseline GUID.
func (*Rebinder) ResolveTargetAttr ¶
ResolveTargetAttr resolves a target item by name in the target env and returns the requested attribute: "id"/"" → the item GUID; "sqlendpoint"/"sqlendpointid" → the lakehouse's SQL endpoint host/id (via the cached endpoint lookup).
func (*Rebinder) SetSubstitutions ¶
func (rb *Rebinder) SetSubstitutions(subs []Substitution)
SetSubstitutions installs the customer's custom find→replace rules. Called by the cmd layer after NewRebinder (config→engine conversion lives there). Each IsRegex rule has its pattern compiled once here; a rule whose pattern is invalid is stored with a nil compiled field and silently skipped at apply time.
type ReportBinding ¶
type ReportBinding struct {
Report string // report display name
Model string // resolved semantic-model name
Workspace string // target workspace the model lives in
}
ReportBinding is a resolved report→semantic-model binding, surfaced in the compare step so the user sees which model a report will bind to (and in which target workspace) before deploying.
type ReportRebindOutcome ¶
ReportRebindOutcome is the result of attempting one pending report rebind. Exactly one of Err / Warning is set when something noteworthy happened; an empty outcome (both blank) means the rebind succeeded cleanly. ReportID matches the report's deployed GUID so callers can fold the outcome back into its Result. (Distinct from RebindOutcome, which is the lakehouse/workspace substitution summary from the rebind package.)
func RebindReports ¶
func RebindReports(client FabricClient, token string, modelsByWS map[string]map[string]string, pending []PendingReportRebind, rebinderActive bool) []ReportRebindOutcome
RebindReports runs AFTER every group has deployed, repointing each published report at the matching semantic model. It is order-independent: modelsByWS is the union of models published across every group, keyed by target workspace, so a model and its report can sit in different folders/groups and still bind — while a model named "X" in one workspace can never bind a report in another (the workspace key isolates them).
rebinderActive signals whether a Rebinder is configured for this run (rb != nil at the call site). This governs the byConnection warning:
- byPath, model found in the report's workspace → RebindReport; an API failure becomes an Err outcome.
- byPath, model NOT in modelsByWS → silent skip. The most common case is an incremental deploy where only the report changed and its SemanticModel was Unchanged (never published this run); the model is already live and the report is already correctly bound — emitting a Warning here is a false alarm.
- byConnection, model CREATED this run (in modelsByWS) → RebindReport; the payload kept the baseline GUID because the model wasn't in the pre-deploy target index at diff time, so we bind it now (mirrors byPath).
- byConnection, rebinderActive=false, model not this run → warn; the payload is unchanged (no rebinder ran the in-payload rewrite) so the report retains its stale dev binding — the operator must verify manually.
- byConnection, rebinderActive=true, model not this run → no-op; the in-payload rewrite (RebindReportConnection) already resolved it, or it was surfaced as unresolved pre-deploy.
- no dataset reference → no outcome (nothing to rebind).
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver expands fabric-cicd dynamic variables against the TARGET workspace at deploy time. It caches workspace and item lookups so repeated variables don't re-hit the API.
The lazy caches (wsByName, itemsWS) are guarded by mu so the resolver is safe to share across the concurrent per-item compare workers. client/token/target are set at construction and never mutated, so they need no lock.
func NewResolver ¶
func NewResolver(client FabricClient, token string, target fabric.Workspace) *Resolver
type Result ¶
type Result struct {
Name string
Type string
Action Action
ID string // deployed item ID
WorkspaceID string // target workspace the item was deployed to / deleted from
Err error
Warning string // non-fatal issue after a successful publish (e.g. description not synced)
}
Result is the per-item outcome of a deploy run.
func BulkImport ¶
func BulkImport(client FabricClient, token string, target fabric.Workspace, items []LocalItem, rb *Rebinder) ([]Result, error)
BulkImport publishes every item in items into target in ONE bulkImportDefinitions call. Callers pass items already grouped by target workspace (the API is workspace-scoped). For each item it runs SubstituteParts (logicalId is a no-op here — idMap is empty; rb applies cross-env rebind of notebook/semantic-model references and custom substitutions, exactly as the per-item backend does), base64-encodes each part under its workspace-absolute path, and appends the item's .platform (with logicalId stripped, so pairing is by name+type).
Report→model byPath references and overall dependency order are resolved by Fabric within the payload, so the per-item BuildPlan/publishOrder and the post-deploy RebindReports byPath pass are NOT needed here. (resolver is passed nil: SubstituteParts does not use it.)
func DeleteItems ¶
func DeleteItems(client FabricClient, token string, target fabric.Workspace, targets []DeleteTarget) []Result
DeleteItems removes each target from the workspace, returning a per-target Result (Action=ActionDelete; Err set on failure, like Execute). It is kept separate from Execute so the destructive path is isolated and auditable. Callers MUST confirm before invoking — deletions are irreversible.
type Source ¶
type Source struct {
// contains filtered or unexported fields
}
Source reads Fabric items from a single git ref (always origin/<default>).
func NewSourceAt ¶ added in v0.8.0
NewSourceAt is NewSource with an explicit branch: a non-empty branch pins the deploy ref to origin/<branch> (a customer deploying from origin/dev, a release branch, or a default branch that isn't main/master), skipping default-branch detection entirely. The pinned ref may not exist locally yet — Fetch verifies it once the remote has been consulted.
func (*Source) DiscoverItems ¶
DiscoverItems lists every tree path at the deploy ref, treats each folder containing a .platform as an item, and reads that folder's files (excluding .platform) as definition parts. Part paths are relative to the item folder.
Efficiency: files are bucketed into item folders in a single O(files×folders) pass, and all blob content is fetched in one git cat-file --batch subprocess call, eliminating the N+1 git show pattern.
func (*Source) Fetch ¶
Fetch updates remote-tracking refs so the deploy ref reflects the latest merged state, then verifies the ref actually exists — the clear error beats the git-flavored one discovery would produce for a mistyped or deleted branch. Always run before discovery.
func (*Source) Head ¶ added in v0.8.0
Head returns the short commit SHA the deploy ref currently points at — the "what exactly was deployed" line for reports. Requires the ref to be known locally (any deploy has already fetched it).
type StringOrSlice ¶
type StringOrSlice []string
StringOrSlice unmarshals a YAML field that may be either a scalar string or a sequence of strings (parameter.yml allows both for item_type/item_name/ file_path). It always presents as a []string.
func (*StringOrSlice) UnmarshalYAML ¶
func (s *StringOrSlice) UnmarshalYAML(value *yaml.Node) error
type Substitution ¶
type Substitution struct {
FindValue string
IsRegex bool
ItemType string
ItemName string
FilePath string
TargetType string
TargetName string
Attr string
Literal string
// contains filtered or unexported fields
}
Substitution is a futils-native find→replace rule, the engine-side mirror of config.Substitution (kept config-free). Replacement is a literal (Literal) or the resolved attribute of a target item looked up by name in the target env. For IsRegex rules, compiled holds the pre-compiled *regexp.Regexp set by SetSubstitutions so the pattern is compiled once instead of once per part.
type UnresolvedRef ¶
type UnresolvedRef struct {
GUID string
ItemType string
Location string // "default_lakehouse" | "known_lakehouses"
ItemName string
Reason string // ReasonNameUnknown | ReasonNotInTarget | ReasonAmbiguous
// Count is how many occurrences collapsed into this ref (a model can carry
// the same broken reference in every table expression). 0 means 1 — set
// only by AddUnresolved.
Count int
}
UnresolvedRef is a baseline GUID the rebinder could not translate. Surfaced to the user so they can register an override (or ignore/strip it). ItemName is the notebook the GUID was found in, filled in by the substitution pass.