shopify-cli 1.0.3 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CONTRIBUTING.md +1 -1
  3. data/.travis.yml +3 -2
  4. data/CHANGELOG.md +20 -0
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +13 -13
  7. data/bin/load_shopify.rb +3 -1
  8. data/bin/shopify +2 -0
  9. data/docs/Gemfile.lock +23 -13
  10. data/docs/getting-started/index.md +3 -2
  11. data/docs/getting-started/install/index.md +55 -9
  12. data/docs/getting-started/uninstall/index.md +1 -1
  13. data/docs/getting-started/upgrade/index.md +8 -4
  14. data/ext/shopify-cli/extconf.rb +40 -20
  15. data/lib/project_types/extension/models/type.rb +1 -0
  16. data/lib/project_types/extension/tasks/create_extension.rb +1 -1
  17. data/lib/project_types/extension/tasks/get_app.rb +1 -1
  18. data/lib/project_types/extension/tasks/update_draft.rb +1 -1
  19. data/lib/project_types/node/commands/create.rb +4 -4
  20. data/lib/project_types/node/commands/deploy/heroku.rb +6 -1
  21. data/lib/project_types/node/commands/generate/billing.rb +7 -5
  22. data/lib/project_types/node/commands/generate/page.rb +9 -5
  23. data/lib/project_types/node/commands/generate/webhook.rb +5 -1
  24. data/lib/project_types/node/commands/serve.rb +5 -5
  25. data/lib/project_types/node/messages/messages.rb +5 -1
  26. data/lib/project_types/rails/commands/create.rb +56 -5
  27. data/lib/project_types/rails/commands/generate.rb +1 -0
  28. data/lib/project_types/rails/commands/generate/webhook.rb +3 -2
  29. data/lib/project_types/rails/commands/serve.rb +11 -7
  30. data/lib/project_types/rails/gem.rb +61 -6
  31. data/lib/project_types/rails/messages/messages.rb +32 -12
  32. data/lib/project_types/script/commands/create.rb +1 -5
  33. data/lib/project_types/script/commands/disable.rb +1 -2
  34. data/lib/project_types/script/commands/enable.rb +5 -5
  35. data/lib/project_types/script/commands/push.rb +1 -6
  36. data/lib/project_types/script/config/extension_points.yml +4 -4
  37. data/lib/project_types/script/errors.rb +8 -0
  38. data/lib/project_types/script/forms/create.rb +1 -1
  39. data/lib/project_types/script/layers/domain/errors.rb +2 -0
  40. data/lib/project_types/script/layers/infrastructure/errors.rb +1 -0
  41. data/lib/project_types/script/layers/infrastructure/script_service.rb +6 -2
  42. data/lib/project_types/script/messages/messages.rb +19 -48
  43. data/lib/project_types/script/ui/error_handler.rb +4 -0
  44. data/lib/rubygems_plugin.rb +18 -10
  45. data/lib/shopify-cli/admin_api/populate_resource_command.rb +1 -1
  46. data/lib/shopify-cli/command.rb +12 -6
  47. data/lib/shopify-cli/commands/connect.rb +7 -75
  48. data/lib/shopify-cli/commands/create.rb +1 -1
  49. data/lib/shopify-cli/commands/system.rb +21 -12
  50. data/lib/shopify-cli/context.rb +68 -0
  51. data/lib/shopify-cli/core/entry_point.rb +4 -1
  52. data/lib/shopify-cli/core/executor.rb +3 -5
  53. data/lib/shopify-cli/core/monorail.rb +1 -1
  54. data/lib/shopify-cli/db.rb +1 -1
  55. data/lib/shopify-cli/git.rb +1 -1
  56. data/lib/shopify-cli/heroku.rb +21 -5
  57. data/lib/shopify-cli/js_deps.rb +2 -2
  58. data/lib/shopify-cli/js_system.rb +2 -2
  59. data/lib/shopify-cli/messages/messages.rb +27 -20
  60. data/lib/shopify-cli/process_supervision.rb +60 -21
  61. data/lib/shopify-cli/project.rb +14 -6
  62. data/lib/shopify-cli/project_type.rb +3 -2
  63. data/lib/shopify-cli/sub_command.rb +1 -0
  64. data/lib/shopify-cli/task.rb +2 -2
  65. data/lib/shopify-cli/tasks.rb +11 -4
  66. data/lib/shopify-cli/tasks/ensure_env.rb +74 -17
  67. data/lib/shopify-cli/tunnel.rb +22 -7
  68. data/lib/shopify-cli/version.rb +1 -1
  69. data/lib/shopify_cli.rb +35 -9
  70. data/shopify-cli.gemspec +4 -1
  71. data/vendor/deps/cli-kit/REVISION +1 -1
  72. data/vendor/deps/cli-kit/lib/cli/kit.rb +1 -1
  73. data/vendor/deps/cli-kit/lib/cli/kit/autocall.rb +2 -2
  74. data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +12 -6
  75. data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +9 -11
  76. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +8 -2
  77. data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +7 -7
  78. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +48 -17
  79. data/vendor/deps/cli-ui/REVISION +1 -1
  80. data/vendor/deps/cli-ui/lib/cli/ui.rb +5 -4
  81. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +9 -3
  82. data/vendor/deps/cli-ui/lib/cli/ui/color.rb +1 -0
  83. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +3 -2
  84. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +1 -0
  85. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/box.rb +13 -5
  86. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +29 -2
  87. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +21 -10
  88. data/vendor/deps/cli-ui/lib/cli/ui/os.rb +63 -0
  89. data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +11 -2
  90. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +1 -0
  91. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +3 -3
  92. data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +6 -8
  93. data/vendor/deps/cli-ui/lib/cli/ui/widgets.rb +2 -0
  94. data/vendor/gen/lib/gen.rb +39 -0
  95. data/vendor/gen/lib/gen/commands.rb +18 -0
  96. data/vendor/gen/lib/gen/commands/help.rb +20 -0
  97. data/vendor/gen/lib/gen/commands/new.rb +21 -0
  98. data/vendor/gen/lib/gen/entry_point.rb +10 -0
  99. data/vendor/gen/lib/gen/generator.rb +165 -0
  100. data/vendor/gen/template/.gitignore +2 -0
  101. data/vendor/gen/template/Gemfile +10 -0
  102. data/vendor/gen/template/README.md +1 -0
  103. data/vendor/gen/template/bin/testunit +23 -0
  104. data/vendor/gen/template/bin/update-deps +97 -0
  105. data/vendor/gen/template/dev-gems.yml +3 -0
  106. data/vendor/gen/template/dev-vendor.yml +4 -0
  107. data/vendor/gen/template/exe/__app__-gems +17 -0
  108. data/vendor/gen/template/exe/__app__-vendor +18 -0
  109. data/vendor/gen/template/lib/__app__.rb +33 -0
  110. data/vendor/gen/template/lib/__app__/commands.rb +18 -0
  111. data/vendor/gen/template/lib/__app__/commands/example.rb +19 -0
  112. data/vendor/gen/template/lib/__app__/commands/help.rb +21 -0
  113. data/vendor/gen/template/lib/__app__/entry_point.rb +10 -0
  114. data/vendor/gen/template/test/example_test.rb +17 -0
  115. data/vendor/gen/template/test/test_helper.rb +22 -0
  116. metadata +25 -4
  117. data/Vagrantfile +0 -17
  118. data/lib/project_types/script/forms/script_form.rb +0 -69
