shopify-cli 1.0.5 → 1.1.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/bin/load_shopify.rb +3 -1
  4. data/bin/shopify +2 -0
  5. data/ext/shopify-cli/extconf.rb +40 -20
  6. data/lib/project_types/node/commands/create.rb +4 -4
  7. data/lib/project_types/node/commands/deploy/heroku.rb +6 -1
  8. data/lib/project_types/node/commands/generate/billing.rb +6 -5
  9. data/lib/project_types/node/commands/generate/page.rb +8 -5
  10. data/lib/project_types/node/commands/generate/webhook.rb +4 -1
  11. data/lib/project_types/node/messages/messages.rb +1 -0
  12. data/lib/project_types/rails/commands/create.rb +52 -4
  13. data/lib/project_types/rails/commands/generate.rb +1 -0
  14. data/lib/project_types/rails/commands/generate/webhook.rb +3 -2
  15. data/lib/project_types/rails/commands/serve.rb +6 -2
  16. data/lib/project_types/rails/gem.rb +61 -6
  17. data/lib/project_types/rails/messages/messages.rb +27 -11
  18. data/lib/project_types/script/config/extension_points.yml +3 -3
  19. data/lib/project_types/script/forms/create.rb +1 -1
  20. data/lib/rubygems_plugin.rb +18 -10
  21. data/lib/shopify-cli/admin_api/populate_resource_command.rb +1 -1
  22. data/lib/shopify-cli/commands/connect.rb +1 -1
  23. data/lib/shopify-cli/commands/system.rb +9 -8
  24. data/lib/shopify-cli/context.rb +28 -0
  25. data/lib/shopify-cli/heroku.rb +18 -2
  26. data/lib/shopify-cli/js_deps.rb +2 -2
  27. data/lib/shopify-cli/js_system.rb +2 -2
  28. data/lib/shopify-cli/process_supervision.rb +52 -15
  29. data/lib/shopify-cli/project.rb +14 -6
  30. data/lib/shopify-cli/tunnel.rb +10 -4
  31. data/lib/shopify-cli/version.rb +1 -1
  32. data/lib/shopify_cli.rb +6 -2
  33. data/shopify-cli.gemspec +4 -1
  34. data/vendor/deps/cli-kit/REVISION +1 -1
  35. data/vendor/deps/cli-kit/lib/cli/kit.rb +1 -1
  36. data/vendor/deps/cli-kit/lib/cli/kit/autocall.rb +2 -2
  37. data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +12 -6
  38. data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +9 -11
  39. data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +8 -2
  40. data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +7 -7
  41. data/vendor/deps/cli-kit/lib/cli/kit/system.rb +48 -17
  42. data/vendor/deps/cli-ui/REVISION +1 -1
  43. data/vendor/deps/cli-ui/lib/cli/ui.rb +5 -4
  44. data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +9 -3
  45. data/vendor/deps/cli-ui/lib/cli/ui/color.rb +1 -0
  46. data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +3 -2
  47. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +1 -0
  48. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/box.rb +13 -5
  49. data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +29 -2
  50. data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +21 -10
  51. data/vendor/deps/cli-ui/lib/cli/ui/os.rb +63 -0
  52. data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +11 -2
  53. data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +1 -0
  54. data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +3 -3
  55. data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +6 -8
  56. data/vendor/deps/cli-ui/lib/cli/ui/widgets.rb +2 -0
  57. data/vendor/gen/lib/gen.rb +39 -0
  58. data/vendor/gen/lib/gen/commands.rb +18 -0
  59. data/vendor/gen/lib/gen/commands/help.rb +20 -0
  60. data/vendor/gen/lib/gen/commands/new.rb +21 -0
  61. data/vendor/gen/lib/gen/entry_point.rb +10 -0
  62. data/vendor/gen/lib/gen/generator.rb +165 -0
  63. data/vendor/gen/template/.gitignore +2 -0
  64. data/vendor/gen/template/Gemfile +10 -0
  65. data/vendor/gen/template/README.md +1 -0
  66. data/vendor/gen/template/bin/testunit +23 -0
  67. data/vendor/gen/template/bin/update-deps +97 -0
  68. data/vendor/gen/template/dev-gems.yml +3 -0
  69. data/vendor/gen/template/dev-vendor.yml +4 -0
  70. data/vendor/gen/template/exe/__app__-gems +17 -0
  71. data/vendor/gen/template/exe/__app__-vendor +18 -0
  72. data/vendor/gen/template/lib/__app__.rb +33 -0
  73. data/vendor/gen/template/lib/__app__/commands.rb +18 -0
  74. data/vendor/gen/template/lib/__app__/commands/example.rb +19 -0
  75. data/vendor/gen/template/lib/__app__/commands/help.rb +21 -0
  76. data/vendor/gen/template/lib/__app__/entry_point.rb +10 -0
  77. data/vendor/gen/template/test/example_test.rb +17 -0
  78. data/vendor/gen/template/test/test_helper.rb +22 -0
  79. metadata +25 -4
  80. data/Vagrantfile +0 -17
  81. data/lib/project_types/script/forms/script_form.rb +0 -69
