shopify-cli 2.24.0 → 2.25.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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +1 -1
  5. data/lib/project_types/extension/commands/serve.rb +57 -3
  6. data/lib/project_types/extension/extension_project.rb +8 -1
  7. data/lib/project_types/extension/loaders/project.rb +3 -2
  8. data/lib/project_types/extension/messages/messages.rb +21 -6
  9. data/lib/project_types/extension/models/server_config/development_renderer.rb +1 -1
  10. data/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +18 -6
  11. data/lib/project_types/theme/commands/serve.rb +15 -3
  12. data/lib/project_types/theme/messages/messages.rb +4 -2
  13. data/lib/shopify_cli/commands/logout.rb +13 -2
  14. data/lib/shopify_cli/environment.rb +1 -1
  15. data/lib/shopify_cli/file_system_listener.rb +30 -0
  16. data/lib/shopify_cli/git.rb +116 -33
  17. data/lib/shopify_cli/identity_auth.rb +1 -0
  18. data/lib/shopify_cli/project.rb +1 -1
  19. data/lib/shopify_cli/tasks/ensure_project_type.rb +3 -1
  20. data/lib/shopify_cli/theme/dev_server/cdn_fonts.rb +1 -1
  21. data/lib/shopify_cli/theme/dev_server/certificate_manager.rb +1 -1
  22. data/lib/shopify_cli/theme/dev_server/errors.rb +9 -0
  23. data/lib/shopify_cli/theme/dev_server/header_hash.rb +1 -1
  24. data/lib/shopify_cli/theme/dev_server/hooks/file_change_hook.rb +77 -0
  25. data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_deleter.rb +1 -1
  26. data/lib/shopify_cli/theme/dev_server/hot_reload/remote_file_reloader.rb +1 -1
  27. data/lib/shopify_cli/theme/dev_server/{hot-reload-no-script.html → hot_reload/resources/hot-reload-no-script.html} +0 -0
  28. data/lib/shopify_cli/theme/dev_server/hot_reload/resources/hot_reload.js +48 -0
  29. data/lib/shopify_cli/theme/dev_server/hot_reload/resources/sse_client.js +43 -0
  30. data/lib/shopify_cli/theme/dev_server/hot_reload/resources/theme.js +114 -0
  31. data/lib/shopify_cli/theme/dev_server/hot_reload/resources/theme_extension.js +121 -0
  32. data/lib/shopify_cli/theme/dev_server/hot_reload/script_injector.rb +57 -0
  33. data/lib/shopify_cli/theme/dev_server/hot_reload/sections_index.rb +1 -1
  34. data/lib/shopify_cli/theme/dev_server/hot_reload.rb +8 -76
  35. data/lib/shopify_cli/theme/dev_server/local_assets.rb +28 -28
  36. data/lib/shopify_cli/theme/dev_server/proxy.rb +33 -25
  37. data/lib/shopify_cli/theme/dev_server/proxy_param_builder.rb +82 -0
  38. data/lib/shopify_cli/theme/dev_server/reload_mode.rb +1 -1
  39. data/lib/shopify_cli/theme/dev_server/remote_watcher/json_files_update_job.rb +1 -1
  40. data/lib/shopify_cli/theme/dev_server/remote_watcher.rb +1 -1
  41. data/lib/shopify_cli/theme/dev_server/sse.rb +1 -1
  42. data/lib/shopify_cli/theme/dev_server/watcher.rb +8 -9
  43. data/lib/shopify_cli/theme/dev_server/web_server.rb +1 -1
  44. data/lib/shopify_cli/theme/dev_server.rb +287 -99
  45. data/lib/shopify_cli/theme/extension/app_extension.rb +40 -0
  46. data/lib/shopify_cli/theme/extension/dev_server/hooks/file_change_hook.rb +68 -0
  47. data/lib/shopify_cli/theme/extension/dev_server/hot_reload/script_injector.rb +30 -0
  48. data/lib/shopify_cli/theme/extension/dev_server/hot_reload.rb +13 -0
  49. data/lib/shopify_cli/theme/extension/dev_server/local_assets.rb +30 -0
  50. data/lib/shopify_cli/theme/{dev_server/proxy/template_param_builder.rb → extension/dev_server/proxy_param_builder.rb} +26 -16
  51. data/lib/shopify_cli/theme/extension/dev_server/watcher.rb +47 -0
  52. data/lib/shopify_cli/theme/extension/dev_server.rb +150 -0
  53. data/lib/shopify_cli/theme/extension/host_theme.rb +104 -0
  54. data/lib/shopify_cli/theme/extension/syncer/extension_serve_job.rb +133 -0
  55. data/lib/shopify_cli/theme/extension/syncer/operation.rb +21 -0
  56. data/lib/shopify_cli/theme/extension/syncer.rb +81 -0
  57. data/lib/shopify_cli/theme/extension/ui/host_theme_progress_bar.rb +35 -0
  58. data/lib/shopify_cli/theme/ignore_helper.rb +31 -0
  59. data/lib/shopify_cli/theme/root.rb +62 -0
  60. data/lib/shopify_cli/theme/syncer.rb +12 -6
  61. data/lib/shopify_cli/theme/theme.rb +10 -52
  62. data/lib/shopify_cli/version.rb +1 -1
  63. metadata +27 -7
  64. data/.github/workflows/triage.yml +0 -22
  65. data/lib/shopify_cli/theme/dev_server/hot-reload.js +0 -194
  66. data/lib/shopify_cli/theme/syncer/ignore_helper.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b181be543b5e3f2d630f728672a49eba4ca45a2bf3515cb795ba4d3ffd816d5a