@@ -12,6 +12,7 @@ module Extension
12
12
  end
13
13
 
14
14
  def inherited(klass)
15
+ super
15
16
  @all_extension_types ||= []
16
17
  @all_extension_types << klass
17
18
  end
@@ -20,7 +20,7 @@ module Extension
20
20
  extension_context: extension_context,
21
21
  }
22
22
 
23
- response = ShopifyCli::PartnersAPI.query(context, GRAPHQL_FILE, input).dig(*RESPONSE_FIELD)
23
+ response = ShopifyCli::PartnersAPI.query(context, GRAPHQL_FILE, **input).dig(*RESPONSE_FIELD)
24
24
  context.abort(context.message('tasks.errors.parse_error')) if response.nil?
25
25
 
26
26
  abort_if_user_errors(context, response)
@@ -12,7 +12,7 @@ module Extension
12
12
  def call(context:, api_key:)
13
13
  input = { api_key: api_key }
14
14
 
15
- response = ShopifyCli::PartnersAPI.query(context, GRAPHQL_FILE, input).dig(*RESPONSE_FIELD)
15
+ response = ShopifyCli::PartnersAPI.query(context, GRAPHQL_FILE, **input).dig(*RESPONSE_FIELD)
16
16
  context.abort(context.message('tasks.errors.parse_error')) if response.nil?
