shopify-cli 1.0.0 → 1.0.5

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -2
  3. data/CHANGELOG.md +20 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +13 -13
  6. data/docs/Gemfile.lock +23 -13
  7. data/docs/getting-started/install/index.md +37 -1
  8. data/docs/getting-started/migrate/index.md +34 -1
  9. data/lib/project_types/extension/cli.rb +1 -1
  10. data/lib/project_types/extension/commands/build.rb +1 -1
  11. data/lib/project_types/extension/models/type.rb +1 -0
  12. data/lib/project_types/extension/tasks/create_extension.rb +1 -1
  13. data/lib/project_types/extension/tasks/get_app.rb +1 -1
  14. data/lib/project_types/extension/tasks/update_draft.rb +1 -1
  15. data/lib/project_types/node/forms/create.rb +3 -54
  16. data/lib/project_types/node/messages/messages.rb +3 -14
  17. data/lib/project_types/rails/cli.rb +0 -1
  18. data/lib/project_types/rails/forms/create.rb +3 -52
  19. data/lib/project_types/rails/messages/messages.rb +2 -13
  20. data/lib/project_types/script/cli.rb +2 -3
  21. data/lib/project_types/script/commands/create.rb +5 -9
  22. data/lib/project_types/script/commands/disable.rb +4 -15
  23. data/lib/project_types/script/commands/enable.rb +37 -13
  24. data/lib/project_types/script/commands/push.rb +8 -13
  25. data/lib/project_types/script/config/extension_points.yml +9 -3
  26. data/lib/project_types/script/errors.rb +8 -0
  27. data/lib/project_types/script/forms/script_form.rb +5 -2
  28. data/lib/project_types/script/layers/application/create_script.rb +7 -6
  29. data/lib/project_types/script/layers/application/disable_script.rb +9 -7
  30. data/lib/project_types/script/layers/application/enable_script.rb +11 -9
  31. data/lib/project_types/script/layers/application/push_script.rb +6 -4
  32. data/lib/project_types/script/layers/domain/errors.rb +2 -0
  33. data/lib/project_types/script/layers/infrastructure/assemblyscript_project_creator.rb +2 -2
  34. data/lib/project_types/script/layers/infrastructure/assemblyscript_task_runner.rb +2 -2
  35. data/lib/project_types/script/layers/infrastructure/errors.rb +2 -0
  36. data/lib/project_types/script/layers/infrastructure/push_package_repository.rb +1 -1
  37. data/lib/project_types/script/layers/infrastructure/script_repository.rb +1 -1
  38. data/lib/project_types/script/layers/infrastructure/script_service.rb +2 -0
  39. data/lib/project_types/script/messages/messages.rb +25 -31
  40. data/lib/project_types/script/script_project.rb +8 -4
  41. data/lib/project_types/script/templates/ts/as-pect.config.js +6 -0
  42. data/lib/project_types/script/ui/error_handler.rb +8 -0
  43. data/lib/project_types/script/ui/printing_spinner.rb +75 -0
  44. data/lib/shopify-cli/admin_api.rb +1 -2
  45. data/lib/shopify-cli/admin_api/populate_resource_command.rb +10 -1
  46. data/lib/shopify-cli/admin_api/schema.rb +20 -8
  47. data/lib/shopify-cli/command.rb +14 -11
  48. data/lib/shopify-cli/commands.rb +1 -0
  49. data/lib/shopify-cli/commands/config.rb +44 -0
  50. data/lib/shopify-cli/commands/connect.rb +8 -69
  51. data/lib/shopify-cli/commands/create.rb +2 -2
  52. data/lib/shopify-cli/commands/help.rb +1 -1
  53. data/lib/shopify-cli/commands/system.rb +14 -6
  54. data/lib/shopify-cli/context.rb +10 -1
  55. data/lib/shopify-cli/core.rb +0 -1
  56. data/lib/shopify-cli/core/entry_point.rb +7 -1
  57. data/lib/shopify-cli/core/executor.rb +3 -5
  58. data/lib/shopify-cli/core/finalize.rb +13 -0
  59. data/lib/shopify-cli/core/monorail.rb +1 -1
  60. data/lib/shopify-cli/db.rb +1 -1
  61. data/lib/shopify-cli/feature.rb +97 -0
  62. data/lib/shopify-cli/heroku.rb +4 -4
  63. data/lib/shopify-cli/messages/messages.rb +51 -12
  64. data/lib/shopify-cli/partners_api/organizations.rb +7 -7
  65. data/lib/shopify-cli/process_supervision.rb +8 -6
  66. data/lib/shopify-cli/project_type.rb +5 -7
  67. data/lib/shopify-cli/sub_command.rb +1 -0
  68. data/lib/shopify-cli/task.rb +2 -2
  69. data/lib/shopify-cli/tasks.rb +12 -4
  70. data/lib/shopify-cli/tasks/ensure_env.rb +72 -16
  71. data/lib/shopify-cli/tasks/select_org_and_shop.rb +77 -0
  72. data/lib/shopify-cli/tasks/update_dashboard_urls.rb +4 -3
  73. data/lib/shopify-cli/tunnel.rb +38 -14
  74. data/lib/shopify-cli/version.rb +1 -1
  75. data/lib/shopify_cli.rb +32 -9
  76. metadata +7 -4
  77. data/lib/project_types/script/forms/enable.rb +0 -24
  78. data/lib/project_types/script/forms/push.rb +0 -19