4
- data.tar.gz: ebe6ae92838de46c6ffbb3d0b311a6bf9f84619908e0fbd4dad0fdba84f5adba
3
+ metadata.gz: dadea44d5b05c23d4e74988aac50cae2713ee0809ad3b92b0a32a2ae51915a35
4
+ data.tar.gz: efaccb6aee8feb1bd714f4b6d2fa2ded67e522ac49eedaaa592fc3dbc066b04d
5
5
  SHA512:
6
- metadata.gz: b8ebcc3d7cc5d7e4ef5c30ff3286a9a853d5b7b8144b8c7f00adb844a4ebaad93b6b5cf9d66ddcd2a33b94378f7b66a0c9a4966c5d5687588dc867769f0d4cd0
7
- data.tar.gz: d5d6c740f02d5e0b6fb40a7c357347a08ef770355908d4418959e89e6b016075bb638722abaa8649ee3a6475391885016e373ea9624459b2a63ce8c8f7891d7f
6
+ metadata.gz: d283f76d8b288ad25487a50f86ff9482abef14f4d9ad7d95e006ea36a976f4cfb2410235e5a18cf12ea5c19d225117fd0516735b574c7c63551809422a102e19
7
+ data.tar.gz: 63e45bf4543a6743859c02a766e8b61c682703145d6230410d5200f76d9ca207c7ae996a1dc89dd8e918dfbb21d17aa17dd834f694df274f96a9fab29a4af606
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@ From version 2.6.0, the sections in this file adhere to the [keep a changelog](h
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## Version 2.25.0 - 2022-09-14
6
+
7
+ ### Added
8
+ * [#2600](https://github.com/Shopify/shopify-cli/pull/2600): Add support to the `SIGTERM` signal
9
+ * [#2602](https://github.com/Shopify/shopify-cli/pull/2602): Add `--only/--ignore` support to the `theme serve` command
10
+
11
+ ### Fixed
12
+ * [#2607](https://github.com/Shopify/shopify-cli/pull/2607): Fix proxy to redirect to host and port set by cli
13
+
5
14
  ## Version 2.24.0 - 2022-08-29
6
15
 
7
16
  ### Fixed
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shopify-cli (2.24.0)
4
+ shopify-cli (2.25.0)
5
5
  bugsnag (~> 6.22)
6
6
  listen (~> 3.7.0)
7
7
  theme-check (~> 1.11.0)
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Shopify CLI 2.0
2
2
 
3
- <a href=""><img src="https://github.com/shopify/shopify-cli/workflows/CI/badge.svg" alt="Shopify"></a>
3
+ <a href="https://github.com/Shopify/shopify-cli/actions/workflows/shopify.yml"><img src="https://github.com/shopify/shopify-cli/actions/workflows/shopify.yml/badge.svg" alt="Shopify"></a>
4
4
  <img src="https://img.shields.io/github/v/release/shopify/shopify-cli?include_prereleases&style=flat-square" alt="Latest Version">
5
5
  <img src="https://img.shields.io/github/forks/shopify/shopify-cli?style=flat-square" alt="GitHub forks">
6
6
  <img src="https://img.shields.io/github/stars/shopify/shopify-cli?style=flat-square" alt="GitHub stars">
@@ -17,6 +17,24 @@ module Extension
17
17
  parser.on("-p", "--port=PORT", "Specify the port to use") do |port|
18
18
  flags[:port] = port.to_i
19
19
  end
20
+ parser.on("-T", "--theme=NAME_OR_ID", "Theme ID or name of the theme app extension host theme.") do |theme|
21
+ flags[:theme] = theme
22
+ end
23
+ parser.on("--api-key=API_KEY", "Connect your extension and app by inserting your app's API key") do |api_key|
24
+ flags[:api_key] = api_key.gsub('"', "")
25
+ end
26
+ parser.on("--api-secret=API_SECRET", "The API secret of the app the script is registered with.") do |api_secret|
27
+ flags[:api_secret] = api_secret.gsub('"', "")
28
+ end
29
+ parser.on("--extension-id=EXTENSION_ID", "The id of the extension's registration.") do |registration_id|
30
+ flags[:registration_id] = registration_id.gsub('"', "")
31
+ end
32
+ parser.on("--extension-title=EXTENSION_TITLE", "The title of the extension") do |extension_title|
33
+ flags[:extension_title] = extension_title.gsub('"', "")
34
+ end
35
+ parser.on("--extension-type=EXTENSION_TYPE", "The type of the extension") do |extension_type|
36
+ flags[:extension_type] = extension_type.gsub('"', "")
37
+ end
20
38
  end
21
39
 
22
40
  class RuntimeConfiguration
@@ -26,13 +44,27 @@ module Extension
26
44
  property :resource_url, accepts: String, default: nil
27
45
  property! :tunnel_requested, accepts: [true, false], reader: :tunnel_requested?, default: true
28
46
  property :port, accepts: (1...(2**16))
47
+ property :theme, accepts: String, default: nil
48
+ property :api_key, accepts: String, default: nil
49
+ property :api_secret, accepts: String, default: nil
50
+ property :registration_id, accepts: String, default: nil
51
+ property :extension_title, accepts: String, default: nil
52
+ property :extension_type, accepts: String, default: nil
29
53
  end
30
54
 
31
- def call(_args, _command_name)
55
+ def call(args, _command_name)
56
+ @ctx.root = args.first || @ctx.root
57
+
32
58
  config = RuntimeConfiguration.new(
33
59
  tunnel_requested: tunnel_requested?,
34
60
  resource_url: options.flags[:resource_url],
35
- port: options.flags[:port]
61
+ port: options.flags[:port],
62
+ theme: options.flags[:theme],
63
+ api_key: options.flags[:api_key],
64
+ api_secret: options.flags[:api_secret],
65
+ registration_id: options.flags[:registration_id],
66
+ extension_title: options.flags[:extension_title],
67
+ extension_type: options.flags[:extension_type],
36
68
  )
37
69
 
38
70
  ShopifyCLI::Result
@@ -49,6 +81,23 @@ module Extension
49
81
 
50
82
  private
51
83
 
84
+ def project
85
+ return super unless options.flags[:extension_type]
86
+
87
+ @project ||= Extension::Loaders::Project.load(
88
+ context: options.flags[:context],
89
+ directory: @ctx.root,
90
+ api_key: options.flags[:api_key],
91
+ api_secret: options.flags[:api_secret],
92
+ registration_id: options.flags[:registration_id],
93
+ env: {
94
+ ExtensionProjectKeys::TITLE_KEY => options.flags[:extension_title],
95
+ ExtensionProjectKeys::REGISTRATION_ID_KEY => options.flags[:registration_id],
96
+ ExtensionProjectKeys::SPECIFICATION_IDENTIFIER_KEY => options.flags[:extension_type],
97
+ }
98
+ )
99
+ end
100
+
52
101
  def tunnel_requested?
53
102
  tunnel = options.flags[:tunnel]
54
103
  tunnel.nil? || !!tunnel
@@ -87,7 +136,12 @@ module Extension
87
136
  context: @ctx,
88
137
  tunnel_url: runtime_configuration.tunnel_url,
89
138
  port: runtime_configuration.port,
90
- resource_url: runtime_configuration.resource_url
139
+ theme: runtime_configuration.theme,
140
+ api_key: runtime_configuration.api_key,
141
+ api_secret: runtime_configuration.api_secret,
142
+ registration_id: runtime_configuration.registration_id,
143
+ resource_url: runtime_configuration.resource_url,
144
+ project: project,
91
145
  )
92
146
  runtime_configuration
93
147
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "shopify_cli"
3
+ require "shopify_cli/environment"
3
4
  require "securerandom"
4
5
 
5
6
  module Extension
@@ -82,7 +83,13 @@ module Extension
82
83
  end
83
84
 
84
85
  def specification_identifier
85
- config[ExtensionProjectKeys::SPECIFICATION_IDENTIFIER_KEY]
86
+ key = ExtensionProjectKeys::SPECIFICATION_IDENTIFIER_KEY
87
+
88
+ if ShopifyCLI::Environment.run_as_subprocess?
89
+ get_extra_field(key)
90
+ else
91
+ config[key]
92
+ end
86
93
  end
87
94
 
88
95
  def registration_id?
@@ -3,12 +3,13 @@
3
3
  module Extension
4
4
  module Loaders
5
5
  module Project
6
- def self.load(context:, directory:, api_key:, registration_id:, api_secret:)
6
+ def self.load(context:, directory:, api_key:, registration_id:, api_secret:, env: {})
7
7
  env_overrides = {
8
8
  "SHOPIFY_API_KEY" => api_key,
9
9
  "SHOPIFY_API_SECRET" => api_secret,
10
10
  "EXTENSION_ID" => registration_id,
11
- }.compact
11
+ }.compact.merge(env)
12
+
12
13
  env_file_present = env_file_exists?(directory)
13
14
  env = if env_file_present
14
15
  ShopifyCLI::Resources::EnvFile.read(directory, overrides: env_overrides)
@@ -102,15 +102,32 @@ module Extension
102
102
  serve: {
103
103
  help: <<~HELP,
104
104
  Serve your extension in a local simulator for development.
105
- Usage: {{command:%s extension serve}}
105
+ Usage: {{command:%s extension serve [ ROOT ]}}
106
106
  Options:
107
- {{command:--tunnel=TUNNEL}} Establish an ngrok tunnel (default: false)
107
+ {{command:-p, --port=PORT}} Local port of the development serve.
108
+ {{command:-T, --theme=NAME_OR_ID}} Theme ID or name of the host theme.
109
+ {{command:--tunnel=TUNNEL}} Establish an ngrok tunnel (default: false).
110
+ {{command:--api-key=API_KEY}} Connect your extension and app by inserting your app's API key (which you can get from your app setup page on shopify.dev).
111
+ {{command:--api-secret=API_SECRET}} The API secret of the app the script is registered with.
112
+ {{command:--extension-id=EXTENSION_ID}} The id of the extension's registration.
108
113
  HELP
109
- frame_title: "Serving extension…",
114
+ frame_title: "Viewing extension…",
110
115
  no_available_ports_found: "No available ports found to run extension.",
111
116
  serve_failure_message: "Failed to run extension code.",
112
117
  serve_missing_information: "Missing shop or api_key.",
113
118
  tunnel_already_running: "A tunnel running on another port has been detected. Close the tunnel and try again.",
119
+ preview_message: <<~PREVIEW_MESSAGE,
120
+ Enable your theme app extension:
121
+ {{green:%s}}
122
+
123
+ Setup your theme app extension in the host theme:
124
+ {{green:%s}}
125
+
126
+ Preview your theme app extension:
127
+ {{green:%s}}
128
+
129
+ (Use Ctrl-C to stop)
130
+ PREVIEW_MESSAGE
114
131
  },
115
132
  tunnel: {
116
133
  duplicate_session: <<~MESSAGE,
@@ -267,9 +284,7 @@ module Extension
267
284
  {{*}} You’re ready to start building {{green:%s}}!
268
285
  MESSAGE
269
286
  },
270
- serve: {
271
- unsupported: "shopify extension serve is not supported for theme app extensions",
272
- },
287
+
273
288
  },
274
289
  },