@@ -0,0 +1,10 @@
1
+ require 'gen'
2
+
3
+ module Gen
4
+ module EntryPoint
5
+ def self.call(args)
6
+ cmd, command_name, args = Gen::Resolver.call(args)
7
+ Gen::Executor.call(cmd, command_name, args)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,165 @@
1
+ require 'gen'
2
+ require 'fileutils'
3
+ require 'open3'
4
+ require 'pathname'
5
+ require 'tmpdir'
6
+
7
+ module Gen
8
+ class Generator
9
+ def self.run(project_name)
10
+ new(project_name).run
11
+ end
12
+
13
+ TEMPLATE_ROOT = File.expand_path('gen/template', Gen::ROOT)
14
+
15
+ VALID_PROJECT_NAME = /\A[a-z][a-z0-9]*\z/
16
+ private_constant :VALID_PROJECT_NAME
17
+
18
+ # false -> delete file
19
+ # string -> rename file before applying template substitutions
20
+ VENDOR_TRANSLATIONS = {
21
+ 'Gemfile' => false,
22
+ 'exe/__app__-gems' => false,
23
+ 'exe/__app__-vendor' => 'exe/__app__',
24
+ 'dev-gems.yml' => false,
25
+ 'dev-vendor.yml' => 'dev.yml',
26
+ }.freeze
27
+ private_constant :VENDOR_TRANSLATIONS
28
+
29
+ BUNDLER_TRANSLATIONS = {
30
+ 'bin/update-deps' => false,
31
+ 'exe/__app__-gems' => 'exe/__app__',
32
+ 'exe/__app__-vendor' => false,
33
+ 'dev-gems.yml' => 'dev.yml',
34
+ 'dev-vendor.yml' => false,
35
+ }.freeze
36
+ private_constant :BUNDLER_TRANSLATIONS
37
+
38
+ def initialize(project_name)
39
+ raise(
40
+ CLI::Kit::Abort,
41
+ "project name must match {{bold:#{VALID_PROJECT_NAME}}} (but can be changed later)"
42
+ ) unless project_name =~ VALID_PROJECT_NAME
43
+ @project_name = project_name
44
+ @title_case_project_name = @project_name.sub(/^./, &:upcase)
45
+ end
46
+
47
+ def run
48
+ vendor = ask_vendor?
49
+ create_project_dir
50
+ if vendor
51
+ copy_files(translations: VENDOR_TRANSLATIONS)
52
+ update_deps
53
+ else
54
+ copy_files(translations: BUNDLER_TRANSLATIONS)
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def ask_vendor?
61
+ return 'vendor' if ENV['DEPS'] == 'vendor'
62
+ return 'bundler' if ENV['DEPS'] == 'bundler'
63
+
64
+ vendor = nil
65
+ CLI::UI::Frame.open('Configuration') do
66
+ q = 'How would you like the application to consume {{command:cli-kit}} and {{command:cli-ui}}?'
67
+ vendor = CLI::UI::Prompt.ask(q) do |c|
68
+ c.option('Vendor {{italic:(faster execution, more difficult to update deps)}}') { 'vendor' }
69
+ c.option('Bundler {{italic:(slower execution, easier dep management)}}') { 'bundler' }
70
+ end
71
+ end
72
+ vendor == 'vendor'
73
+ end
74
+
75
+ def create_project_dir
76
+ info(create: '')
77
+ FileUtils.mkdir(@project_name)
78
+ rescue Errno::EEXIST
79
+ error("directory already exists: #{@project_name}")
80
+ end
81
+
82
+ def copy_files(translations:)
83
+ each_template_file do |source_name|
84
+ target_name = translations.fetch(source_name, source_name)
85
+ next if target_name == false
86
+ target_name = apply_template_variables(target_name)
87
+
88
+ source = File.join(TEMPLATE_ROOT, source_name)
89
+ target = File.join(@project_name, target_name)
90
+
91
+ info(create: target_name)
92
+
93
+ if Dir.exist?(source)
94
+ FileUtils.mkdir(target)
95
+ else
96
+ content = apply_template_variables(File.read(source))
97
+ File.write(target, content)
98
+ end
99
+ File.chmod(File.stat(source).mode, target)
100
+ end
101
+ end
102
+
103
+ def update_deps
104
+ Dir.mktmpdir do |tmp|
105
+ clone(tmp, 'cli-ui')
106
+ clone(tmp, 'cli-kit')
107
+ info(run: 'bin/update-deps')
108
+ Dir.chdir(@project_name) do
109
+ system({ 'SOURCE_ROOT' => tmp }, 'bin/update-deps')
110
+ end
111
+ end
112
+ end
113
+
114
+ def clone(dir, repo)
115
+ info(clone: repo)
116
+ out, stat = Open3.capture2e('git', '-C', dir, 'clone', "https://github.com/shopify/#{repo}")
117
+ unless stat.success?
118
+ STDERR.puts(out)
119
+ error("git clone failed")
120
+ end
121
+ end
122
+
123
+ def each_template_file
124
+ return enum_for(:each_template_file) unless block_given?
125
+
126
+ root = Pathname.new(TEMPLATE_ROOT)
127
+ Dir.glob("#{TEMPLATE_ROOT}/**/*").each do |f|
128
+ el = Pathname.new(f)
129
+ yield(el.relative_path_from(root).to_s)
130
+ end
131
+ end
132
+
133
+ def apply_template_variables(s)
134
+ s
135
+ .gsub(/__app__/, @project_name)
136
+ .gsub(/__App__/, @title_case_project_name)
137
+ .gsub(/__cli-kit-version__/, cli_kit_version)
138
+ .gsub(/__cli-ui-version__/, cli_ui_version)
139
+ end
140
+
141
+ def cli_kit_version
142
+ require 'cli/kit/version'
143
+ CLI::Kit::VERSION.to_s
144
+ end
145
+
146
+ def cli_ui_version
147
+ require 'cli/ui/version'
148
+ CLI::UI::VERSION.to_s
149
+ end
150
+
151
+ def info(create: nil, clone: nil, run: nil)
152
+ if clone
153
+ puts(CLI::UI.fmt("\t{{bold:{{yellow:clone}}\t#{clone}}}"))
154
+ elsif create
155
+ puts(CLI::UI.fmt("\t{{bold:{{blue:create}}\t#{create}}}"))
156
+ elsif run
157
+ puts(CLI::UI.fmt("\t{{bold:{{green:run}}\t#{run}}}"))
158
+ end
159
+ end
160
+
161
+ def error(msg)
162
+ raise(CLI::Kit::Abort, msg)
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,2 @@
1
+ /Gemfile.lock
2
+ /.bundle
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'cli-kit', '~> __cli-kit-version__'
4
+ gem 'cli-ui', '~> __cli-ui-version__'
5
+
6
+ group :test do
7
+ gem 'mocha', '~> 1.5.0', require: false
8
+ gem 'minitest', '>= 5.0.0', require: false
9
+ gem 'minitest-reporters', require: false
10
+ end
@@ -0,0 +1 @@
1
+ # __app__
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+
6
+ root = File.expand_path('../..', __FILE__)
7
+ TEST_ROOT = root + '/test'
8
+
9
+ $LOAD_PATH.unshift(TEST_ROOT)
10
+
11
+ def test_files
12
+ Dir.glob(TEST_ROOT + "/**/*_test.rb")
13
+ end
14
+
15
+ if ARGV.empty?
16
+ test_files.each { |f| require(f) }
17
+ exit 0
18
+ end
19
+
20
+ # A list of files is presumed to be specified
21
+ ARGV.each do |a|
22
+ require a.sub(%r{^test/}, '')
23
+ end
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path("../../vendor/deps/cli-ui/lib", __FILE__))
4
+ require 'open3'
5
+ require 'fileutils'
6
+
7
+ def fmt(tag, msg)
8
+ # Not really CLI::UI.fmt compatible: no nesting support, for example.
9
+ # While we could pull in CLI::UI here, it makes it more difficult to
10
+ # bootstrap a new project and to fix a broken vendor.
11
+ fmt_msg = msg
12
+ .gsub(/{{yellow:(.*?)}}/, "\x1b[33m\\1\x1b[31m")
13
+ .gsub(/{{green:(.*?)}}/, "\x1b[32m\\1\x1b[31m")
14
+ .gsub(/{{blue:(.*?)}}/, "\x1b[34m\\1\x1b[31m")
15
+ .gsub(/{{bold_blue:(.*?)}}/, "\x1b[1;34m\\1\x1b[0;31m")
16
+ .gsub(/{{bold_green:(.*?)}}/, "\x1b[1;32m\\1\x1b[0;31m")
17
+ "\x1b[1;31m[#{tag}] \x1b[0;31m#{fmt_msg}\x1b[0m"
18
+ end
19
+
20
+ def bail(msg)
21
+ STDERR.puts(fmt("ERROR", msg))
22
+ exit(1)
23
+ end
24
+
25
+ def warn(msg)
26
+ STDERR.puts(fmt("WARNING", msg))
27
+ end
28
+
29
+ def source_path
30
+ File.expand_path(ENV.fetch('SOURCE_ROOT', File.expand_path('../../..', __FILE__)))
31
+ end
32
+
33
+ deps = %w(cli-ui cli-kit)
34
+
35
+ deps.each do |dep|
36
+ path = File.expand_path(dep, source_path)
37
+
38
+ unless Dir.exist?(path)
39
+ bail(
40
+ "dependency is not checked out: {{yellow:#{dep}}}.\n" \
41
+ " This repo {{bold_blue:(github.com/shopify/#{dep})}} must be cloned at" \
42
+ " {{bold_blue:#{path}}} for this script to succeed.\n" \
43
+ " Currently, SOURCE_ROOT is set to {{bold_blue:#{source_path}}}.\n" \
44
+ " Alternatively, you can set {{bold_blue:SOURCE_ROOT}} to a directory containing {{yellow:#{dep}}}.\n" \
45
+ " {{bold_blue:SOURCE_ROOT}} defaults to {{bold_blue:../}}."
46
+ )
47
+ end
48
+
49
+ head_sha = nil
50
+ dirty = false
51
+
52
+ Dir.chdir(path) do
53
+ _, _, stat = Open3.capture3('git fetch origin master')
54
+ bail("couldn't git fetch in dependency: {{yellow:#{dep}}}") unless stat.success?
55
+
56
+ head_sha, stat = Open3.capture2('git rev-parse HEAD')
57
+ bail("couldn't determine HEAD: {{yellow:#{dep}}}") unless stat.success?
58
+ head_sha.chomp!
59
+
60
+ fetch_head_sha, stat = Open3.capture2('git rev-parse FETCH_HEAD')
61
+ bail("couldn't determine FETCH_HEAD: {{yellow:#{dep}}}") unless stat.success?
62
+ fetch_head_sha.chomp!
63
+
64
+ git_status, stat = Open3.capture2('git status --porcelain')
65
+ bail("couldn't determine git status: {{yellow:#{dep}}}") unless stat.success?
66
+
67
+ if head_sha != fetch_head_sha
68
+ warn(
69
+ "Copying files from {{yellow:#{path}}} to satisfy dependency {{yellow:#{dep}}}.\n" \
70
+ " However, the repo at {{yellow:#{path}}} isn't up to date.\n" \
71
+ " The checked-out revision is {{yellow:#{head_sha[0..8]}}}, and "\
72
+ "{{yellow:origin/master}} is {{yellow:#{fetch_head_sha[0..8]}}}.\n" \
73
+ " Unless you know what you're doing, you should {{green:cd}} to that" \
74
+ " repo and {{green:git pull}}, then run this again."
75
+ )
76
+ end
77
+
78
+ unless git_status.chomp.empty?
79
+ dirty = true
80
+ warn("importing uncommitted changes from dependency: {{yellow:#{dep}}}")
81
+ end
82
+ end
83
+
84
+ depdir = File.expand_path("../../vendor/deps/#{dep}", __FILE__)
85
+ FileUtils.rm_rf(depdir)
86
+ FileUtils.mkdir_p(depdir)
87
+ dstlib = File.expand_path('lib', depdir)
88
+ srclib = File.expand_path('lib', path)
89
+
90
+ FileUtils.cp_r(srclib, dstlib)
91
+
92
+ rev = head_sha
93
+ rev << " (dirty)" if dirty
94
+ rev << "\n"
95
+
96
+ File.write("#{depdir}/REVISION", rev)
97
+ end
@@ -0,0 +1,3 @@
1
+ up:
2
+ - ruby: 2.5.0
3
+ - bundler
@@ -0,0 +1,4 @@
1
+ up:
2
+ - ruby: 2.5.0
3
+
4
+ build: bin/update-deps
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ Encoding.default_external = Encoding::UTF_8
4
+ Encoding.default_internal = Encoding::UTF_8
5
+
6
+ unshift_path = ->(path) {
7
+ p = File.expand_path("../../#{path}", __FILE__)
8
+ $LOAD_PATH.unshift(p) unless $LOAD_PATH.include?(p)
9
+ }
10
+ unshift_path.call('lib')
11
+
12
+ require 'bundler/setup'
13
+ require '__app__'
14
+
15
+ exit(__App__::ErrorHandler.call do
16
+ __App__::EntryPoint.call(ARGV.dup)
17
+ end)
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby --disable-gems
2
+
3
+ Encoding.default_external = Encoding::UTF_8
4
+ Encoding.default_internal = Encoding::UTF_8
5
+
6
+ unshift_path = ->(path) {
7
+ p = File.expand_path("../../#{path}", __FILE__)
8
+ $LOAD_PATH.unshift(p) unless $LOAD_PATH.include?(p)
9
+ }
10
+ unshift_path.call('vendor/deps/cli-ui/lib')
11
+ unshift_path.call('vendor/deps/cli-kit/lib')
12
+ unshift_path.call('lib')
13
+
14
+ require '__app__'
15
+
16
+ exit(__App__::ErrorHandler.call do
17
+ __App__::EntryPoint.call(ARGV.dup)
18
+ end)
@@ -0,0 +1,33 @@
1
+ require 'cli/ui'
2
+ require 'cli/kit'
3
+
4
+ CLI::UI::StdoutRouter.enable
5
+
6
+ module __App__
7
+ extend CLI::Kit::Autocall
8
+
9
+ TOOL_NAME = '__app__'
10
+ ROOT = File.expand_path('../..', __FILE__)
11
+ LOG_FILE = '/tmp/__app__.log'
12
+
13
+ autoload(:EntryPoint, '__app__/entry_point')
14
+ autoload(:Commands, '__app__/commands')
15
+
16
+ autocall(:Config) { CLI::Kit::Config.new(tool_name: TOOL_NAME) }
17
+ autocall(:Command) { CLI::Kit::BaseCommand }
18
+
19
+ autocall(:Executor) { CLI::Kit::Executor.new(log_file: LOG_FILE) }
20
+ autocall(:Resolver) do
21
+ CLI::Kit::Resolver.new(
22
+ tool_name: TOOL_NAME,
23
+ command_registry: __App__::Commands::Registry
24
+ )
25
+ end
26
+
27
+ autocall(:ErrorHandler) do
28
+ CLI::Kit::ErrorHandler.new(
29
+ log_file: LOG_FILE,
30
+ exception_reporter: nil
31
+ )
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ require '__app__'
2
+
3
+ module __App__
4
+ module Commands
5
+ Registry = CLI::Kit::CommandRegistry.new(
6
+ default: 'help',
7
+ contextual_resolver: nil
8
+ )
9
+
10
+ def self.register(const, cmd, path)
11
+ autoload(const, path)
12
+ Registry.add(->() { const_get(const) }, cmd)
13
+ end
14
+
15
+ register :Example, 'example', '__app__/commands/example'
16
+ register :Help, 'help', '__app__/commands/help'
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ require '__app__'
2
+
3
+ module __App__
4
+ module Commands
5
+ class Example < __App__::Command
6
+ def call(_args, _name)
7
+ puts 'neato'
8
+
9
+ if rand < 0.05
10
+ raise(CLI::Kit::Abort, "you got unlucky!")
11
+ end
12
+ end
13
+
14
+ def self.help
15
+ "A dummy command.\nUsage: {{command:#{__App__::TOOL_NAME} example}}"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ require '__app__'
2
+
3
+ module __App__
4
+ module Commands
5
+ class Help < __App__::Command
6
+ def call(args, _name)
7
+ puts CLI::UI.fmt("{{bold:Available commands}}")
8
+ puts ""
9
+
10
+ __App__::Commands::Registry.resolved_commands.each do |name, klass|
11
+ next if name == 'help'
12
+ puts CLI::UI.fmt("{{command:#{__App__::TOOL_NAME} #{name}}}")
13
+ if help = klass.help
14
+ puts CLI::UI.fmt(help)
15
+ end
16
+ puts ""
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end