@@ -41,6 +41,8 @@ module ShopifyCli
41
41
  return @ctx.puts(output)
42
42
  end
43
43
 
44
+ @shop ||= Project.current.env.shop || get_shop(@ctx)
45
+
44
46
  if @silent
45
47
  spin_group = CLI::UI::SpinGroup.new
46
48
  spin_group.add(@ctx.message('core.populate.populating', @count, camel_case_resource_type)) do |spinner|
@@ -114,7 +116,7 @@ module ShopifyCli
114
116
 
115
117
  def run_mutation(data)
116
118
  kwargs = { input: data }
117
- kwargs[:shop] = @shop if @shop
119
+ kwargs[:shop] = @shop
118
120
  resp = AdminAPI.query(
119
121
  @ctx, "create_#{snake_case_resource_type}", kwargs
120
122
  )
@@ -145,6 +147,13 @@ module ShopifyCli
145
147
 
146
148
  private
147
149
 
150
+ def get_shop(ctx)
151
+ res = ShopifyCli::Tasks::SelectOrgAndShop.call(ctx)
152
+ domain = res[:shop_domain]
153
+ Project.current.env.update(ctx, :shop, domain)
154
+ domain
155
+ end
156
+
148
157
  def camel_case_resource_type
149
158
  @camel_case_resource_type ||= self.class.to_s.split('::').last
150
159
  end
@@ -3,15 +3,27 @@ require 'shopify_cli'
3
3
  module ShopifyCli
4
4
  class AdminAPI
5
5
  class Schema < Hash
6
- def self.get(ctx)
7
- unless ShopifyCli::DB.exists?(:shopify_admin_schema)
8
- schema = AdminAPI.query(ctx, 'admin_introspection')
9
- ShopifyCli::DB.set(shopify_admin_schema: JSON.dump(schema))
6
+ class << self
7
+ def get(ctx)
8
+ unless ShopifyCli::DB.exists?(:shopify_admin_schema)
9
+ shop = Project.current.env.shop || get_shop(ctx)
10
+ schema = AdminAPI.query(ctx, 'admin_introspection', shop: shop)
11
+ ShopifyCli::DB.set(shopify_admin_schema: JSON.dump(schema))
12
+ end
13
+ # This is ruby magic for making a new hash with another hash.
14
+ # It wraps the JSON in our Schema Class to have the helper methods
15
+ # available
16
+ self[JSON.parse(ShopifyCli::DB.get(:shopify_admin_schema))]
17
+ end
18
+
19
+ private
20
+
21
+ def get_shop(ctx)
22
+ res = ShopifyCli::Tasks::SelectOrgAndShop.call(ctx)
23
+ domain = res[:shop_domain]
24
+ Project.current.env.update(ctx, :shop, domain)
25
+ domain
10
26
  end
