shopify-cli 2.6.5 → 2.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer.json +5 -0
  3. data/.github/DESIGN.md +1 -1
  4. data/.github/ISSUE_TEMPLATE.md +7 -0
  5. data/.gitignore +1 -0
  6. data/.vscode/extensions.json +5 -0
  7. data/.vscode/settings.json +9 -0
  8. data/CHANGELOG.md +42 -4
  9. data/CONTRIBUTING.md +1 -21
  10. data/{Dockerfile → Codespace.dockerfile} +11 -3
  11. data/Gemfile +1 -0
  12. data/Gemfile.lock +6 -4
  13. data/README.md +20 -99
  14. data/Rakefile +27 -0
  15. data/Tests.dockerfile +35 -0
  16. data/assets/logo.png +0 -0
  17. data/dev.yml +0 -3
  18. data/docs/README.md +13 -0
  19. data/docs/contributors/testing.md +27 -0
  20. data/docs/users/installation.md +46 -0
  21. data/{THEMEKIT_MIGRATION.md → docs/users/migrate-from-themekit.md} +1 -1
  22. data/ext/javy/javy.rb +186 -0
  23. data/ext/javy/version +1 -0
  24. data/lib/project_types/extension/cli.rb +7 -3
  25. data/lib/project_types/extension/commands/build.rb +4 -8
  26. data/lib/project_types/extension/commands/create.rb +3 -5
  27. data/lib/project_types/extension/commands/extension_command.rb +1 -1
  28. data/lib/project_types/extension/features/argo.rb +1 -8
  29. data/lib/project_types/extension/features/argo_serve.rb +9 -23
  30. data/lib/project_types/extension/forms/create.rb +1 -1
  31. data/lib/project_types/extension/forms/questions/ask_template.rb +3 -6
  32. data/lib/project_types/extension/messages/messages.rb +1 -2
  33. data/lib/project_types/extension/models/development_server.rb +2 -2
  34. data/lib/project_types/extension/models/development_server_requirements.rb +2 -3
  35. data/lib/project_types/extension/models/server_config/app.rb +13 -0
  36. data/lib/project_types/extension/models/server_config/development.rb +5 -4
  37. data/lib/project_types/extension/models/server_config/development_renderer.rb +1 -1
  38. data/lib/project_types/extension/models/server_config/development_resource.rb +13 -0
  39. data/lib/project_types/extension/models/server_config/extension.rb +4 -0
  40. data/lib/project_types/extension/models/server_config/root.rb +4 -1
  41. data/lib/project_types/extension/tasks/convert_server_config.rb +65 -0
  42. data/lib/project_types/extension/tasks/ensure_resource_url.rb +39 -0
  43. data/lib/project_types/extension/tasks/find_package_from_json.rb +37 -0
  44. data/lib/project_types/extension/tasks/merge_server_config.rb +32 -0
  45. data/lib/project_types/extension/tasks/run_extension_command.rb +11 -10
  46. data/lib/project_types/node/cli.rb +0 -16
  47. data/lib/project_types/node/forms/create.rb +5 -5
  48. data/lib/project_types/node/messages/messages.rb +2 -144
  49. data/lib/project_types/php/cli.rb +0 -11
  50. data/lib/project_types/php/forms/create.rb +5 -6
  51. data/lib/project_types/php/messages/messages.rb +2 -161
  52. data/lib/project_types/rails/cli.rb +0 -16
  53. data/lib/project_types/rails/commands/create.rb +43 -16
  54. data/lib/project_types/rails/forms/create.rb +5 -7
  55. data/lib/project_types/rails/messages/messages.rb +6 -151
  56. data/lib/project_types/script/cli.rb +7 -1
  57. data/lib/project_types/script/commands/create.rb +3 -8
  58. data/lib/project_types/script/commands/javy.rb +29 -0
  59. data/lib/project_types/script/commands/push.rb +3 -2
  60. data/lib/project_types/script/config/extension_points.yml +3 -26
  61. data/lib/project_types/script/errors.rb +0 -18
  62. data/lib/project_types/script/forms/ask_app.rb +32 -0
  63. data/lib/project_types/script/forms/ask_org.rb +30 -0
  64. data/lib/project_types/script/forms/ask_script_uuid.rb +22 -0
  65. data/lib/project_types/script/forms/run_against_shopify_org.rb +14 -0
  66. data/lib/project_types/script/layers/application/build_script.rb +0 -1
  67. data/lib/project_types/script/layers/application/connect_app.rb +73 -0
  68. data/lib/project_types/script/layers/application/create_script.rb +2 -2
  69. data/lib/project_types/script/layers/domain/script_json.rb +1 -1
  70. data/lib/project_types/script/layers/domain/script_project.rb +4 -0
  71. data/lib/project_types/script/layers/infrastructure/api_clients/partners_proxy_api_client.rb +0 -4
  72. data/lib/project_types/script/layers/infrastructure/errors.rb +8 -4
  73. data/lib/project_types/script/layers/infrastructure/languages/assemblyscript_task_runner.rb +1 -5
  74. data/lib/project_types/script/layers/infrastructure/languages/typescript_task_runner.rb +25 -4
  75. data/lib/project_types/script/layers/infrastructure/script_project_repository.rb +3 -4
  76. data/lib/project_types/script/layers/infrastructure/script_service.rb +1 -1
  77. data/lib/project_types/script/messages/messages.rb +16 -20
  78. data/lib/project_types/script/ui/error_handler.rb +1 -32
  79. data/lib/project_types/theme/cli.rb +1 -1
  80. data/lib/project_types/theme/commands/check.rb +1 -1
  81. data/lib/project_types/theme/commands/delete.rb +1 -1
  82. data/lib/project_types/theme/commands/init.rb +1 -1
  83. data/lib/project_types/theme/commands/language_server.rb +1 -1
  84. data/lib/project_types/theme/commands/package.rb +1 -1
  85. data/lib/project_types/theme/commands/publish.rb +1 -1
  86. data/lib/project_types/theme/commands/pull.rb +4 -1
  87. data/lib/project_types/theme/commands/push.rb +7 -2
  88. data/lib/project_types/theme/commands/serve.rb +9 -2
  89. data/lib/project_types/theme/messages/messages.rb +39 -1
  90. data/lib/project_types/theme/ui/sync_progress_bar.rb +2 -2
  91. data/lib/shopify_cli/admin_api/populate_resource_command.rb +1 -1
  92. data/lib/shopify_cli/api.rb +7 -2
  93. data/lib/shopify_cli/app_type_detector.rb +24 -20
  94. data/lib/shopify_cli/command/app_sub_command.rb +10 -0
  95. data/lib/shopify_cli/command/project_command.rb +31 -0
  96. data/lib/shopify_cli/command/sub_command.rb +19 -0
  97. data/lib/shopify_cli/command.rb +7 -2
  98. data/lib/shopify_cli/commands/app/connect.rb +22 -0
  99. data/lib/shopify_cli/commands/app/create/node.rb +36 -0
  100. data/lib/shopify_cli/commands/app/create/php.rb +36 -0
  101. data/lib/shopify_cli/commands/app/create/rails.rb +38 -0
  102. data/lib/shopify_cli/commands/app/create.rb +28 -0
  103. data/lib/shopify_cli/commands/app/deploy.rb +49 -0
  104. data/lib/shopify_cli/commands/app/open.rb +19 -0
  105. data/lib/shopify_cli/commands/app/serve.rb +49 -0
  106. data/lib/shopify_cli/commands/app/tunnel.rb +43 -0
  107. data/lib/shopify_cli/commands/app.rb +29 -0
  108. data/lib/shopify_cli/commands/config.rb +2 -2
  109. data/lib/shopify_cli/commands.rb +1 -0
  110. data/lib/shopify_cli/constants.rb +7 -0
  111. data/lib/shopify_cli/context.rb +9 -0
  112. data/lib/shopify_cli/environment.rb +4 -0
  113. data/lib/shopify_cli/exception_reporter.rb +8 -6
  114. data/lib/shopify_cli/git.rb +12 -1
  115. data/lib/shopify_cli/github/issue_url_generator.rb +19 -0
  116. data/lib/shopify_cli/github.rb +5 -0
  117. data/lib/shopify_cli/identity_auth.rb +18 -0
  118. data/lib/shopify_cli/messages/messages.rb +254 -9
  119. data/lib/shopify_cli/migrator.rb +9 -11
  120. data/lib/shopify_cli/partners_api.rb +1 -8
  121. data/lib/shopify_cli/project.rb +5 -1
  122. data/lib/shopify_cli/project_commands.rb +1 -1
  123. data/lib/shopify_cli/services/app/connect_service.rb +25 -0
  124. data/lib/shopify_cli/services/app/create/node_service.rb +155 -0
  125. data/lib/shopify_cli/services/app/create/php_service.rb +152 -0
  126. data/lib/shopify_cli/services/app/create/rails_service.rb +215 -0
  127. data/lib/shopify_cli/services/app/deploy/heroku/node_service.rb +101 -0
  128. data/lib/shopify_cli/services/app/deploy/heroku/php_service.rb +135 -0
  129. data/lib/shopify_cli/services/app/deploy/heroku/rails_service.rb +120 -0
  130. data/lib/shopify_cli/services/app/open_service.rb +19 -0
  131. data/lib/shopify_cli/services/app/serve/node_service.rb +42 -0
  132. data/lib/shopify_cli/services/app/serve/php_service.rb +46 -0
  133. data/lib/shopify_cli/services/app/serve/rails_service.rb +48 -0
  134. data/lib/shopify_cli/services/app/tunnel/auth_service.rb +21 -0
  135. data/lib/shopify_cli/services/app/tunnel/start_service.rb +20 -0
  136. data/lib/shopify_cli/services/app/tunnel/stop_service.rb +20 -0
  137. data/lib/shopify_cli/services.rb +31 -0
  138. data/lib/shopify_cli/tasks/ensure_authenticated.rb +9 -3
  139. data/lib/shopify_cli/theme/dev_server/local_assets.rb +1 -1
  140. data/lib/shopify_cli/theme/dev_server.rb +35 -17
  141. data/lib/shopify_cli/theme/syncer/error_reporter.rb +45 -0
  142. data/lib/shopify_cli/theme/syncer/operation.rb +56 -0
  143. data/lib/shopify_cli/theme/syncer/standard_reporter.rb +32 -0
  144. data/lib/shopify_cli/theme/syncer.rb +40 -39
  145. data/lib/shopify_cli/theme/theme.rb +31 -19
  146. data/lib/shopify_cli/tunnel.rb +25 -22
  147. data/lib/shopify_cli/version.rb +1 -1
  148. data/lib/shopify_cli.rb +1 -2
  149. data/shopify-cli.gemspec +2 -6
  150. data/shopify-dev +18 -0
  151. data/utilities/constants.rb +7 -0
  152. data/utilities/docker/container.rb +30 -2
  153. data/utilities/docker.rb +3 -2
  154. data/utilities/utilities.rb +1 -0
  155. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +1 -1
  156. metadata +64 -54
  157. data/docs/_config.yml +0 -2
  158. data/docs/app/node/commands/index.md +0 -4
  159. data/docs/app/node/index.md +0 -4
  160. data/docs/app/rails/commands/index.md +0 -4
  161. data/docs/app/rails/index.md +0 -4
  162. data/docs/core/index.md +0 -4
  163. data/docs/getting-started/index.md +0 -4
  164. data/docs/getting-started/install/index.md +0 -4
  165. data/docs/getting-started/migrate/index.md +0 -4
  166. data/docs/getting-started/uninstall/index.md +0 -4
  167. data/docs/getting-started/upgrade/index.md +0 -4
  168. data/docs/help/start-app/index.md +0 -4
  169. data/docs/index.md +0 -4
  170. data/ext/shopify-cli/extconf.rb +0 -60
  171. data/install.sh +0 -7
  172. data/lib/project_types/extension/tasks/converters/server_config_converter.rb +0 -31
  173. data/lib/project_types/extension/tasks/load_server_config.rb +0 -23
  174. data/lib/project_types/node/commands/connect.rb +0 -21
  175. data/lib/project_types/node/commands/create.rb +0 -125
  176. data/lib/project_types/node/commands/deploy/heroku.rb +0 -96
  177. data/lib/project_types/node/commands/deploy.rb +0 -32
  178. data/lib/project_types/node/commands/generate.rb +0 -22
  179. data/lib/project_types/node/commands/open.rb +0 -18
  180. data/lib/project_types/node/commands/serve.rb +0 -45
  181. data/lib/project_types/node/commands/tunnel.rb +0 -41
  182. data/lib/project_types/php/commands/connect.rb +0 -19
  183. data/lib/project_types/php/commands/create.rb +0 -143
  184. data/lib/project_types/php/commands/deploy/heroku.rb +0 -129
  185. data/lib/project_types/php/commands/deploy.rb +0 -32
  186. data/lib/project_types/php/commands/open.rb +0 -16
  187. data/lib/project_types/php/commands/serve.rb +0 -48
  188. data/lib/project_types/php/commands/tunnel.rb +0 -37
  189. data/lib/project_types/rails/commands/connect.rb +0 -21
  190. data/lib/project_types/rails/commands/deploy/heroku.rb +0 -115
  191. data/lib/project_types/rails/commands/deploy.rb +0 -32
  192. data/lib/project_types/rails/commands/generate/webhook.rb +0 -42
  193. data/lib/project_types/rails/commands/generate.rb +0 -60
  194. data/lib/project_types/rails/commands/open.rb +0 -18
  195. data/lib/project_types/rails/commands/serve.rb +0 -51
  196. data/lib/project_types/rails/commands/tunnel.rb +0 -41
  197. data/lib/project_types/script/graphql/app_script_update_or_create.graphql +0 -0
  198. data/lib/project_types/script/tasks/ensure_env.rb +0 -106
  199. data/lib/shopify_cli/sub_command.rb +0 -17
  200. data/shopify.fish +0 -12
  201. data/shopify.sh +0 -11