17
17
 
18
18
  Converters::AppConverter.from_hash(response.dig(APP_FIELD))
@@ -18,7 +18,7 @@ module Extension
18
18
  config: JSON.generate(config),
19
19
  extension_context: extension_context,
20
20
  }
21
- response = ShopifyCli::PartnersAPI.query(context, GRAPHQL_FILE, input).dig(*RESPONSE_FIELD)
21
+ response = ShopifyCli::PartnersAPI.query(context, GRAPHQL_FILE, **input).dig(*RESPONSE_FIELD)
22
22
  context.abort(context.message('tasks.errors.parse_error')) if response.nil?
23
23
 
24
24
  abort_if_user_errors(context, response)
@@ -54,8 +54,8 @@ module Node
54
54
  private
55
55
 
56
56
  def check_node
57
- _, stat = @ctx.capture2e('which', 'node')
58
- @ctx.abort(@ctx.message('node.create.error.node_required')) unless stat.success?
57
+ cmd_path = @ctx.which('node')
58
+ @ctx.abort(@ctx.message('node.create.error.node_required')) if cmd_path.nil?
59
59
 
60
60
  version, stat = @ctx.capture2e('node', '-v')
61
61
  @ctx.abort(@ctx.message('node.create.error.node_version_failure')) unless stat.success?
@@ -64,8 +64,8 @@ module Node
64
64
  end
65
65
 
66
66
  def check_npm
67
- _, stat = @ctx.capture2e('which', 'npm')
68
- @ctx.abort(@ctx.message('node.create.error.npm_required')) unless stat.success?
67
+ cmd_path = @ctx.which('npm')
68
+ @ctx.abort(@ctx.message('node.create.error.npm_required')) if cmd_path.nil?
69
69
 
70
70
  version, stat = @ctx.capture2e('npm', '-v')
71
71
  @ctx.abort(@ctx.message('node.create.error.npm_version_failure')) unless stat.success?
@@ -19,10 +19,15 @@ module Node
19
19
  end
20
20
  spin_group.wait
21
21
 
22
- spin_group.add(@ctx.message('node.deploy.heroku.installing')) do |spinner|
22
+ install_message = @ctx.message(
23
+ @ctx.windows? ? 'node.deploy.heroku.installing_windows' : 'node.deploy.heroku.installing'
24
+ )
25
+ spin_group.add(install_message) do |spinner|
23
26
  heroku_service.install
24
27
  spinner.update_title(@ctx.message('node.deploy.heroku.installed'))
25
28
  end
29
+ spin_group.wait
30
+
26
31
  spin_group.add(@ctx.message('node.deploy.heroku.git.checking')) do |spinner|
27
32
  ShopifyCli::Git.init(@ctx)
28
33
  spinner.update_title(@ctx.message('node.deploy.heroku.git.initialized'))
@@ -5,8 +5,8 @@ module Node
5
5
  class Generate
6
6
  class Billing < ShopifyCli::SubCommand
7
7
  BILLING_TYPES = {
8
- 'recurring-billing' => './node_modules/.bin/generate-node-app recurring-billing',
9
- 'one-time-billing' => './node_modules/.bin/generate-node-app one-time-billing',
8
+ 'recurring-billing' => ['./node_modules/.bin/generate-node-app', 'recurring-billing'],
9
+ 'one-time-billing' => ['./node_modules/.bin/generate-node-app', 'one-time-billing'],
10
10
  }
11
11
  def call(args, _name)
12
12
  selected_type = BILLING_TYPES[args[1]]
@@ -18,11 +18,13 @@ module Node
18
18
  end
19
19
  end
20
20
  billing_type_name = BILLING_TYPES.key(selected_type)
21
+ selected_type[0] = File.join(ShopifyCli::Project.current.directory, selected_type[0])
22
+ selected_type[0] = "\"#{selected_type[0]}\""
23
+ selected_type = selected_type.join(' ')
24
+
21
25
  spin_group = CLI::UI::SpinGroup.new
22
26
  spin_group.add(@ctx.message('node.generate.billing.generating', billing_type_name)) do |spinner|
23
- Node::Commands::Generate.run_generate(
24
- selected_type, billing_type_name, @ctx
25
- )
27
+ Node::Commands::Generate.run_generate(selected_type, billing_type_name, @ctx)
26
28
  spinner.update_title(@ctx.message('node.generate.billing.generated', billing_type_name))