11
- # This is ruby magic for making a new hash with another hash.
12
- # It wraps the JSON in our Schema Class to have the helper methods
13
- # available
14
- self[JSON.parse(ShopifyCli::DB.get(:shopify_admin_schema))]
15
27
  end
16
28
 
17
29
  def type(name)
@@ -3,30 +3,29 @@ require 'shopify_cli'
3
3
 
4
4
  module ShopifyCli
5
5
  class Command < CLI::Kit::BaseCommand
6
+ extend Feature::Set
7
+
6
8
  attr_writer :ctx
7
9
  attr_accessor :options
8
10
 
9
11
  class << self
10
- attr_writer :ctx
11
- attr_reader :hidden
12
+ attr_writer :ctx, :task_registry
12
13
 
13
14
  def call(args, command_name)
14
15
  subcommand, resolved_name = subcommand_registry.lookup_command(args.first)
15
16
  if subcommand
16
17
  subcommand.ctx = @ctx
18
+ subcommand.task_registry = @task_registry
17
19
  subcommand.call(args, resolved_name, command_name)
18
20
  else
19
21
  cmd = new(@ctx)
20
22
  cmd.options.parse(@_options, args)
21
23
  return call_help(command_name) if cmd.options.help
24
+ run_prerequisites
22
25
  cmd.call(args, command_name)
23
26
  end
24
27
  end
25
28
 
26
- def hidden_command
27
- @hidden = true
28
- end
29
-
30
29
  def options(&block)
31
30
  @_options = block
32
31
  end
@@ -44,13 +43,16 @@ module ShopifyCli
44
43
  end
45
44
 
46
45
  def prerequisite_task(*tasks)
47
- tasks.each do |task|
48
- prerequisite_tasks[task] = ShopifyCli::Tasks::Registry[task]
49
- end
46
+ @prerequisite_tasks ||= []
47
+ @prerequisite_tasks += tasks
48
+ end
49
+
50
+ def run_prerequisites
51
+ (@prerequisite_tasks || []).each { |task| task_registry[task]&.call(@ctx) }
50
52
  end
51
53
 
52
- def prerequisite_tasks
53
- @prerequisite_tasks ||= {}
54
+ def task_registry
55
+ @task_registry || ShopifyCli::Tasks::Registry
54
56
  end
55
57
 
56
58
  def call_help(*cmds)
@@ -60,6 +62,7 @@ module ShopifyCli
60
62
  end
61
63
 
62
64
  def initialize(ctx = nil)
65
+ super()
63
66
  @ctx = ctx || ShopifyCli::Context.new
64
67
  self.options = Options.new
65
68
  end
@@ -18,6 +18,7 @@ module ShopifyCli
18
18
  @core_commands.include?(cmd)
19
19
  end
20
20
 
21
+ register :Config, 'config', 'shopify-cli/commands/config', true
21
22
  register :Connect, 'connect', 'shopify-cli/commands/connect', true
22
23
  register :Create, 'create', 'shopify-cli/commands/create', true
23
24
  register :Help, 'help', 'shopify-cli/commands/help', true
