Repository navigation
Add shopify app import channel-config for partner channel spec import - #8360
Conversation
shopify app channel-spec generate for partner channel spec exportshopify app import channel-config for partner channel spec import
791e783 to
6082b1e
Compare
tauthomas01
left a comment
There was a problem hiding this comment.
Looks good! I left some minor comments regarding the channel context. I am not the best person to review the CLI modules/architecture so it would be best to have someone from that team to review.
- Mark the command hidden: the backend only serves an allowlisted cohort of channel partners, so keep it out of public help, README and the commands snapshot until the rollout widens. - ensureExtensionConfig only checked that extensions/channel-config/shopify.extension.toml existed. If that file declared a different extension type, the imported spec was written under specifications/ and reported as success even though deploy would never bundle it as channel_config. Parse the existing file and abort with guidance unless type == "channel_config"; run the check before writing the spec so an abort leaves no stray file. Addresses review feedback on Shopify#8360.
tauthomas01
left a comment
There was a problem hiding this comment.
Thank you for addressing my comments. Looks good from Channels side.
| body = await response.json() | ||
| // eslint-disable-next-line no-catch-all/no-catch-all | ||
| } catch { | ||
| body = undefined |
There was a problem hiding this comment.
if we can't parse the body, shouldn't we report it as ok: false regardless of what response.ok says? there was clearly an error somewhere
There was a problem hiding this comment.
Good point - updated to false!
09ad398 to
fa25686
Compare
- Mark the command hidden: the backend only serves an allowlisted cohort of channel partners, so keep it out of public help, README and the commands snapshot until the rollout widens. - ensureExtensionConfig only checked that extensions/channel-config/shopify.extension.toml existed. If that file declared a different extension type, the imported spec was written under specifications/ and reported as success even though deploy would never bundle it as channel_config. Parse the existing file and abort with guidance unless type == "channel_config"; run the check before writing the spec so an abort leaves no stray file. Addresses review feedback on Shopify#8360.
fa25686 to
f8f253c
Compare
|
|
||
| const EXTENSION_CONFIG_FILENAME = 'shopify.extension.toml' | ||
| const EXTENSION_CONFIG_CONTENT = 'name = "Channel config"\ntype = "channel_config"\nhandle = "channel-config"\n' | ||
| const CHANNEL_CONFIG_EXTENSION_TYPE = 'channel_config' |
There was a problem hiding this comment.
Better to define this one in the specification in channel.ts and import it here (because the value is hardcoded there too)
There was a problem hiding this comment.
channel.ts exports CHANNEL_CONFIG_INDENTIFIER now and we're now importing here.
| return {extensionDirectory: existing[0]!.directory, createExtension: false} | ||
| } | ||
|
|
||
| const extensionDirectories = app.configuration.extension_directories ?? DEFAULT_EXTENSION_DIRECTORIES |
There was a problem hiding this comment.
you need to check here if app.configuration.extension_directories not only exists but is also not empty.
There was a problem hiding this comment.
I fixed the check to check for the empty case and added a test case as well.
Imports the Shopify-authored channel spec for a linked app as a deployable channel_config extension, so partners migrating off Shopify-managed specs get a ready-to-edit TOML instead of authoring one from the docs. The command calls the App Management endpoint added in shop/world#2051080 (GET /app_management/:version/organizations/:org/apps/:app/channel_spec_export.json) through a new DeveloperPlatformClient.channelSpecExport method, then writes specifications/<handle>.toml into the app's channel_config extension: - reuses the existing channel_config extension if the app has one, wherever it lives and whichever TOML layout it uses - otherwise scaffolds extensions/channel-config, but aborts before writing if extension_directories wouldn't discover it or another extension already occupies that directory - --force to overwrite an existing spec; --json for machine output Status handling mirrors the server contract: 200 success, 422 with a reason code for well-formed "not exportable" failures, 404 as endpoint/app unavailable, 426 as outdated CLI, 401/403 as auth. The command is hidden while the backend is allowlist-gated to a small cohort of channel partners.
…as default - Export CHANNEL_CONFIG_IDENTIFIER and CHANNEL_CONFIG_SPECIFICATIONS_DIRECTORY from the channel spec and use them in the import command instead of duplicating the literals. - Fall back to extensions/* when extension_directories is an empty array, matching the app loader, instead of aborting with an empty directory list.
0d5fe02 to
c906c26
Compare
| if (!wouldBeDiscovered) { | ||
| throw new AbortError( | ||
| `Your shopify.app.toml only loads extensions from ${extensionDirectories.join(', ')}, so a new extension at ${CHANNEL_SPEC_EXTENSION_DIRECTORY} wouldn't be deployed.`, | ||
| `Create a ${CHANNEL_CONFIG_IDENTIFIER} extension in one of those directories with a ${EXTENSION_CONFIG_FILENAME} like:\n\n${EXTENSION_CONFIG_CONTENT}\nthen re-run this command.`, |
There was a problem hiding this comment.
We could also print here the full channel config we downloaded, since we are asking them to create a file with some content, why not use the real content directly? or will it be too big?
isaacroldan
left a comment
There was a problem hiding this comment.
I have not tophatted, but code looks good
|
I ran this locally from the PR branch against our team's dogfooding app: Screen.Recording.2026-10-02.at.1.34.25.PM.mov
|
|
/snapit |
|
Unable to request a snapshot: Only users with write permission to the repository can run /snapit. |
WHY are these changes introduced?
Legacy channel apps need a migration path onto partner-owned
channel_configextension 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 CLI team reviewed the proposal (thread) and approved this as a new
importnamespace underapp:This PR adds
app import channel-configand introduces theapp importtopic. A follow-up PR from the CLI team will migrate the existingimport-extensions/import-custom-data-definitionscommands into the same namespace.WHAT is this pull request doing?
Adds a new command:
Flow:
linkedAppContext(standard--path/--client-id/--config/--resetflags).services/import-channel-config/fetch.ts) that delegates to the canonical Channels-owned exporter (Channels::PartnerChannelSpecExport). The CLI never transforms or validates spec fields — the backend response is the deployable artifact.extensions/channel-config/specifications/<handle>.toml(creates directories as needed).Behavior details:
--overwriteis passed.--stdoutprints only the TOML to stdout (pipeable); warnings go to stderr.{code, message}, e.g.automatic_product_feed_management) render out-of-band — never written into the TOML.no_exportable_frozen_record,multiple_exportable_records,not_allowlisted,contains_no_public_fields,invalid_public_schema.shopify app deployas the next step.Files:
packages/app/src/cli/commands/app/import/channel-config.tsapp:import:channel-config)packages/app/src/cli/services/import-channel-config/import.tspackages/app/src/cli/services/import-channel-config/fetch.tspackages/app/src/cli/services/import-channel-config/import.test.tspackages/app/src/cli/services/import-channel-config/fetch.test.tspackages/app/src/cli/index.tspackages/cli/package.jsonapp:importtopic descriptionTransport / backend contract
The command calls
GET app_management/unstable/organizations/:orgId/apps/:id/channel_spec_export.json:{handle, filename, toml, warnings}422 {error: "not_exportable_yet", reason}404is reserved for "endpoint unavailable / app not found" and surfaces as an abort.The corresponding App Management endpoint is being added in shop/world (rework of shop/world#2051080).
Design constraints
channel_configvalidation contract; the exporter fails closed otherwise.--specification-handle <handle>flag is reserved for future multi-spec support and intentionally not included here.