@@ -4,7 +4,6 @@ module Script
4
4
  module Layers
5
5
  module Infrastructure
6
6
  module Errors
7
- class AppNotInstalledError < ScriptProjectError; end
8
7
  class BuildError < ScriptProjectError; end
9
8
  class ScriptJsonSyntaxError < ScriptProjectError; end
10
9
 
@@ -57,8 +56,15 @@ module Script
57
56
  end
58
57
  end
59
58
 
59
+ class DeprecatedEPError < ScriptProjectError
60
+ attr_reader(:extension_point)
61
+ def initialize(extension_point)
62
+ super()
63
+ @extension_point = extension_point
64
+ end
65
+ end
66
+
60
67
  class DependencyInstallError < ScriptProjectError; end
61
- class DeprecatedEPError < ScriptProjectError; end
62
68
  class EmptyResponseError < ScriptProjectError; end
63
69
  class InvalidResponseError < ScriptProjectError; end
64
70
  class ForbiddenError < ScriptProjectError; end
@@ -101,10 +107,8 @@ module Script
101
107
  end
102
108
 
103
109
  class ScriptProjectAlreadyExistsError < ScriptProjectError; end
104
- class ShopAuthenticationError < ScriptProjectError; end
105
110
  class TaskRunnerNotFoundError < ScriptProjectError; end