275
290
  }
@@ -25,7 +25,7 @@ module Extension
25
25
  when "checkout_post_purchase"
26
26
  new(name: "@shopify/post-purchase-ui-extensions", version: "^0.13.2")
27
27
  when "pos_ui_extension"
28
- new(name: "@shopify/retail-ui-extensions", version: "^0.1.0")
28
+ new(name: "@shopify/retail-ui-extensions", version: "^0.12.0")
29
29
  when "web_pixel_extension"
30
30
  nil
31
31
  else
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require "base64"
3
3
  require "json"
4
+ require "shopify_cli/theme/extension/dev_server"
4
5
 
5
6
  module Extension
6
7
  module Models
@@ -64,16 +65,27 @@ module Extension
64
65
  "Theme App Extension"
65
66
  end
66
67
 
67
- def choose_port?(ctx)
68
- ctx.abort(ctx.message("serve.unsupported"))
68
+ def choose_port?(_ctx)
69
+ false
69
70
  end
70
71
 
71
- def establish_tunnel?(ctx)
72
- ctx.abort(ctx.message("serve.unsupported"))
72
+ def establish_tunnel?(_ctx)
73
+ false
73
74
  end
74
75
 
75
- def serve(ctx)
76
- ctx.abort(ctx.message("serve.unsupported"))
76
+ def serve(**options)
77
+ @ctx = options[:context]
78
+ root = options[:context]&.root
79
+ project = options[:project]
80
+ properties = options
81
+ .slice(:port, :theme)
82
+ .compact
83
+ .merge({
84
+ project: project,
85
+ specification_handler: self,
86
+ })
87
+
88
+ ShopifyCLI::Theme::Extension::DevServer.start(@ctx, root, **properties)
77
89
  end