@@ -0,0 +1,44 @@
1
+ require 'shopify_cli'
2
+
3
+ module ShopifyCli
4
+ module Commands
5
+ class Config < ShopifyCli::Command
6
+ hidden_feature(feature_set: :debug)
7
+
8
+ subcommand :Feature, 'feature'
9
+
10
+ def call(*)
11
+ @ctx.puts(self.class.help)
12
+ end
13
+
14
+ def self.help
15
+ ShopifyCli::Context.message('core.config.help', ShopifyCli::TOOL_NAME)
16
+ end
17
+
18
+ class Feature < ShopifyCli::SubCommand
19
+ options do |parser, flags|
20
+ parser.on('--enable') { flags[:action] = 'enable' }
21
+ parser.on('--disable') { flags[:action] = 'disable' }
22
+ parser.on('--status') { flags[:action] = 'status' }
23
+ end
24
+
25
+ def call(args, _name)
26
+ feature_name = args.shift
27
+ return @ctx.puts(@ctx.message('core.config.help', ShopifyCli::TOOL_NAME)) if feature_name.nil?
28
+ is_enabled = ShopifyCli::Feature.enabled?(feature_name)
29
+ if options.flags[:action] == 'disable' && is_enabled
30
+ ShopifyCli::Feature.disable(feature_name)
31
+ @ctx.puts(@ctx.message('core.config.feature.disabled', is_enabled))
32
+ elsif options.flags[:action] == 'enable' && !is_enabled
33
+ ShopifyCli::Feature.enable(feature_name)
34
+ @ctx.puts(@ctx.message('core.config.feature.enabled', feature_name))
35
+ elsif is_enabled
36
+ @ctx.puts(@ctx.message('core.config.feature.is_enabled', feature_name))
37
+ else
38
+ @ctx.puts(@ctx.message('core.config.feature.is_disabled', feature_name))
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -6,27 +6,20 @@ module ShopifyCli
6
6
  def call(*)
7
7
  project_type = ask_project_type unless Project.has_current?
8
8
 
9
- if Project.has_current? && Project.current
9
+ if Project.has_current? && Project.current && Project.current.env
10
10
  @ctx.puts @ctx.message('core.connect.already_connected_warning')
11
11
  prod_warning = @ctx.message('core.connect.production_warning')
12
12
  @ctx.puts prod_warning if [:rails, :node].include?(Project.current_project_type)
13
13
  end
14
14
 
15
- env_data = begin
16
- Resources::EnvFile.parse_external_env
17
- rescue Errno::ENOENT
18
- {}
19
- end
20
-
21
- org = fetch_org
22
- id = org['id']
23
- app = get_app(org['apps'])
24
- shop = get_shop(org['stores'], id)
25
-
26
- write_env(app, shop, env_data[:scopes], env_data[:extra])
27
- write_cli_yml(project_type, id) unless Project.has_current?
15
+ org = ShopifyCli::Tasks::EnsureEnv.call(@ctx, regenerate: true)
16
+ api_key = Project.current.env['api_key']
17
+ write_cli_yml(project_type, org['id']) unless Project.has_current?
18
+ @ctx.puts(@ctx.message('core.connect.connected', get_app(org['apps'], api_key).first["title"]))
19
+ end
28
20
 
29
- @ctx.puts(@ctx.message('core.connect.connected', app.first['title']))
21
+ def get_app(apps, api_key)
22
+ apps.select { |app| app["apiKey"] == api_key }
30
23
  end
31
24
 
32
25
  def ask_project_type
@@ -37,60 +30,6 @@ module ShopifyCli
37
30
  end
38
31
  end
39
32
 
40
- def fetch_org
41
- orgs = PartnersAPI::Organizations.fetch_with_app(@ctx)
42
- org_id = if orgs.count == 1
43
- orgs.first["id"]
44
- else
45
- CLI::UI::Prompt.ask(@ctx.message('core.connect.organization_select')) do |handler|
46
- orgs.each do |org|
47
- handler.option(
48
- ctx.message('core.partners_api.org_name_and_id', org['businessName'], org['id'])
49
- ) { org["id"] }
50
- end
51
- end
52
- end
53
- org = orgs.find { |o| o["id"] == org_id }
54
- org
55
- end
56
-
57
- def get_app(apps)
58
- app_id = if apps.count == 1
59
- apps.first["id"]
60
- else
61
- CLI::UI::Prompt.ask(@ctx.message('core.connect.app_select')) do |handler|
62
- apps.each { |app| handler.option(app["title"]) { app["id"] } }
63
- end
64
- end
65
- apps.select { |app| app["id"] == app_id }
66
- end
67
-
68
- def get_shop(shops, id)
69
- if shops.count == 1
70
- shop = shops.first["shopDomain"]
71
- elsif shops.count == 0
72
- @ctx.puts(@ctx.message('core.connect.no_development_stores', id))
73
- else
74
- shop = CLI::UI::Prompt.ask(@ctx.message('core.connect.development_store_select')) do |handler|
75
- shops.each { |s| handler.option(s["shopName"]) { s["shopDomain"] } }
76
- end
77
- end
78
- shop
79
- end
80
-
81
- def write_env(app, shop, scopes, extra)
82
- scopes = 'write_products,write_customers,write_draft_orders' if scopes.nil?
83
- extra = {} if extra.nil?
84
-
85
- Resources::EnvFile.new(
86
- api_key: app.first["apiKey"],
87
- secret: app.first["apiSecretKeys"].first["secret"],
88
- shop: shop,
89
- scopes: scopes,
90
- extra: extra,
91
- ).write(@ctx)
92
- end
93
-
94
33
  def write_cli_yml(project_type, org_id)
