Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Coverage report
Test suite run success4032 tests passing in 1541 suites. Report generated by 🧪jest coverage report action from 7aae5d3 |
|
/snapit |
|
🫰✨ Thanks @dmerand! Your snapshot has been published to npm. Test the snapshot by installing your package globally: npm i -g --@shopify:registry=https://registry.npmjs.org @shopify/cli@0.0.0-snapshot-20260327162821Caution After installing, validate the version by running |
e15c53b to
893af23
Compare
|
/snapit |
|
🫰✨ Thanks @dmerand! Your snapshot has been published to npm. Test the snapshot by installing your package globally: npm i -g --@shopify:registry=https://registry.npmjs.org @shopify/cli@0.0.0-snapshot-20260327203223Caution After installing, validate the version by running |
97030c7 to
0a0c94d
Compare
3d0c271 to
c83e839
Compare
There was a problem hiding this comment.
Pull request overview
Adds a new “store” workflow to Shopify CLI that can authenticate an app against a store (PKCE, per-user online tokens) and then execute Admin API GraphQL operations without requiring a local app project.
Changes:
- Introduces
shopify store auth(PKCE OAuth to loopback callback; persists online auth per store/user). - Introduces
shopify store execute(Admin API GraphQL execution with variable/query file support, mutation safety via--allow-mutations, and error/failure handling). - Adds supporting session storage, context/version resolution, transport helpers, and CLI docs/topic updates.
Reviewed changes
Copilot reviewed 24 out of 25 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/cli/src/index.ts | Registers store:auth and store:execute commands with the CLI entrypoint. |
| packages/cli/src/cli/commands/store/auth.ts | New shopify store auth command wiring/flags. |
| packages/cli/src/cli/commands/store/auth.test.ts | Tests flag parsing and service invocation for store auth. |
| packages/cli/src/cli/commands/store/execute.ts | New shopify store execute command wiring/flags. |
| packages/cli/src/cli/commands/store/execute.test.ts | Tests flag parsing and service invocation for store execute. |
| packages/cli/src/cli/services/store/auth-config.ts | Defines store auth client id, callback path/port, session keying, token masking. |
| packages/cli/src/cli/services/store/session.ts | Implements persisted per-store/per-user session bucket and expiry helper. |
| packages/cli/src/cli/services/store/session.test.ts | Unit tests for session bucket behavior and expiry margin logic. |
| packages/cli/src/cli/services/store/auth.ts | Implements PKCE flow, loopback callback server, token exchange, and persistence. |
| packages/cli/src/cli/services/store/auth.test.ts | Unit tests for PKCE helpers, callback server, token exchange, and persistence. |
| packages/cli/src/cli/services/store/execute-request.ts | Parses query/query-file, variables, validates single operation and mutation gating. |
| packages/cli/src/cli/services/store/execute-request.test.ts | Unit tests for request preparation and validation errors. |
| packages/cli/src/cli/services/store/admin-graphql-context.ts | Loads/refreshes stored auth and resolves Admin API version. |
| packages/cli/src/cli/services/store/admin-graphql-context.test.ts | Tests refresh flow, invalid auth paths, and version selection/validation. |
| packages/cli/src/cli/services/store/admin-graphql-transport.ts | Executes Admin GraphQL request and normalizes 401/GraphQL-error handling. |
| packages/cli/src/cli/services/store/admin-graphql-transport.test.ts | Tests success, 401 clearing + reauth message, GraphQL errors, and passthrough errors. |
| packages/cli/src/cli/services/store/graphql-targets.ts | Adds an internal target seam (currently Admin-only) for store-scoped GraphQL APIs. |
| packages/cli/src/cli/services/store/graphql-targets.test.ts | Tests target context preparation and execution delegation. |
| packages/cli/src/cli/services/store/execute-result.ts | Writes results to file or stdout and emits a success UI message. |
| packages/cli/src/cli/services/store/execute-result.test.ts | Tests file output vs stdout output behavior. |
| packages/cli/src/cli/services/store/execute.ts | Orchestrates request prep → context load → execution → result emission. |
| packages/cli/src/cli/services/store/execute.test.ts | End-to-end-ish unit tests for execute flow behaviors and error cases. |
| packages/cli/package.json | Adds a new store topic/category for CLI command grouping. |
| packages/cli/README.md | Adds docs entries for new commands (and includes regenerated usage sections). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This comment has been minimized.
This comment has been minimized.
870584e to
a462574
Compare
| @@ -0,0 +1,168 @@ | |||
| import {fetchApiVersions} from '@shopify/cli-kit/node/api/admin' | |||
There was a problem hiding this comment.
there are a lot of different concerns going on in this services area. it'd be nice to establish some clearer patterns of organization around things.
There was a problem hiding this comment.
I asked this question myself and landed here. This is definitely a big PR, and if you can believe it the current state is a significant decomposition of several earlier attempts. I agree that we can find more patterns, but I'd like to defer further architectural work until we have either better repo guidelines for service architecture, or more APIs we want to use for store execute that would guide patterns.
| outputContent`Refreshing expired token for ${outputToken.raw(session.store)} (expired at ${outputToken.raw(session.expiresAt ?? 'unknown')}, refresh_token=${outputToken.raw(maskToken(session.refreshToken))})`, | ||
| ) | ||
|
|
||
| const response = await fetch(endpoint, { |
There was a problem hiding this comment.
it'd be nice to split out the gql scaffolding from the specific queries/mutations
There was a problem hiding this comment.
Similar comment here, decomposing the API contexts further is good follow-up work IMO.
| } catch (error) { | ||
| if ( | ||
| error instanceof AbortError && | ||
| error.message.includes(`Error connecting to your store ${adminSession.storeFqdn}:`) && |
There was a problem hiding this comment.
can we add a code comment to address the string parsing later?
| [key: string]: StoredStoreAppSessionBucket | ||
| } | ||
|
|
||
| let _storeSessionStorage: LocalStorage<StoreSessionSchema> | undefined |
There was a problem hiding this comment.
ideally this gets initialized as part of some lifecycle and not inlined
There was a problem hiding this comment.
I agree, but I'd like to address in follow-up given shipping pressure unless you think it's broken.
| } | ||
|
|
||
| export function getStoreGraphQLTarget(api: StoreGraphQLApi): StoreGraphQLTarget<AdminStoreGraphQLContext> { | ||
| switch (api) { |
There was a problem hiding this comment.
what other targets are we going to be supporting?
There was a problem hiding this comment.
Admin API is a known first target, and we know we're going to be adding more -- the conceptual limit would be all of the APIs that dev-mcp supports. Since each API has its own auth/setup concerns, I've designed it to make those pluggable. It's arguably not necessary for this first PR, but I also wanted to guide agents not to make monolithic additions, which were definitely their preference here.
| allowMutations: input.allowMutations, | ||
| }) | ||
|
|
||
| const context = await renderSingleTask({ |
There was a problem hiding this comment.
unrelated as it's an existing pattern, but i really want us to split out rendering and data flow more
There was a problem hiding this comment.
Agree. This could be a function that returns an object that is renderable, or a task, and the rendering could be separate. Not to litigate in this PR though.
ryancbahan
left a comment
There was a problem hiding this comment.
code generally makes sense. i think there are some improvements we can make to organization, and generally to establishing more clarity around what goes where.
ryancbahan
left a comment
There was a problem hiding this comment.
tophatted and works as expected. nice job! a few small notes:
- the "authorization successful" callback view that opens in browser after auth is totally unstyled. would be nice to improve that
- after running auth, the command outputs a "next steps" block. this is nice, but has two minor issues:
- it renders backticks inline, presumably for markdown codelbock that doesn't exist
- when the text breaks to a newline, it adds a pipe
|where the newline break is, which breaks the ability to copy/paste
|
/snapit |
|
🫰✨ Thanks @craigmichaelmartin! Your snapshot has been published to npm. Test the snapshot by installing your package globally: npm i -g --@shopify:registry=https://registry.npmjs.org @shopify/cli@0.0.0-snapshot-20260401163359Caution After installing, validate the version by running |

What
Add
shopify store authandshopify store executeso Shopify CLI can authenticate an app against a store and then run Admin API GraphQL without needing a local app project in the current repo.shopify store authstarts a PKCE OAuth flow and stores online per-user auth for later store commands.shopify store executeruns Admin API GraphQL against that stored auth.--allow-mutations.shopify app executewhere it makes sense.Why
We want a clear store workflow in CLI:
This makes store operations usable outside an app project while still using standard app auth and explicit mutation safety.
How
shopify store authAdd a new
shopify store authcommand that:response_type=code,code_challenge,code_challenge_method=S256,code_verifier)127.0.0.1shopify store executeAdd a new
shopify store executecommand that:shopify app execute--allow-mutationsis passedSession handling
Store auth is persisted per store and per user for the configured app client ID.
When stored auth is expired,
shopify store executerefreshes it before version resolution / execution. When stored auth is no longer valid, the current stored session is cleared and the user is prompted to re-runshopify store auth.That invalid-auth path is handled consistently for:
Internal structure
The execute path is split into request preparation, auth/context loading, transport, and result emission helpers so the command stays thin and the execution flow is easier to reason about.
The implementation also adds a small internal target seam for future store-scoped GraphQL APIs while keeping the current behavior Admin-only.
Manual testing