78
90
 
79
91
  private
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
  require "shopify_cli/theme/dev_server"
3
3
  require "project_types/theme/commands/common/root_helper"
4
+ require "shopify_cli/theme/ignore_filter"
5
+ require "shopify_cli/theme/include_filter"
6
+ require "project_types/theme/conversions/include_glob"
7
+ require "project_types/theme/conversions/ignore_glob"
4
8
 
5
9
  module Theme
6
10
  class Command
@@ -12,6 +16,9 @@ module Theme
12
16
  DEFAULT_HTTP_HOST = "127.0.0.1"
13
17
 
14
18
  options do |parser, flags|
19
+ Conversions::IncludeGlob.register(parser)
20
+ Conversions::IgnoreGlob.register(parser)
21
+
15
22
  parser.on("--host=HOST") { |host| flags[:host] = host.to_s }
16
23
  parser.on("--port=PORT") { |port| flags[:port] = port.to_i }
17
24
  parser.on("--poll") { flags[:poll] = true }
@@ -19,6 +26,14 @@ module Theme
19
26
  parser.on("--theme-editor-sync") { flags[:editor_sync] = true }
20
27
  parser.on("--stable") { flags[:stable] = true }
21
28
  parser.on("-t", "--theme=NAME_OR_ID") { |theme| flags[:theme] = theme }
