squared 0.9.1 → 0.9.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ed5b9e99423ac6374d36ad8dffd6a50f2714cef292d6324e358ae19d6a547ed9
4
- data.tar.gz: 1e6358b439d1defc1f6d9601e915c49b619b00614fc104911ac0554dd61ce6cd
3
+ metadata.gz: 7bc8d196a7c5068373757f7bbb9ba037cafd30500875d924628690f57451b927
4
+ data.tar.gz: '00078ddc57f3f357cc9833a679c42d636936d0db91e75a327a07ba19dedd0165'
5
5
  SHA512:
6
- metadata.gz: 0e973fe0eb9137cd8e822b3eb5e28fc8275e69f17414373205944c28350b616cb58dae4fe41eb647ab2a018f5a21a3e7e43854cf0711a4f59c15614948bb328c
7
- data.tar.gz: '0293c7c12c7c8d66a7783162a4d1fd5b9a0ae87f3476e0c0dae73f9a3d918fcda4d93a31894b327161a6b571be9c4988bcb0fb2abff0c10995fe4a64af4e93de'
6
+ metadata.gz: c5b85d626962791e9da8355f82999b0ab8bcd1e63f88605c6a3dfecb8b5b515469373acb8f7d79eab4887b3b3f826e1d540133361b74e5c3e3160545215b3641
7
+ data.tar.gz: ee09108c1abea47c45ae2a931e01a9b1e42097dd597537796543321aaca03b9825d9942a26274c7fbca344f748b1d4b8765102dcd7b74f54489e7dc2115b5a97
data/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.2] - 2026-11-04
4
+
5
+ ### Added
6
+
7
+ - sqd: Option --no-gemfile for undoing bundle exec project wrapper.
8
+ - sqd: Option --commit for pushing files to remote branch.
9
+ - sqd: Option --mulitline for writing messages with newlines.
10
+ - sqd: Option --amend[-orig] for replacing current commit.
11
+ - sqd: Option --project can match common subtasks.
12
+
13
+ ### Changed
14
+
15
+ - Logger warn statements from project misconfiguration were downgraded.
16
+ - sqd: Task creation from joins are validated before consuming arguments.
17
+
18
+ ### Fixed
19
+
20
+ - sqd: Option --project did not support multiple patterns.
21
+
22
+ ## [0.8.8]
23
+
24
+ - See `0.7.15`.
25
+
26
+ ## [0.7.15]
27
+
28
+ ### Added
29
+
30
+ - Git command commit action pathspec was implemented.
31
+ - Common base argument TAB for column alignment was created.
32
+ - Node command package action deny can modify currently approved scripts.
33
+
34
+ ### Changed
35
+
36
+ - Node command package action [approve|deny] uses install-scripts ls.
37
+
38
+ ### Fixed
39
+
40
+ - Bundle exec wrapper as a Hash did not support using alternate Gemfile.
41
+ - Common base ARG data objects were not frozen with __freeze__.
42
+
3
43
  ## [0.9.1] - 2026-10-22
4
44
 
5
45
  ### Added
@@ -99,7 +139,7 @@
99
139
 
100
140
  - OptionPartition did not parse quoted option values.
101
141
 
102
- ## [0.6.20] - 2026-09-25
142
+ ## [0.6.20]
103
143
 
104
144
  ### Added
105
145
 
@@ -2234,8 +2274,10 @@
2234
2274
 
2235
2275
  - Changelog was created.
2236
2276
 
