squared 0.6.1 → 0.6.3
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 +4 -4
- data/CHANGELOG.md +88 -0
- data/README.md +23 -13
- data/lib/squared/common/base.rb +2 -2
- data/lib/squared/common/format.rb +39 -32
- data/lib/squared/common/prompt.rb +6 -4
- data/lib/squared/common/shell.rb +5 -4
- data/lib/squared/config.rb +7 -8
- data/lib/squared/version.rb +1 -1
- data/lib/squared/workspace/application.rb +72 -23
- data/lib/squared/workspace/project/base.rb +205 -116
- data/lib/squared/workspace/project/docker.rb +20 -24
- data/lib/squared/workspace/project/git.rb +100 -47
- data/lib/squared/workspace/project/node.rb +226 -132
- data/lib/squared/workspace/project/python.rb +20 -18
- data/lib/squared/workspace/project/ruby.rb +62 -60
- data/lib/squared/workspace/project/support/class.rb +88 -616
- data/lib/squared/workspace/project/support/optionpartition.rb +641 -0
- data/lib/squared/workspace/project.rb +0 -1
- data/lib/squared/workspace/repo.rb +23 -14
- data/lib/squared/workspace/series.rb +10 -8
- data/squared.gemspec +2 -2
- metadata +2 -2
- data/lib/squared/workspace/project/support.rb +0 -3
|
@@ -7,16 +7,16 @@ module Squared
|
|
|
7
7
|
DEP_PYTHON = %w[poetry.lock setup.cfg pyproject.toml setup.py requirements.txt].freeze
|
|
8
8
|
DIR_PYTHON = (DEP_PYTHON + %w[README.rst]).freeze
|
|
9
9
|
OPT_PYTHON = {
|
|
10
|
-
common: %w[b B d E h i I O P q s S u v x c=q m=b W=b X=q check-hash-based-pycs=b].freeze,
|
|
10
|
+
common: %w[b=+ B d E h i I O P q=+ s S u v=+ x c=q m=b W=b X=q check-hash-based-pycs=b].freeze,
|
|
11
11
|
build: %w[n|no-isolation s|sdist x|skip-dependency-check v|verbose w|wheel C|config-setting=q installer=b
|
|
12
12
|
o|outdir=p].freeze,
|
|
13
13
|
venv: %w[clear copies symlinks system-site-packages upgrade upgrade-deps without-scm-ignore-files without-pip
|
|
14
14
|
prompt=q].freeze
|
|
15
15
|
}.freeze
|
|
16
16
|
OPT_PIP = {
|
|
17
|
-
common: %w[debug disable-pip-version-check isolated no-cache-dir no-color no-input
|
|
18
|
-
v|verbose cache-dir=p cert=p client-cert=p exists-action=b keyring-provider=b log=p
|
|
19
|
-
python=q resume-retries=i retries=i timeout=i trusted-host=b use-deprecated=b
|
|
17
|
+
common: %w[debug disable-pip-version-check isolated no-cache-dir no-color no-input require-virtualenv
|
|
18
|
+
q|quiet=+ v|verbose=+ cache-dir=p cert=p client-cert=p exists-action=b keyring-provider=b log=p
|
|
19
|
+
proxy=q python=q resume-retries=i retries=i timeout=i trusted-host=b use-deprecated=b
|
|
20
20
|
use-feature=b].freeze,
|
|
21
21
|
cache: %w[format=b].freeze,
|
|
22
22
|
completion: %w[b|bash f|fish p|powershell z|zsh].freeze,
|
|
@@ -43,21 +43,21 @@ module Squared
|
|
|
43
43
|
wheel: %w[no-verify w|wheel-dir=p].freeze
|
|
44
44
|
}.freeze
|
|
45
45
|
OPT_POETRY = {
|
|
46
|
-
common: %w[ansi no-ansi no-cache n|no-interaction no-plugins q|quiet v|verbose P|project=p].freeze,
|
|
46
|
+
common: %w[ansi no-ansi no-cache n|no-interaction no-plugins q|quiet=+ v|verbose=+ P|project=p].freeze,
|
|
47
47
|
build: %w[clean config-settings=qq f|format=b o|output=p].freeze,
|
|
48
48
|
publish: %w[build dry-run skip-existing cert=p client-cert=p dist-dir=p p|password=b r|repository=q
|
|
49
49
|
u|username=b].freeze
|
|
50
50
|
}.freeze
|
|
51
51
|
OPT_PDM = {
|
|
52
52
|
common: %w[I|ignore-python no-cache n|non-interactive].freeze,
|
|
53
|
-
build: %w[C=bm no-clean no-isolation no-sdist no-wheel quiet verbose config-setting=q d|dest=p
|
|
54
|
-
k|skip=b].freeze,
|
|
55
|
-
publish: %w[no-build no-very-ssl quiet S|sign skip-existing verbose ca-certs=p c|comment=q d|dest=p
|
|
53
|
+
build: %w[C=bm no-clean no-isolation no-sdist no-wheel q|quiet v|verbose=+ config-setting=q d|dest=p
|
|
54
|
+
p|project=p k|skip=b].freeze,
|
|
55
|
+
publish: %w[no-build no-very-ssl q|quiet S|sign skip-existing v|verbose=+ ca-certs=p c|comment=q d|dest=p
|
|
56
56
|
i|identity=b P|password=q p|project=p r|repository=q k|skip=b u|username=b].freeze
|
|
57
57
|
}.freeze
|
|
58
58
|
OPT_HATCH = {
|
|
59
59
|
common: %w[color interactive no-color no-interactive cache-dir=p config=p data-dir=p e|env=b p|project=b
|
|
60
|
-
q|quiet v|verbose].freeze,
|
|
60
|
+
q|quiet=+ v|verbose=+].freeze,
|
|
61
61
|
build: %w[clean-hooks-after ext hooks-only no-hooks c|clean t|target=b].freeze,
|
|
62
62
|
publish: %w[initialize-auth n|no-prompt y|yes a|auth=q ca-cert=p client-cert=p client-key=p o|option=q
|
|
63
63
|
p|publisher=b r|repo=q u|user=q].freeze
|
|
@@ -269,7 +269,7 @@ module Squared
|
|
|
269
269
|
when :freeze
|
|
270
270
|
format_desc action, flag, "file?=#{DEP_PYTHON[4]},opts*"
|
|
271
271
|
task flag do |_, args|
|
|
272
|
-
if (file = pip(flag, opts: args.to_a, banner: true)) &&
|
|
272
|
+
if (file = pip(flag, opts: args.to_a, banner: true)) && !silent?
|
|
273
273
|
puts File.read(file)
|
|
274
274
|
end
|
|
275
275
|
end
|
|
@@ -457,15 +457,15 @@ module Squared
|
|
|
457
457
|
styles = color(:yellow)
|
|
458
458
|
else
|
|
459
459
|
styles = color(:green)
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
460
|
+
sub_style!(line, if type == 2
|
|
461
|
+
styles += [:bold]
|
|
462
|
+
theme[:major]
|
|
463
|
+
else
|
|
464
|
+
theme[:active]
|
|
465
|
+
end, pat: /^(\S+)(.+)$/)
|
|
466
466
|
end
|
|
467
|
-
|
|
468
|
-
|
|
467
|
+
sub_style!(line, **opt_style(tc, /^(.+)(#{Regexp.escape(current)})(.+)$/, 2)) if tc
|
|
468
|
+
sub_style!(line, **opt_style(styles, /^(.+)(#{Regexp.escape(latest)})(.+)$/, 2))
|
|
469
469
|
found += 1
|
|
470
470
|
end
|
|
471
471
|
if items
|
|
@@ -923,6 +923,8 @@ module Squared
|
|
|
923
923
|
case flag
|
|
924
924
|
when :python
|
|
925
925
|
/\A(?:v+|q+|b+)\z/
|
|
926
|
+
when :pdm
|
|
927
|
+
/\Av+\z/
|
|
926
928
|
when :twine
|
|
927
929
|
nil
|
|
928
930
|
else
|
|
@@ -217,33 +217,41 @@ module Squared
|
|
|
217
217
|
when 'rake'
|
|
218
218
|
next unless rakefile
|
|
219
219
|
|
|
220
|
-
format_desc action, nil, "task
|
|
220
|
+
format_desc action, nil, "task+|#{indexchar}index+,opts*|#,pattern*"
|
|
221
221
|
task action, [:command] do |_, args|
|
|
222
222
|
if args.command == '#'
|
|
223
223
|
format_list(raketasks, "rake[#{indexchar}N]", 'tasks', grep: args.extras, from: rakefile, &:join)
|
|
224
224
|
else
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
225
|
+
tasks = raketasks
|
|
226
|
+
cmd = nil
|
|
227
|
+
opts = []
|
|
228
|
+
queue = lambda do
|
|
229
|
+
if cmd
|
|
230
|
+
cmd += "[#{opts.join(',')}]" unless opts.empty?
|
|
231
|
+
rake(cmd, banner: true)
|
|
232
|
+
cmd = nil
|
|
233
|
+
elsif !opts.empty?
|
|
234
|
+
rake(banner: true, opts: opts)
|
|
235
|
+
end
|
|
236
|
+
opts.clear
|
|
237
|
+
end
|
|
238
|
+
args.to_a.each do |item|
|
|
239
|
+
if (n, pre = indexitem(item))
|
|
240
|
+
queue.call
|
|
231
241
|
if (item = tasks[n.pred])
|
|
232
|
-
cmd = pre
|
|
242
|
+
cmd = [pre, item.first].compact.join(' ')
|
|
233
243
|
elsif exception
|
|
234
244
|
indexerror n, tasks
|
|
235
245
|
else
|
|
236
246
|
log.warn "rake task #{n} of #{tasks.size} (out of range)"
|
|
237
|
-
next
|
|
238
|
-
end
|
|
239
|
-
if opts.empty?
|
|
240
|
-
rake(cmd, banner: true)
|
|
241
|
-
else
|
|
242
|
-
rake(cmd + "[#{opts.join(',')}]", banner: true)
|
|
243
247
|
opts.clear
|
|
248
|
+
next
|
|
244
249
|
end
|
|
250
|
+
else
|
|
251
|
+
opts << item
|
|
245
252
|
end
|
|
246
253
|
end
|
|
254
|
+
queue.call
|
|
247
255
|
end
|
|
248
256
|
end
|
|
249
257
|
when 'irb'
|
|
@@ -344,9 +352,9 @@ module Squared
|
|
|
344
352
|
end
|
|
345
353
|
gem(flag, opts: opts, banner: true, filter: {
|
|
346
354
|
semver: semver,
|
|
347
|
-
update: opts
|
|
348
|
-
interactive: opts
|
|
349
|
-
select: opts
|
|
355
|
+
update: has_value!(opts, 'u', 'update'),
|
|
356
|
+
interactive: has_value!(opts, 'i', 'interactive'),
|
|
357
|
+
select: has_value!(opts, 's', 'select')
|
|
350
358
|
})
|
|
351
359
|
end
|
|
352
360
|
when :build, :push, :exec, :update
|
|
@@ -390,7 +398,7 @@ module Squared
|
|
|
390
398
|
format_desc action, flag, 'f/orce?,opts*'
|
|
391
399
|
task flag do |_, args|
|
|
392
400
|
opts = args.to_a
|
|
393
|
-
opts << 'redownload' if opts
|
|
401
|
+
opts << 'redownload' if has_value!(opts, 'f', 'force')
|
|
394
402
|
if (lock = basepath('Gemfile.lock')).exist?
|
|
395
403
|
config = basepath '.bundle', 'config'
|
|
396
404
|
if config.exist? && config.read.match?(/\bBUNDLE_FROZEN:\s+"true"/)
|
|
@@ -476,17 +484,17 @@ module Squared
|
|
|
476
484
|
end
|
|
477
485
|
|
|
478
486
|
def copy(from: gemlib, into: @gemdir, override: false, **kwargs)
|
|
487
|
+
return if @copy == false
|
|
488
|
+
|
|
479
489
|
glob = kwargs[:include]
|
|
480
490
|
pass = kwargs[:exclude]
|
|
481
491
|
if @copy && !override
|
|
482
492
|
return super unless @copy.is_a?(Hash)
|
|
483
493
|
|
|
484
494
|
from = @copy[:from] if @copy.key?(:from)
|
|
495
|
+
into = @copy[:into] if @copy.key?(:into)
|
|
485
496
|
glob = @copy[:include] if @copy.key?(:include)
|
|
486
497
|
pass = @copy[:exclude] if @copy.key?(:exclude)
|
|
487
|
-
into = @copy[:into] if @copy.key?(:into)
|
|
488
|
-
elsif @copy == false
|
|
489
|
-
return
|
|
490
498
|
end
|
|
491
499
|
return unless into
|
|
492
500
|
|
|
@@ -499,7 +507,7 @@ module Squared
|
|
|
499
507
|
b = dest + val
|
|
500
508
|
c = glob[i] || glob.first
|
|
501
509
|
log.info "cp #{a + c} #{b}"
|
|
502
|
-
copy_dir(a, b, c, pass: pass, verbose:
|
|
510
|
+
copy_dir(a, b, c, pass: pass, verbose: !silent?)
|
|
503
511
|
rescue StandardError => e
|
|
504
512
|
on_error e, :copy
|
|
505
513
|
end
|
|
@@ -509,8 +517,8 @@ module Squared
|
|
|
509
517
|
def outdated(flag = nil, opts = [], sync: invoked_sync?('outdated', flag))
|
|
510
518
|
cmd = bundle_output 'outdated'
|
|
511
519
|
if flag
|
|
512
|
-
se = (opts
|
|
513
|
-
up = opts
|
|
520
|
+
se = has_value!(opts, 's', 'select') && !stdin?
|
|
521
|
+
up = has_value!(opts, 'u', 'update')
|
|
514
522
|
cmd << "--#{flag}"
|
|
515
523
|
OptionPartition.new(opts, bundleopts(:outdated), cmd, project: self)
|
|
516
524
|
.clear
|
|
@@ -540,13 +548,13 @@ module Squared
|
|
|
540
548
|
c = cur.join
|
|
541
549
|
l = lat.join
|
|
542
550
|
styles = []
|
|
543
|
-
|
|
551
|
+
ma = lambda do
|
|
544
552
|
styles = %i[green bold]
|
|
545
553
|
major += 1
|
|
546
554
|
end
|
|
547
|
-
|
|
555
|
+
mi = -> { styles[0] = cur[2] == lat[2] ? :yellow : :green }
|
|
548
556
|
if data.empty?
|
|
549
|
-
semmajor?(cur, lat) ?
|
|
557
|
+
semmajor?(cur, lat) ? ma.call : mi.call
|
|
550
558
|
else
|
|
551
559
|
data.each do |val|
|
|
552
560
|
break unless line =~ /(>=?|=|~>|!=|<=?) (#{Regexp.escape(val.join)})/
|
|
@@ -554,11 +562,11 @@ module Squared
|
|
|
554
562
|
v = semver(val).join
|
|
555
563
|
case $1
|
|
556
564
|
when '>', '>='
|
|
557
|
-
semmajor?(cur, lat) ?
|
|
565
|
+
semmajor?(cur, lat) ? ma.call : mi.call
|
|
558
566
|
when '<', '<='
|
|
559
567
|
if c <= v
|
|
560
568
|
if semmajor?(cur, lat)
|
|
561
|
-
|
|
569
|
+
ma.call
|
|
562
570
|
else
|
|
563
571
|
styles[0] = :yellow
|
|
564
572
|
end
|
|
@@ -584,14 +592,14 @@ module Squared
|
|
|
584
592
|
unless styles.empty?
|
|
585
593
|
case styles.first
|
|
586
594
|
when :green
|
|
587
|
-
|
|
595
|
+
sub_style!(line, **opt_style(theme[styles.last == :bold ? :major : :active], /^(\S+)(.+)$/))
|
|
588
596
|
found += 1
|
|
589
597
|
when :yellow
|
|
590
598
|
found += 1
|
|
591
599
|
end
|
|
592
600
|
l = Regexp.escape(l)
|
|
593
|
-
|
|
594
|
-
|
|
601
|
+
sub_style!(line, **opt_style(tc, /^(.+)(#{Regexp.escape(c)})(.+)$/, 2)) if tc
|
|
602
|
+
sub_style!(line, **opt_style(colormap(styles), /^((?:\S+\s+){2})(#{l})(.*)$/, 2))
|
|
595
603
|
end
|
|
596
604
|
end
|
|
597
605
|
items&.push([line, name])
|
|
@@ -743,6 +751,7 @@ module Squared
|
|
|
743
751
|
end
|
|
744
752
|
out.map!(&:split)
|
|
745
753
|
pad = out.map(&:first).map!(&:size).max
|
|
754
|
+
print_item
|
|
746
755
|
puts(out.map! { |line| '%*s %s' % [pad, line.first, line[1..-1].join(' ')] })
|
|
747
756
|
end
|
|
748
757
|
return
|
|
@@ -790,8 +799,7 @@ module Squared
|
|
|
790
799
|
when :build, :cert, :generate_index, :mirror, :outdated, :push, :server, :signin, :signout, :sources, :stale
|
|
791
800
|
opts.concat(args)
|
|
792
801
|
end
|
|
793
|
-
op = OptionPartition.new(opts, gemopts(flag), gem_session(flag
|
|
794
|
-
no: OPT_GEM[:no][flag])
|
|
802
|
+
op = OptionPartition.new(opts, gemopts(flag), gem_session(flag), project: self, no: OPT_GEM[:no][flag])
|
|
795
803
|
from = :"gem:#{flag}"
|
|
796
804
|
if flag == :outdated
|
|
797
805
|
op.adjoin(gempwd, start: 0) if gempwd
|
|
@@ -839,6 +847,7 @@ module Squared
|
|
|
839
847
|
e = 0
|
|
840
848
|
f = 0
|
|
841
849
|
j = 0
|
|
850
|
+
col4 = 0
|
|
842
851
|
queue = nil
|
|
843
852
|
rows.each do |row|
|
|
844
853
|
a, b, c = row.first
|
|
@@ -852,14 +861,15 @@ module Squared
|
|
|
852
861
|
a, b, c = row.first
|
|
853
862
|
if i == 0
|
|
854
863
|
line = '%-*s %-*s %*s %*s' % [pad, ' #', d, a, e, b, f, c]
|
|
855
|
-
|
|
864
|
+
col4 = line.size
|
|
865
|
+
s = ARG[:BORDER][1] * col4
|
|
856
866
|
queue = if stdin?
|
|
857
|
-
[line,
|
|
867
|
+
[line, s]
|
|
858
868
|
else
|
|
859
869
|
2.times do
|
|
860
|
-
|
|
870
|
+
sub_style!(line, **opt_style(theme[:header], /^(.+)(?<!\dm)(#{a}|#{c})(.*)$/, 2))
|
|
861
871
|
end
|
|
862
|
-
[line, sub_style(
|
|
872
|
+
[line, sub_style(s, borderstyle)]
|
|
863
873
|
end
|
|
864
874
|
else
|
|
865
875
|
g = a.ljust(d)
|
|
@@ -875,7 +885,7 @@ module Squared
|
|
|
875
885
|
next
|
|
876
886
|
end
|
|
877
887
|
unless stdin?
|
|
878
|
-
|
|
888
|
+
sub_style! g, theme[:major]
|
|
879
889
|
styles = %i[green bold]
|
|
880
890
|
pat = (pat.first if pre)
|
|
881
891
|
latest << :bold
|
|
@@ -889,7 +899,7 @@ module Squared
|
|
|
889
899
|
next
|
|
890
900
|
end
|
|
891
901
|
unless stdin?
|
|
892
|
-
|
|
902
|
+
sub_style! g, theme[:active]
|
|
893
903
|
styles = %i[green]
|
|
894
904
|
pat = pre ? pat.last : pat.first
|
|
895
905
|
end
|
|
@@ -908,8 +918,8 @@ module Squared
|
|
|
908
918
|
b = b.rjust(e)
|
|
909
919
|
h = c.rjust(f)
|
|
910
920
|
unless stdin?
|
|
911
|
-
|
|
912
|
-
|
|
921
|
+
sub_style!(b, **opt_style(colormap(styles), pat, 2))
|
|
922
|
+
sub_style!(h, **opt_style(latest.flatten.compact, pat, 2))
|
|
913
923
|
end
|
|
914
924
|
j += 1
|
|
915
925
|
if queue
|
|
@@ -922,7 +932,7 @@ module Squared
|
|
|
922
932
|
else
|
|
923
933
|
out.call('%*s %s' % [pad, "#{j}.", s])
|
|
924
934
|
end
|
|
925
|
-
update.delete(a) if ia && !confirm_outdated(a, c, row.last, timeout: 0)
|
|
935
|
+
update.delete(a) if ia && !confirm_outdated(a, c, row.last, col4: col4, timeout: 0)
|
|
926
936
|
end
|
|
927
937
|
end
|
|
928
938
|
end
|
|
@@ -1134,9 +1144,7 @@ module Squared
|
|
|
1134
1144
|
output = val.match?(/(?:un)?set/)
|
|
1135
1145
|
else
|
|
1136
1146
|
a = op.dup
|
|
1137
|
-
b = op.
|
|
1138
|
-
c = op.shift
|
|
1139
|
-
d = op.shift
|
|
1147
|
+
b, c, d = op.slice!(0, 3)
|
|
1140
1148
|
e = op.arg?('global', 'local')
|
|
1141
1149
|
op << b
|
|
1142
1150
|
getname = -> { op << (c || readline('Enter name', force: true)) }
|
|
@@ -1250,7 +1258,7 @@ module Squared
|
|
|
1250
1258
|
dir = basepath sig, file.dirname
|
|
1251
1259
|
dir.mkpath unless dir.exist?
|
|
1252
1260
|
base = file.basename.to_s
|
|
1253
|
-
rbs = dir.join(base.stripext
|
|
1261
|
+
rbs = dir.join("#{base.stripext}.rbs")
|
|
1254
1262
|
status = if rbs.exist?
|
|
1255
1263
|
case y
|
|
1256
1264
|
when '0', 'false'
|
|
@@ -1387,18 +1395,11 @@ module Squared
|
|
|
1387
1395
|
end
|
|
1388
1396
|
|
|
1389
1397
|
def gem_session(*cmd, **kwargs)
|
|
1390
|
-
|
|
1391
|
-
return ret if cmd.empty?
|
|
1392
|
-
|
|
1393
|
-
ret.merge(preopts)
|
|
1398
|
+
session('gem', *cmd, *preopts, **kwargs)
|
|
1394
1399
|
end
|
|
1395
1400
|
|
|
1396
1401
|
def bundle_session(*cmd, **kwargs)
|
|
1397
|
-
|
|
1398
|
-
return ret if cmd.empty?
|
|
1399
|
-
|
|
1400
|
-
append_nocolor
|
|
1401
|
-
ret.merge(preopts)
|
|
1402
|
+
session('bundle', *cmd, *preopts, **kwargs).tap { append_nocolor }
|
|
1402
1403
|
end
|
|
1403
1404
|
|
|
1404
1405
|
def rake_session(*cmd, **kwargs)
|
|
@@ -1451,7 +1452,7 @@ module Squared
|
|
|
1451
1452
|
end
|
|
1452
1453
|
|
|
1453
1454
|
def preopts
|
|
1454
|
-
verbose?
|
|
1455
|
+
verbose? ? ['--verbose'] : []
|
|
1455
1456
|
end
|
|
1456
1457
|
|
|
1457
1458
|
def variables
|
|
@@ -1531,8 +1532,7 @@ module Squared
|
|
|
1531
1532
|
end
|
|
1532
1533
|
|
|
1533
1534
|
def gemlib
|
|
1534
|
-
@gemlib ||=
|
|
1535
|
-
lib = Set.new(['lib'])
|
|
1535
|
+
@gemlib ||= Set.new(['lib']).yield_self do |lib|
|
|
1536
1536
|
if (spec = gemspec)
|
|
1537
1537
|
lib.merge(spec.require_paths || [])
|
|
1538
1538
|
end
|
|
@@ -1545,7 +1545,9 @@ module Squared
|
|
|
1545
1545
|
end
|
|
1546
1546
|
|
|
1547
1547
|
def gemdir?
|
|
1548
|
-
|
|
1548
|
+
return false unless @gemdir
|
|
1549
|
+
|
|
1550
|
+
@gemdir.exist? && !@gemdir.empty?
|
|
1549
1551
|
end
|
|
1550
1552
|
end
|
|
1551
1553
|
|