106
111
  class BuildScriptNotFoundError < ScriptProjectError; end
107
- class InvalidBuildScriptError < ScriptProjectError; end
108
112
 
109
113
  class WebAssemblyBinaryNotFoundError < ScriptProjectError
110
114
  def initialize
@@ -48,7 +48,7 @@ module Script
48
48
  end
49
49
 
50
50
  def library_version(library_name)
51
- output = JSON.parse(CommandRunner.new(ctx: ctx).call("npm list --json"))
51
+ output = JSON.parse(CommandRunner.new(ctx: ctx).call("npm -s list --json"))
52
52
  library_version_from_npm_list(output, library_name)
53
53
  rescue Errors::SystemCallFailureError => error
54
54
  library_version_from_npm_list_error_output(error, library_name)
@@ -98,10 +98,6 @@ module Script
98
98
 
99
99
  raise Errors::BuildScriptNotFoundError,
100
100
  "Build script not found" if build_script.nil?
101
-
102
- unless build_script.start_with?("shopify-scripts")
103
- raise Errors::InvalidBuildScriptError, "Invalid build script"
104
- end
105
101
  end
106
102
 
107
103
  def bytecode
@@ -48,8 +48,33 @@ module Script
48
48
  Domain::Metadata.create_from_json(@ctx, raw_contents)
49
49
  end
50
50
 
51
+ def library_version(library_name)
52
+ output = JSON.parse(CommandRunner.new(ctx: ctx).call("npm -s list --json"))
53
+ library_version_from_npm_list(output, library_name)
54
+ rescue Errors::SystemCallFailureError => error
55
+ library_version_from_npm_list_error_output(error, library_name)
56
+ end
57
+
51
58
  private