2277
+ [0.9.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.9.2
2237
2278
  [0.9.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.9.1
2238
2279
  [0.9.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.9.0
2280
+ [0.8.8]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.8
2239
2281
  [0.8.7]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.7
2240
2282
  [0.8.6]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.6
2241
2283
  [0.8.5]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.5
@@ -2244,6 +2286,7 @@
2244
2286
  [0.8.2]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.2
2245
2287
  [0.8.1]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.1
2246
2288
  [0.8.0]: https://github.com/anpham6/squared-ruby/releases/tag/v0.8.0
2289
+ [0.7.15]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.15
2247
2290
  [0.7.14]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.14
2248
2291
  [0.7.13]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.13
2249
2292
  [0.7.12]: https://github.com/anpham6/squared-ruby/releases/tag/v0.7.12
data/README.md CHANGED
@@ -32,7 +32,7 @@ gem install squared
32
32
  There is an additional program wrapper for ``rake`` to be used when running batch commands and to enable global settings more easily.
33
33
 
34
34
  ```xml
35
- Usage: sqd [options] [rake options] [global...] [:targets...] -- [rake targets...]
35
+ Usage: sqd [options] [rake options] [pathspec...] -- [global...] [:targets...] -- [rake targets...]
36
36
 
37
37
  -R, --ref STR,STR Run global commands by class reference
38
38
  -G, --group STR,STR Run global commands by project group
@@ -49,14 +49,19 @@ Usage: sqd [options] [rake options] [global...] [:targets...] -- [rake targets..
49
49
  -y, --yes [STR,STR] Enable auto-approve through ENV by application
50
50
  -d, --dry-run Run commands without any modifying actions
51
51
 
52
- -S, --select PAGE[:CHOICE] Prompt menu selection pagination display (e.g. 15:75)
52
+ -S, --select PAGE[:CHOICE] Prompt menu selection pagination display (e.g. 20:60)
53
+ -M, --multiline STR Use advanced editor with custom message terminator (e.g. EOF)
53
54
 
54
- -g, --gemfile Execute commands using bundle exec and nearest Gemfile
55
+ -g, --[no-]gemfile Execute commands using bundle exec and nearest Gemfile
55
56
  -p, --pipe 0|1|2 Set file descriptor for communication
56
57
  -o, --pipe-out FILE Write command data stream to a file
57
58
  --fail 0..4 Set application exception level
58
59
  --log-level SEVERITY Set logger and exception state (debug, info, warn, error, fatal)
59
60
 
61
+ -c, --commit Push files remotely on active branch from current directory
62
+ --amend Replace the tip of the active branch remotely with a force push
63
+ --amend-orig Perform an amend commit without changing the message
64
+
60
65
  --[no-]banner Display executing command attributes
61
66
  -x, --[no-]color Use enhanced styling for terminal output
62
67
  -X, --plain Disable banner and styling for all commands
data/exe/sqd CHANGED
@@ -1,43 +1,46 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- $:.unshift(File.expand_path('../lib', __dir__)) if File.exist?(File.expand_path('../squared.gemspec', __dir__))
5
-
6
4
  require 'squared'
7
5
  require 'rake'
8
6
  require 'optparse'
9
7
 
10
8
  class SqdParser < OptionParser
11
- include Squared::Common::Format
9
+ include Squared
10
+ include Common::Format
12
11
  include Utils
13
12
 
13
+ OPT_DASH = /^--?[^-]/.freeze
14
+
14
15
  attr_reader :options, :global
16
+ attr_accessor :argv
15
17
 
16
18
  def initialize(*)
17
- super
18
19
  @options = { 'project-args': [], 'project-env': [] }
19
20
  @global = []
21
+ @argv = ARGV.dup
22
+ super
20
23
  @program_name = 'squared'
21
24
  @raise_unknown = false
22
25
  @require_exact = true
23
26
  end
24
27
 
25
28
  def parse_global(list, assert:)
26
- raise ArgumentError, "cannot be used with --#{assert}" if options.key?(assert)
27
-
28
- ARGV.each_with_index do |val, index|
29
+ error_exit "cannot be used with --#{assert}" if options.key?(assert)
30
+ each_with_index do |val, index|
29
31
  if val == '--'
30
32
  separator!(index: index)
31
33
  break
32
34
  end
33
- next if val.start_with?('-') || val.include?(':') || val.empty?
35
+ found = list.map { |name| "#{val}:#{name}" }.select { |name| tasks.include?(name) }
36
+ next if found.empty?
34
37
 
35
- global.concat(list.map { |name| "#{val}:#{name}" })
38
+ global.concat(found)
36
39
  ARGV[index] = ''
37
40
  end
38
41
  end
39
42
 
40
- def parse_common(list, session:, arg:)
43
+ def parse_common(list, arg:)
41
44
  if list && !list.include?('*')
42
45
  session[arg].merge!(list.to_h { |key| [key, 'true'] })
43
46
  else
@@ -45,7 +48,7 @@ class SqdParser < OptionParser
45
48
  end
46
49
  end
47
50
 
48
- def parse_series(list, session:, flag:)
51
+ def parse_series(list, flag:)
49
52
  pattern, parallel = list.partition do |val|
50
53
  next false if val.match?(%r{^[\w~`!@#%&\-=:;"'<>/]+$})
51
54
 
@@ -58,18 +61,146 @@ class SqdParser < OptionParser
58
61
  (session[:APP][:"build_parallel#{s}"] ||= []).concat(parallel) unless parallel.empty?
59
62
  end
60
63
 
61
- def banner_set(val, session:)
64
+ def parse_range(val, max:, arg:, min: 0)
65
+ error_exit('out of range', hint: val) unless val.between?(min, max)
66
+ session[arg] = val
67
+ end
68
+
69
+ def parse_rakefile
70
+ OptionParser.new do |opts|
71
+ opts.raise_unknown = false
72
+ opts.on('--rakefile', '-f [FILENAME]', '') do |val|
73
+ return val || ''
74
+ end
75
+ end.parse(ARGV.dup)
76
+ nil
77
+ end
78
+
79
+ def option_select(val)
80
+ page, choice = val.split(/\D/, 2).map(&:to_i)
81
+ error_exit('not an integer', hint: val) if choice.nil? ? page == 0 : choice == 0
82
+ session[:PAGE] = page if page > 0
83
+ session[:CHOICE] = choice if choice.to_i > 0
84
+ ENV['SELECT'] = "#{page}:#{choice}"
85
+ end
86
+
87
+ def option_multiline(val)
88
+ Common::Prompt.multiline = val
89
+ rescue NotImplementedError => e
90
+ warn log_warn(e, hint: argv.find { |val| multilineopts.include?(val) })
91
+ end
92
+
93
+ def option_gemfile(val)
94
+ require 'pathname'
95
+ session[:APP][:ruby_gemfile] = val
96
+ return unless val
97
+ return if ENV['RUBYOPT']&.match?(/(^|\b)-r.+#{File::SEPARATOR}bundler#{File::SEPARATOR}setup\b/)
98
+
99
+ gemfile = Pathname.pwd.ascend { |path| break path if path.join('Gemfile').file? } || ENV['BUNDLE_GEMFILE']
100
+ return unless gemfile && (argv.delete('--gemfile') || argv.delete('-g'))
101
+
102
+ argv.unshift('sqd')
103
+ if gemfile.is_a?(String)
104
+ gemfile = Common::Shell.shell_escape(Pathname.new(gemfile).relative_path_from(Dir.pwd), quote: true)
105
+ argv.unshift("--gemfile=#{gemfile}")
106
+ gemfile = Dir.pwd
107
+ end
108
+ ENV['BUNDLE_GEMFILE'] = nil
109
+ exec(Common::Shell.shell_bin('bundle'), 'exec', *argv, chdir: gemfile)
110
+ end
111
+
112
+ def option_loglevel(val)
113
+ require 'logger'
114
+ severity = case val
115
+ when :debug then Logger::DEBUG
116
+ when :info then Logger::INFO
117
+ when :warn then Logger::WARN
118
+ when :error then Logger::ERROR
119
+ when :fatal then Logger::FATAL
120
+ else
121
+ val.to_i
122
+ end
123
+ session[:LEVEL] = severity
124
+ ENV['LOG_LEVEL'] = severity.to_s
125
+ end
126
+
127
+ def option_commit(*opts, flag:)
128
+ require 'squared/workspace'
129
+ c = argv.index { |val| opts.include?(val) } || error_exit('option was not found', hint: opts.last)
130
+ check_exclusive('-c', '--commit', '--amend', '--amend-orig', flag: argv[c])
131
+ first = lambda do
132
+ if ARGV.empty?
133
+ m = argv.index { |val| multilineopts.include?(val) }
134
+ t = argv.index { |val| !val.match?(OPT_DASH) } || -1
135
+ return true if c == 0 || (m == 0 && c == 2) || t > c
136
+ end
137
+ !rakefile?
138
+ end
139
+ refs = []
140
+ found = argv[c.succ]
141
+ j = -1
142
+ each_with_index do |pat, i|
143
+ found = nil if pat == found
144
+ if i == j
145
+ ARGV[i.pred] = ''
146
+ ARGV[i] = ''
147
+ option_multiline pat
148
+ next
149
+ end
150
+ next if found
151
+
152
+ case pat
153
+ when '--'
154
+ ARGV[i] = ''
155
+ break
156
+ when *multilineopts
157
+ j = i.succ
158
+ next
159
+ when OPT_DASH
160
+ next if refs.empty? || Dir[pat].empty?
161
+ else
162
+ next if refs.empty? && Dir[pat].empty?
163
+ end
164
+ refs << pat
165
+ ARGV[i] = ''
166
+ end
167
+ if refs.empty?
168
+ if !`git status -s --ignore-submodules=all --ignored=no`.empty?
169
+ refs << '*'
170
+ elsif first.call
171
+ warn 'No files to commit.'
172
+ exit 1
173
+ else
174
+ return
175
+ end
176
+ end
177
+ Workspace::Application
178
+ .new
179
+ .add(File.basename(Dir.pwd), ref: :git) { commit(flag, refs: refs.uniq, pass: nil) }
180
+ exit if first.call
181
+ self.argv = ARGV.dup
182
+ end
183
+
184
+ def option_verbose
185
+ unless session[:VERBOSE]
186
+ session[:VERBOSE] = $VERBOSE ? 1 : 0
187
+ global.unshift('--verbose')
188
+ end
189
+ ENV['VERBOSE'] = (session[:VERBOSE] += 1).to_s
190
+ end
191
+
192
+ def banner_set(val)
62
193
  session[:BANNER] = val
63
194
  ENV['BANNER'] = val ? 'true' : 'false'
64
195
  end
65
196
 
66
- def color_set(val, session:)
197
+ def color_set(val)
67
198
  if (session[:COLOR] = val)
68
199
  enable_aixterm
69
200
  enable_drawing
70
201
  else
71
202
  enable_drawing false
72
- Squared::Common.const_get(:VAR)[:colors].freeze
203
+ Common.const_get(:VAR)[:colors].freeze
73
204
  end
74
205
  ENV['NO_COLOR'] = val ? nil : '1'
75
206
  ENV['FORCE_COLOR'] = val ? '2' : '0'
@@ -86,57 +217,111 @@ class SqdParser < OptionParser
86
217
  rescue RegexpError
87
218
  false
88
219
  end
220
+ subtasks = []
89
221
  pattern.each do |val|
90
222
  val = if val == '*'
91
223
  '[^:]+'
92
224
  else
93
225
  val.sub(/^\*/, '.*')
94
226
  .sub(/(?<!\.)\*$/, '.*')
95
- .gsub(/\.([*+])/, '[^:]\1?')
227
+ .gsub(/(?<!\\)\.([*+])(?!\?)/, '[^:]\1?')
96
228
  end
97
- IO.popen("rake -T \"^#{val}:\"").each { |line| project << $1 if line =~ /\Arake (#{val}):/o }
229
+ pat = /^(#{val.chomp(':')}):/
230
+ tasks.each do |task|
231
+ next unless task =~ pat
232
+
233
+ subtasks << $1
234
+ name = task.split(':', 2).first
235
+ project << name unless project.include?(name)
236
+ end
98
237
  end
99
238
  exit 1 if project.empty? && !pattern.empty?
100
- project.uniq!
239
+ subtasks.uniq!
101
240
  options[:'project-env'].each do |val|
102
241
  n = val.index('=')
103
- key = val[...n]
104
- val = val[n.succ..].stripquote
242
+ key = val[0...n]
243
+ val = val[n.succ..-1].stripquote
105
244
  project.each { |name| ENV["#{key}_#{env_key(name.split(':').first)}"] = val }
106
245
  end
107
- ARGV.each_with_index do |val, index|
246
+ each_with_index do |val, index|
108
247
  if val == '--'
109
248
  separator!(index: index)
110
249
  break
111
250
  end
112
251
  next unless val.start_with?(':')
113
252
 
114
- args = options[:'project-args'].shift
115
- global.concat(project.map { |name| "#{name}#{val}#{"[#{args}]" if args}" })
253
+ found = subtasks.map { |name| "#{name}#{val}" }.select { |name| tasks.include?(name) }
254
+ if (args = options[:'project-args'].shift)
255
+ found.map! { |name| "#{name}[#{args}]" }
256
+ end
257
+ global.concat(found)
116
258
  ARGV[index] = ''
117
259
  end
118
260
  end
119
- ARGV.reject!(&:empty?)
120
261
  Rake.application.run(global.concat(ARGV))
121
262
  end
122
263
 
264
+ def tasks
265
+ @tasks ||= Rake.with_application do |rake|
266
+ if (rakefile = parse_rakefile)
267
+ rakefiles = rake.instance_variable_get(:@rakefiles)
268
+ rakefiles.clear
269
+ rakefiles << rakefile
270
+ end
271
+ rake.raw_load_rakefile
272
+ end.tasks.map(&:name)
273
+ end
274
+
123
275
  def separator!(index:)
124
276
  ARGV[index] = '' if ARGV.grep('--').size > 1
125
277
  end
126
278
 
127
- def error_exit(*val, hint)
279
+ def check_exclusive(*args, flag:)
280
+ args.delete(flag)
281
+ return unless (n = index(*args))
282
+
283
+ error_exit "cannot be used with #{argv[n]}"
284
+ end
285
+
286
+ def error_exit(*val, hint: nil)
128
287
  raise OptionParser::InvalidArgument, message(*val, hint: hint), caller(1)
129
288
  end
289
+
290
+ def session
291
+ Common::ARG
292
+ end
293
+
294
+ def multilineopts
295
+ %w[-M --multiline]
296
+ end
297
+
298
+ def each_with_index(&blk)
299
+ ARGV.each_with_index(&blk)
300
+ ARGV.reject!(&:empty?)
301
+ end
302
+
303
+ def index(*args)
304
+ argv.each_with_index do |val, i|
305
+ return i if args.any? { |s| val == s || (s.start_with?('--') && val.start_with?("#{s}=")) }
306
+ end
307
+ nil
308
+ end
309
+
310
+ def warn(*args)
311
+ $stderr.puts(*args)
312
+ end
313
+
314
+ def rakefile?
315
+ Rake.application.find_rakefile_location || parse_rakefile ? true : false
316
+ end
130
317
  end
131
318
 
132
319
  SqdParser.new do |sqd|
133
- session = Squared::Common::ARG
134
- argv = ARGV.dup
135
- sqd.banner = 'Usage: sqd [options] [rake options] [global...] [:targets...] -- [rake targets...]'
320
+ sqd.banner = 'Usage: sqd [options] [rake options] [pathspec...] -- [global...] [:targets...] -- [rake targets...]'
136
321
  sqd.separator ''
137
322
  sqd.on('-R', '--ref STR,STR', Array, 'Run global commands by class reference') do |val|
138
323
  sqd.parse_global(val, assert: :group)
139
- session[:APP][:series_ref_min] = 1
324
+ sqd.session[:APP][:series_ref_min] = 1
140
325
  end
141
326
  sqd.on('-G', '--group STR,STR', Array, 'Run global commands by project group') do |val|
142
327
  sqd.parse_global(val, assert: :ref)
@@ -147,105 +332,84 @@ SqdParser.new do |sqd|
147
332
  sqd.options[:'project-args'] << val
148
333
  end
149
334
  sqd.on('-e', '--project-env KEY=STR', 'Provide command ENV variables for :targets (multiple)') do |val|
150
- sqd.error_exit('missing value', "#{val}=") unless val.include?('=')
335
+ sqd.error_exit('missing value', hint: "#{val}=") unless val.include?('=')
151
336
  sqd.options[:'project-env'] << val
152
337
  end
153
338
  sqd.separator ''
154
339
  sqd.on('-s', '--[no-]strict', 'Do not run commands with invalid arguments') do |val|
155
- session[:STRICT] = val
340
+ sqd.session[:STRICT] = val
156
341
  ENV['STRICT'] = val ? 'true' : 'false'
157
342
  end
158
343
  sqd.on('--update', 'Use update strategy for applicable commands') do
159
344
  ENV['UPDATE'] = 'true'
160
345
  end
161
346
  sqd.on('-u', '--parallel STR,PAT', Array, 'Run standard tasks with multi-task threads') do |val|
162
- sqd.parse_series(val, session: session, flag: :parallel)
347
+ sqd.parse_series(val, flag: :parallel)
163
348
  end
164
349
  sqd.on('-U', '--sync STR,PAT', Array, 'Run parallel tasks in a series sequentially') do |val|
165
- sqd.parse_series(val, session: session, flag: :sync)
350
+ sqd.parse_series(val, flag: :sync)
166
351
  end
167
352
  sqd.on('-F', '--force [STR,STR]', Array, 'Enable force through ENV by application') do |val|
168
- sqd.parse_common(val, session: session, arg: :FORCE)
353
+ sqd.parse_common(val, arg: :FORCE)
169
354
  end
170
355
  sqd.on('-y', '--yes [STR,STR]', Array, 'Enable auto-approve through ENV by application') do |val|
171
- sqd.parse_common(val, session: session, arg: :YES)
356
+ sqd.parse_common(val, arg: :YES)
172
357
  end
173
358
  sqd.on('-d', '--dry-run', 'Run commands without any modifying actions') do
174
359
  ENV['DRY_RUN'] = 'true'
175
360
  end
176
361
  sqd.separator ''
177
- sqd.on('-S', '--select PAGE[:CHOICE]', 'Prompt menu selection pagination display (e.g. 15:75)') do |val|
178
- page, choice = val.split(/\D/, 2).map(&:to_i)
179
- sqd.error_exit('not an integer', val) if choice.nil? ? page == 0 : choice == 0
180
- session[:PAGE] = page if page > 0
181
- session[:CHOICE] = choice if choice.to_i > 0
182
- ENV['SELECT'] = "#{page}:#{choice}"
362
+ sqd.on('-S', '--select PAGE[:CHOICE]', 'Prompt menu selection pagination display (e.g. 20:60)') do |val|
363
+ sqd.option_select(val)
364
+ end
365
+ sqd.on('-M', '--multiline STR', 'Use advanced editor with custom message terminator (e.g. EOF)') do |val|
366
+ sqd.option_multiline(val)
183
367
  end
184
368
  sqd.separator ''
185
- sqd.on('-g', '--gemfile', 'Execute commands using bundle exec and nearest Gemfile') do
186
- require 'pathname'
187
- next if ENV['RUBYOPT']&.match?(/(^|\b)-r.+#{File::SEPARATOR}bundler#{File::SEPARATOR}setup\b/)
188
-
189
- gemfile = Pathname.pwd.ascend { |path| break path if path.join('Gemfile').file? } || ENV['BUNDLE_GEMFILE']
190
- next unless gemfile && (argv.delete('--gemfile') || argv.delete('-g'))
191
-
192
- include Squared::Common
193
- include Shell
194
-
195
- argv.unshift('sqd')
196
- if gemfile.is_a?(String)
197
- argv.unshift("--gemfile=#{shell_escape(Pathname.new(gemfile).relative_path_from(Dir.pwd), quote: true)}")
198
- gemfile = Dir.pwd
199
- end
200
- ENV['BUNDLE_GEMFILE'] = nil
201
- exec(shell_bin('bundle'), 'exec', *argv, chdir: gemfile)
369
+ sqd.on('-g', '--[no-]gemfile', 'Execute commands using bundle exec and nearest Gemfile') do |val|
370
+ sqd.option_gemfile(val)
202
371
  end
203
372
  sqd.on('-p', '--pipe 0|1|2', Integer, 'Set file descriptor for communication') do |val|
204
- sqd.error_exit('out of range', val) unless val.between?(0, 2)
205
- session[:PIPE] = val
373
+ sqd.parse_range(val, max: 2, arg: :PIPE)
206
374
  end
207
375
  sqd.on('-o', '--pipe-out FILE', 'Write command data stream to a file') do |val|
208
- sqd.error_exit("missing an explicit \".#{File::SEPARATOR}\"", val) unless val.include?(File::SEPARATOR)
209
- session[:OUT] = val
376
+ sqd.error_exit("missing an explicit \".#{File::SEPARATOR}\"", hint: val) unless val.include?(File::SEPARATOR)
377
+ sqd.session[:OUT] = val
210
378
  end
211
379
  sqd.on('--fail 0..4', Integer, 'Set application exception level') do |val|
212
- sqd.error_exit('out of range', val) unless val.between?(0, 4)
213
- session[:FAIL] = val
380
+ sqd.parse_range(val, max: 4, arg: :FAIL)
214
381
  end
215
382
  %i[debug info warn error fatal].then do |state|
216
383
  values = state + (0..4).map(&:to_s)
217
384
  sqd.on('--log-level SEVERITY', values, "Set logger and exception state (#{state.join(', ')})") do |val|
218
- require 'logger'
219
- severity = case val
220
- when :debug then Logger::DEBUG
221
- when :info then Logger::INFO
222
- when :warn then Logger::WARN
223
- when :error then Logger::ERROR
224
- when :fatal then Logger::FATAL
225
- else
226
- val.to_i
227
- end
228
- session[:LEVEL] = severity
229
- ENV['LOG_LEVEL'] = severity.to_s
385
+ sqd.option_loglevel(val)
386
+ end
387
+ end
388
+ if Pathname.pwd.join('.git').directory?
389
+ sqd.separator ''
390
+ sqd.on('-c', '--commit', 'Push files remotely on active branch from current directory') do
391
+ sqd.option_commit('-c', '--commit', flag: :pathspec)
392
+ end
393
+ sqd.on('--amend', 'Replace the tip of the active branch remotely with a force push') do
394
+ sqd.option_commit('--amend', flag: :amend)
395
+ end
396
+ sqd.on('--amend-orig', 'Perform an amend commit without changing the message') do
397
+ sqd.option_commit('--amend-orig', flag: :'amend-orig')
230
398
  end
231
399
  end
232
400
  sqd.separator ''
233
401
  sqd.on('--[no-]banner', 'Display executing command attributes') do |val|
234
- sqd.banner_set(val, session: session)
402
+ sqd.banner_set(val)
235
403
  end
236
404
  sqd.on('-x', '--[no-]color', 'Use enhanced styling for terminal output') do |val|
237
- sqd.color_set(val, session: session)
405
+ sqd.color_set(val)
238
406
  end
239
407
  sqd.on('-X', '--plain', 'Disable banner and styling for all commands') do
240
- sqd.banner_set(false, session: session)
241
- sqd.color_set(false, session: session)
408
+ sqd.banner_set(false)
409
+ sqd.color_set(false)
242
410
  end
243
411
  sqd.on('-v', '--verbose', 'Increase output verbosity (multiple)') do
244
- unless session[:VERBOSE]
245
- session[:VERBOSE] = $VERBOSE ? 1 : 0
246
- sqd.global.unshift('--verbose')
247
- end
248
- ENV['VERBOSE'] = (session[:VERBOSE] += 1).to_s
412
+ sqd.option_verbose
249
413
  end
250
414
  sqd.on('--version', 'Print the current version') do
251
415
  puts Squared::VERSION
@@ -17,6 +17,7 @@ module Squared
17
17
  BANNER: true,
18
18
  PAGE: 20,
19
19
  CHOICE: 100,
20
+ TAB: 4,
20
21
  QUOTE: "'",
21
22
  SPACE: ' => ',
22
23
  SUCCESS: 'Success',
@@ -105,6 +106,7 @@ module Squared
105
106
 
106
107
  def __freeze__
107
108
  PATH.freeze
109
+ ARG.each_value { |val| val.freeze if val.is_a?(::Hash) || val.is_a?(::Array) }
108
110
  ARG.freeze
109
111
  VAR.each_value(&:freeze)
110
112
  VAR[:theme].each_value { |val| val.freeze.each_value(&:freeze) }
@@ -301,7 +301,7 @@ module Squared
301
301
  if footer
302
302
  unless sub.empty? && !right
303
303
  footer.map! do |s|
304
- s = s.rjust(n + 4) if right
304
+ s = s.rjust(n + ARG[:TAB]) if right
305
305
  sub.each { |h| sub_style!(s, **h) }
306
306
  s
307
307
  end
@@ -5,12 +5,30 @@ require 'timeout'
5
5
  module Squared
6
6
  module Common
7
7
  module Prompt
8
- def self.reline
9
- require 'reline'
10
- Reline
11
- rescue LoadError
12
- require 'readline'
13
- Readline
8
+ class << self
9
+ def reline
10
+ require 'reline'
11
+ Reline
12
+ rescue LoadError
13
+ require 'readline'
14
+ Readline
15
+ end
16
+
17
+ def multiline=(val)
18
+ raise NotImplementedError, 'multiline is available only with reline' unless multiline?
19
+
20
+ @multiline = case val
21
+ when '', '0', 'false' then nil
22
+ else
23
+ val.split(',').map(&:strip)
24
+ end
25
+ end
26
+
27
+ def multiline?
28
+ reline.respond_to?(:readmultiline)
29
+ end
30
+
31
+ attr_reader :multiline
14
32
  end
15
33
 
16
34
  module_function
@@ -169,8 +187,8 @@ module Squared
169
187
  end
170
188
  end
171
189
 
172
- def readline(msg, history = false, force: nil, multiline: nil, &blk)
173
- multiline = nil unless Prompt.reline.respond_to?(:readmultiline)
190
+ def readline(msg, history = false, force: nil, multiline: Prompt.multiline, &blk)
191
+ multiline = nil unless Prompt.multiline?
174
192
  read = lambda do
175
193
  if !multiline
176
194
  Prompt.reline.readline(msg, history)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Squared
4
- VERSION = '0.9.1'
4
+ VERSION = '0.9.2'
5
5
  end
@@ -607,10 +607,10 @@ module Squared
607
607
  @clean.each do |val|
608
608
  src = basepath(val = val.to_s)
609
609
  if src.directory? && val.match?(%r{[\\/]\z})
610
- log&.warn "rm -rf #{src}"
610
+ log&.info "rm -rf #{src}"
611
611
  rm_rf(src, verbose: !silent?)
612
612
  else
613
- log&.warn "rm #{src}"
613
+ log&.info "rm #{src}"
614
614
  (val.include?('*') ? Dir[src] : [src]).each do |file|
615
615
  next unless File.file?(file)
616
616
 
@@ -864,7 +864,7 @@ module Squared
864
864
  instance_variable_set :"@#{key}", [blk]
865
865
  end
866
866
  else
867
- log&.warn "series: @#{key}".subhint('invalid')
867
+ log&.debug "series: @#{key}".subhint('invalid')
868
868
  end
869
869
  self
870
870
  end
@@ -876,7 +876,7 @@ module Squared
876
876
  return
877
877
  end
878
878
  cmd = cmd.target if cmd.is_a?(OptionPartition)
879
- if interactive && sync && !(@session && option('y'))
879
+ if interactive && sync && !(@session && option('y', 'yes'))
880
880
  print_item(series: true)
881
881
  msg, y, h = case interactive
882
882
  when Array
@@ -1019,7 +1019,7 @@ module Squared
1019
1019
  instance_variable_set(:"@#{key}", val)
1020
1020
  end
1021
1021
  else
1022
- log&.warn "variable_set: @#{key}".subhint('private')
1022
+ log&.debug "variable_set: @#{key}".subhint('private')
1023
1023
  end
1024
1024
  self
1025
1025
  end
@@ -1701,7 +1701,7 @@ module Squared
1701
1701
  ret = case key.downcase
1702
1702
  when 'force'
1703
1703
  getarg.call(:FORCE)
1704
- when 'y'
1704
+ when 'y', 'yes'
1705
1705
  getarg.call(:YES)
1706
1706
  end
1707
1707
  return yield(ret) if !ret.nil? && block_given?
@@ -2161,7 +2161,7 @@ module Squared
2161
2161
  end
2162
2162
 
2163
2163
  def confirm_basic(msg, hint, accept = 'Y', style: :inline, target: @session, prefix: target&.first, **kwargs)
2164
- return true if option('y', prefix: prefix)
2164
+ return true if option('y', 'yes', prefix: prefix)
2165
2165
 
2166
2166
  confirm("#{msg} [#{sub_style(hint.to_s, style.is_a?(Symbol) ? theme[style] : style)}]", accept, **kwargs)
2167
2167
  end
@@ -2726,10 +2726,12 @@ module Squared
2726
2726
  end
2727
2727
 
2728
2728
  def checkdir?(val)
2729
- return true if val.directory? && !val.empty?
2730
-
2731
- log&.warn "directory \"#{val}\"".subhint(val.directory? ? 'empty' : 'missing')
2732
- false
2729
+ if val.directory? && !val.empty?
2730
+ true
2731
+ else
2732
+ log&.warn "directory \"#{val}\"".subhint(val.directory? ? 'empty' : 'missing')
2733
+ false
2734
+ end
2733
2735
  end
2734
2736
 
2735
2737
  def printfirst?
@@ -422,7 +422,7 @@ module Squared
422
422
  def clean(*, sync: invoked_sync?('clean'), **)
423
423
  if runnable?(@clean)
424
424
  super
425
- elsif sync || option('y', prefix: 'docker')
425
+ elsif sync || option('y', 'yes', prefix: 'docker')
426
426
  image :rm
427
427
  end
428
428
  end
@@ -731,7 +731,7 @@ module Squared
731
731
  return
732
732
  end
733
733
  op << id
734
- if option('y')
734
+ if option('y', 'yes')
735
735
  exception = false
736
736
  banner = false
737
737
  end
@@ -966,7 +966,7 @@ module Squared
966
966
  pwd_set(from: from) do
967
967
  index = 1
968
968
  all = option('all', prefix: 'docker')
969
- y = from == :'image:rm' && option('y', prefix: 'docker')
969
+ y = from == :'image:rm' && option('y', 'yes', prefix: 'docker')
970
970
  filter = env('DOCKER_FILTER', filter).to_s
971
971
  pat = if OptionPartition.pattern?(filter)
972
972
  Regexp.new(filter)
@@ -1013,7 +1013,7 @@ module Squared
1013
1013
  end
1014
1014
  pad = cols.map(&:size).max
1015
1015
  cols.each { |key| puts "#{g + f} #{key.rjust(pad)}: #{Array(data[key]).join(', ')}" }
1016
- w = 9 + flag.to_s.size + 4 + ee.size
1016
+ w = 9 + flag.to_s.size + ARG[:TAB] + ee.size
1017
1017
  puts g + sub_style(ARG[:BORDER][6] + (ARG[:BORDER][1] * w), theme[:inline])
1018
1018
  index += 1
1019
1019
  next unless confirm("#{h + b}?", no ? 'N' : 'Y')
@@ -363,7 +363,7 @@ module Squared
363
363
  subtasks({
364
364
  'branch' => %i[create track delete move copy list all current].freeze,
365
365
  'checkout' => %i[commit branch track detach path].freeze,
366
- 'commit' => %i[head add all amend amend-orig fixup].freeze,
366
+ 'commit' => %i[head add all pathspec amend amend-orig fixup].freeze,
367
367
  'clone' => %i[branch tag].freeze,
368
368
  'diff' => %i[head branch files view between contain].freeze,
369
369
  'range-diff' => %i[view between contain].freeze,
@@ -530,7 +530,7 @@ module Squared
530
530
  if flag == :add
531
531
  opts = param_guard(action, flag, args: args.to_a)
532
532
  elsif (refs = args.to_a).empty?
533
- refs = readline('Enter file patterns', force: true).shellsplit
533
+ refs = readline('Enter glob patterns', force: true).shellsplit
534
534
  end
535
535
  end
536
536
  commit(flag, opts, refs: refs, ref: ref, squash: squash, pick: pick)
@@ -1773,7 +1773,7 @@ module Squared
1773
1773
  pathspec = false if pick == 'reword'
1774
1774
  end
1775
1775
  if pathspec.nil?
1776
- pathspec = if (flag == :fixup || amend) && refs.size == 1 && refs.first == '*'
1776
+ pathspec = if (flag == :pathspec || flag == :fixup || amend) && refs.size == 1 && refs.first == '*'
1777
1777
  '--all'
1778
1778
  elsif (refs = projectmap(refs)).empty?
1779
1779
  raise 'no qualified pathspec'
@@ -1847,7 +1847,7 @@ module Squared
1847
1847
  elsif stdout?
1848
1848
  puts log_message('nothing to commit', subject: name, hint: flag)
1849
1849
  end
1850
- exit 1
1850
+ exit 1 unless pass.nil?
1851
1851
  end
1852
1852
  end
1853
1853
 
@@ -11,7 +11,7 @@ module Squared
11
11
  install_a: %w[audit=! bin-links=! engine-strict=!? foreground-scripts=!? fund=! ignore-scripts=!?
12
12
  install-links=!? package-lock=! strict-peer-deps=!? include=b install-strategy=b omit=b].freeze,
13
13
  install_b: %w[no-save B|save-bundle D|save-dev O|save-optional save-peer P|save-prod g|global=!?
14
- S|save=!?].freeze,
14
+ min-release-age=i min-release-age-exclude=q S|save=!?].freeze,
15
15
  install_c: %w[allow-directory=b? allow-file=b? allow-git=b? allow-remote=b?].freeze,
16
16
  run: %w[foreground-scripts=!? if-present=!? ignore-scripts=!? script-shell=p].freeze,
17
17
  exec: %w[c|call=q package=b].freeze,
@@ -230,7 +230,7 @@ module Squared
230
230
  elsif exception?
231
231
  indexerror n, list
232
232
  else
233
- log.warn "run script #{n} of #{list.size}".subhint('out of range')
233
+ log.debug "run script #{n} of #{list.size}".subhint('out of range')
234
234
  next
235
235
  end
236
236
  else
@@ -330,26 +330,39 @@ module Squared
330
330
  when 'package'
331
331
  case flag
332
332
  when :approve, :deny
333
- format_desc action, flag, 'name*|:,opts*'
333
+ format_desc(action, flag, 'opts*', before: flag == :approve ? 'name*|:' : 'name*|:|::')
334
334
  task flag do |_, args|
335
335
  args = args.to_a
336
- if choice!(args)
336
+ if choice!(args) || (flag == :deny && (all = choice!(args, char: '::')))
337
+ list = []
338
+ pad = 0
339
+ size = ->(s) { pad = [pad, s.size].max }
337
340
  pwd_set do
338
- pad = 0
339
- list = IO.popen(npm_output('approve-scripts --allow-scripts-pending').to_s)
340
- .each_with_object([]) do |line, out|
341
- next unless line =~ /^\s+([a-z\d@][^@]*)@(\S+)/
342
-
343
- pad = [pad, $1.size].max
344
- out << [$1, $2]
345
- end
346
- .map { |name, ver| name.ljust(pad + 4) + ver }
347
- if list.empty?
348
- puts log_message('no packages to review', subject: name, hint: "#{flag}-scripts")
349
- exit
341
+ json = JSON.parse(`#{npm_output('install-scripts ls --json')}`)
342
+ json&.fetch('allowScripts', [])&.each do |data|
343
+ line = data['changes']&.find { |item| item['change'] == 'pending' }
344
+ next unless line && line['key'] =~ /^(.+)@([^@]+)$/
345
+
346
+ size.call($1)
347
+ list << [$1, $2]
348
+ end
349
+ if all
350
+ current = read_package 'allowScripts'
351
+ current&.each_pair do |key, val|
352
+ next unless val
353
+
354
+ md = key.match(/^(.+?)(?:@([^@]+))?$/)
355
+ size.call(md[1])
356
+ list << [md[1], sub_style(md[2] || 'any', theme[:inline])]
357
+ end
350
358
  end
351
- args.concat(choice_index('Select packages', list, multiple: true, column: /^(\S+)/))
352
359
  end
360
+ if list.empty?
361
+ puts log_message('no packages to review', subject: name, hint: "#{flag}-scripts")
362
+ exit
363
+ end
364
+ list.map! { |name, ver| name.ljust(pad + ARG[:TAB]) + ver }
365
+ args.concat(choice_index('Select packages', list, multiple: true, column: /^(\S+)/))
353
366
  end
354
367
  package flag, args
355
368
  end
@@ -885,9 +898,9 @@ module Squared
885
898
  end
886
899
  print_item banner unless sync
887
900
  if !found.empty?
888
- col1 = width.call(found, 0) + 4
889
- col2 = width.call(found, 1) + 4
890
- col3 = pad.call(found, found.size).size + 2 + col1 + col2 + width.call(found, 2)
901
+ col1 = width.call(found, 0) + ARG[:TAB]
902
+ col2 = width.call(found, 1) + ARG[:TAB]
903
+ col3 = pad.call(found, found.size).size + (ARG[:TAB] / 2) + col1 + col2 + width.call(found, 2)
891
904
  packages = []
892
905
  pat = ->(a) { /("#{Regexp.escape(a[0])}"\s*:\s*)"([~^])#{'?' if a[4]}#{Regexp.escape(a[1])}"/ }
893
906
  edit = lambda do |a, pkg, mod|
@@ -983,9 +996,9 @@ module Squared
983
996
  end
984
997
  end
985
998
  elsif !avail.empty?
986
- col1 = width.call(avail, 0) + 4
999
+ col1 = width.call(avail, 0) + ARG[:TAB]
987
1000
  col2 = width.call(avail, 1)
988
- col3 = width.call(avail, 2) + 4
1001
+ col3 = width.call(avail, 2) + ARG[:TAB]
989
1002
  avail.each_with_index do |item, i|
990
1003
  a, b, c, d = item
991
1004
  a = a.ljust(col1)
@@ -1381,7 +1394,7 @@ module Squared
1381
1394
  op.exist?(add: true)
1382
1395
  end
1383
1396
  end
1384
- op.append(flags, escape: kwargs.fetch(:escape, false), quote: kwargs.fetch(:quote, false))
1397
+ op.append(flags, context: kwargs)
1385
1398
  .clear
1386
1399
  print_run(op, banner, **kwargs)
1387
1400
  start = time_epoch if kwargs.fetch(:verbose, verbose? && !stdin?)
@@ -221,7 +221,7 @@ module Squared
221
221
  indexerror n, list
222
222
  else
223
223
  found |= 2
224
- log.warn "run script #{n} of #{list.size}".subhint('out of range')
224
+ log.debug "run script #{n} of #{list.size}".subhint('out of range')
225
225
  end
226
226
  else
227
227
  case (i > 1 && poetry? ? 0 : i)
@@ -235,7 +235,7 @@ module Squared
235
235
  raise message('script', val) if exception?
236
236
 
237
237
  found |= 2
238
- log.warn "run script \"#{val}\"".subhint('not indexed')
238
+ log.debug "run script \"#{val}\"".subhint('not indexed')
239
239
  end
240
240
  end
241
241
  end
@@ -253,7 +253,7 @@ module Squared
253
253
  task action do |_, args|
254
254
  args = args.to_a
255
255
  cmd = if (i = args.delete(':')) && !windows?
256
- readline('Enter script', force: true, multiline: %w[## ;])
256
+ readline('Enter script', force: true, multiline: Prompt.multiline || %w[## ;])
257
257
  elsif i || args.empty?
258
258
  readline('Enter command', force: true)
259
259
  else
@@ -803,7 +803,7 @@ module Squared
803
803
  op.clear
804
804
  else
805
805
  op.exist?(add: true, first: true) unless op.arg?('m')
806
- op.append(escape: kwargs.fetch(:escape, false), quote: kwargs.fetch(:quote, false))
806
+ op.append(context: kwargs)
807
807
  end
808
808
  end
809
809
  var = kwargs[:env]
@@ -914,7 +914,7 @@ module Squared
914
914
  when :install, :uninstall
915
915
  raise ArgumentError, message('no tags listed', hint: flag) if op.empty?
916
916
  end
917
- op << '-y' if option('y')
917
+ op << '-y' if option('y', 'yes')
918
918
  op.append
919
919
  print_run(op, banner, **kwargs)
920
920
  run(sync: sync, banner: banner, exception: exception?(**kwargs), from: symjoin('py', flag))
@@ -952,7 +952,7 @@ module Squared
952
952
  @dependindex = index
953
953
  @dependfile = val
954
954
  else
955
- log.warn "variable_set: @dependfile=#{val}".subhint('not supported')
955
+ log.debug "variable_set: @dependfile=#{val}".subhint('not supported')
956
956
  end
957
957
  end
958
958
  when :venv
@@ -304,7 +304,7 @@ module Squared
304
304
  elsif exception?
305
305
  indexerror n, tasks
306
306
  else
307
- log.warn "rake task #{n} of #{tasks.size}".subhint('out of range')
307
+ log.debug "rake task #{n} of #{tasks.size}".subhint('out of range')
308
308
  opts.clear
309
309
  next
310
310
  end
@@ -572,10 +572,10 @@ module Squared
572
572
  when ';', /^#+$/
573
573
  args.shift
574
574
  else
575
- %w[## ;]
575
+ Prompt.multiline || %w[## ;]
576
576
  end
577
577
  unless (command = ENV['RUBY_E'])
578
- command = if Prompt.reline.respond_to?(:readmultiline)
578
+ command = if Prompt.multiline?
579
579
  require 'ripper'
580
580
  readline('ruby>', force: true, multiline: multiline) do |code|
581
581
  next if code.strip.empty? || !Ripper.sexp(code)
@@ -956,7 +956,7 @@ module Squared
956
956
  end
957
957
  s = '%2d. %s' % [start, line]
958
958
  if ia
959
- unless confirm_semver(s.ljust(col + 4 + line.size - n), type)
959
+ unless confirm_semver(s.ljust(col + ARG[:TAB] + line.size - n), type)
960
960
  start += 1
961
961
  next
962
962
  end
@@ -1049,7 +1049,7 @@ module Squared
1049
1049
  if op.arg?('e')
1050
1050
  op.clear
1051
1051
  else
1052
- op.append(delim: true, escape: kwargs.fetch(:escape, false), quote: kwargs.fetch(:quote, false))
1052
+ op.append(delim: true, context: kwargs)
1053
1053
  end
1054
1054
  end
1055
1055
  print_run(op, banner, **kwargs) unless flag || op.stdout
@@ -1394,10 +1394,10 @@ module Squared
1394
1394
  end
1395
1395
  if flag == :install
1396
1396
  op.append_any(escape: true)
1397
+ op.delim << post if post
1397
1398
  else
1398
1399
  op.append(escape: true)
1399
1400
  end
1400
- op.delim << post if post
1401
1401
  when :check, :cleanup, :contents, :fetch, :list, :lock, :rdoc
1402
1402
  op.append
1403
1403
  when :dependency, :info, :search
@@ -1630,7 +1630,7 @@ module Squared
1630
1630
  case flag
1631
1631
  when :prototype
1632
1632
  sig = args.shift
1633
- y = option('y', ignore: false)
1633
+ y = option('y', 'yes', ignore: false)
1634
1634
  i = 1
1635
1635
  args.map { |val| basepath(val).relative_path_from(path) }.each do |file|
1636
1636
  dir = basepath sig, file.dirname
@@ -1769,7 +1769,7 @@ module Squared
1769
1769
  end
1770
1770
 
1771
1771
  def gemname
1772
- @gemname ||= ((spec = gemspec) ? spec.name : project)
1772
+ @gemname ||= gemspec&.name || project
1773
1773
  end
1774
1774
 
1775
1775
  def project
@@ -1922,24 +1922,22 @@ module Squared
1922
1922
  end
1923
1923
 
1924
1924
  def bundle_args(*args)
1925
- if (val = @bundle[:exec])
1926
- app = args.first
1927
- if val.is_a?(Hash)
1928
- env = val[app.to_s] || val[app.to_sym]
1929
- return args unless env
1930
-
1931
- val = env.is_a?(String) ? basepath!(env) : true
1932
- elsif app == 'ruby'
1933
- return args
1934
- end
1935
- if val.is_a?(String)
1936
- return args unless (val = basepath!(val))
1937
-
1938
- args.unshift("--gemfile=#{shell_escape(val.relative_path_from(path), quote: true)}")
1939
- end
1940
- args.unshift('bundle', 'exec')
1925
+ gemfile = ARG[:APP][:ruby_gemfile]
1926
+ val = gemfile.is_a?(Hash) ? gemfile : @bundle[:exec]
1927
+ return args unless gemfile || (val && gemfile != false)
1928
+
1929
+ app = args.first
1930
+ if val.is_a?(Hash)
1931
+ val = val[app.to_s] || val[app.to_sym]
1932
+ elsif app == 'ruby' && !gemfile
1933
+ return args
1934
+ end
1935
+ case val
1936
+ when String, Pathname
1937
+ val = basepath!(val, type: 'f')
1938
+ args.unshift("--gemfile=#{shell_escape(val.relative_path_from(path), quote: true)}") if val
1941
1939
  end
1942
- args
1940
+ val ? args.unshift('bundle', 'exec') : args
1943
1941
  end
1944
1942
 
1945
1943
  def bundle_load
@@ -2133,7 +2131,7 @@ module Squared
2133
2131
  def serve?
2134
2132
  !Gem::Specification.find_by_name('webrick').nil?
2135
2133
  rescue Gem::MissingSpecError => e
2136
- log.warn e if @serve
2134
+ log.debug e if @serve
2137
2135
  false
2138
2136
  end
2139
2137
  end
@@ -58,10 +58,10 @@ module Squared
58
58
  ret.map! do |val|
59
59
  next val if opt?(val)
60
60
 
61
- if !(pa = val.is_a?(Pathname)) && escape
62
- shell_escape(val, quote: quote, double: double)
63
- elsif quote || pa
61
+ if (quote && !escape) || val.is_a?(Pathname)
64
62
  shell_quote(val, double: double, **kwargs)
63
+ elsif escape
64
+ shell_escape(val, quote: quote, double: double)
65
65
  else
66
66
  val
67
67
  end
@@ -487,7 +487,11 @@ module Squared
487
487
  self
488
488
  end
489
489
 
490
- def append(*args, clear: false, preserve: '"', **kwargs, &blk)
490
+ def append(*args, clear: false, preserve: '"', context: nil, **kwargs, &blk)
491
+ if context
492
+ kwargs[:escape] = context.fetch(:escape, false) unless kwargs.key?(:escape)
493
+ kwargs[:quote] = context.fetch(:quote, false) unless kwargs.key?(:quote)
494
+ end
491
495
  args = clear ? extras.dup.tap { extras.clear } : extras if args.empty?
492
496
  pass = kwargs[:pass] ||= []
493
497
  OptionPartition.append(target, *args, preserve: preserve, **kwargs, &blk)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squared
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham
@@ -142,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
142
  - !ruby/object:Gem::Version
143
143
  version: '0'
144
144
  requirements: []
145
- rubygems_version: 4.0.23
145
+ rubygems_version: 4.0.20
146
146
  specification_version: 4
147
147
  summary: Rake task generator for managing multi-language workspaces.
148
148
  test_files: []