27
29
  end
28
30
  spin_group.wait
@@ -5,10 +5,10 @@ module Node
5
5
  class Generate
6
6
  class Page < ShopifyCli::SubCommand
7
7
  PAGE_TYPES = {
8
- 'empty-state' => './node_modules/.bin/generate-node-app empty-state-page',
9
- 'two-column' => './node_modules/.bin/generate-node-app two-column-page',
10
- 'annotated' => './node_modules/.bin/generate-node-app settings-page',
11
- 'list' => './node_modules/.bin/generate-node-app list-page',
8
+ 'empty-state' => ['./node_modules/.bin/generate-node-app', 'empty-state-page'],
9
+ 'two-column' => ['./node_modules/.bin/generate-node-app', 'two-column-page'],
10
+ 'annotated' => ['./node_modules/.bin/generate-node-app', 'settings-page'],
11
+ 'list' => ['./node_modules/.bin/generate-node-app', 'list-page'],
12
12
  }
13
13
 
14
14
  options do |parser, flags|
@@ -37,9 +37,13 @@ module Node
37
37
  end
38
38
  end
39
39
  end
40
+ page_type_name = PAGE_TYPES.key(selected_type)
41
+ selected_type[0] = File.join(ShopifyCli::Project.current.directory, selected_type[0])
42
+ selected_type[0] = "\"#{selected_type[0]}\""
43
+ selected_type = selected_type.join(' ')
40
44
 
41
45
  spin_group = CLI::UI::SpinGroup.new
42
- spin_group.add(@ctx.message('node.generate.page.generating', selected_type)) do |spinner|
46
+ spin_group.add(@ctx.message('node.generate.page.generating', page_type_name)) do |spinner|
43
47
  Node::Commands::Generate.run_generate("#{selected_type} #{name}", name, @ctx)
44
48
  spinner.update_title(@ctx.message('node.generate.page.generated', name, name))
45
49
  end
@@ -15,9 +15,13 @@ module Node
15
15
  end
16
16
  end
17
17
  end
18
+
19
+ generate_path = File.join(ShopifyCli::Project.current.directory, "node_modules/.bin/generate-node-app")
20
+ generate_path = "\"#{generate_path}\""
21
+
18
22
  spin_group = CLI::UI::SpinGroup.new
19
23
  spin_group.add(@ctx.message('node.generate.webhook.generating', selected_type)) do |spinner|
20
- Node::Commands::Generate.run_generate("./node_modules/.bin/generate-node-app webhook #{selected_type}",
24
+ Node::Commands::Generate.run_generate("#{generate_path} webhook #{selected_type}",
21
25
  selected_type, @ctx)
22
26
  spinner.update_title(@ctx.message('node.generate.webhook.generated', selected_type))
23
27
  end
@@ -20,12 +20,12 @@ module Node
20
20
  url: url,
21
21
  callback_url: "/auth/callback",
22
22
  )
23
- if @ctx.mac? && project.env.shop
24
- @ctx.puts(@ctx.message('node.serve.open_info', project.env.shop))
25
- @ctx.on_siginfo do
26
- @ctx.open_url!("#{project.env.host}/auth?shop=#{project.env.shop}")
27
- end
23
+
24
+ if project.env.shop
25
+ project_url = "#{project.env.host}/auth?shop=#{project.env.shop}"
26
+ @ctx.puts("\n" + @ctx.message('node.serve.open_info', project_url) + "\n")
28
27
  end
28
+
29
29
  CLI::UI::Frame.open(@ctx.message('node.serve.running_server')) do
30
30
  env = project.env.to_h
31
31
  env['PORT'] = ShopifyCli::Tunnel::PORT.to_s
@@ -54,6 +54,7 @@ module Node
54
54
  downloading: "Downloading Heroku CLI…",
55
55
  downloaded: "Downloaded Heroku CLI",
56
56
  installing: "Installing Heroku CLI…",
57
+ installing_windows: "Running Heroku CLI install wizard…",
57
58
  installed: "Installed Heroku CLI",
58
59
  authenticating: "Authenticating with Heroku…",
59
60
  authenticated: "{{v}} Authenticated with Heroku",
@@ -201,7 +202,10 @@ module Node
201
202
  host_must_be_https: "HOST must be a HTTPS url.",
202
203
  },
203
204
 