29
+ parser.on("-o", "--only=PATTERN", Conversions::IncludeGlob) do |pattern|
30
+ flags[:includes] ||= []
31
+ flags[:includes] |= pattern
32
+ end
33
+ parser.on("-x", "--ignore=PATTERN", Conversions::IgnoreGlob) do |pattern|
34
+ flags[:ignores] ||= []
35
+ flags[:ignores] |= pattern
36
+ end
22
37
  end
23
38
 
24
39
  def call(_args, name)
@@ -31,9 +46,6 @@ module Theme
31
46
  ShopifyCLI::Theme::DevServer.start(@ctx, root, host: host, **flags) do |syncer|
32
47
  UI::SyncProgressBar.new(syncer).progress(:upload_theme!, delay_low_priority_files: true)
33
48
  end
34
- rescue ShopifyCLI::Theme::DevServer::AddressBindingError
35
- raise ShopifyCLI::Abort,
36
- ShopifyCLI::Context.message("theme.serve.error.address_binding_error", ShopifyCLI::TOOL_NAME)
37
49
  end
38
50
 
39
51
  def self.as_reload_mode(mode)
@@ -138,6 +138,8 @@ module Theme
138
138
  viewing_theme: "Viewing theme…",
139
139
  syncing_theme: "Syncing theme #%s on %s",
140
140
  open_fail: "Couldn't open the theme",
141
+ stop_signal: "Stop signal: \"%s\"",
142
+ stopping: "Stopping…",
141
143
  auth: {
142
144
  error_message: <<~ERROR_MESSAGE,
143
145
  It looks like you are using credentials that do not work with {{command:%s theme serve}}.
@@ -230,8 +232,6 @@ module Theme
230
232
  },