95
34
  ShopifyCli::Project.write(
96
35
  @ctx,
@@ -28,11 +28,11 @@ module ShopifyCli
28
28
  def self.all_visible_type
29
29
  ProjectType
30
30
  .load_all
31
- .select { |type| !type.hidden }
31
+ .select { |type| !type.hidden? }
32
32
  end
33
33
 
34
34
  def self.help
35
- project_types = all_visible_type.map(&:project_type).join(" | ")
35
+ project_types = all_visible_type.map(&:project_type).sort.join(" | ")
36
36
  ShopifyCli::Context.message('core.create.help', ShopifyCli::TOOL_NAME, project_types)
37
37
  end
38
38
 
@@ -47,7 +47,7 @@ module ShopifyCli
47
47
 
48
48
  def core_commands
49
49
  resolved_commands
50
- .select { |_name, c| !c.hidden }
50
+ .select { |_name, c| !c.hidden? }
51
51
  .select { |name, _c| Commands.core_command?(name) }
52
52
  end
53
53
 
@@ -4,7 +4,7 @@ require 'rbconfig'
4
4
  module ShopifyCli
5
5
  module Commands
6
6
  class System < ShopifyCli::Command
7
- hidden_command
7
+ hidden_feature(feature_set: :debug)
8
8
 
9
9
  def call(args, _name)
10
10
  show_all_details = false
@@ -36,11 +36,13 @@ module ShopifyCli
36
36
  cli_constants_extra = %w(
37
37
  PROJECT_TYPES_DIR
38
38
  TEMP_DIR
39
- CACHE_DIR
40
- TOOL_CONFIG_PATH
41
- LOG_FILE
42
- DEBUG_LOG_FILE
43
39
  )
40
+ cli_path_methods = [
41
+ :cache_dir,
42
+ :tool_config_path,
43
+ :log_file,
44
+ :debug_log_file,
45
+ ]
44
46
 
45
47
  cli_constants += cli_constants_extra if show_all_details
46
48
 
@@ -48,6 +50,12 @@ module ShopifyCli
48
50
  cli_constants.each do |s|
49
51
  @ctx.puts(" " + @ctx.message('core.system.const', s, ShopifyCli.const_get(s.to_sym)) + "\n")
50
52
  end
53
+
54
+ if show_all_details
55
+ cli_path_methods.each do |m|
56
+ @ctx.puts(" " + @ctx.message('core.system.const', m.upcase, ShopifyCli.send(m)) + "\n")
57
+ end
58
+ end
51
59
  end
52
60
 
53
61
  def display_cli_ruby(_show_all_details)
@@ -74,7 +82,7 @@ module ShopifyCli
74
82
  end
75
83
 
76
84
  def display_ngrok
77
- ngrok_location = File.join(ShopifyCli::CACHE_DIR, 'ngrok')
85
+ ngrok_location = File.join(ShopifyCli.cache_dir, 'ngrok')
78
86
  if File.exist?(ngrok_location)
79
87
  @ctx.puts(" " + @ctx.message('core.system.ngrok_available', ngrok_location))
80
88
  else
@@ -146,10 +146,19 @@ module ShopifyCli
146
146
  # #### Parameters
147
147
  # * `path` - the file path to a directory, relative to the context root to remove from the FS
148
148
  #
149
- def exist?(path)
149
+ def dir_exist?(path)
150
150
  Dir.exist?(ctx_path(path))
151
151
  end
152
152
 
153
+ # checks if a file exists, the filepath is relative to the command root unless absolute
154
+ #
155
+ # #### Parameters
156
+ # * `path` - the file path to a file, relative to the context root to remove from the FS
157
+ #
158
+ def file_exist?(path)
159
+ File.exist?(ctx_path(path))
160
+ end
161
+
153
162
  # will recursively copy a directory from the FS, the filepath is relative to the command
154
163
  # root unless absolute
155
164
  #
@@ -4,6 +4,5 @@ module ShopifyCli
4
4
  autoload :Executor, 'shopify-cli/core/executor'
5
5
  autoload :HelpResolver, 'shopify-cli/core/help_resolver'
6
6
  autoload :Monorail, 'shopify-cli/core/monorail'
7
- autoload :Update, 'shopify-cli/core/update'
8
7
  end
9
8
  end
@@ -11,6 +11,12 @@ module ShopifyCli
11
11
  IO.open(9) { is_shell_shim = true }
12
12
  rescue Errno::EBADF
13
13
  # This is expected if the descriptor doesn't exist
14
+ rescue ArgumentError => e
15
+ # This can happen on RVM, because it can use fd 9 itself and block access to it. That only happens if the fd
16
+ # did not exist beforehand, so that means there was no fd 9 before Ruby started.
17
+ unless e.message == 'The given fd is not accessible because RubyVM reserves it'
18
+ raise e
19
+ end
14
20
  end
15
21
 
16
22
  if !ctx.testing? && is_shell_shim
@@ -29,7 +35,7 @@ module ShopifyCli
29
35
  task_registry = ShopifyCli::Tasks::Registry
30
36
 
31
37
  command, command_name, args = ShopifyCli::Resolver.call(args)
32
- executor = ShopifyCli::Core::Executor.new(ctx, task_registry, log_file: ShopifyCli::LOG_FILE)
38
+ executor = ShopifyCli::Core::Executor.new(ctx, task_registry, log_file: ShopifyCli.log_file)
33
39
  ShopifyCli::Core::Monorail.log(command_name, args) do
34
40
  executor.call(command, command_name, args)
35
41
  end
@@ -3,16 +3,14 @@ require 'shopify_cli'
3
3
  module ShopifyCli
4
4
  module Core
5
5
  class Executor < CLI::Kit::Executor
6
- def initialize(ctx, task_registry, *args)
6
+ def initialize(ctx, task_registry, *args, **kwargs)
7
7
  @ctx = ctx || ShopifyCli::Context.new
8
8
  @task_registry = task_registry || ShopifyCli::Tasks::TaskRegistry.new
9
- super(*args)
9
+ super(*args, **kwargs)
10
10
  end
11
11
 
12
12
  def call(command, command_name, args)
13
- command.prerequisite_tasks.each do |task, _|
14
- @task_registry[task]&.call(@ctx)
15
- end
13
+ command.task_registry = @task_registry
16
14
  command.ctx = @ctx
17
15
  super
18
16
  end
@@ -0,0 +1,13 @@
1
+ module ShopifyCli
2
+ module Core
3
+ # This class is just a dummy to make sure that we don't trigger warnings on the first time the updated code runs.
4
+ # The old code would try to call the Finalizer after it is done updating, which would then trigger an autoload of
5
+ # this class and fail.
6
+ module Finalize
7
+ class << self
8
+ def deliver!
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -99,7 +99,7 @@ module ShopifyCli
99
99
  success: err.nil?,
100
100
  error_message: err,
101
101
  uname: RbConfig::CONFIG["host"],
102
- cli_version: ShopifyCli::Git.sha(dir: ShopifyCli::ROOT),
102
+ cli_version: ShopifyCli::VERSION,
103
103
  ruby_version: RUBY_VERSION,
104
104
  }.tap do |payload|
105
105
  payload[:metadata] = JSON.dump(metadata) unless metadata.empty?