@veyralabs/skills 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/shopify-dev.md +3 -0
- package/commands/shopify-store.md +3 -0
- package/package.json +7 -2
- package/skills/shopify-suite/shopify-dev/SKILL.md +409 -0
- package/skills/shopify-suite/shopify-dev/references/app-architecture.md +322 -0
- package/skills/shopify-suite/shopify-dev/references/cli-workflows.md +257 -0
- package/skills/shopify-suite/shopify-dev/references/graphql-queries.md +298 -0
- package/skills/shopify-suite/shopify-dev/references/liquid-patterns.md +286 -0
- package/skills/shopify-suite/shopify-store/SKILL.md +283 -0
- package/skills/shopify-suite/shopify-store/references/app-stack.md +175 -0
- package/skills/shopify-suite/shopify-store/references/audit-framework.md +206 -0
- package/skills/shopify-suite/shopify-store/references/mcp-queries.md +216 -0
- package/skills/shopify-suite/shopify-store/references/product-optimization.md +266 -0
- package/skills/shopify-suite/shopify-store/references/seo-shopify.md +165 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# App Architecture — shopify-dev Reference
|
|
2
|
+
|
|
3
|
+
Decisions and patterns for Shopify app structure, auth, billing, and production readiness.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Tech Stack (2025+)
|
|
8
|
+
|
|
9
|
+
Shopify's recommended stack for new apps:
|
|
10
|
+
|
|
11
|
+
| Layer | Tech |
|
|
12
|
+
|-------|------|
|
|
13
|
+
| Framework | Remix (replaces Express) |
|
|
14
|
+
| UI | Polaris + App Bridge |
|
|
15
|
+
| ORM | Prisma (SQLite dev, Postgres prod) |
|
|
16
|
+
| Auth | OAuth client credentials (Dev Dashboard) |
|
|
17
|
+
| Hosting | Fly.io, Railway, Vercel, Render |
|
|
18
|
+
| Extensions | Checkout UI, Admin UI, Web Pixel |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Project Structure
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
my-shopify-app/
|
|
26
|
+
├── app/
|
|
27
|
+
│ ├── routes/
|
|
28
|
+
│ │ ├── app._index.tsx ← main embedded page
|
|
29
|
+
│ │ ├── app.products.tsx ← products feature page
|
|
30
|
+
│ │ ├── app.settings.tsx ← settings
|
|
31
|
+
│ │ ├── webhooks.tsx ← webhook receiver
|
|
32
|
+
│ │ └── auth.$.tsx ← Shopify OAuth flow (auto-generated)
|
|
33
|
+
│ ├── shopify.server.ts ← auth config, API client
|
|
34
|
+
│ ├── db.server.ts ← Prisma client singleton
|
|
35
|
+
│ └── root.tsx ← App wrapper with AppProvider
|
|
36
|
+
├── extensions/
|
|
37
|
+
│ └── checkout-ui/ ← checkout extension (if any)
|
|
38
|
+
├── prisma/
|
|
39
|
+
│ ├── schema.prisma
|
|
40
|
+
│ └── migrations/
|
|
41
|
+
├── .env ← never commit
|
|
42
|
+
├── shopify.app.toml ← app config for CLI
|
|
43
|
+
└── remix.config.js
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Authentication
|
|
49
|
+
|
|
50
|
+
### How it works (OAuth client credentials, 2025+)
|
|
51
|
+
|
|
52
|
+
1. Merchant installs app from App Store / Dev Dashboard
|
|
53
|
+
2. Shopify redirects to your `/auth` route with shop domain
|
|
54
|
+
3. Your app redirects to Shopify OAuth screen
|
|
55
|
+
4. Merchant approves — Shopify redirects back with authorization code
|
|
56
|
+
5. Your app exchanges code for session token
|
|
57
|
+
6. Session stored in DB (per-shop)
|
|
58
|
+
|
|
59
|
+
All this is handled by `@shopify/shopify-app-remix` — you don't implement it manually.
|
|
60
|
+
|
|
61
|
+
### shopify.server.ts pattern
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import "@shopify/shopify-app-remix/adapters/node";
|
|
65
|
+
import {
|
|
66
|
+
AppDistribution,
|
|
67
|
+
shopifyApp,
|
|
68
|
+
LATEST_API_VERSION,
|
|
69
|
+
} from "@shopify/shopify-app-remix/server";
|
|
70
|
+
import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
|
|
71
|
+
import { PrismaClient } from "@prisma/client";
|
|
72
|
+
|
|
73
|
+
const prisma = new PrismaClient();
|
|
74
|
+
|
|
75
|
+
const shopify = shopifyApp({
|
|
76
|
+
apiKey: process.env.SHOPIFY_API_KEY,
|
|
77
|
+
apiSecretKey: process.env.SHOPIFY_API_SECRET || "",
|
|
78
|
+
apiVersion: LATEST_API_VERSION,
|
|
79
|
+
scopes: process.env.SCOPES?.split(","),
|
|
80
|
+
appUrl: process.env.SHOPIFY_APP_URL || "",
|
|
81
|
+
authPathPrefix: "/auth",
|
|
82
|
+
sessionStorage: new PrismaSessionStorage(prisma),
|
|
83
|
+
distribution: AppDistribution.AppStore,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export default shopify;
|
|
87
|
+
export const apiVersion = LATEST_API_VERSION;
|
|
88
|
+
export const addDocumentResponseHeaders = shopify.addDocumentResponseHeaders;
|
|
89
|
+
export const authenticate = shopify.authenticate;
|
|
90
|
+
export const unauthenticated = shopify.unauthenticated;
|
|
91
|
+
export const login = shopify.login;
|
|
92
|
+
export const registerWebhooks = shopify.registerWebhooks;
|
|
93
|
+
export const sessionStorage = shopify.sessionStorage;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Using auth in routes
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
// Embedded page (inside Shopify admin)
|
|
100
|
+
export const loader = async ({ request }) => {
|
|
101
|
+
const { admin, session } = await authenticate.admin(request);
|
|
102
|
+
// admin.graphql() for API calls
|
|
103
|
+
// session.shop = "store-name.myshopify.com"
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Webhook (not embedded — different auth method)
|
|
107
|
+
export const action = async ({ request }) => {
|
|
108
|
+
const { shop, topic, payload } = await authenticate.webhook(request);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Public (no auth — for storefront-facing APIs)
|
|
112
|
+
export const loader = async ({ request }) => {
|
|
113
|
+
const { storefront } = await unauthenticated.storefront("store.myshopify.com");
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Database
|
|
120
|
+
|
|
121
|
+
### Prisma schema for session storage
|
|
122
|
+
|
|
123
|
+
```prisma
|
|
124
|
+
model Session {
|
|
125
|
+
id String @id
|
|
126
|
+
shop String
|
|
127
|
+
state String
|
|
128
|
+
isOnline Boolean @default(false)
|
|
129
|
+
scope String?
|
|
130
|
+
expires DateTime?
|
|
131
|
+
accessToken String
|
|
132
|
+
userId BigInt?
|
|
133
|
+
firstName String?
|
|
134
|
+
lastName String?
|
|
135
|
+
email String?
|
|
136
|
+
accountOwner Boolean @default(false)
|
|
137
|
+
locale String?
|
|
138
|
+
collaborator Boolean? @default(false)
|
|
139
|
+
emailVerified Boolean? @default(false)
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### App data example (products table)
|
|
144
|
+
|
|
145
|
+
```prisma
|
|
146
|
+
model ProductSetting {
|
|
147
|
+
id String @id @default(uuid())
|
|
148
|
+
shop String
|
|
149
|
+
productId String
|
|
150
|
+
badgeText String?
|
|
151
|
+
isHidden Boolean @default(false)
|
|
152
|
+
createdAt DateTime @default(now())
|
|
153
|
+
updatedAt DateTime @updatedAt
|
|
154
|
+
|
|
155
|
+
@@unique([shop, productId])
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Dev vs prod DB
|
|
160
|
+
|
|
161
|
+
- Dev: SQLite (zero setup, file-based)
|
|
162
|
+
- Prod: Postgres (Railway, Supabase, PlanetScale)
|
|
163
|
+
|
|
164
|
+
Switch in `prisma/schema.prisma`:
|
|
165
|
+
```prisma
|
|
166
|
+
datasource db {
|
|
167
|
+
provider = "postgresql" // "sqlite" for dev
|
|
168
|
+
url = env("DATABASE_URL")
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Billing
|
|
175
|
+
|
|
176
|
+
Shopify billing API requires app to charge via Shopify (mandatory for App Store apps).
|
|
177
|
+
|
|
178
|
+
### Create a subscription
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
export const loader = async ({ request }) => {
|
|
182
|
+
const { billing } = await authenticate.admin(request);
|
|
183
|
+
|
|
184
|
+
const { hasActivePayment, appSubscriptions } = await billing.check({
|
|
185
|
+
plans: ["Basic Plan"],
|
|
186
|
+
isTest: process.env.NODE_ENV !== "production",
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
if (!hasActivePayment) {
|
|
190
|
+
await billing.request({
|
|
191
|
+
plan: "Basic Plan",
|
|
192
|
+
isTest: process.env.NODE_ENV !== "production",
|
|
193
|
+
returnUrl: `${process.env.SHOPIFY_APP_URL}/app`,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Define plans in shopify.server.ts
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const shopify = shopifyApp({
|
|
203
|
+
// ...existing config...
|
|
204
|
+
billing: {
|
|
205
|
+
"Basic Plan": {
|
|
206
|
+
amount: 9.99,
|
|
207
|
+
currencyCode: "USD",
|
|
208
|
+
interval: BillingInterval.Monthly,
|
|
209
|
+
},
|
|
210
|
+
"Pro Plan": {
|
|
211
|
+
amount: 29.99,
|
|
212
|
+
currencyCode: "USD",
|
|
213
|
+
interval: BillingInterval.Monthly,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Check subscription status in any route
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
const { billing } = await authenticate.admin(request);
|
|
223
|
+
const { hasActivePayment } = await billing.check({ plans: ["Pro Plan"] });
|
|
224
|
+
|
|
225
|
+
if (!hasActivePayment) {
|
|
226
|
+
return redirect("/app/upgrade");
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## Webhooks
|
|
233
|
+
|
|
234
|
+
### Register in shopify.app.toml
|
|
235
|
+
|
|
236
|
+
```toml
|
|
237
|
+
[[webhooks.subscriptions]]
|
|
238
|
+
topics = ["products/update", "products/delete"]
|
|
239
|
+
uri = "/webhooks"
|
|
240
|
+
|
|
241
|
+
[[webhooks.subscriptions]]
|
|
242
|
+
topics = ["app/uninstalled"]
|
|
243
|
+
uri = "/webhooks"
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Or register programmatically:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// root.tsx or entry point
|
|
250
|
+
import { registerWebhooks } from "./shopify.server";
|
|
251
|
+
|
|
252
|
+
export const action = async ({ request }) => {
|
|
253
|
+
const { topic, shop } = await authenticate.webhook(request);
|
|
254
|
+
await registerWebhooks({ session });
|
|
255
|
+
};
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Webhook handler
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// app/routes/webhooks.tsx
|
|
262
|
+
export const action = async ({ request }) => {
|
|
263
|
+
const { topic, shop, session, admin, payload } =
|
|
264
|
+
await authenticate.webhook(request);
|
|
265
|
+
|
|
266
|
+
if (!admin && topic !== "APP_UNINSTALLED") {
|
|
267
|
+
throw new Response();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
switch (topic) {
|
|
271
|
+
case "APP_UNINSTALLED":
|
|
272
|
+
if (session) {
|
|
273
|
+
await db.session.deleteMany({ where: { shop } });
|
|
274
|
+
}
|
|
275
|
+
break;
|
|
276
|
+
|
|
277
|
+
case "PRODUCTS_UPDATE":
|
|
278
|
+
await syncProduct(shop, payload);
|
|
279
|
+
break;
|
|
280
|
+
|
|
281
|
+
default:
|
|
282
|
+
throw new Response("Unhandled webhook topic", { status: 404 });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return new Response();
|
|
286
|
+
};
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Production Checklist
|
|
292
|
+
|
|
293
|
+
Before submitting to App Store:
|
|
294
|
+
|
|
295
|
+
- [ ] `LATEST_API_VERSION` set and updated quarterly
|
|
296
|
+
- [ ] Webhooks registered for `APP_UNINSTALLED`, `CUSTOMERS_DATA_REQUEST`, `CUSTOMERS_REDACT`, `SHOP_REDACT` (GDPR — mandatory)
|
|
297
|
+
- [ ] Billing implemented if charging merchants
|
|
298
|
+
- [ ] Session storage uses Postgres (not SQLite)
|
|
299
|
+
- [ ] All Admin API calls handle `userErrors`
|
|
300
|
+
- [ ] Rate limit handling: retry on 429 with exponential backoff
|
|
301
|
+
- [ ] App URL set in `.env` and `shopify.app.toml`
|
|
302
|
+
- [ ] Polaris `AppProvider` wraps entire app with i18n translations
|
|
303
|
+
- [ ] No API keys or secrets in client-side code
|
|
304
|
+
- [ ] App passes Shopify's built-in review checklist (run `shopify app build` and check output)
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Rate Limits
|
|
309
|
+
|
|
310
|
+
Shopify Admin GraphQL: 1000 points/second per store.
|
|
311
|
+
|
|
312
|
+
Query cost: each field costs 1 point; connections multiply by requested count.
|
|
313
|
+
`products(first: 250)` = ~250 points. `products.variants.metafields` = ~250 × 250.
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
// Check cost in response headers
|
|
317
|
+
const response = await admin.graphql(QUERY);
|
|
318
|
+
const headers = response.headers;
|
|
319
|
+
// X-Shopify-Shop-Api-Call-Limit: used/max
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
For bulk operations (catalog exports, large migrations), use `bulkOperationRunQuery` — no rate limits.
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# CLI Workflows — shopify-dev Reference
|
|
2
|
+
|
|
3
|
+
Shopify CLI commands for theme and app development. Verified against CLI 3.x.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g @shopify/cli@latest
|
|
11
|
+
shopify version
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Requires Node 18+. Authentication is store-scoped (you authenticate per store).
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Theme Development
|
|
19
|
+
|
|
20
|
+
### Basic workflow
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Authenticate and pull from live theme
|
|
24
|
+
shopify theme dev --store your-store.myshopify.com
|
|
25
|
+
|
|
26
|
+
# Start dev server on specific theme
|
|
27
|
+
shopify theme dev --store your-store.myshopify.com --theme THEME_ID
|
|
28
|
+
|
|
29
|
+
# Check what themes exist
|
|
30
|
+
shopify theme list --store your-store.myshopify.com
|
|
31
|
+
|
|
32
|
+
# Pull theme files to local
|
|
33
|
+
shopify theme pull --store your-store.myshopify.com --theme THEME_ID
|
|
34
|
+
|
|
35
|
+
# Push to a specific theme (NOT live — always push to unpublished theme first)
|
|
36
|
+
shopify theme push --store your-store.myshopify.com --theme THEME_ID
|
|
37
|
+
|
|
38
|
+
# Push to new theme (creates it)
|
|
39
|
+
shopify theme push --unpublished --store your-store.myshopify.com
|
|
40
|
+
|
|
41
|
+
# Publish a theme (makes it live)
|
|
42
|
+
shopify theme publish --store your-store.myshopify.com --theme THEME_ID
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Targeting specific files
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Push only changed files
|
|
49
|
+
shopify theme push --only sections/header.liquid
|
|
50
|
+
|
|
51
|
+
# Ignore files during push/pull
|
|
52
|
+
shopify theme push --ignore "config/settings_data.json"
|
|
53
|
+
shopify theme pull --ignore "templates/*.json"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Multiple environments
|
|
57
|
+
|
|
58
|
+
Define in `shopify.theme.toml`:
|
|
59
|
+
|
|
60
|
+
```toml
|
|
61
|
+
[environments.development]
|
|
62
|
+
store = "dev-store.myshopify.com"
|
|
63
|
+
theme = "123456789"
|
|
64
|
+
ignore = ["config/settings_data.json"]
|
|
65
|
+
|
|
66
|
+
[environments.staging]
|
|
67
|
+
store = "staging-store.myshopify.com"
|
|
68
|
+
theme = "987654321"
|
|
69
|
+
|
|
70
|
+
[environments.production]
|
|
71
|
+
store = "prod-store.myshopify.com"
|
|
72
|
+
theme = "111111111"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Then:
|
|
76
|
+
```bash
|
|
77
|
+
shopify theme dev --environment development
|
|
78
|
+
shopify theme push --environment staging
|
|
79
|
+
shopify theme push --environment production
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Check for errors
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
shopify theme check
|
|
86
|
+
shopify theme check --output json
|
|
87
|
+
|
|
88
|
+
# Check specific files
|
|
89
|
+
shopify theme check sections/product-form.liquid
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`theme check` validates Liquid syntax, section schemas, deprecated filters, accessibility issues.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## App Development
|
|
97
|
+
|
|
98
|
+
### Scaffold a new app
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
npm init @shopify/app@latest
|
|
102
|
+
# Prompts: app name, template (Remix recommended), language (TypeScript or JS)
|
|
103
|
+
cd my-app
|
|
104
|
+
npm install
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Start dev server
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
shopify app dev
|
|
111
|
+
# Starts: local server + tunnel (Cloudflare ngrok) + updates app URLs in Dev Dashboard
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Dev server requirements:
|
|
115
|
+
- Authenticated Shopify Partner/Dev Dashboard account
|
|
116
|
+
- App created in Dev Dashboard (first run prompts you to create)
|
|
117
|
+
- `.shopify/project.toml` created automatically on first `dev`
|
|
118
|
+
|
|
119
|
+
### Environment config
|
|
120
|
+
|
|
121
|
+
`.env` file (auto-created, never commit):
|
|
122
|
+
```
|
|
123
|
+
SHOPIFY_API_KEY=your-client-id
|
|
124
|
+
SHOPIFY_API_SECRET=your-client-secret
|
|
125
|
+
SCOPES=write_products,read_orders
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Deploy to production
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
shopify app deploy
|
|
132
|
+
# Builds, runs type check, deploys extensions to Shopify
|
|
133
|
+
# Does NOT deploy the web server — deploy that to Vercel/Railway/etc.
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Generate extension scaffold
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# Checkout UI extension
|
|
140
|
+
shopify app generate extension --type checkout_ui_extension
|
|
141
|
+
|
|
142
|
+
# Product subscription
|
|
143
|
+
shopify app generate extension --type product_subscription
|
|
144
|
+
|
|
145
|
+
# Admin action
|
|
146
|
+
shopify app generate extension --type admin_action
|
|
147
|
+
|
|
148
|
+
# Web pixel
|
|
149
|
+
shopify app generate extension --type web_pixel_extension
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### App info
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
shopify app info
|
|
156
|
+
# Shows: app name, client ID, extensions, deployment status
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Authentication
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# Login to a store
|
|
165
|
+
shopify auth login --store your-store.myshopify.com
|
|
166
|
+
|
|
167
|
+
# Check current auth status
|
|
168
|
+
shopify auth status
|
|
169
|
+
|
|
170
|
+
# Logout
|
|
171
|
+
shopify auth logout
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
First `shopify app dev` or `shopify theme dev` on a new machine prompts for auth.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Useful Flags
|
|
179
|
+
|
|
180
|
+
| Flag | Command | What it does |
|
|
181
|
+
|------|---------|-------------|
|
|
182
|
+
| `--store` | theme | Target store domain |
|
|
183
|
+
| `--theme THEME_ID` | theme | Target specific theme |
|
|
184
|
+
| `--environment` | theme | Use environment from .toml |
|
|
185
|
+
| `--only file.liquid` | theme push/pull | Scope to specific files |
|
|
186
|
+
| `--ignore "pattern"` | theme push/pull | Exclude files |
|
|
187
|
+
| `--unpublished` | theme push | Create new unpublished theme |
|
|
188
|
+
| `--live` | theme push | Push to currently active theme |
|
|
189
|
+
| `--json` | most commands | Machine-readable output |
|
|
190
|
+
| `--verbose` | most commands | Debug output |
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## CI/CD Pattern
|
|
195
|
+
|
|
196
|
+
### GitHub Actions — theme deploy
|
|
197
|
+
|
|
198
|
+
```yaml
|
|
199
|
+
name: Deploy Theme
|
|
200
|
+
|
|
201
|
+
on:
|
|
202
|
+
push:
|
|
203
|
+
branches: [main]
|
|
204
|
+
|
|
205
|
+
jobs:
|
|
206
|
+
deploy:
|
|
207
|
+
runs-on: ubuntu-latest
|
|
208
|
+
steps:
|
|
209
|
+
- uses: actions/checkout@v3
|
|
210
|
+
|
|
211
|
+
- name: Setup Node
|
|
212
|
+
uses: actions/setup-node@v3
|
|
213
|
+
with:
|
|
214
|
+
node-version: 18
|
|
215
|
+
|
|
216
|
+
- name: Install Shopify CLI
|
|
217
|
+
run: npm install -g @shopify/cli@latest
|
|
218
|
+
|
|
219
|
+
- name: Deploy to staging
|
|
220
|
+
env:
|
|
221
|
+
SHOPIFY_CLI_THEME_TOKEN: ${{ secrets.SHOPIFY_CLI_THEME_TOKEN }}
|
|
222
|
+
run: |
|
|
223
|
+
shopify theme push \
|
|
224
|
+
--store ${{ secrets.SHOPIFY_STORE }} \
|
|
225
|
+
--theme ${{ secrets.STAGING_THEME_ID }} \
|
|
226
|
+
--allow-live \
|
|
227
|
+
--no-color
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Generate `SHOPIFY_CLI_THEME_TOKEN`:
|
|
231
|
+
1. Shopify Admin → Settings → Apps and sales channels → Develop apps
|
|
232
|
+
2. Create app with `write_themes` scope
|
|
233
|
+
3. Get Admin API access token
|
|
234
|
+
|
|
235
|
+
### GitHub Actions — app deploy extensions
|
|
236
|
+
|
|
237
|
+
```yaml
|
|
238
|
+
- name: Deploy app extensions
|
|
239
|
+
env:
|
|
240
|
+
SHOPIFY_API_KEY: ${{ secrets.SHOPIFY_API_KEY }}
|
|
241
|
+
SHOPIFY_API_SECRET: ${{ secrets.SHOPIFY_API_SECRET }}
|
|
242
|
+
run: shopify app deploy --no-release
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Common Errors
|
|
248
|
+
|
|
249
|
+
**`Error: No store provided`** — add `--store` flag or set in `shopify.theme.toml`
|
|
250
|
+
|
|
251
|
+
**`Error: Theme not found`** — wrong THEME_ID. Run `shopify theme list` to get correct ID.
|
|
252
|
+
|
|
253
|
+
**`Error: Cannot push to live theme`** — use `--allow-live` flag explicitly, or push to unpublished theme first.
|
|
254
|
+
|
|
255
|
+
**`Error: SHOPIFY_CLI_THEME_TOKEN not set`** — CI/CD needs token. See token generation above.
|
|
256
|
+
|
|
257
|
+
**`Error: Extensions failed to deploy`** — check extension `shopify.extension.toml` for schema errors. Run `shopify app generate extension` again if structure is corrupted.
|