shopify-cli 2.24.0 → 2.25.0

Sign up to get free protection for your applications and to get access to all the features.
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