52
59
 
60
+ def library_version_from_npm_list_error_output(error, library_name)
61
+ # npm list can return a failure status code, even when returning the correct data.
62
+ # This causes the CommandRunner to throw a SystemCallFailure error that contains the data.
63
+ # In here, we check that the output contains `npm list`'s structure and extract the version.
64
+ output = JSON.parse(error.out)
65
+ raise error unless output.key?("dependencies")
66
+
67
+ library_version_from_npm_list(output, library_name)
68
+ rescue JSON::ParserError
69
+ raise error
70
+ end
71
+
72
+ def library_version_from_npm_list(output, library_name)
73
+ output.dig("dependencies", library_name, "version").tap do |version|
74
+ raise Errors::APILibraryNotFoundError, library_name unless version
75
+ end
76
+ end
77
+
53
78
  def check_node_version!
54
79
  output, status = @ctx.capture2e("node", "--version")
55
80
  raise Errors::DependencyInstallError, output unless status.success?
@@ -75,10 +100,6 @@ module Script
75
100
 
76
101
  raise Errors::BuildScriptNotFoundError,
77
102
  "Build script not found" if build_script.nil?
78
-
79
- unless build_script.start_with?("javy")
80
- raise Errors::InvalidBuildScriptError, "Invalid build script"
81
- end
82
103
  end
83
104
 
84
105
  def bytecode
@@ -81,10 +81,10 @@ module Script
81
81
  )
82
82
  end
83
83
 
84
- def update_or_create_script_json(title:, configuration_ui: false)
84
+ def update_or_create_script_json(title:)
85
85
  script_json = ScriptJsonRepository
86
86
  .new(ctx: ctx)
87
- .update_or_create(title: title, configuration_ui: configuration_ui)
87
+ .update_or_create(title: title)
88
88
 
