dev-kit 0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9f29c2743d231ff3c8b7485ed1f98e50e6246bad
4
+ data.tar.gz: '0389cdeaabb2b1b3a649f2abc9431e769a44dd94'
5
+ SHA512:
6
+ metadata.gz: 49126e8a61f5ade5609d21e6dd1d7721ab4c36436b5dcd9749718c4d2652eef2500abc4aa6a2e6eaa0b092347aa41dd2dcc868c65f2fd3e47d29aeab7dca7661
7
+ data.tar.gz: 6c612d9fe65a830bd1775ee4fe9eea768f6a7d9d586227e41572c2de434121aa646da6ec1fd8025ed64d082244898ec683d7cef60bd05e2334512a709479a8c8
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ *.gem
2
+ build
3
+ .vagrant
4
+ .DS_Store
5
+ .bundle
6
+
7
+ .starscope.db
8
+ cscope.out
9
+ tags
10
+
11
+ .byebug_history
12
+
13
+ .rubocop-*
14
+ doc
data/.rubocop.yml ADDED
@@ -0,0 +1,23 @@
1
+ inherit_from:
2
+ - http://shopify.github.io/ruby-style-guide/rubocop.yml
3
+
4
+ AllCops:
5
+ Exclude:
6
+ - 'vendor/**/*'
7
+ TargetRubyVersion: 2.0
8
+
9
+ # This doesn't understand that <<~ doesn't exist in 2.0
10
+ Style/IndentHeredoc:
11
+ Enabled: false
12
+
13
+ # This doesn't take into account retrying from an exception
14
+ Lint/HandleExceptions:
15
+ Enabled: false
16
+
17
+ # allow String.new to create mutable strings
18
+ Style/EmptyLiteral:
19
+ Enabled: false
20
+
21
+ # allow the use of globals which makes sense in a CLI app like this
22
+ Style/GlobalVars:
23
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.3
5
+ before_install: gem install bundler -v 1.15.0
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ # NOTE: These are development-only dependencies
2
+ source "https://rubygems.org"
3
+
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem 'rubocop'
8
+ gem 'byebug'
9
+ gem 'method_source'
10
+ end
11
+
12
+ group :test do
13
+ gem 'mocha', require: false
14
+ gem 'minitest', '>= 5.0.0', require: false
15
+ gem 'minitest-reporters', require: false
16
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ dev-kit (0.1.0)
5
+ dev-ui (>= 0.1.0)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ansi (1.5.0)
11
+ ast (2.3.0)
12
+ builder (3.2.3)
13
+ byebug (9.0.6)
14
+ dev-ui (0.1.0)
15
+ metaclass (0.0.4)
16
+ method_source (0.8.2)
17
+ minitest (5.10.2)
18
+ minitest-reporters (1.1.14)
19
+ ansi
20
+ builder
21
+ minitest (>= 5.0)
22
+ ruby-progressbar
23
+ mocha (1.2.1)
24
+ metaclass (~> 0.0.1)
25
+ parallel (1.11.2)
26
+ parser (2.4.0.0)
27
+ ast (~> 2.2)
28
+ powerpack (0.1.1)
29
+ rainbow (2.2.2)
30
+ rake
31
+ rake (10.5.0)
32
+ rubocop (0.49.1)
33
+ parallel (~> 1.10)
34
+ parser (>= 2.3.3.1, < 3.0)
35
+ powerpack (~> 0.1)
36
+ rainbow (>= 1.99.1, < 3.0)
37
+ ruby-progressbar (~> 1.7)
38
+ unicode-display_width (~> 1.0, >= 1.0.1)
39
+ ruby-progressbar (1.8.1)
40
+ unicode-display_width (1.3.0)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ bundler (~> 1.15)
47
+ byebug
48
+ dev-kit!
49
+ method_source
50
+ minitest (>= 5.0.0)
51
+ minitest-reporters
52
+ mocha
53
+ rake (~> 10.0)
54
+ rubocop
55
+
56
+ BUNDLED WITH
57
+ 1.15.0
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Burke Libbey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # dev-kit
2
+
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "dev/kit"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/testunit ADDED
@@ -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
+ DEV_TEST_ROOT = root + '/test'
8
+
9
+ $LOAD_PATH.unshift(DEV_TEST_ROOT)
10
+
11
+ def test_files
12
+ Dir.glob(DEV_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
data/dev-kit.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "dev/kit/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "dev-kit"
8
+ spec.version = Dev::Kit::VERSION
9
+ spec.authors = ["Burke Libbey", "Julian Nadeau"]
10
+ spec.email = ["burke.libbey@shopify.com", "julian.nadeau@shopify.com"]
11
+
12
+ spec.summary = %q{Terminal UI framework extensions}
13
+ spec.description = %q{Terminal UI framework extensions}
14
+ spec.homepage = "https://github.com/shopify/dev-kit"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_runtime_dependency "dev-ui", ">= 0.1.0"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.15"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest", "~> 5.0"
29
+ end
data/dev.yml ADDED
@@ -0,0 +1,8 @@
1
+ up:
2
+ - homebrew:
3
+ - ruby: 2.3.3
4
+ - bundler
5
+
6
+ commands:
7
+ test: bin/testunit
8
+ style: "bundle exec rubocop -D"
data/lib/dev/kit.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'dev/ui'
2
+
3
+ module Dev
4
+ module Kit
5
+ autoload :System, 'dev/kit/system'
6
+ end
7
+ end
@@ -0,0 +1,179 @@
1
+ require 'dev/kit'
2
+
3
+ require 'open3'
4
+ require 'English'
5
+
6
+ module Dev
7
+ module Kit
8
+ module System
9
+ class << self
10
+ SUDO_PROMPT = Dev::UI.fmt("{{info:(sudo)}} Password: ")
11
+
12
+ # Ask for sudo access with a message explaning the need for it
13
+ # Will make subsequent commands capable of running with sudo for a period of time
14
+ #
15
+ # #### Parameters
16
+ # - `msg`: A message telling the user why sudo is needed
17
+ #
18
+ # #### Usage
19
+ # `ctx.sudo_reason("We need to do a thing")`
20
+ #
21
+ def sudo_reason(msg)
22
+ # See if sudo has a cached password
23
+ `env SUDO_ASKPASS=/usr/bin/false sudo -A true`
24
+ return if $CHILD_STATUS.success?
25
+ Dev::UI.with_frame_color(:blue) do
26
+ puts(Dev::UI.fmt("{{i}} #{msg}"))
27
+ end
28
+ end
29
+
30
+ # Execute a command in the user's environment
31
+ # This is meant to be largely equivalent to backticks, only with the env passed in.
32
+ # Captures the results of the command without output to the console
33
+ #
34
+ # #### Parameters
35
+ # - `*a`: A splat of arguments evaluated as a command. (e.g. `'rm', folder` is equivalent to `rm #{folder}`)
36
+ # - `sudo`: If truthy, run this command with sudo. If String, pass to `sudo_reason`
37
+ # - `env`: process environment with which to execute this command
38
+ #
39
+ # #### Returns
40
+ # - `output`: output (STDOUT) of the command execution
41
+ # - `status`: boolean success status of the command execution
42
+ #
43
+ # #### Usage
44
+ # `out, stat = Dev::Kit::System.capture2('ls', 'a_folder')`
45
+ #
46
+ def capture2(*a, sudo: false, env: ENV)
47
+ delegate_open3(*a, sudo: sudo, env: env, method: :capture2)
48
+ end
49
+
50
+ # Execute a command in the user's environment
51
+ # This is meant to be largely equivalent to backticks, only with the env passed in.
52
+ # Captures the results of the command without output to the console
53
+ #
54
+ # #### Parameters
55
+ # - `*a`: A splat of arguments evaluated as a command. (e.g. `'rm', folder` is equivalent to `rm #{folder}`)
56
+ # - `sudo`: If truthy, run this command with sudo. If String, pass to `sudo_reason`
57
+ # - `env`: process environment with which to execute this command
58
+ #
59
+ # #### Returns
60
+ # - `output`: output (STDOUT merged with STDERR) of the command execution
61
+ # - `status`: boolean success status of the command execution
62
+ #
63
+ # #### Usage
64
+ # `out_and_err, stat = Dev::Kit::System.capture2e('ls', 'a_folder')`
65
+ #
66
+ def capture2e(*a, sudo: false, env: ENV)
67
+ delegate_open3(*a, sudo: sudo, env: env, method: :capture2e)
68
+ end
69
+
70
+ # Execute a command in the user's environment
71
+ # This is meant to be largely equivalent to backticks, only with the env passed in.
72
+ # Captures the results of the command without output to the console
73
+ #
74
+ # #### Parameters
75
+ # - `*a`: A splat of arguments evaluated as a command. (e.g. `'rm', folder` is equivalent to `rm #{folder}`)
76
+ # - `sudo`: If truthy, run this command with sudo. If String, pass to `sudo_reason`
77
+ # - `env`: process environment with which to execute this command
78
+ #
79
+ # #### Returns
80
+ # - `output`: STDOUT of the command execution
81
+ # - `error`: STDERR of the command execution
82
+ # - `status`: boolean success status of the command execution
83
+ #
84
+ # #### Usage
85
+ # `out, err, stat = Dev::Kit::System.capture3('ls', 'a_folder')`
86
+ #
87
+ def capture3(*a, sudo: false, env: ENV)
88
+ delegate_open3(*a, sudo: sudo, env: env, method: :capture3)
89
+ end
90
+
91
+ # Execute a command in the user's environment
92
+ # Outputs result of the command without capturing it
93
+ #
94
+ # #### Parameters
95
+ # - `*a`: A splat of arguments evaluated as a command. (e.g. `'rm', folder` is equivalent to `rm #{folder}`)
96
+ # - `sudo`: If truthy, run this command with sudo. If String, pass to `sudo_reason`
97
+ # - `env`: process environment with which to execute this command
98
+ # - `**kwargs`: additional keyword arguments to pass to Process.spawn
99
+ #
100
+ # #### Returns
101
+ # - `status`: boolean success status of the command execution
102
+ #
103
+ # #### Usage
104
+ # `stat = Dev::Kit::System.system('ls', 'a_folder')`
105
+ #
106
+ def system(*a, sudo: false, env: ENV, **kwargs)
107
+ a = apply_sudo(*a, sudo)
108
+
109
+ out_r, out_w = IO.pipe
110
+ err_r, err_w = IO.pipe
111
+ in_stream = STDIN.closed? ? :close : STDIN
112
+ pid = Process.spawn(env, *resolve_path(a, env), 0 => in_stream, :out => out_w, :err => err_w, **kwargs)
113
+ out_w.close
114
+ err_w.close
115
+
116
+ handlers = if block_given?
117
+ { out_r => ->(data) { yield(data.force_encoding(Encoding::UTF_8), '') },
118
+ err_r => ->(data) { yield('', data.force_encoding(Encoding::UTF_8)) }, }
119
+ else
120
+ { out_r => ->(data) { STDOUT.write(data) },
121
+ err_r => ->(data) { STDOUT.write(data) }, }
122
+ end
123
+
124
+ loop do
125
+ ios = [err_r, out_r].reject(&:closed?)
126
+ break if ios.empty?
127
+
128
+ readers, = IO.select(ios)
129
+ readers.each do |io|
130
+ begin
131
+ handlers[io].call(io.readpartial(4096))
132
+ rescue IOError
133
+ io.close
134
+ end
135
+ end
136
+ end
137
+
138
+ Process.wait(pid)
139
+ $CHILD_STATUS
140
+ end
141
+
142
+ private
143
+
144
+ def apply_sudo(*a, sudo)
145
+ a.unshift('sudo', '-S', '-p', SUDO_PROMPT) if sudo
146
+ sudo_reason(sudo) if sudo.is_a?(String)
147
+ a
148
+ end
149
+
150
+ def delegate_open3(*a, sudo: raise, env: raise, method: raise)
151
+ a = apply_sudo(*a, sudo)
152
+ Open3.send(method, env, *resolve_path(a, env))
153
+ rescue Errno::EINTR
154
+ raise(Errno::EINTR, "command interrupted: #{a.join(' ')}")
155
+ end
156
+
157
+ # Ruby resolves the program to execute using its own PATH, but we want it to
158
+ # use the provided one, so we ensure ruby chooses to spawn a shell, which will
159
+ # parse our command and properly spawn our target using the provided environment.
160
+ #
161
+ # This is important because dev clobbers its own environment such that ruby
162
+ # means /usr/bin/ruby, but we want it to select the ruby targeted by the active
163
+ # project.
164
+ #
165
+ # See https://github.com/Shopify/dev/pull/625 for more details.
166
+ def resolve_path(a, env)
167
+ # If only one argument was provided, make sure it's interpreted by a shell.
168
+ return ["true ; " + a[0]] if a.size == 1
169
+ return a if a.first.include?('/')
170
+ item = env.fetch('PATH', '').split(':').detect do |f|
171
+ File.exist?("#{f}/#{a.first}")
172
+ end
173
+ a[0] = "#{item}/#{a.first}" if item
174
+ a
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,5 @@
1
+ module Dev
2
+ module Kit
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dev-kit
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Burke Libbey
8
+ - Julian Nadeau
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2017-06-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: dev-ui
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 0.1.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 0.1.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.15'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.15'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '10.0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '10.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: minitest
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '5.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '5.0'
70
+ description: Terminal UI framework extensions
71
+ email:
72
+ - burke.libbey@shopify.com
73
+ - julian.nadeau@shopify.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".gitignore"
79
+ - ".rubocop.yml"
80
+ - ".travis.yml"
81
+ - Gemfile
82
+ - Gemfile.lock
83
+ - LICENSE.txt
84
+ - README.md
85
+ - bin/console
86
+ - bin/testunit
87
+ - dev-kit.gemspec
88
+ - dev.yml
89
+ - lib/dev/kit.rb
90
+ - lib/dev/kit/system.rb
91
+ - lib/dev/kit/version.rb
92
+ homepage: https://github.com/shopify/dev-kit
93
+ licenses:
94
+ - MIT
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 2.6.10
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: Terminal UI framework extensions
116
+ test_files: []