squared 0.7.4 → 0.7.6

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.
@@ -156,7 +156,7 @@ module Squared
156
156
  'package' => %i[install add update dedupe rebuild reinstall].freeze,
157
157
  'outdated' => %i[major minor patch].freeze,
158
158
  'bump' => %i[version major minor patch].freeze,
159
- 'publish' => %i[latest tag].freeze,
159
+ 'publish' => %i[latest tag verify].freeze,
160
160
  'tsc' => %i[project build].freeze,
161
161
  'add' => nil,
162
162
  'run' => nil,
@@ -209,11 +209,10 @@ module Squared
209
209
  depend(:add, packages: packages, save: save)
210
210
  end
211
211
  when 'run'
212
- next if scripts.empty?
213
-
214
212
  format_desc action, nil, "script,opts*|#{indexchar}index+|#,pattern*"
215
213
  task action, [:script] do |_, args|
216
- list = scripts.to_a
214
+ next if (list = scripts.to_a).empty?
215
+
217
216
  if args.script == '#'
218
217
  format_list(list, "run[#{indexchar}N]", 'scripts', grep: args.extras, from: dependfile)
219
218
  else
@@ -242,7 +241,7 @@ module Squared
242
241
  else
243
242
  npmopts :run
244
243
  end
245
- OptionPartition.new(opts, list, session(dependbin, 'run'), project: self)
244
+ OptionPartition.new(opts, list, session(dependbin, 'run'), project: self, strict: strict?)
246
245
  .add_first
247
246
  .append(delim: true, quote: false)
248
247
  run(from: :run)
@@ -261,7 +260,7 @@ module Squared
261
260
  list = npmopts :exec
262
261
  session 'npm', 'exec'
263
262
  end
264
- op = OptionPartition.new(args, list, cmd, project: self)
263
+ op = OptionPartition.new(args, list, cmd, project: self, strict: strict?)
265
264
  if op.empty?
266
265
  op << package
267
266
  if (args = readline('Enter arguments', force: false))
@@ -334,21 +333,34 @@ module Squared
334
333
  end
335
334
  end
336
335
  when 'publish'
337
- format_desc(action, flag, 'otp?,p/ublic|r/estricted?,d/ry-run?', before: ('tag' if flag == :tag))
338
- task flag do |_, args|
339
- args = args.to_a
340
- access = if has_value!(args, 'r', 'restricted')
341
- 'restricted'
342
- elsif has_value!(args, 'p', 'public')
343
- 'public'
344
- end
345
- dryrun = has_value!(args, 'd', 'dry-run')
346
- if flag == :latest
347
- otp = args.first
348
- else
349
- tag, otp = param_guard(action, flag, args: args)
336
+ if flag == :verify
337
+ format_desc action, flag, 'version?,ext*'
338
+ task flag, [:version] do |_, args|
339
+ ext = args.extras
340
+ if (version = args.version)&.match?(/^\.?[a-z]+$/i)
341
+ ext.unshift(version)
342
+ version = nil
343
+ end
344
+ publish(flag, version: version, ext: ext)
345
+ end
346
+ else
347
+ format_desc(action, flag, 'otp?,p/ublic|r/estricted?,d/ry-run?',
348
+ before: ('tag' if flag == :tag))
349
+ task flag do |_, args|
350
+ args = args.to_a
351
+ access = if has_value!(args, 'r', 'restricted')
352
+ 'restricted'
353
+ elsif has_value!(args, 'p', 'public')
354
+ 'public'
355
+ end
356
+ dryrun = has_value!(args, 'd', 'dry-run')
357
+ if flag == :latest
358
+ otp = args.first
359
+ else
360
+ tag, otp = param_guard(action, flag, args: args)
361
+ end
362
+ publish(flag, otp: otp, tag: tag, access: access, dryrun: dryrun)
350
363
  end
351
- publish(flag, otp: otp, tag: tag, access: access, dryrun: dryrun)
352
364
  end
353
365
  when 'tsc'
354
366
  break unless @tsfile
@@ -532,6 +544,7 @@ module Squared
532
544
  end
533
545
  save, exact, omit = save if save.is_a?(Array)