89
89
  Domain::ScriptProject.new(
90
90
  id: ctx.root,
@@ -148,11 +148,10 @@ module Script
148
148
  current_script_json || raise(Domain::Errors::NoScriptJsonFile)
149
149
  end
150
150
 
151
- def update_or_create(title:, configuration_ui:)
151
+ def update_or_create(title:)
152
152
  json = current_script_json&.content || {}
153
153
  json["version"] ||= "1"
154
154
  json["title"] = title
155
- json["configurationUi"] = !!configuration_ui
156
155
 
157
156
  ctx.write(SCRIPT_JSON_FILENAME, JSON.pretty_generate(json))
158
157
 
@@ -46,7 +46,7 @@ module Script
46
46
 
47
47
  if user_errors.any? { |e| e["tag"] == "already_exists_error" }
48
48
  raise Errors::ScriptRepushError, uuid
49
- elsif (e = user_errors.any? { |err| err["tag"] == "configuration_syntax_error" })
49
+ elsif (e = user_errors.any? { |err| err["tag"] == "configuration_definition_syntax_error" })
50
50
  raise Errors::ScriptJsonSyntaxError
51
51
  elsif (e = user_errors.find { |err| err["tag"] == "configuration_definition_missing_keys_error" })
52
52
  raise Errors::ScriptJsonMissingKeysError, e["message"]
@@ -27,10 +27,6 @@ module Script
27
27
  "extension_point_type or script_name.",
28
28
  invalid_context_help: "Add these values and try again.",
29
29
 
30
- invalid_config_props_cause: "{{command:--config-props}} is formatted incorrectly.",
31
- invalid_config_props_help: "Try again using this format: "\
32
- "{{cyan:--config-props='name1:value1, name2:value2'}}",
33
-
34
30
  invalid_script_name_cause: "Invalid script name.",
35
31
  invalid_script_name_help: "Replace or remove unsupported characters. Valid characters "\
36
32
  "are numbers, letters, hyphens, or underscores.",
@@ -42,9 +38,6 @@ module Script
42
38
  no_existing_orgs_cause: "You don't have any partner organizations.",
43
39
  no_existing_orgs_help: "Visit https://partners.shopify.com/ to create a partners account.",
44
40
 
45
- no_existing_stores_cause: "You don't have any stores in your Partner Dashboard.",
46
- no_existing_stores_help: "Visit https://partners.shopify.com/%{organization_id}/stores/ to create one.",
47
-
48
41
  project_exists_cause: "A directory with this same name already exists.",
49
42
  project_exists_help: "Try again and enter a different name for the script.",
50
43
 
@@ -54,9 +47,6 @@ module Script
54
47
  invalid_language_cause: "Invalid language %s.",
55
48
  invalid_language_help: "Allowed values: %s.",
56
49
 
57
- invalid_config: "Can't change the configuration values because %1$s is missing or "\
58
- "it isn't formatted properly.",
59
-
60
50
  missing_script_json_field_cause: "The script.json file is missing the required %s field.",
61
51
  missing_script_json_field_help: "Add the field and try again.",
62
52
 
@@ -91,8 +81,8 @@ module Script
91
81
  system_call_failure_cause: "An error was returned while running {{command:%{cmd}}}.",
92
82
  system_call_failure_help: "Review the following error and try again.\n{{red:%{out}}}",
93
83
 
94
- metadata_validation_cause: "Invalid Script API metadata.",
95
- metadata_validation_help: "Ensure the 'shopify/scripts-toolchain-as' package is up to date.",
84
+ metadata_validation_cause: "The Script API metadata is incorrect.",
85
+ metadata_validation_help: "The 'schemaVersions.major' field contains an unsupported version.",
96
86
 
97
87
  metadata_schema_versions_missing: "Invalid Script metadata:" \
98
88
  " 'schemaVersions' field is missing",
@@ -107,7 +97,6 @@ module Script
107
97
  metadata_not_found_help: "Ensure the 'shopify/scripts-toolchain-as' package is up to date and " \
108
98
  "'package.json' contains a 'scripts/build' entry with a " \
109
99
  "'--metadata build/metadata.json' argument",
110
- app_not_installed_cause: "App not installed on store.",
111
100
 
112
101
  build_error_cause: "Something went wrong while building the script.",
113
102
  build_error_help: "Correct the errors and try again.",
@@ -126,11 +115,6 @@ module Script
126
115
  script_repush_cause: "A version of this script already exists on the app.",
127
116
  script_repush_help: "Use {{cyan:--force}} to replace the existing script.",
128
117
 
129
- shop_auth_cause: "Unable to authenticate with the store.",
130
- shop_auth_help: "Try again.",
131
-
132
- invalid_build_script: "The root package.json contains an invalid build command that " \
133
- "is needed to compile your script to WebAssembly.",
134
118
  build_script_not_found: "The root package.json is missing the build command that " \
135
119
  "is needed to compile your script to WebAssembly.",
136
120
  # rubocop:disable Layout/LineLength
@@ -162,8 +146,7 @@ module Script
162
146
  Usage: {{command:%1$s script create}}
163
147
  Options:
164
148
  {{command:--name=NAME}} Script project name. Use any string.
165
- {{command:--extension-point=TYPE}} Script API name. Allowed values: %2$s.
166
- {{command:--no-config-ui}} Specify this option when you don’t want your script to render an interface in Shopify admin.
149
+ {{command:--api=TYPE}} Script API name. Allowed values: %2$s.
167
150
  HELP
168
151
 
169
152
  error: {
@@ -191,6 +174,19 @@ module Script
191
174
  script_pushed: "{{v}} Script pushed to app (API key: %{api_key}).",
192
175
  },
193
176
 
177
+ javy: {
178
+ help: <<~HELP,
179
+ Compile the JavaScript code into WebAssembly.
180
+ Usage: {{command:%s script javy}}
181
+ Options:
182
+ {{command:--in}} The name of the JavaScript file that will be compiled.
183
+ {{command:--out}} The name of the file that the WebAssembly should be written to.
184
+ HELP
185
+ errors: {
186
+ invalid_arguments: "Javy was run with invalid arguments. Run {{command: %s script javy --help}} for more information.",
187
+ },
188
+ },
189
+
194
190
  project_deps: {
195
191
  none_required: "{{v}} None required",
196
192
  checking_with_npm: "Checking dependencies with npm",
@@ -44,15 +44,6 @@ module Script
44
44
  cause_of_error: ShopifyCLI::Context.message("script.error.invalid_context_cause"),
45
45
  help_suggestion: ShopifyCLI::Context.message("script.error.invalid_context_help"),
46
46
  }
47
- when Errors::InvalidConfigProps
48
- {
49
- cause_of_error: ShopifyCLI::Context.message("script.error.invalid_config_props_cause"),
50
- help_suggestion: ShopifyCLI::Context.message("script.error.invalid_config_props_help"),
51
- }
52
- when Errors::InvalidConfigYAMLError
53
- {
54
- cause_of_error: ShopifyCLI::Context.message("script.error.invalid_config", e.config_file),
55
- }
56
47
  when Layers::Infrastructure::Errors::InvalidLanguageError
57
48
  {
58
49
  cause_of_error: ShopifyCLI::Context.message("script.error.invalid_language_cause", e.language),
@@ -76,14 +67,6 @@ module Script
76
67
  cause_of_error: ShopifyCLI::Context.message("script.error.no_existing_orgs_cause"),
77
68
  help_suggestion: ShopifyCLI::Context.message("script.error.no_existing_orgs_help"),
78
69
  }
79
- when Errors::NoExistingStoresError
80
- {
81
- cause_of_error: ShopifyCLI::Context.message("script.error.no_existing_stores_cause"),
82
- help_suggestion: ShopifyCLI::Context.message(
83
- "script.error.no_existing_stores_help",
84
- organization_id: e.organization_id
85
- ),
86
- }
87
70
  when Layers::Infrastructure::Errors::ScriptProjectAlreadyExistsError
88
71
  {
89
72
  cause_of_error: ShopifyCLI::Context.message("script.error.project_exists_cause"),
@@ -91,7 +74,7 @@ module Script
91
74
  }
92
75
  when Layers::Infrastructure::Errors::DeprecatedEPError
93
76
  {
94
- cause_of_error: ShopifyCLI::Context.message("script.error.deprecated_ep", e.ep),
77
+ cause_of_error: ShopifyCLI::Context.message("script.error.deprecated_ep", e.extension_point),
95
78
  help_suggestion: ShopifyCLI::Context.message("script.error.deprecated_ep_cause"),
96
79
  }
97
80
  when Layers::Domain::Errors::InvalidExtensionPointError
@@ -135,10 +118,6 @@ module Script
135
118
  cause_of_error: ShopifyCLI::Context.message("script.error.no_script_json_file_cause"),
136
119
  help_suggestion: ShopifyCLI::Context.message("script.error.no_script_json_file_help"),
137
120
  }
138
- when Layers::Infrastructure::Errors::AppNotInstalledError
139
- {
140
- cause_of_error: ShopifyCLI::Context.message("script.error.app_not_installed_cause"),
141
- }
142
121
  when Layers::Infrastructure::Errors::BuildError
143
122
  {
144
123
  cause_of_error: ShopifyCLI::Context.message("script.error.build_error_cause"),
@@ -217,21 +196,11 @@ module Script
217
196
  cause_of_error: ShopifyCLI::Context.message("script.error.script_repush_cause"),
218
197
  help_suggestion: ShopifyCLI::Context.message("script.error.script_repush_help"),
219
198
  }