204
- open_info: "{{*}} Press {{yellow: Control-T}} to open this project in {{green:%s}} ",
205
+ open_info: <<~MESSAGE,
206
+ {{*}} To install and start using your app, open this URL in your browser:
207
+ {{green:%s}}
208
+ MESSAGE
205
209
  running_server: "Running server...",
206
210
  },
207
211
 
@@ -30,6 +30,9 @@ module Rails
30
30
  @ctx.abort(@ctx.message('rails.create.error.invalid_ruby_version')) unless
31
31
  Ruby.version(@ctx).satisfies?('~>2.5')
32
32
 
33
+ check_node
34
+ check_yarn
35
+
33
36
  build(form.name, form.db)
34
37
  set_custom_ua
35
38
  ShopifyCli::Project.write(
@@ -65,12 +68,54 @@ module Rails
65
68
 
66
69
  private
67
70
 
71
+ def check_node
72
+ cmd_path = @ctx.which('node')
73
+ if cmd_path.nil?
74
+ @ctx.abort(@ctx.message('rails.create.error.node_required')) unless @ctx.windows?
75
+ @ctx.puts("{{x}} {{red:" + @ctx.message('rails.create.error.node_required') + "}}")
76
+ @ctx.puts(@ctx.message('rails.create.info.open_new_shell', 'node'))
77
+ raise ShopifyCli::AbortSilent
78
+ end
79
+
80
+ version, stat = @ctx.capture2e('node', '-v')
81
+ unless stat.success?
82
+ @ctx.abort(@ctx.message('rails.create.error.node_version_failure')) unless @ctx.windows?
83
+ # execution stops above if not Windows
84
+ @ctx.puts("{{x}} {{red:" + @ctx.message('rails.create.error.node_version_failure') + "}}")
85
+ @ctx.puts(@ctx.message('rails.create.info.open_new_shell', 'node'))
86
+ raise ShopifyCli::AbortSilent
87
+ end
88
+
89
+ @ctx.done(@ctx.message('rails.create.node_version', version))
90
+ end
91
+
92
+ def check_yarn
93
+ cmd_path = @ctx.which('yarn')
94
+ if cmd_path.nil?
95
+ @ctx.abort(@ctx.message('rails.create.error.yarn_required')) unless @ctx.windows?
96
+ @ctx.puts("{{x}} {{red:" + @ctx.message('rails.create.error.yarn_required') + "}}")
97
+ @ctx.puts(@ctx.message('rails.create.info.open_new_shell', 'yarn'))
98
+ raise ShopifyCli::AbortSilent
99
+ end
100
+
101
+ version, stat = @ctx.capture2e('yarn', '-v')
102
+ unless stat.success?
103
+ @ctx.abort(@ctx.message('rails.create.error.yarn_version_failure')) unless @ctx.windows?
104
+ @ctx.puts("{{x}} {{red:" + @ctx.message('rails.create.error.yarn_version_failure') + "}}")
105
+ @ctx.puts(@ctx.message('rails.create.info.open_new_shell', 'yarn'))
106
+ raise ShopifyCli::AbortSilent
107
+ end
108
+
109
+ @ctx.done(@ctx.message('rails.create.yarn_version', version))
110
+ end
111
+
68
112
  def build(name, db)
69
- install_gem('rails')
70
- CLI::UI::Frame.open(@ctx.message('rails.create.installing_bundler')) do
71
- install_gem('bundler', '~>1.0')
113
+ @ctx.abort(@ctx.message('rails.create.error.install_failure', 'rails')) unless install_gem('rails')
114
+ @ctx.abort(@ctx.message('rails.create.error.install_failure', 'bundler ~>2.0')) unless
72
115
  install_gem('bundler', '~>2.0')
73
- end
116
+
117
+ full_path = File.join(@ctx.root, name)
118
+ @ctx.abort(@ctx.message('rails.create.error.dir_exists', name)) if Dir.exist?(full_path)
74
119
 
75
120
  CLI::UI::Frame.open(@ctx.message('rails.create.generating_app', name)) do
76
121
  new_command = %w(rails new)
@@ -82,7 +127,7 @@ module Rails
82
127
  syscall(new_command)
83
128
  end
84
129
 
85
- @ctx.root = File.join(@ctx.root, name)
130
+ @ctx.root = full_path
86
131
 
87
132
  File.open(File.join(@ctx.root, '.gitignore'), 'a') { |f| f.write('.env') }
88
133
 
@@ -106,6 +151,12 @@ module Rails
106
151
  syscall(%w(rails db:create))
107
152
  syscall(%w(rails db:migrate RAILS_ENV=development))
108
153
  end
154
+
155
+ unless File.exist?(File.join(@ctx.root, 'config/webpacker.yml'))
156
+ CLI::UI::Frame.open(@ctx.message('rails.create.running_webpacker_install')) do
157
+ syscall(%w(rails webpacker:install))
158
+ end
159
+ end
109
160
  end
110
161
 
111
162
  def set_custom_ua
@@ -28,6 +28,7 @@ module Rails
28
28
  end
29
29
 
30
30
  def self.run_generate(script, name, ctx)
31
+ Gem.gem_path(ctx)
31
32
  stat = ctx.system(script)
32
33
  unless stat.success?
33
34
  ctx.abort(response(stat.exitstatus, name, ctx))
@@ -29,9 +29,10 @@ module Rails
29
29
 
30
30
  def generate_command(selected_type)
31
31
  parts = selected_type.downcase.split("_")
32
- env = ShopifyCli::Project.current.env.host
32
+ host = ShopifyCli::Project.current.env.host
33
33
  selected_type = parts[0..-2].join("_") + "/" + parts[-1]
34
- "rails g shopify_app:add_webhook -t #{selected_type} -a #{env}/webhooks/#{selected_type.downcase}"
34
+ command = @ctx.windows? ? "ruby bin\\rails" : "bin/rails"
35
+ "#{command} g shopify_app:add_webhook -t #{selected_type} -a #{host}/webhooks/#{selected_type.downcase}"
35
36
  end
36
37
  end
37
38
  end
@@ -20,18 +20,22 @@ module Rails
20
20
  url: url,
21
21
  callback_url: "/auth/shopify/callback",
22
22
  )