231
233
  },
232
234
  error: {
233
- address_binding_error: "Couldn't bind to localhost."\
234
- " To serve your theme, set a different address with {{command:%s theme serve --host=<address>}}",
235
235
  invalid_subdirectory: <<~MESSAGE,
236
236
  The presence of %s in the directory structure isn't supported.
237
237
 
@@ -262,6 +262,8 @@ module Theme
262
262
  ENSURE_USER
263
263
  address_already_in_use: "The address \"%s\" is already in use.",
264
264
  try_port_option: "Use the --port=PORT option to serve the theme in a different port.",
265
+ binding_error: "Couldn't bind to localhost." \
266
+ " To serve your theme, set a different address with {{command:%s theme serve --host=<address>}}",
265
267
  },
266
268
  check: {
267
269
  help: <<~HELP,
@@ -1,11 +1,14 @@
1
1
  require "shopify_cli"
2
2
  require "shopify_cli/theme/development_theme"
3
+ require "shopify_cli/theme/extension/host_theme"
3
4
 
4
5
  module ShopifyCLI
5
6
  module Commands
6
7
  class Logout < ShopifyCLI::Command
7
8
  def call(*)
8
9
  try_delete_development_theme
10
+ try_delete_host_theme
11
+
9
12
  ShopifyCLI::IdentityAuth.delete_tokens_and_keys
10
13
  ShopifyCLI::DB.del(:shop) if has_shop?
11
14
  ShopifyCLI::DB.del(:organization_id) if has_organization_id?
@@ -31,8 +34,16 @@ module ShopifyCLI
31
34
  return unless has_shop?
32
35
 
33
36
  ShopifyCLI::Theme::DevelopmentTheme.delete(@ctx)
34
- rescue ShopifyCLI::API::APIRequestError, ShopifyCLI::Abort, ShopifyCLI::AbortSilent
35
- # Ignore since we can't delete it
37
+ rescue ShopifyCLI::API::APIRequestError, ShopifyCLI::Abort, ShopifyCLI::AbortSilent => e
38
+ @ctx.debug("[Logout Error]: #{e.message}")
39
+ end
40
+
41
+ def try_delete_host_theme
42
+ return unless has_shop?
43
+
44
+ ShopifyCLI::Theme::Extension::HostTheme.delete(@ctx)
45
+ rescue ShopifyCLI::API::APIRequestError, ShopifyCLI::Abort, ShopifyCLI::AbortSilent => e
46
+ @ctx.debug("[Logout Error]: #{e.message}")
36
47
  end
37
48
  end
38
49
  end
@@ -159,7 +159,7 @@ module ShopifyCLI
159
159
  env_variable_truthy?(
160
160
  Constants::EnvironmentVariables::MONORAIL_REAL_EVENTS,
161
161
  env_variables: env_variables
162
- )
162
+ ) && !run_as_subprocess?(env_variables: env_variables)
163
163
  end
164
164
 
165
165
  def self.auth_token(env_variables: ENV)
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ require "listen"
3
+ require "observer"
4
+
5
+ module ShopifyCLI
6
+ class FileSystemListener
7
+ include Observable
8
+
9
+ def initialize(root:, force_poll:, ignore_regex:)
10
+ @root = root
11
+ @force_poll = force_poll
12
+ @ignore_regex = ignore_regex
13
+
14
+ @listener = Listen.to(@root, force_polling: @force_poll, ignore: @ignore_regex) do |updated, added, removed|
15
+ changed
16
+ notify_observers(updated, added, removed)
17
+ end
18
+ end
19
+
20
+ def start
21
+ @listener.start
22
+ rescue ArgumentError
23
+ # Ignore errors during the transition of 'listen' events
24
+ end
25
+
26
+ def stop
27
+ @listener.stop
28
+ end
29
+ end
30
+ end
@@ -46,12 +46,77 @@ module ShopifyCLI
46
46
  end
47
47
 
48
48
  ##
49
- # will make calls to git to clone a new repo into a supplied destination,
50
- # it will also output progress of the cloning process.
49
+ # returns array with components of git clone command
51
50
  #
52
51
  # #### Parameters
53
52
  #
