Repository navigation
Description
Problem or use case
Today apiops extract accepts --filter <path> to restrict which resources are pulled out of an APIM instance, using an inclusion-list YAML (FilterConfig). The same subsetting capability does not exist on the publish side.
Users who want to publish only a subset of an artifact tree currently have to either:
- maintain separate per-environment artifact directories, or
- run bespoke preprocessing scripts that delete unwanted artifacts before invoking
apiops publish(e.g. theprepare-apiops-artifacts.ps1script inAzure-Samples/Apim-Samples, which physically stages and prunes a copy of the artifacts tree just to gate what publish sees).
Both approaches are error-prone, duplicate the filter concept, and don't reuse the well-tested filter machinery already in the CLI.
Proposed solution
Add a --filter <path> flag to apiops publish that:
- Uses the same flag name as
apiops extract --filter. - Uses the same YAML format (
FilterConfig), parsed by the existingloadFilterConfig()insrc/lib/config-loader.ts— including per-API and per-workspace sub-filters, wildcard patterns, and legacy*Namesaliases. - Applies the filter to the descriptor set inside publish (
filterResources()fromsrc/services/filter-service.ts) so unlisted resources are not PUT. - Reuses the same case-insensitive, wildcard, API-revision, and parent→child inheritance semantics that extract already has, so a single filter file works for both directions.
Transitive dependency inclusion
To stay consistent with apiops extract, publish must also expand the filter set to include transitive dependencies by default, with the same opt-out flag:
- Default (transitive on): after applying the filter, scan the on-disk artifacts of the matched resources for references to other resources and add those to the publish set. This mirrors
extract's default behavior and prevents a common failure mode: publishing an API that references a NamedValue, Backend, PolicyFragment, or VersionSet that isn't published, which succeeds at PUT time but fails at runtime. --no-transitive: same flag name asextract, disables the expansion pass. Users get exactly what the filter matches, nothing more. This is the equivalent of the PoC script's strict allowlist behavior.
References to resolve (reuse src/services/transitive-resolver.ts where possible):
- Policy XML (in service policy, API policies, operation policies, product policies, resolver policies, policy fragments):
{{named-value}}→ NamedValue<set-backend-service backend-id="...">→ Backend<include-fragment fragment-id="...">→ PolicyFragment
apiInformation.json→apiVersionSetId→ VersionSet
Backend-pool services[].id cross-references, productApi links, gatewayApi links, and subscription scope/apiId are common publish-time failure sources but are not covered by extract's transitive resolver today. Adding them is a natural extension of this work; call it out as a follow-up if it doesn't fit in one PR.
Transitive resolution runs after the filter and before any --commit-id intersection is finalized, so a resource pulled in transitively is always published even if it wasn't in the git diff (it's a dependency the user's diff needs).
Behavior with existing modes:
- Default publish: only filter-matching descriptors (plus transitive deps) are published.
--commit-id(incremental): intersect the diff with the filter, then expand transitively.--dry-run: report shows the final expanded set.--delete-unmatchedinteraction: out of scope for this issue — track separately. Combining--filterwith--delete-unmatchedshould fail fast with a clear error until scoped-delete semantics are designed.
Implementation notes (non-binding)
PublishConfiginsrc/models/config.tsgains an optionalfilter?: FilterConfigandincludeTransitive: boolean(defaulttrue) to matchExtractConfig.publish-command.tsmirrorsextract-command.ts's filter loading (loadFilterConfig, missing-file check, error exit) and adds the same--no-transitiveoption.publish-service.ts → determinePublishTargetscallsfilterResources(targetDescriptors, config.filter)after enumeration (and aftercomputeGitDiffin incremental mode), then runs the transitive expansion pass reading artifacts from the source directory.- Reuse
scanPolicyReferences()and related helpers fromsrc/services/transitive-resolver.ts. Extract-side code reads references out of the fetched HTTP responses; publish-side needs an equivalent reader that walks the on-disk artifact tree viaArtifactStore. Factor the reference-scanning logic so both sides share it. - No changes to filter schema, filter loader, or
filter-servicesemantics.
Out of scope (follow-ups)
- Strict mode / audit manifest for publish filtering.
- Interaction between
--filterand--delete-unmatched(scoped-delete design). - Unified environment-profile file (
namespace:+overrides:). - Pool-composition pre-flight validation.
- Extending transitive resolution to structural JSON references (backend-pool
services[].id,productApi,gatewayApi, subscriptionscope/apiId) if not delivered in this issue.
Acceptance criteria
-
apiops publish --filter <path>accepts the same YAML files thatapiops extract --filteraccepts. - Only resources matching the filter (plus transitive deps by default) are PUT during publish.
-
--no-transitivedisables the transitive expansion pass; only exact filter matches are published. - Transitive expansion covers, at minimum: NamedValue, Backend, PolicyFragment refs in policy XML, and VersionSet refs via
apiVersionSetId(matching extract). -
--filtercomposes correctly with--commit-id(intersection, then transitive expansion) and--dry-run(report reflects final set). -
--filtercombined with--delete-unmatchedfails fast with a clear error. - Unit tests cover: no filter (unchanged behavior), simple allowlist per resource type, wildcards, API sub-filters, incremental + filter intersection, dry-run + filter, transitive expansion on/off,
--filter+--delete-unmatchedrejection. - Docs / README publish section updated with a short "subsetting what gets published" example that reuses the extract filter file, including a note on transitive behavior and
--no-transitive.
Affected command
apiops publish
Activity
[-]feat(publish): add `--filter` flag to subset resources during publish[/-][+]Add `--filter` flag to subset resources during publish[/+]github-actions commentedon Aug 2, 2026
It will be automatically closed after 60 days of inactivity.
To keep this issue open:
stalelabel.