23
- if @ctx.mac? && project.env.shop
24
- @ctx.puts(@ctx.message('rails.serve.open_info', project.env.shop))
25
- @ctx.on_siginfo do
26
- @ctx.open_url!("#{project.env.host}/login?shop=#{project.env.shop}")
27
- end
23
+
24
+ if project.env.shop
25
+ project_url = "#{project.env.host}/login?shop=#{project.env.shop}"
26
+ @ctx.puts("\n" + @ctx.message('rails.serve.open_info', project_url) + "\n")
28
27
  end
29
- Gem.gem_home(@ctx)
28
+
30
29
  CLI::UI::Frame.open(@ctx.message('rails.serve.running_server')) do
31
30
  env = ShopifyCli::Project.current.env.to_h
32
31
  env.delete('HOST')
33
32
  env['PORT'] = ShopifyCli::Tunnel::PORT.to_s
34
- @ctx.system('bin/rails server', env: env)
33
+ env['GEM_PATH'] = Gem.gem_path(@ctx)
34
+ if @ctx.windows?
35
+ @ctx.system("ruby bin\\rails server", env: env)
36
+ else
37
+ @ctx.system('bin/rails server', env: env)
38
+ end
35
39
  end
36
40
  end
37
41
 
@@ -15,30 +15,65 @@ module Rails
15
15
  version = args.shift
16
16
  gem = new(ctx: ctx, name: name, version: version)
17
17
  ctx.debug(ctx.message('rails.gem.installed_debug', name, gem.installed?))
18
- gem.install! unless gem.installed?
18
+ gem.installed? ? true : gem.install!
19
19
  end
20
20
 
21
21
  def binary_path_for(ctx, binary)
22
- File.join(gem_home(ctx), 'bin', binary)
22
+ path_to_binary = File.join(gem_home(ctx), 'bin', binary)
23
+ File.exist?(path_to_binary) ? path_to_binary : binary
23
24
  end
24
25
 
25
26
  def gem_home(ctx)
26
27
  ctx.getenv('GEM_HOME') || apply_gem_home(ctx)
27
28
  end
28
29
 
30
+ def gem_path(ctx)
31
+ ctx.getenv('GEM_PATH') || apply_gem_path(ctx)
32
+ end
33
+
29
34
  private
30
35
 
31
36
  def apply_gem_home(ctx)