534
546
  ws = env('NODE_WORKSPACES', equals: '0')
547
+ ci = option('ci')
535
548
  om = lambda do |cmd|
536
549
  if omit
537
550
  save = case save
@@ -569,7 +582,7 @@ module Squared
569
582
  end
570
583
  if nolockfile?('yarn')
571
584
  cmd << '--no-lockfile'
572
- elsif option('ci')
585
+ elsif ci
573
586
  if yarn == 1
574
587
  cmd << '--frozen-lockfile'
575
588
  elsif !flag
@@ -605,15 +618,15 @@ module Squared
605
618
  '--force'
606
619
  elsif nolockfile?('pnpm')
607
620
  '--no-lockfile'
608
- elsif option('ci')
621
+ elsif ci
609
622
  '--frozen-lockfile'
610
623
  end
611
624
  cmd << '--ignore-scripts' if option('ignore-scripts')
612
625
  cmd << '--dangerously-allow-all-builds' if option('approve-builds')
613
626
  else
614
- cmd = session 'npm'
615
- cmd << (ci = option('ci') ? 'ci' : 'install')
627
+ cmd = session('npm', ci ? 'ci' : 'install')
616
628
  cmd << '--workspaces=false' if ws
629
+ cmd << '--force' if option('force')
617
630
  append_nocolor
618
631
  append_loglevel
619
632
  if omit
@@ -881,47 +894,122 @@ module Squared
881
894
  package(:update, from: :update)
882
895
  end
883
896
 
884
- def publish(flag = nil, *, sync: invoked_sync?('publish', flag), otp: nil, tag: nil, access: nil, dryrun: nil)
897
+ def publish(flag = nil, *, sync: invoked_sync?('publish', flag), otp: nil, tag: nil, access: nil, dryrun: nil,
898
+ version: nil, ext: [], workspace: false)
885
899
  if read_package('private')
900
+ return if workspace
901
+
886
902
  ws = children.select { |proj| proj.ref?(Node.ref) }
887
903
  if ws.empty?
888
904
  print_error('nothing to publish', subject: name, hint: 'private')
889
- elsif confirm_basic('Publish workspace?', ws.map(&:name).join(', '), 'N')
890
- ws.each { |proj| proj.publish(flag, sync: sync, otp: otp, tag: tag, access: access, dryrun: dryrun) }
905
+ elsif confirm_basic("#{flag == :verify ? 'Verify' : 'Publish'} workspace?", ws.map(&:name).join(', '), 'N')
906
+ ws.each do |proj|
907
+ proj.publish(flag, sync: sync, otp: otp, tag: tag, access: access, dryrun: dryrun, version: version,
908
+ ext: ext, workspace: true)
909
+ end
891
910
  end
