#!/usr/bin/env python3 """shopify-cli-scan — free Shopify CLI / App migration & risk scanner by PatchVault (patchvault-81y.pages.dev). Single file, Python 3.8+ standard library only. Reads files locally; sends nothing anywhere. Usage: python3 <(curl -sL https://patchvault-81y.pages.dev/starters/shopify-cli-scan.py) [dir] [--demo] [--json] [--no-color] If the directory has no matching project files (or --demo is given) it runs a clearly labelled showcase on a built-in SAMPLE repository so you can see the report format. Exit code is always 0 (it is a report, not a gate).""" import json,os,re,sys CFG=json.loads(r'''{"name": "shopify-cli-scan", "framework": "Shopify CLI / App", "detect": ["shopify.app.toml", "shopify.extension.toml", "shopify.web.toml", "package.json", ".js", ".ts", ".tsx", ".jsx", ".sh", ".yml", ".yaml", ".toml"], "rules": [{"id": "sh-poll", "sev": "HIGH", "title": "`theme dev --poll` flag was removed", "rx": "(theme\\s+dev\\b[^\\n;&|]*?)\\s--poll\\b", "advice": "Drop --poll; theme dev now uses file events.", "fix": "\\1", "flags": "", "unless": null, "ext": null, "fix_unless": null}, {"id": "sh-logs", "sev": "HIGH", "title": "`shopify app logs sources` was removed", "rx": "\\bshopify\\s+app\\s+logs\\s+sources\\b", "advice": "Use `shopify app logs` with --source filters.", "fix": null, "flags": "", "unless": null, "ext": null, "fix_unless": null}, {"id": "sh-token", "sev": "MEDIUM", "title": "Deprecated CLI automation token variable", "rx": "\\b(?:SHOPIFY_APP_AUTOMATION_TOKEN|SHOPIFY_CLI_PARTNERS_TOKEN)\\b", "advice": "Re-check CI auth against the current Shopify CLI docs before the next deploy.", "fix": null, "flags": "", "unless": null, "ext": null, "fix_unless": null}, {"id": "sh-themelist-json", "sev": "HIGH", "title": "`theme list --json` now returns {\"themes\":[...]} with string IDs", "rx": "\\btheme\\s+list\\b[^\\n]*--json", "advice": "Read `.themes[]` and treat ids as strings.", "fix": null, "flags": "", "unless": null, "ext": null, "fix_unless": null}, {"id": "sh-push-json", "sev": "HIGH", "title": "`theme push --json` result shape changed", "rx": "\\btheme\\s+push\\b[^\\n]*--json", "advice": "Re-map the parser to the new result object.", "fix": null, "flags": "", "unless": null, "ext": null, "fix_unless": null}, {"id": "sh-combined", "sev": "MEDIUM", "title": "JSON parsed from combined stdout+stderr", "rx": "\\bshopify\\b[^\\n]*--json[^\\n]*2>&1", "advice": "Parse stdout only; send stderr elsewhere.", "fix": null, "flags": "", "unless": null, "ext": null, "fix_unless": null}, {"id": "sh-toml-legacy", "sev": "HIGH", "title": "Legacy app config key `api_key` (current schema uses client_id)", "rx": "^\\s*api_key\\s*=", "advice": "Run `shopify app config link` to regenerate shopify.app.toml with client_id and [access_scopes].", "fix": null, "flags": "", "unless": null, "ext": [".toml"], "fix_unless": null}, {"id": "sh-toml-scopes", "sev": "HIGH", "title": "Top-level `scopes` outside an [access_scopes] table (legacy schema)", "rx": "^\\s*scopes\\s*=", "advice": "Run `shopify app config link` to regenerate shopify.app.toml with client_id and [access_scopes].", "fix": null, "flags": "", "unless": "\\[access_scopes\\]", "ext": [".toml"], "fix_unless": null}, {"id": "sh-toml-subs", "sev": "MEDIUM", "title": "Webhook subscriptions declared without the [webhooks] api_version block", "rx": "^\\s*\\[\\[webhooks\\.subscriptions\\]\\]", "advice": "Ensure [webhooks] has api_version set and each subscription has `uri` + `topics`.", "fix": null, "flags": "", "unless": "api_version\\s*=", "ext": [".toml"], "fix_unless": null}, {"id": "sh-rest", "sev": "HIGH", "title": "Deprecated REST Admin endpoint: migrate to the GraphQL Admin API", "rx": "/admin/api/[0-9]{4}-[0-9]{2}/(?:products|variants|orders|customers|inventory_levels|custom_collections)[^'\"`\\s]*\\.json", "advice": "Rewrite with the GraphQL Admin API (REST product/variant APIs are legacy since 2024-04).", "fix": null, "flags": "", "unless": null, "ext": null, "fix_unless": null}, {"id": "sh-rest-client", "sev": "MEDIUM", "title": "shopify.clients.Rest usage (REST Admin client)", "rx": "\\bclients\\.Rest\\b|\\bnew\\s+shopify\\.clients\\.Rest\\b", "advice": "Switch to shopify.clients.Graphql.", "fix": null, "flags": "", "unless": null, "ext": null, "fix_unless": null}, {"id": "sh-delivery", "sev": "MEDIUM", "title": "Webhook DeliveryMethod registration in code: move webhooks to shopify.app.toml", "rx": "\\bDeliveryMethod\\.(?:Http|EventBridge|PubSub)\\b", "advice": "Declare app-specific webhooks in TOML; remove runtime registration.", "fix": null, "flags": "", "unless": null, "ext": null, "fix_unless": null}, {"id": "sh-appbridge-v3", "sev": "HIGH", "title": "App Bridge v3 `createApp` pattern (v4 uses the global `shopify` object)", "rx": "\\bcreateApp\\s*\\(|from\\s+['\"]@shopify/app-bridge/actions['\"]", "advice": "Load app-bridge.js via