54
- # * `repository` - a git url for git to clone the repo from
53
+ # * `repo` - repo url without branch name
54
+ # * `dest` - a filepath to where the repo should be cloned to
55
+ # * `branch` - branch name when cloning
56
+ #
57
+ # #### Returns
58
+ #
59
+ # * array of strings
60
+ #
61
+ # #### Example
62
+ #
63
+ # ["clone", "--single-branch", "--branch", "test-branch", "test-app"]
64
+ #
65
+ def git_clone_command(repo, dest, branch)
66
+ if branch
67
+ ["clone", "--single-branch", "--branch", branch, repo, dest]
68
+ else
69
+ ["clone", "--single-branch", repo, dest]
70
+ end
71
+ end
72
+
73
+ ##
74
+ # calls git to clone a new repo into a supplied destination,
75
+ # it will also call a supplied block with the percentage of clone completion
76
+ #
77
+ # #### Parameters
78
+ #
79
+ # * `repo_with_branch` - a git url for git to clone the repo from
80
+ # * `dest` - a filepath to where the repo should be cloned to
81
+ # * `ctx` - the current running context of your command, defaults to a new context.
82
+ #
83
+ # #### Returns
84
+ #
85
+ # * `sha_string` - string of the sha of the most recent commit to the repo
86
+ #
87
+ # #### Example
88
+ #
89
+ # ShopifyCLI::Git.raw_clone('git@github.com:shopify/test.git', 'test-app')
90
+ #
91
+ def raw_clone(repo_with_branch, dest, ctx: Context.new)
92
+ if Dir.exist?(dest) && !Dir.empty?(dest)
93
+ ctx.abort(ctx.message("core.git.error.directory_exists"))
94
+ else
95
+ msg = []
96
+ # require at usage point to not slow down CLI startup
97
+ # https://github.com/Shopify/shopify-cli/pull/698#discussion_r444342445
98
+ require "open3"
99
+
100
+ repo, branch = repo_with_branch.split("#")
101
+ git_cmd = git_clone_command(repo, dest, branch)
102
+
103
+ success = Open3.popen3("git", *git_cmd, "--progress") do |_stdin, _stdout, stderr, thread|
104
+ msg = clone_progress(stderr, bar: nil)
105
+
106
+ thread.value
107
+ end.success?
108
+
109
+ ctx.abort((msg.join("\n"))) unless success
110
+ end
111
+ end
112
+
113
+ ##
114
+ # calls git to clone a new repo into a supplied destination,
115
+ # it will also output progress of the cloning process into a new progress bar
116
+ #
117
+ # #### Parameters
118
+ #
119
+ # * `repo_with_branch` - a git url for git to clone the repo from
55
120
  # * `dest` - a filepath to where the repo should be cloned to
56
121
  # * `ctx` - the current running context of your command, defaults to a new context.
57
122
  #
@@ -63,17 +128,30 @@ module ShopifyCLI
63
128
  #
64
129
  # ShopifyCLI::Git.clone('git@github.com:shopify/test.git', 'test-app')
65
130
  #
66
- def clone(repository, dest, ctx: Context.new)
67
- if Dir.exist?(dest)
131
+ def clone(repo_with_branch, dest, ctx: Context.new)
132
+ if Dir.exist?(dest) && !Dir.empty?(dest)
68
133
  ctx.abort(ctx.message("core.git.error.directory_exists"))
69
134
  else
70
- repo, branch = repository.split("#")
135
+ msg = []
136
+ # require at usage point to not slow down CLI startup
137
+ # https://github.com/Shopify/shopify-cli/pull/698#discussion_r444342445
138
+ require "open3"
139
+
140
+ repo, branch = repo_with_branch.split("#")
141
+ git_cmd = git_clone_command(repo, dest, branch)
142
+
71
143
  success_message = ctx.message("core.git.cloned", dest)
144
+
72
145
  CLI::UI::Frame.open(ctx.message("core.git.cloning", repo, dest), success_text: success_message) do
73
- if branch
74
- clone_progress("clone", "--single-branch", "--branch", branch, repo, dest, ctx: ctx)
75
- else
76
- clone_progress("clone", "--single-branch", repo, dest, ctx: ctx)
146
+ CLI::UI::Progress.progress do |bar|
147
+ success = Open3.popen3("git", *git_cmd, "--progress") do |_stdin, _stdout, stderr, thread|
148
+ msg = clone_progress(stderr, bar: bar)
149
+
150
+ thread.value
151
+ end.success?
152
+
153
+ ctx.abort((msg.join("\n"))) unless success
154
+ bar.tick(set_percent: 1.0)
77
155
  end