32
- path = File.join(ctx.getenv('HOME'), '.gem', 'ruby', RUBY_VERSION)
37
+ path = ''
38
+ # extract GEM_HOME from `gem environment home` command
39
+ out, stat = ctx.capture2e('gem', 'environment', 'home')
40
+ path = out&.empty? ? '' : out.strip if stat.success?
41
+ # fallback if return from `gem environment home` is empty (somewhat unlikely)
42
+ path = fallback_gem_home_path(ctx) if path.empty?
43
+ # fallback if path isn't writable (if using a system installed ruby)
44
+ path = fallback_gem_home_path(ctx) unless File.writable?(path)
33
45
  ctx.mkdir_p(path) unless Dir.exist?(path)
46
+ ctx.debug(ctx.message('rails.gem.setting_gem_home', path))
34
47
  ctx.setenv('GEM_HOME', path)
35
48
  end
49
+
50
+ def apply_gem_path(ctx)
51
+ path = ''
52
+ out, stat = ctx.capture2e('gem', 'environment', 'path')
53
+ path = out&.empty? ? '' : out.strip if stat.success?
54
+ # usually GEM_PATH already contains GEM_HOME
55
+ # if gem_home() falls back to our fallback path, we need to add it
56
+ path = gem_home(ctx) + File::PATH_SEPARATOR + path unless path.include?(gem_home(ctx))
57
+ ctx.debug(ctx.message('rails.gem.setting_gem_path', path))
58
+ ctx.setenv('GEM_PATH', path)
59
+ end
60
+
61
+ def fallback_gem_home_path(ctx)
62
+ File.join(ctx.getenv('HOME'), '.gem', 'ruby', RUBY_VERSION)
63
+ end
36
64
  end
37
65
 
38
66
  def installed?