220
- when Layers::Infrastructure::Errors::ShopAuthenticationError
221
- {
222
- cause_of_error: ShopifyCLI::Context.message("script.error.shop_auth_cause"),
223
- help_suggestion: ShopifyCLI::Context.message("script.error.shop_auth_help"),
224
- }
225
199
  when Layers::Infrastructure::Errors::BuildScriptNotFoundError
226
200
  {
227
201
  cause_of_error: ShopifyCLI::Context.message("script.error.build_script_not_found"),
228
202
  help_suggestion: ShopifyCLI::Context.message("script.error.build_script_suggestion"),
229
203
  }
230
- when Layers::Infrastructure::Errors::InvalidBuildScriptError
231
- {
232
- cause_of_error: ShopifyCLI::Context.message("script.error.invalid_build_script"),
233
- help_suggestion: ShopifyCLI::Context.message("script.error.build_script_suggestion"),
234
- }
235
204
  when Layers::Infrastructure::Errors::WebAssemblyBinaryNotFoundError
236
205
  {
237
206
  cause_of_error: ShopifyCLI::Context.message("script.error.web_assembly_binary_not_found"),
@@ -5,7 +5,7 @@ module Theme
5
5
  register_messages(Theme::Messages::MESSAGES)
6
6
  end
7
7
 
8
- class Command < ShopifyCLI::ProjectCommands
8
+ class Command < ShopifyCLI::Command::ProjectCommand
9
9
  subcommand :Init, "init", Project.project_filepath("commands/init")
10
10
  subcommand :Serve, "serve", Project.project_filepath("commands/serve")
11
11
  subcommand :Pull, "pull", Project.project_filepath("commands/pull")
@@ -3,7 +3,7 @@ require "theme_check"
3
3
 
4
4
  module Theme
5
5
  class Command
6
- class Check < ShopifyCLI::SubCommand
6
+ class Check < ShopifyCLI::Command::SubCommand
7
7
  class Options < ShopifyCLI::Options
8
8
  def initialize(theme_check)
9
9
  super()
@@ -4,7 +4,7 @@ require "shopify_cli/theme/development_theme"
4
4
 
5
5
  module Theme
6
6
  class Command
7
- class Delete < ShopifyCLI::SubCommand
7
+ class Delete < ShopifyCLI::Command::SubCommand
8
8
  options do |parser, flags|
9
9
  parser.on("-d", "--development") { flags[:development] = true }
10
10
  parser.on("-a", "--show-all") { flags[:show_all] = true }
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Theme
4
4
  class Command
5
- class Init < ShopifyCLI::SubCommand
5
+ class Init < ShopifyCLI::Command::SubCommand
6
6
  options do |parser, flags|
7
7
  parser.on("-u", "--clone-url URL") { |url| flags[:clone_url] = url }
8
8
  end
@@ -3,7 +3,7 @@ require "theme_check"
3
3
 
4
4
  module Theme
5
5
  class Command
6
- class LanguageServer < ShopifyCLI::SubCommand
6
+ class LanguageServer < ShopifyCLI::Command::SubCommand
7
7
  def call(*)
8
8
  ThemeCheck::LanguageServer.start
9
9
  end
@@ -4,7 +4,7 @@ require "json"
4
4
 
5
5
  module Theme
6
6
  class Command
7
- class Package < ShopifyCLI::SubCommand
7
+ class Package < ShopifyCLI::Command::SubCommand
8
8
  THEME_DIRECTORIES = %w[
9
9
  assets
10
10
  config
@@ -3,7 +3,7 @@ require "shopify_cli/theme/theme"
3
3
 
4
4
  module Theme
5
5
  class Command
6
- class Publish < ShopifyCLI::SubCommand
6
+ class Publish < ShopifyCLI::Command::SubCommand
7
7
  options do |parser, flags|
8
8
  parser.on("-f", "--force") { flags[:force] = true }
9
9
  end
@@ -5,10 +5,11 @@ require "shopify_cli/theme/syncer"
5
5
 
6
6
  module Theme
7
7
  class Command
8
- class Pull < ShopifyCLI::SubCommand
8
+ class Pull < ShopifyCLI::Command::SubCommand
9
9
  options do |parser, flags|
10
10
  parser.on("-n", "--nodelete") { flags[:nodelete] = true }
11
11
  parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
12
+ parser.on("-l", "--live") { flags[:live] = true }
12
13
  parser.on("-x", "--ignore=PATTERN") do |pattern|
13
14
  flags[:ignores] ||= []
14
15
  flags[:ignores] << pattern
@@ -21,6 +22,8 @@ module Theme
21
22
 
22
23
  theme = if (theme_id = options.flags[:theme_id])
23
24
  ShopifyCLI::Theme::Theme.new(@ctx, root: root, id: theme_id)
25
+ elsif options.flags[:live]
26
+ ShopifyCLI::Theme::Theme.live(@ctx, root: root)
24
27
  else
25
28
  form = Forms::Select.ask(
26
29
  @ctx,
@@ -6,10 +6,11 @@ require "shopify_cli/theme/syncer"
6
6
 
7
7
  module Theme
8
8
  class Command
9
- class Push < ShopifyCLI::SubCommand
9
+ class Push < ShopifyCLI::Command::SubCommand
10
10
  options do |parser, flags|
11
11
  parser.on("-n", "--nodelete") { flags[:nodelete] = true }
12
12
  parser.on("-i", "--themeid=ID") { |theme_id| flags[:theme_id] = theme_id }
13
+ parser.on("-l", "--live") { flags[:live] = true }
13
14
  parser.on("-d", "--development") { flags[:development] = true }
14
15
  parser.on("-u", "--unpublished") { flags[:unpublished] = true }
15
16
  parser.on("-j", "--json") { flags[:json] = true }
@@ -27,6 +28,8 @@ module Theme
27
28
 
28
29
  theme = if (theme_id = options.flags[:theme_id])
29
30
  ShopifyCLI::Theme::Theme.new(@ctx, root: root, id: theme_id)
31
+ elsif options.flags[:live]
32
+ ShopifyCLI::Theme::Theme.live(@ctx, root: root)
30
33
  elsif options.flags[:development]
31
34
  theme = ShopifyCLI::Theme::DevelopmentTheme.new(@ctx, root: root)
32
35
  theme.ensure_exists!
@@ -47,7 +50,8 @@ module Theme
47
50
  form.theme
48
51
  end
49
52
 
50
- if theme.live? && !options.flags[:allow_live]
53
+ is_confirm_required = !options.flags[:allow_live] && !options.flags[:live]
54
+ if theme.live? && is_confirm_required
51
55
  return unless CLI::UI::Prompt.confirm(@ctx.message("theme.push.live"))
52
56
  end
53
57
 
@@ -72,6 +76,7 @@ module Theme
72
76
  @ctx.done(@ctx.message("theme.push.done", theme.preview_url, theme.editor_url))
73
77
  end
74
78
  end
79
+ raise ShopifyCLI::AbortSilent if syncer.has_any_error?
75
80
  rescue ShopifyCLI::API::APIRequestNotFoundError
76
81
  @ctx.abort(@ctx.message("theme.push.theme_not_found", theme.id))
77
82
  ensure
@@ -3,17 +3,24 @@ require "shopify_cli/theme/dev_server"
3
3
 
4
4
  module Theme
5
5
  class Command
6
- class Serve < ShopifyCLI::SubCommand
6
+ class Serve < ShopifyCLI::Command::SubCommand
7
+ DEFAULT_HTTP_HOST = "127.0.0.1"
8
+
7
9
  options do |parser, flags|
10
+ parser.on("--host=HOST") { |host| flags[:host] = host.to_s }
8
11
  parser.on("--port=PORT") { |port| flags[:port] = port.to_i }
9
12
  parser.on("--poll") { flags[:poll] = true }
10
13
  end
11
14
 
12
15
  def call(*)
13
16
  flags = options.flags.dup
14
- ShopifyCLI::Theme::DevServer.start(@ctx, ".", **flags) do |syncer|
17
+ host = flags[:host] || DEFAULT_HTTP_HOST
18
+ ShopifyCLI::Theme::DevServer.start(@ctx, ".", host: host, **flags) do |syncer|
15
19
  UI::SyncProgressBar.new(syncer).progress(:upload_theme!, delay_low_priority_files: true)
16
20
  end
21
+ rescue ShopifyCLI::Theme::DevServer::AddressBindingError
22
+ raise ShopifyCLI::Abort,
23
+ ShopifyCLI::Context.message("theme.serve.error.address_binding_error", ShopifyCLI::TOOL_NAME)
17
24
  end
18
25
 
19
26
  def self.help
@@ -58,6 +58,7 @@ module Theme
58
58
 
59
59
  Options:
60
60
  {{command:-i, --themeid=THEMEID}} Theme ID. Must be an existing theme on your store.
61
+ {{command:-l, --live}} Push to your remote live theme, and update your live store.
61
62
  {{command:-d, --development}} Push to your remote development theme, and create it if needed.
62
63
  {{command:-u, --unpublished}} Create a new unpublished theme and push to it.
63
64
  {{command:-n, --nodelete}} Runs the push command without deleting remote files from Shopify.
@@ -94,9 +95,45 @@ module Theme
94
95
  Options:
95
96
  {{command:--port=PORT}} Local port to serve theme preview from
96
97
  {{command:--poll}} Force polling to detect file changes
98
+ {{command:--host=HOST}} Set which network interface the web server listens on. The default value is 127.0.0.1.
97
99
  HELP
98
- serve: "Viewing theme…",
100
+ viewing_theme: "Viewing theme…",
101
+ syncing_theme: "Syncing theme #%s on %s",
99
102
  open_fail: "Couldn't open the theme",
103
+ operation: {
104
+ status: {
105
+ error: "ERROR",
106
+ synced: "Synced",
107
+ fixed: "Fixed",
108
+ },
109
+ },
110
+ error: {
111
+ address_binding_error: "Couldn't bind to localhost."\
112
+ " To serve your theme, set a different address with {{command:%s theme serve --host=<address>}}",
113
+ },
114
+ serving: <<~SERVING,
115
+
116
+ Serving %s
117
+
118
+ SERVING
119
+ customize_or_preview: <<~CUSTOMIZE_OR_PREVIEW,
120
+
121
+ Customize this theme in the Online Store Editor:
122
+ {{green:%s}}
123
+
124
+ Share this theme preview:
125
+ {{green:%s}}
126
+
127
+ (Use Ctrl-C to stop)
128
+ CUSTOMIZE_OR_PREVIEW
129
+ ensure_user: <<~ENSURE_USER,
130
+ You are not authorized to edit themes on %s.
131
+ Make sure you are a user of that store, and allowed to edit themes.
132
+ ENSURE_USER
133
+ already_in_use_error: "Error",
134
+ address_already_in_use: "The address \"%s\" is already in use.",
135
+ try_this: "Try this",
136
+ try_port_option: "Use the --port=PORT option to serve the theme in a different port.",
100
137
  },
101
138
  check: {
102
139
  help: <<~HELP,
@@ -152,6 +189,7 @@ module Theme
152
189
 
153
190
  Options:
154
191
  {{command:-i, --themeid=THEMEID}} The Theme ID. Must be an existing theme on your store.
192
+ {{command:-l, --live}} Pull theme files from your remote live theme.
155
193
  {{command:-n, --nodelete}} Runs the pull command without deleting local files.
156
194
 
157
195
  Run without options to select theme from a list.
@@ -6,14 +6,14 @@ module Theme
6
6
  end
7
7
 
8
8
  def progress(method, **args)
9
- @syncer.delay_errors!
9
+ @syncer.lock_io!
10
10
  CLI::UI::Progress.progress do |bar|
11
11
  @syncer.public_send(method, **args) do |left, total|
12
12
  bar.tick(set_percent: 1 - left.to_f / total)
13
13
  end
14
14
  bar.tick(set_percent: 1)
15
15
  end
16
- @syncer.report_errors!
16
+ @syncer.unlock_io!
17
17
  end
18
18
  end
19
19
  end
@@ -3,7 +3,7 @@ require "optparse"
3
3
 
4
4
  module ShopifyCLI
5
5
  class AdminAPI
6
- class PopulateResourceCommand < ShopifyCLI::SubCommand
6
+ class PopulateResourceCommand < ShopifyCLI::Command::SubCommand
7
7
  DEFAULT_COUNT = 5
8
8
 
9
9
  attr_reader :input
@@ -108,9 +108,14 @@ module ShopifyCLI
108
108
  private
109
109
 
110
110
  def default_headers
111
+ sha = ShopifyCLI.sha
112
+ user_agent = "Shopify CLI; v=#{ShopifyCLI::VERSION}"
113
+ sec_ch_ua = user_agent
114
+ sec_ch_ua += " sha=#{sha}" unless sha.nil?
115
+
111
116
  {
112
- "User-Agent" => "Shopify CLI; v=#{ShopifyCLI::VERSION}",
113
- "Sec-CH-UA" => "Shopify CLI; v=#{ShopifyCLI::VERSION} sha=#{ShopifyCLI.sha}",
117
+ "User-Agent" => user_agent,
118
+ "Sec-CH-UA" => sec_ch_ua,
114
119
  "Sec-CH-UA-PLATFORM" => ctx.os.to_s,
115
120
  "X-Request-Id" => SecureRandom.uuid,
116
121
  }.tap do |headers|
@@ -3,30 +3,34 @@ require "json"
3
3
  module ShopifyCLI
4
4
  class AppTypeDetector
5
5
  Error = Class.new(StandardError)
6
+ MissingShopifyCLIYamlError = Class.new(Error)
6
7
  TypeNotFoundError = Class.new(Error)
7
-
8
- def self.detect(project_directory:)
9
- return :node if node?(project_directory: project_directory)
10
- return :rails if rails?(project_directory: project_directory)
11
- return :php if php?(project_directory: project_directory)
12
- raise TypeNotFoundError, "Couldn't detect the project type in directory: #{project_directory}"
13
- end
14
-
15
- def self.node?(project_directory:)
16
- package_json_path = File.join(project_directory, "package.json")
17
- return false unless File.exist?(package_json_path)
18
- package_json = JSON.parse(File.read(package_json_path))
19
- !package_json.dig("scripts", "dev").nil?
8
+ class InvalidTypeError < Error
9
+ attr_reader :project_type
10
+ def initialize(message, project_type:)
11
+ @project_type = project_type
12
+ super(message)
13
+ end
20
14
  end
21
15
 
22
- def self.rails?(project_directory:)
23
- rails_binstub_path = File.join(project_directory, "bin/rails")
24
- File.exist?(rails_binstub_path)
25
- end
16
+ def self.detect(project_directory:)
17
+ require "yaml" # takes 20ms, so deferred as late as possible.
26
18
 
27
- def self.php?(project_directory:)
28
- bootstrap_app_path = File.join(project_directory, "bootstrap/app.php")
29
- File.exist?(bootstrap_app_path)
19
+ shopify_cli_yml_path = File.join(project_directory, Constants::Files::SHOPIFY_CLI_YML)
20
+ unless File.exist?(shopify_cli_yml_path)
21
+ raise MissingShopifyCLIYamlError,
22
+ "#{Constants::Files::SHOPIFY_CLI_YML} was not found in directory #{project_directory}"
23
+ end
24
+ shopify_cli = YAML.load_file(shopify_cli_yml_path)
25
+ case shopify_cli["project_type"]&.to_sym
26
+ when :node, :rails, :php
27
+ shopify_cli["project_type"].to_sym
28
+ when nil
29
+ raise TypeNotFoundError, "Couldn't detect the project type in directory: #{project_directory}"
30
+ else
31
+ raise InvalidTypeError.new("The project found '' is not supported",
32
+ project_type: shopify_cli["project_type"])
33
+ end
30
34
  end
31
35
  end
32
36
  end
@@ -1,6 +1,16 @@
1
1
  module ShopifyCLI
2
2
  class Command
3
3
  class AppSubCommand < SubCommand
4
+ def detect_app(directory: Dir.pwd)
5
+ AppTypeDetector.detect(project_directory: directory)
6
+ rescue ShopifyCLI::AppTypeDetector::TypeNotFoundError
7
+ raise ShopifyCLI::Abort, @ctx.message("core.app.error.type_not_found", directory)
8
+ rescue ShopifyCLI::AppTypeDetector::MissingShopifyCLIYamlError
9
+ raise ShopifyCLI::Abort, @ctx.message("core.app.error.missing_shopify_cli_yml", directory)
10
+ rescue ShopifyCLI::AppTypeDetector::InvalidTypeError => error
11
+ raise ShopifyCLI::Abort, @ctx.message("core.app.error.invalid_project_type", error.project_type)
12
+ end
13
+
4
14
  class << self
5
15
  def call_help(*)
6
16
  output = help