78
156
  end
79
157
  end
@@ -197,6 +275,34 @@ module ShopifyCLI
197
275
  end
198
276
  end
199
277
 
278
+ ##
279
+ # handles showing the progress of the git clone command.
280
+ # if block given, assumes passing percent to block, otherwise
281
+ # increments bar for progress bar
282
+ #
283
+ # #### Parameters
284
+ #
285
+ # * `stderr` - Open3.popen3 output stream
286
+ # * `bar` - progress bar object to set percent
287
+ #
288
+ def clone_progress(stderr, bar: nil)
289
+ msg = []
290
+
291
+ while (line = stderr.gets)
292
+ msg << line.chomp
293
+ next unless line.strip.start_with?("Receiving objects:")
294
+ percent = (line.match(/Receiving objects:\s+(\d+)/)[1].to_f / 100).round(2)
295
+
296
+ if block_given?
297
+ yield percent
298
+ elsif !bar.nil?
299
+ bar.tick(set_percent: percent)
300
+ end
301
+ end
302
+
303
+ msg
304
+ end
305
+
200
306
  private
201
307
 
202
308
  def exec(*args, dir: Dir.pwd, default: nil, ctx: Context.new)
@@ -209,29 +315,6 @@ module ShopifyCLI
209
315
  def rev_parse(*args, dir: nil, ctx: Context.new)
210
316
  exec("rev-parse", *args, dir: dir, ctx: ctx)
211
317
  end
212
-
213
- def clone_progress(*git_command, ctx:)
214
- CLI::UI::Progress.progress do |bar|
215
- msg = []
216
- require "open3"
217
-
218
- success = Open3.popen3("git", *git_command, "--progress") do |_stdin, _stdout, stderr, thread|
219
- while (line = stderr.gets)
220
- msg << line.chomp
221
- next unless line.strip.start_with?("Receiving objects:")
222
- percent = (line.match(/Receiving objects:\s+(\d+)/)[1].to_f / 100).round(2)
223
- bar.tick(set_percent: percent)
224
- next
225
- end
226
-
227
- thread.value
228
- end.success?
229
-
230
- ctx.abort(msg.join("\n")) unless success
231
- bar.tick(set_percent: 1.0)
232
- success
233
- end
234
- end
235
318
  end
236
319
  end
237
320
  end
@@ -87,6 +87,7 @@ module ShopifyCLI
87
87
 
88
88
  def fetch_or_auth_partners_token
89
89
  if EnvAuthToken.partners_token_present?
90
+ return Environment.auth_token if Environment.run_as_subprocess?
90
91
  return EnvAuthToken.fetch_exchanged_partners_token do |env_token|
91
92
  exchange_partners_auth_token(env_token)
92
93
  end
@@ -109,7 +109,7 @@ module ShopifyCLI
109
109
 
110
110
  def at(dir)
111
111
  proj_dir = directory(dir)
112
- unless proj_dir
112
+ if !proj_dir && !ShopifyCLI::Environment.run_as_subprocess?
113
113
  raise(ShopifyCLI::Abort, Context.message("core.project.error.not_in_project"))
114
114
  end
115
115
  @at ||= Hash.new { |h, k| h[k] = new(directory: k) }
@@ -4,7 +4,9 @@ module ShopifyCLI
4
4
  module Tasks
5
5
  class EnsureProjectType < ShopifyCLI::Task
6
6
  def call(ctx, project_type)
7
- return true if project_type.to_sym == ShopifyCLI::Project.current_project_type
7
+ if project_type.to_sym == ShopifyCLI::Project.current_project_type || Environment.run_as_subprocess?
8
+ return true
9
+ end
8
10
  ctx.abort(ctx.message("core.tasks.ensure_project_type.wrong_project_type", project_type))
9
11
  end
10
12
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ShopifyCLI
4
4
  module Theme
5
- module DevServer
5
+ class DevServer
6
6
  class CdnFonts
7
7
  FONTS_PATH = "/fonts"
8
8
  FONTS_CDN = "https://fonts.shopifycdn.com"
@@ -4,7 +4,7 @@ require "openssl"
4
4
 
5
5
  module ShopifyCLI
6
6
  module Theme
7
- module DevServer
7
+ class DevServer
8
8
  class CertificateManager
9
9
  attr_reader :ctx, :domain_name, :certificate, :private_key
10
10