892
- return
893
- end
894
- return print_error("version: #{dependname}", subject: name, hint: 'not found') unless version
895
-
896
- cmd = session 'npm', 'publish'
897
- cmd << basic_option('otp', otp) if otp ||= option('otp')
898
- cmd << basic_option('tag', tag.tr(' ', '-')) if tag ||= option('tag')
899
- case access || option('access')
900
- when 'p', 'public'
901
- cmd << '--access=public'
902
- when 'r', 'restricted'
903
- cmd << '--access=restricted'
904
- end
905
- dryrun ||= dryrun?('npm')
906
- if dryrun
907
- cmd << '--dry-run'
908
- else
909
- from = :'npm:publish'
910
- log.info cmd.to_s
911
- end
912
- if sync
913
- run(sync: sync, from: from, interactive: !dryrun && ['Publish', 'N', npmname])
914
- else
915
- require 'open3'
916
- on :first, from
917
- pwd_set(cmd, dryrun: dryrun) do
918
- cmd = session_done cmd
919
- Open3.popen2e(cmd) do |_, out|
920
- write_lines(out, banner: format_banner(cmd),
921
- sub: npmnotice(opt_style(color(:bright_blue), /^(.+)(Tarball .+)$/, 2)))
911
+ elsif version ||= self.version
912
+ if flag == :verify
913
+ require 'open-uri'
914
+ require 'digest'
915
+ ext = ext.map { |val| val[0] == '.' ? val : ".#{val}" }
916
+ url = "https://unpkg.com/#{read_package('name')}@#{version}?meta"
917
+ print_item format_banner(url, command: false)
918
+ URI.open(url) do |f|
919
+ doc = JSON.parse(f.read)
920
+ n = doc['prefix'].size
921
+ i = 0
922
+ j = doc['files'].size
923
+ pad = j.to_s.size
924
+ m = 0
925
+ c = 0
926
+ e = 0
927
+ doc['files'].each do |item|
928
+ path = item['path'][n..-1]
929
+ js = File.extname(path)
930
+ next unless ext.empty? || ext.any? { |val| val == js }
931
+
932
+ begin
933
+ raise unless (file = basepath!(path)) && item['integrity'] =~ /^(sha\d+)-(.+)$/
934
+
935
+ hash = case $1
936
+ when 'sha384'
937
+ Digest::SHA384
938
+ when 'sha512'
939
+ Digest::SHA512
940
+ else
941
+ Digest::SHA256
942
+ end
943
+ .base64digest(file.read)
944
+ status = if hash == $2
945
+ m += 1
946
+ sub_style 'match', color(:green)
947
+ else
948
+ c += 1
949
+ sub_style 'check', theme[:caution]
950
+ end
951
+ rescue
952
+ status = sub_style 'error', theme[:warn]
953
+ e += 1
954
+ end
955
+ i += 1
956
+ puts "#{i.to_s.rjust(pad)}. #{status} #{path}"
957
+ end
958
+ if i == 0
959
+ puts("No files #{ext.empty? ? 'found' : "matched: #{ext.join(', ')}"}")
960
+ else
961
+ total = ["match #{m}"]
962
+ total << "check #{c}" unless c == 0
963
+ total << "error #{e}" unless e == 0
964
+ unless i == j
965
+ s = "in #{j}"
966
+ if total.size == 1
967
+ total[0] += " #{s}"
968
+ else
969
+ total << s
970
+ end
971
+ end
972
+ puts print_footer(total.join(' / '), right: true, sub: [
973
+ opt_style(color(:green), /^(.*)(match)(.+)$/, 2),
974
+ opt_style(theme[:caution], /^(.+)(check)(.+)$/, 2),
975
+ opt_style(theme[:warn], /^(.+)(error)(.+)$/, 2)
976
+ ])
977
+ end
978
+ end
979
+ else
980
+ cmd = session 'npm', 'publish'
981
+ cmd << basic_option('otp', otp) if otp ||= option('otp')
982
+ cmd << basic_option('tag', tag.tr(' ', '-')) if tag ||= option('tag')
983
+ case access || option('access')
984
+ when 'p', 'public'
985
+ cmd << '--access=public'
986
+ when 'r', 'restricted'
987
+ cmd << '--access=restricted'
988
+ end
989
+ dryrun ||= dryrun?('npm')
990
+ if dryrun
991
+ cmd << '--dry-run'
992
+ else
993
+ from = :'npm:publish'
994
+ log.info cmd.to_s
995
+ end
996
+ if sync
997
+ run(sync: sync, from: from, interactive: !dryrun && ['Publish', 'N', npmname])
998
+ else
999
+ require 'open3'
1000
+ on :first, from
1001
+ pwd_set(cmd, dryrun: dryrun) do
1002
+ cmd = session_done cmd
1003
+ Open3.popen2e(cmd) do |_, out|
1004
+ write_lines(out, banner: format_banner(cmd),
1005
+ sub: npmnotice(opt_style(color(:bright_blue), /^(.+)(Tarball .+)$/, 2)))
1006
+ end
1007
+ end
1008
+ on :last, from
922
1009
  end
923
1010
  end
924
- on :last, from
1011
+ elsif !workspace
1012
+ print_error("version: #{dependname}", subject: name, hint: 'not found')
925
1013
  end
926
1014
  end
927
1015
 
@@ -958,7 +1046,7 @@ module Squared
958
1046
  when :add, :update then OPT_BERRY[:add_a]
959
1047
  else []
960
1048
  end
961
- end, cmd, project: self)
1049
+ end, cmd, project: self, strict: strict?)
962
1050
  if yarn == 1 && flag != :reinstall
963
1051
  op << '--no-lockfile' if nolockfile?('yarn')
964
1052
  op << '--ignore-engines' if option('ignore-engines')
@@ -1027,7 +1115,7 @@ module Squared
1027
1115
  session('npm', flag)
1028
1116
  ]
1029
1117
  end
1030
- op = OptionPartition.new(*args, no: no, project: self)
1118
+ op = OptionPartition.new(*args, no: no, project: self, strict: strict?)
1031
1119
  append_platform if flag == :install
1032
1120
  append_nocolor
1033
1121
  end
@@ -1047,7 +1135,8 @@ module Squared
1047
1135
  end
1048
1136
  end
1049
1137
  op.swap.concat(packages)
1050
- raise_error ArgumentError, 'no packages to add' if op.empty? && spec == 1
1138
+ raise ArgumentError, 'no packages to add' if op.empty? && spec == 1
1139
+
1051
1140
  op.append(quote: true)
1052
1141
  .clear(errors: true)
1053
1142
  else
@@ -1092,10 +1181,11 @@ module Squared
1092
1181
 
1093
1182
  cmd = session dependbin, 'pack'
1094
1183
  if dependtype(:yarn) > 1
1095
- op = OptionPartition.new(opts, OPT_BERRY[:pack], cmd, project: self)
1184
+ op = OptionPartition.new(opts, OPT_BERRY[:pack], cmd, project: self, strict: strict?)
1096
1185
  op.append?('out', Pathname.pwd + "#{project}-#{version}.tgz")
1097
1186
  else
1098
- op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : npmopts(:pack), cmd, project: self)
1187
+ op = OptionPartition.new(opts, pnpm? ? OPT_PNPM[:pack] : npmopts(:pack), cmd, project: self,
1188
+ strict: strict?)
1099
1189
  unless pnpm?
1100
1190
  op.each do |opt|
1101
1191
  next unless opt =~ op.values
@@ -1130,7 +1220,7 @@ module Squared
1130
1220
  list.concat(OPT_TSC[:watch])
1131
1221
  cmd << '-w'
1132
1222
  end
1133
- op = OptionPartition.new(args, list, cmd, project: self, sep: ' ')
1223
+ op = OptionPartition.new(args, list, cmd, project: self, strict: strict?, sep: ' ')
1134
1224
  unless p
1135
1225
  if b.is_a?(String)
1136
1226
  op.add_path(b)
@@ -216,7 +216,8 @@ module Squared
216
216
  found |= 1
217
217
  run(pdm_session('run', val), from: :run)
218
218
  else
219
- raise_error "script: #{val}" if exception
219
+ raise "script: #{val}" if exception
220
+
220
221
  found |= 2
221
222
  log.warn "run script \"#{val}\"".subhint('not indexed')
222
223
  end
@@ -227,12 +228,12 @@ module Squared
227
228
  end
228
229
  next if found.anybits?(1)
229
230
 
230
- puts log_message(found == 0 ? Logger::INFO : Logger.WARN,
231
+ puts log_message(found == 0 ? Logger::INFO : Logger::WARN,
231
232
  "no scripts #{found == 0 ? 'found' : 'executed'}",
232
233
  subject: name, hint: pyprojectfile)
233
234
  end
234
235
  when 'exec'
235
- format_desc action, nil, ':|command,args*'
236
+ format_desc action, nil, 'command|:,args*'
236
237
  task action do |_, args|
237
238
  args = args.to_a
238
239
  cmd = if (i = args.delete(':')) && !workspace.windows?
@@ -243,7 +244,7 @@ module Squared
243
244
  command_args(args, min: 1, prefix: 'python')
244
245
  args.join(' ')
245
246
  end
246
- shell(cmd, name: :exec, chdir: path)
247
+ run(cmd, send: :exec, banner: false)
247
248
  end
248
249
  end
249
250
  else
@@ -296,11 +297,24 @@ module Squared
296
297
  install flag, ['upgrade', *args.to_a, 'pip']
297
298
  end
298
299
  when :freeze
299
- format_desc action, flag, "file?=#{DEP_PYTHON[4]},opts*"
300
+ format_desc action, flag, "file?=#{DEP_PYTHON[4]},u/uninstall,opts*"
300
301
  task flag do |_, args|
301
- next unless (file = pip(flag, opts: args.to_a, banner: true)) && !silent?
302
-
303
- puts File.read(file)
302
+ opts = args.to_a
303
+ if has_value!(opts, 'u', 'uninstall')
304
+ unless venv
305
+ print_error('no venv detected', subject: name)
306
+ exit 1
307
+ end
308
+ require 'tempfile'
309
+ temp = Tempfile.new("#{name}-")
310
+ temp.close
311
+ requirement = Pathname.new(temp)
312
+ end
313
+ file = pip(flag, opts: opts, banner: requirement.nil?, requirement: requirement)
314
+ puts File.read(file) unless silent?
315
+ if requirement && confirm_basic('Uninstall?', file)
316
+ pip(:uninstall, opts: ['y', "r=#{shell_quote(file)}"])
317
+ end
304
318
  end
305
319
  when :uninstall
306
320
  format_desc action, flag, 'package+,opts*'
@@ -590,7 +604,7 @@ module Squared
590
604
  def install(flag, opts = [], packages: [], banner: !silent?)
591
605
  if flag == :poetry
592
606
  OptionPartition.new(opts, OPT_POETRY[:install] + OPT_POETRY[:common], poetry_session('install'),
593
- project: self, single: singleopt(flag))
607
+ project: self, strict: strict?, single: singleopt(flag))
594
608
  else
595
609
  op = append_pip(flag, opts, pipopts(:install), target: pip_session('install'))
596
610
  end
@@ -600,7 +614,7 @@ module Squared
600
614
  op.clear
601
615
  when :user, :upgrade
602
616
  op.concat(packages)
603
- raise_error 'no packages listed', hint: flag if op.empty?
617
+ raise_error ArgumentError, 'no packages listed', hint: flag if op.empty?
604
618
  op << "--#{flag}"
605
619
  op.append
606
620
  python_session('-m pip', *op.to_a.drop(1)) if workspace.windows?
@@ -626,7 +640,7 @@ module Squared
626
640
  cmd, opts = python_session('-m build', opts: opts)
627
641
  OPT_PYTHON[:build]
628
642
  end
629
- op = OptionPartition.new(opts, list, cmd, project: self, single: singleopt(flag))
643
+ op = OptionPartition.new(opts, list, cmd, project: self, strict: strict?, single: singleopt(flag))
630
644
  case flag
631
645
  when :hatch
632
646
  op.add_path(outdir) if !ENV['HATCH_BUILD_LOCATION'] && (outdir ||= op.shift)
@@ -662,7 +676,9 @@ module Squared
662
676
  from = symjoin flag, 'build'
663
677
  if flag == :meson
664
678
  cmd = session_output 'meson', 'compile', quote_option('C', basepath(outdir))
665
- OptionPartition.new(op.extras, OPT_MESON[:compile], cmd, project: self).append unless op.empty?
679
+ unless op.empty?
680
+ OptionPartition.new(op.extras, OPT_MESON[:compile], cmd, project: self, strict: strict?).append
681
+ end
666
682
  run_s(op, cmd.done, from: from)
667
683
  else
668
684
  op.clear
@@ -685,7 +701,7 @@ module Squared
685
701
  session 'twine', 'upload'
686
702
  OPT_TWINE[:publish]
687
703
  end
688
- op = OptionPartition.new(opts, list, @session, project: self, single: singleopt(flag))
704
+ op = OptionPartition.new(opts, list, @session, project: self, strict: strict?, single: singleopt(flag))
689
705
  dist = lambda do
690
706
  dir = basepath 'dist'
691
707
  return dir if dir.directory? && !dir.empty?
@@ -720,8 +736,8 @@ module Squared
720
736
  def python(*args, sync: true, banner: verbose?, with: nil, pass: PASS_PYTHON[:python], **kwargs)
721
737
  op = OptionPartition.new(session_opts(with, args: args, kwargs: kwargs, pass: pass), OPT_PYTHON[:common],
722
738
  session('python', path: !venv?),
723
- project: self, multiple: [/^-c/], single: singleopt(:python), args: true,
724
- stdin: true)
739
+ project: self, strict: strict?, multiple: [/^-c/], single: singleopt(:python),
740
+ args: true, stdin: true)
725
741
  op.concat(args)
726
742
  if op.include?('-')
727
743
  op.exist?(add: true)
@@ -755,7 +771,7 @@ module Squared
755
771
  when :install, :uninstall
756
772
  op << '.' if installable? && !op.arg?('r', 'requirement')
757
773
  else
758
- raise_error 'no packages listed', hint: flag
774
+ raise_error ArgumentError, 'no packages listed', hint: flag
759
775
  end
760
776
  elsif flag == :install
761
777
  op.append_any
@@ -769,7 +785,12 @@ module Squared
769
785
  when :freeze
770
786
  venv_init
771
787
  op << '>'
772
- op.add_quote(ret = basepath(op.detect { |val| op.exist?(val) } || DEP_PYTHON[4]))
788
+ ret = kwargs[:requirement] || begin
789
+ file = op.detect { |val| op.exist?(val) }
790
+ op.remove(file) if file
791
+ basepath(file || DEP_PYTHON[4])
792
+ end
793
+ op.add_quote(ret)
773
794
  .clear
774
795
  when :cache
775
796
  op.concat(args)
@@ -852,7 +873,7 @@ module Squared
852
873
  when :editable
853
874
  editable_set args.first
854
875
  when :venv
855
- @venv = args.empty? || args.first.nil? ? nil : basepath(*args)
876
+ @venv = (basepath(*args) unless args.empty? || args.first.nil?)
856
877
  else
857
878
  super
858
879
  end
@@ -867,7 +888,7 @@ module Squared
867
888
  end
868
889
 
869
890
  def venv?
870
- !venv.nil?
891
+ !venv.nil? && venv.directory? && !venv.empty?
871
892
  end
872
893
 
873
894
  def serve?
@@ -877,11 +898,18 @@ module Squared
877
898
  private
878
899
 
879
900
  def pip_session(*cmd)
880
- session('pip', *cmd, *preopts, path: !venv?)
901
+ main = venv?
902
+ ret = session('pip', *cmd, *preopts, main: main, path: false)
903
+ return ret if main
904
+
905
+ python_session '-m', ret.to_s
881
906
  end
882
907
 
883
908
  def pip_output(*cmd)
884
- session_output('pip', *cmd, *preopts, path: !venv?)
909
+ ret = session_output('pip', *cmd, path: false)
910
+ return ret if venv?
911
+
912
+ session_output 'python', '-m', ret.to_s
885
913
  end
886
914
 
887
915
  def python_session(*cmd, opts: nil)
@@ -910,12 +938,12 @@ module Squared
910
938
  pre = preopts(quiet: quiet)
911
939
  return session(name, *pre, *cmd, path: !venv?) unless opts
912
940
 
913
- op = OptionPartition.new(opts, common, project: self, single: singleopt(name.to_sym))
941
+ op = OptionPartition.new(opts, common, project: self, strict: strict?, single: singleopt(name.to_sym))
914
942
  [session(name, *pre, *op.to_a, *cmd, path: !venv?), op.extras]
915
943
  end
916
944
 
917
945
  def append_pip(flag, opts, list, target: @session)
918
- op = OptionPartition.new(opts, list, target, project: self, single: singleopt)
946
+ op = OptionPartition.new(opts, list, target, project: self, strict: strict?, single: singleopt)
919
947
  append_global(target: target)
920
948
  case flag
921
949
  when :install, :lock, :wheel, :editable, :upgrade
@@ -1172,7 +1200,7 @@ module Squared
1172
1200
  :venv
1173
1201
  end
1174
1202
  cmd, opts = python_session('-m', flag, opts: opts)
1175
- op = OptionPartition.new(opts, OPT_PYTHON[flag], cmd, project: self)
1203
+ op = OptionPartition.new(opts, OPT_PYTHON[flag], cmd, project: self, strict: strict?)
1176
1204
  op.append(dir, delim: true)
1177
1205
  .clear(pass: false)
1178
1206
  status ||= op.arg?(/\A-v+\z/)