39
- !!Dir.glob("#{self.class.gem_home(ctx)}/gems/#{name}-*").detect do |f|
40
- f =~ %r{/#{Regexp.quote(name)}-\d}
67
+ found = false
68
+ paths = self.class.gem_path(ctx).split(File::PATH_SEPARATOR)
69
+ paths.each do |path|
70
+ ctx.debug(ctx.message('rails.gem.checking_installation_path', "#{path}/gems/", name))
71
+ found = !!Dir.glob("#{path}/gems/#{name}-*").detect do |f|
72
+ gem_satisfies_version?(f)
73
+ end
74
+ break if found
41
75
  end
76
+ found
42
77
  end
43
78
 
44
79
  def install!
@@ -46,11 +81,31 @@ module Rails
46
81
  spin.add(ctx.message('rails.gem.installing', name)) do |spinner|
47
82
  args = %w(gem install)
48
83
  args.push(name)
49
- args.push('-v', version) unless version.nil?
84
+ unless version.nil?
85
+ if ctx.windows? && version.include?('~')
86
+ args.push('-v', "\"#{version}\"")
87
+ else
88
+ args.push('-v', version)
89
+ end
90
+ end
50
91
  ctx.system(*args)
51
92
  spinner.update_title(ctx.message('rails.gem.installed', name))
52
93
  end
53
94
  spin.wait
54
95
  end
96
+
97
+ def gem_satisfies_version?(path)
98
+ if version
99
+ # there was a specific version given during new(), so
100
+ # check version of gem found to determine match
101
+ require 'semantic/semantic'
102
+ found_version, _ = path.match(%r{/#{Regexp.quote(name)}-([\d\.]+)})&.captures
103
+ found_version.nil? ? false : Semantic::Version.new(found_version).satisfies?(version)
104
+ else
105
+ # otherwise ignore the actual version number,
106
+ # just check there's an initial digit
107
+ %r{/#{Regexp.quote(name)}-\d}.match?(path)
108
+ end
109
+ end
55
110
  end
56
111
  end
@@ -9,9 +9,12 @@ module Rails
9
9
  },
10
10
 
11
11
  gem: {
12
+ checking_installation_path: "Checking path %s for gem %s",
13
+ installed: "Installed %s gem",
12
14
  installed_debug: "%s installed: %s",
13
15
  installing: "Installing %s gem...",
14
- installed: "Installed %s gem",
16
+ setting_gem_home: "GEM_HOME being set to %s",
17
+ setting_gem_path: "GEM_PATH being set to %s",
15
18
  },
16
19
 
17
20
  create: {
@@ -33,6 +36,15 @@ module Rails
33
36
  See {{underline:https://github.com/Shopify/shopify-app-cli/blob/master/docs/installing-ruby.md}}
34
37
  for our recommended method of installing ruby.
35
38
  MSG
39
+ dir_exists: "Project directory %s already exists. Please use a different name.",
40
+ install_failure: "Error installing %s gem",
41
+ node_required: "node is required to create a rails project. Download at https://nodejs.org/en/download.",
42
+ node_version_failure: "Failed to get the current node version. Please make sure it is installed as " \
43
+ "per the instructions at https://nodejs.org/en.",
44
+ yarn_required: "yarn is required to create a rails project. Download at " \
45
+ "https://classic.yarnpkg.com/en/docs/install.",
46
+ yarn_version_failure: "Failed to get the current yarn version. Please make sure it is installed as per " \
47
+ "the instructions at https://classic.yarnpkg.com/en/docs/install.",
36
48
  },
37
49
 
38
50
  info: {
@@ -40,13 +52,18 @@ module Rails
40
52
  serve: "{{*}} Change directories to your new project folder {{green:%s}} and run {{command:%s serve}} " \
41
53
  "to start a local server",
42
54
  install: "{{*}} Then, visit {{underline:%s/test}} to install {{green:%s}} on your Dev Store",
55
+ open_new_shell: "{{*}} {{yellow:After installing %s, please open a new Command Prompt or PowerShell " \
56
+ "window to continue.}}",
43
57
  },
44
- installing_bundler: "Installing bundler",
58
+ installing_bundler: "Installing bundler...",
45
59
  generating_app: "Generating new rails app project in %s...",
46
- adding_shopify_gem: "{{v}} Adding shopify_app gem",
60
+ adding_shopify_gem: "{{v}} Adding shopify_app gem...",
61
+ node_version: "node %s",
62
+ yarn_version: "yarn %s",
47
63
  running_bundle_install: "Running bundle install...",
48
64
  running_generator: "Running shopify_app generator...",
49
- running_migrations: "Running migrations",
65
+ running_migrations: "Running migrations...",
66
+ running_webpacker_install: "Running webpacker:install...",
50
67
  },
51
68
 
52
69
  deploy: {
@@ -65,14 +82,14 @@ module Rails
65
82
  Deploy the current Rails project to Heroku
66
83
  Usage: {{command:%s deploy heroku}}
67
84
  HELP
68
- downloading: "Downloading Heroku CLI",
85
+ downloading: "Downloading Heroku CLI...",
69
86
  downloaded: "Downloaded Heroku CLI",
70
- installing: "Installing Heroku CLI",
87
+ installing: "Installing Heroku CLI...",
71
88
  installed: "Installed Heroku CLI",
72
89
  authenticated_with_account: "{{v}} Authenticated with Heroku as `%s`",
73
- authenticating: "Authenticating with Heroku",
90
+ authenticating: "Authenticating with Heroku...",
74
91
  authenticated: "{{v}} Authenticated with Heroku",
75
- deploying: "Deploying to Heroku",
92
+ deploying: "Deploying to Heroku...",
76
93
  deployed: "{{v}} Deployed to Heroku",
77
94
  db_check: {
78
95
  validating: "Validating application...",
@@ -86,7 +103,7 @@ module Rails
86
103
  SQLITE
87
104
  },
88
105
  git: {
89
- checking: "Checking git repo",
106
+ checking: "Checking git repo...",
90
107
  initialized: "Git repo initialized",
91
108
  what_branch: "What branch would you like to deploy?",
92
109
  branch_selected: "{{v}} Git branch `%s` selected for deploy",
@@ -95,10 +112,10 @@ module Rails
95
112
  no_apps_found: "No existing Heroku app found. What would you like to do?",
96
113
  name: "What is your Heroku app’s name?",
97
114
  select: "Specify an existing Heroku app",
98
- selecting: "Selecting Heroku app `%s`…",
115
+ selecting: "Selecting Heroku app `%s`...",
99
116
  selected: "{{v}} Heroku app `%s` selected",
100
117
  create: "Create a new Heroku app",
101
- creating: "Creating new Heroku app",
118
+ creating: "Creating new Heroku app...",
102
119
  created: "{{v}} New Heroku app created",
103
120
  },
104
121
  },
@@ -203,7 +220,10 @@ module Rails
203
220
  host_must_be_https: "{{red:HOST must be a HTTPS url.}}",
204
221
  },
205
222
 
206
- open_info: "{{*}} Press {{yellow: Control-T}} to open this project in {{green:%s}} ",
223
+ open_info: <<~MESSAGE,
224
+ {{*}} To install and start using your app, open this URL in your browser:
225
+ {{green:%s}}
226
+ MESSAGE
207
227
  running_server: "Running server...",
208
228
  },
209
229