Skip to content

[Prototype] Add shopify app channel-spec generate for partner channel spec export - #8360

Draft
suzkiee wants to merge 2 commits into
Shopify:mainfrom
suzkiee:suzkiee/channel-spec-generate-command
Draft

[Prototype] Add shopify app channel-spec generate for partner channel spec export#8360
suzkiee wants to merge 2 commits into
Shopify:mainfrom
suzkiee:suzkiee/channel-spec-generate-command

Conversation

@suzkiee

@suzkiee suzkiee commented Aug 21, 2026

Copy link
Copy Markdown

Note

Draft / directional prototype — the backend endpoint this calls is not shipped yet.

WHY are these changes introduced?

Legacy channel apps need a migration path onto partner-owned channel_config extension specs. Shopify already has app-level FrozenRecord fallback specs for some legacy channels. Partners need a safe way to receive a deployable, partner-owned copy of the public parts of that spec.

The Channels team is prototyping two surfaces over one canonical, Channels-owned exporter (Channels::PartnerChannelSpecExport, shop/world#2007891):

Prototype Surface Role
A Partner Dashboard TOML download (shop/world#2007895, Shopify/partners#63101) First shippable fallback if CLI ownership is blocked
B CLI-assisted generation (this PR) Preferred long-term, developer-native workflow — if the CLI team can support it

WHAT is this pull request doing?

Adds a new command:

shopify app channel-spec generate [--stdout] [--overwrite]

Flow:

  1. Resolves the app from local context via linkedAppContext (standard --path / --client-id / --config / --reset flags).
  2. Calls a thin transport (services/channel-spec/fetch.ts) that delegates to the canonical Channels exporter. The CLI never transforms or validates spec fields — the backend response is the deployable artifact.
  3. Writes extensions/channel-config/specifications/<handle>.toml (creates directories as needed).

Behavior details:

  • Refuses to overwrite an existing file unless --overwrite is passed.
  • --stdout prints only the TOML to stdout (pipeable); warnings go to stderr.
  • Backend warnings ({code, message}, e.g. automatic_product_feed_management) render out-of-band — never written into the TOML.
  • Failure reason codes map to partner-facing guidance: no_exportable_frozen_record, multiple_exportable_records, not_allowlisted, contains_no_public_fields, invalid_public_schema.
  • Never deploys; success output points at shopify app deploy as the next step.

Files:

File Purpose
packages/app/src/cli/commands/app/channel-spec/generate.ts oclif command
packages/app/src/cli/services/channel-spec/generate.ts file/stdout UX + failure mapping
packages/app/src/cli/services/channel-spec/fetch.ts transport + response parsing
packages/app/src/cli/services/channel-spec/generate.test.ts 7 service tests (real temp dirs)
index.ts + generated files command registration, oclif manifest, README, dev-docs data

Design constraints (from the prototype design doc)

  • Export is app-level and shop-independent — app-owner/collaborator auth only; no shop/admin/runtime auth contexts.
  • Internal-only fields are omitted entirely server-side via a public-field allowlist. Generated TOML must pass the public channel_config validation contract; the exporter fails closed otherwise.
  • Single-spec legacy apps only for MVP (TikTok / multi-channel out of scope). A --specification-handle <handle> flag is reserved for future multi-spec support and intentionally not included here.
  • No comments, provenance, or warnings inside the TOML file.

What we'd like the dev tools team's steer on

  1. Transport selection (main question): this prototype calls a proposed App Management path (app_management/unstable/organizations/:orgId/apps/:id/channel_spec_export.json, contract per shop/world#2007902 — {success, handle, filename, toml, warnings} / 422 {error: "not_exportable_yet", reason}). Should this instead be a GraphQL operation on App Management, a wrapper API, or a CLI-owned route? The only hard requirement: it must invoke the canonical Channels exporter.
  2. Command naming/placement: app channel-spec generate vs. something else in the topic tree.
  3. Ownership: can the CLI team support this surface, or should we ship the Partner Dashboard download first and follow up here?
  4. Telemetry: the design calls for channel_spec_export_cli_invoked/succeeded/failed events with app_id, spec_handle, surface, result, failure_reason_code, warning_types dimensions (never logging manifest contents). What's the right way to emit these from the CLI — existing command metadata hooks or custom events?
  5. Gating: should the command be hidden or flag-gated while the backend is staff-/allowlist-gated?

How to test your changes?

End-to-end tophatting is not possible yet — the backend endpoint hasn't shipped. Verified locally:

Check Result
pnpm vitest run packages/app/src/cli/services/channel-spec/generate.test.ts 7/7 passing (file write, overwrite guard, --overwrite, --stdout purity, out-of-band warnings, failure mapping)
pnpm nx run app:type-check Clean
pnpm eslint on changed files Clean
pnpm refresh-manifests / pnpm build-dev-docs Regenerated

No changeset added — this is a prototype and not ready for public release notes.

Post-release steps

None — not releasable until the backend contract is agreed and shipped; rollout will be staff-gated/allowlisted per the prototype rollout plan.

Generates a deployable channel_config spec TOML from the Shopify-authored
default channel specification. The TOML is produced and validated server-side
by the Channels-owned exporter; the CLI handles app context, transport, and
local file UX only.

- shopify app channel-spec generate writes extensions/channel-config/specifications/<handle>.toml
- --stdout prints only the TOML (warnings go to stderr)
- --overwrite required to replace an existing file
- Backend warnings ({code, message}) are rendered out-of-band, never into the TOML
- Failure reason codes map to partner-facing guidance
…el spec fetch

- A 404 is not part of the export contract (failures are 422 with a
  reason code), so stop mapping it to no_exportable_frozen_record and
  abort with an endpoint-unavailable message instead.
- Validate that backend warnings are {code, message} objects before
  rendering them, dropping malformed entries.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant