squared 0.4.25 → 0.5.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.
@@ -57,10 +57,10 @@ module Squared
57
57
  when 'verbose'
58
58
  @verbose = 1
59
59
  if script.is_a?(Array)
60
- script[0] = task_join(script[0], 'verbose')
60
+ script[0] = task_join script[0], 'verbose'
61
61
  script
62
62
  else
63
- task_join(script, 'verbose')
63
+ task_join script, 'verbose'
64
64
  end
65
65
  when 'silent'
66
66
  @verbose = false
@@ -81,8 +81,8 @@ module Squared
81
81
  data[:run] = run
82
82
  end
83
83
  data[:global] = true
84
- data[:dev] = env_match('REPO_DEV', dev)
85
- data[:prod] = env_match('REPO_PROD', prod)
84
+ data[:dev] = env_match 'REPO_DEV', dev
85
+ data[:prod] = env_match 'REPO_PROD', prod
86
86
  if (val = env('REPO_GROUP'))
87
87
  script_set(data, group: val.split(','))
88
88
  found = true
@@ -113,39 +113,56 @@ module Squared
113
113
  def __repo__(**kwargs)
114
114
  kwargs.delete(:parallel) if env('REPO_SYNC', ignore: '0')
115
115
 
116
- namespace task_name('repo') do |ns|
117
- path = ns.scope.path
116
+ namespace(name = task_name('repo')) do |repo|
118
117
  branch = env('REPO_MANIFEST') || Repo.read_manifest(root)
119
118
  target = branch || manifest
120
119
  stage = nil
121
- opts = %w[force rebase detach submodules fail no-update gc]
120
+ failfast = true
121
+ cmd = []
122
+ newline = ARGV.index { |val| val.start_with?('repo:') }.to_i > 0
123
+ parse_opts = lambda do |args|
124
+ args.to_a.each do |val|
125
+ case val
126
+ when 'no-fail'
127
+ failfast = false
128
+ when 'force'
129
+ cmd << '--force-checkout'
130
+ when 'rebase'
131
+ cmd << '--rebase'
132
+ when 'detach'
133
+ cmd << '--detach'
134
+ when 'gc'
135
+ cmd << '--auto-gc'
136
+ when 'no-update'
137
+ cmd << '--no-manifest-update'
138
+ end
139
+ end
140
+ end
122
141
  desc = lambda do |val, alt = nil|
123
142
  if (ver = branch || alt)
124
- val = val.sub('{0}', "opts*=#{opts.join(',')}")
125
- task_desc(path, val, ver)
143
+ val = val.sub('{0}', 'opts*=force,rebase,detach,gc,no-update,no-fail')
144
+ task_desc(task_name('repo'), val, ver)
126
145
  else
127
146
  task_desc 'inactive'
128
147
  end
129
148
  end
130
149
 
131
150
  desc.call('all[{0}]')
132
- task 'all' do |_, args|
151
+ task 'all', [:opts] do |_, args|
152
+ parse_opts.call(args)
133
153
  stage ||= 'all'
134
- ns['sync'].invoke(*args.to_a)
135
- next if (stage = env('REPO_STAGE')) == '1'
154
+ repo['sync'].invoke
155
+ next if env('REPO_DRYRUN', equals: '2')
136
156
 
137
157
  @project.select do |_, proj|
138
158
  next unless proj.enabled?(proj.workspace.baseref)
139
159
 
140
160
  proj.depend(sync: true) if proj.depend?
141
- next if stage == '2'
142
-
143
- proj.build?
161
+ proj.build? unless env('REPO_DRYRUN', ignore: '0')
144
162
  end
145
163
  .each_value do |proj|
146
164
  proj.build(sync: true)
147
- next if stage == '3'
148
- next unless proj.copy? && (proj.dev? || stage == '4')
165
+ next unless proj.dev? && proj.copy?
149
166
 
150
167
  if (ws = proj.workspace).task_defined?(target = task_join(proj.name, 'copy'))
151
168
  task_invoke(target, **ws.invokeargs)
@@ -155,71 +172,39 @@ module Squared
155
172
  end
156
173
  end
157
174
 
158
- desc.call("init[manifest?=#{target},groups?,{0}]", target)
159
- task 'init' do |_, args|
160
- args = args.to_a
161
- u = env('REPO_URL') || manifest_url
162
- m = args.first && !opts.include?(args.first) ? args.shift : target
163
- g = args.first && !opts.include?(args.first) ? args.shift : nil
164
- g = case (val = env('REPO_GROUPS'))
165
- when '', NilClass
166
- g
167
- when '0', 'false'
168
- nil
169
- else
170
- val
171
- end
175
+ desc.call("init[manifest?=#{target},{0}]", target)
176
+ task 'init', [:manifest, :opts] do |_, args|
177
+ parse_opts.call(args)
172
178
  stage = 'init'
173
- opts = repo_opts "-u #{u}", "-m #{m}.xml"
174
- opts << "-g #{g}" if g
175
- opts << '--submodules' if repo_submodules?(args.include?('submodules'))
176
- repo_run "#{repo_bin} init #{opts.uniq.join(' ')}"
177
- next if env('REPO_STAGE', equals: '0')
178
-
179
- ns['all'].invoke(*args)
179
+ puts if newline
180
+ system("repo init -u #{env('REPO_URL') || manifest_url} -m #{args.manifest || target}.xml", chdir: root)
181
+ repo['all'].invoke
180
182
  end
181
183
 
182
184
  desc.call('sync[{0}]')
183
- task 'sync' do |t, args|
184
- opts = if stage == 'init'
185
- []
186
- else
187
- raise_error 'repo not initialized' unless branch
188
- repo_opts
189
- end
190
- args.to_a.each do |val|
191
- case val
192
- when 'force'
193
- opts << '--force-checkout'
194
- when 'rebase', 'detach'
195
- opts << "--#{val}"
196
- when 'submodules'
197
- opts << '--fetch-submodules' if repo_submodules?(true)
198
- when 'fail'
199
- opts << '--fail-fast'
200
- when 'no-update'
201
- opts << '--no-manifest-update'
202
- when 'gc'
203
- opts << '--auto-gc'
204
- end
185
+ task 'sync', [:opts] do |_, args|
186
+ unless branch || stage == 'init'
187
+ raise_error('repo not initialized', hint: task_name('repo:init'), kind: LoadError)
205
188
  end
206
- opts << "-j#{ENV.fetch('REPO_JOBS', Rake::CpuCounter.count)}" unless opts.grep(/^--?j(?:obs)?/).empty?
207
- opts << '--fetch-submodules' if repo_submodules?
189
+ parse_opts.call(args)
190
+ cmd << "-j#{ENV.fetch('REPO_JOBS', Rake::CpuCounter.count)}"
191
+ cmd << '--fail-fast' if failfast
192
+ puts if newline && stage != 'init'
208
193
  begin
209
- repo_run("#{repo_bin} sync #{opts.uniq.join(' ')}", exception: opts.include?('--fail-fast'))
194
+ Common::System.shell("repo sync #{cmd.join(' ')}", chdir: root, exception: failfast)
210
195
  rescue Errno::ENOENT => e
211
196
  emphasize(e, title: root)
212
197
  raise
213
198
  rescue StandardError => e
214
- emphasize(e, title: "rake stash #{t.name}")
199
+ emphasize(e, title: "rake stash #{task_name(task_join('repo', stage || 'sync'))}")
215
200
  raise
216
201
  end
217
202
  end
218
203
 
219
- series.sync.push(
220
- task_join(path, 'all'),
221
- task_join(path, 'init'),
222
- task_join(path, 'sync')
204
+ series.sync.append(
205
+ task_join(name, 'all'),
206
+ task_join(name, 'init'),
207
+ task_join(name, 'sync')
223
208
  )
224
209
  end
225
210
  end
@@ -235,34 +220,10 @@ module Squared
235
220
  )
236
221
  end
237
222
 
238
- def repo_run(cmd, exception: false)
239
- puts log_message(Logger::INFO, cmd, subject: main, hint: root) if verbose
240
- Common::System.shell(cmd, chdir: root, exception: exception)
241
- end
242
-
243
- def repo_bin
244
- Common::Shell.shell_bin('repo')
245
- end
246
-
247
- def repo_opts(*args)
248
- return args unless (n = ARGV.index('--'))
249
-
250
- ARGV[(n + 1)..-1].concat(args)
251
- end
252
-
253
223
  def repo?
254
224
  !manifest_url.nil? && (repo_install? || @repo_override == true)
255
225
  end
256
226
 
257
- def repo_submodules?(val = false)
258
- case (s = env('REPO_SUBMODULES'))
259
- when '0', 'false'
260
- false
261
- else
262
- s ? true : val
263
- end
264
- end
265
-
266
227
  def repo_install?(dir = root, parent: false)
267
228
  return true if root?(dir, pass: ['.repo']) || dir.join('.repo').directory?
268
229
 
@@ -10,16 +10,16 @@ module Squared
10
10
 
11
11
  TASK_BASE = []
12
12
  TASK_BATCH = {}
13
- TASK_EXTEND = Support.hashlist
13
+ TASK_EXTEND = {}
14
14
  TASK_KEYS = []
15
- TASK_ALIAS = Support.hashobj
15
+ TASK_ALIAS = {}
16
16
  TASK_NAME = {}
17
17
  private_constant :TASK_BASE, :TASK_BATCH, :TASK_EXTEND, :TASK_KEYS, :TASK_ALIAS, :TASK_NAME
18
18
 
19
19
  class << self
20
20
  def add(task, obj)
21
21
  key_set task
22
- TASK_EXTEND[task] << obj
22
+ (TASK_EXTEND[task] ||= []) << obj
23
23
  end
24
24
 
25
25
  def batch(*args, obj)
@@ -37,7 +37,7 @@ module Squared
37
37
 
38
38
  def alias(ref, obj)
39
39
  if obj.is_a?(Hash)
40
- obj.each { |key, val| TASK_ALIAS[key][ref] = val }
40
+ obj.each { |key, val| (TASK_ALIAS[key] ||= {})[ref] = val }
41
41
  else
42
42
  TASK_ALIAS[obj]&.delete(ref)
43
43
  end
@@ -49,7 +49,11 @@ module Squared
49
49
 
50
50
  def base_set(obj)
51
51
  TASK_BASE.clear
52
- TASK_BASE.concat(obj.tasks.reject { |val| TASK_KEYS.include?(val) })
52
+ .concat((if TASK_KEYS.empty?
53
+ obj.tasks
54
+ else
55
+ obj.tasks.reject { |val| TASK_KEYS.include?(val) }
56
+ end).freeze)
53
57
  end
54
58
 
55
59
  private
@@ -64,7 +68,7 @@ module Squared
64
68
 
65
69
  attr_reader :sync, :multiple, :parallel
66
70
 
67
- def_delegators :@data, :[], :each, :each_key, :keys, :fetch, :to_a, :to_s, :inspect, :merge!, :key?
71
+ def_delegators :@data, :[], :each, :each_key, :keys, :key?, :fetch, :update, :merge!, :to_a, :to_s, :inspect
68
72
  def_delegators :@workspace, :task_desc, :task_name, :task_namespace, :task_join, :format_desc
69
73
 
70
74
  def initialize(workspace, exclude: [])
@@ -75,8 +79,8 @@ module Squared
75
79
  @chain = {}
76
80
  @exclude = exclude.freeze
77
81
  @session = {
78
- group: Support.hashlist,
79
- parent: Support.hashlist,
82
+ group: {},
83
+ parent: {},
80
84
  id: []
81
85
  }
82
86
  @data = {}
@@ -91,7 +95,7 @@ module Squared
91
95
 
92
96
  if (g = proj.group)
93
97
  id << g
94
- group[:"#{key}:#{g}"].concat(tasks)
98
+ (group[:"#{key}:#{g}"] ||= []).concat(tasks)
95
99
  else
96
100
  items.concat(tasks)
97
101
  end
@@ -102,35 +106,35 @@ module Squared
102
106
  next unless (b = ws.find_base(proj)) && (n = b.ref.to_s) != g
103
107
 
104
108
  id << n
105
- parent[:"#{key}:#{n}"].concat(tasks)
109
+ (parent[:"#{key}:#{n}"] ||= []).concat(tasks)
106
110
  end
107
111
  end
108
112
 
109
113
  def build(parallel: [], pattern: [], **)
110
114
  subcheck = ->(val) { (ns = task_namespace(val)) && parallel.include?(ns) }
111
- @data.merge!(@session[:parent]) if @session[:id].uniq.size > 1
112
- @data.merge!(@session[:group])
115
+ @data.update(@session[:parent]) if @session[:id].uniq.size > 1
116
+ @data.update(@session[:group])
113
117
  @data.each do |key, items|
114
118
  next if exclude?(key, true) || @workspace.task_exclude?(t = name_get(key))
115
119
 
116
120
  key = task_name t
117
- val = format_desc(key, out: true)
121
+ title = format_desc(key, out: true)
118
122
  if items.size > 1
119
123
  @multiple << key
120
124
  if parallel.include?(t) || pattern.any? { |pat| t.match?(pat) } || subcheck.call(t)
121
- task_desc("#{val} (thread)", name: key)
125
+ task_desc("#{title} (thread)", name: key) if title
122
126
  multitask key => items
123
127
  @parallel << key
124
128
 
125
- s = task_join(key, 'sync')
126
- task_desc("#{val} (sync)", name: s)
129
+ s = task_join key, 'sync'
130
+ task_desc("#{title} (sync)", name: s) if title
127
131
  task s => items
128
132
  @sync << s
129
133
  next
130
134
  end
131
135
  end
132
136
 
133
- task_desc(val, name: key)
137
+ task_desc(title, name: key) if title
134
138
  task key => items
135
139
  end
136
140
  @multiple.concat(sync)
@@ -206,25 +210,27 @@ module Squared
206
210
 
207
211
  def chain?(val)
208
212
  @chain.each_value do |tasks|
209
- tasks.flatten(1).each do |name|
210
- next unless (task = invoked_get(name))
213
+ tasks.flatten(1).each do |task|
214
+ next unless Rake::Task[task].already_invoked
211
215
 
212
- return true if name == val || task.prerequisites.any? { |pr| pr == val && Rake::Task[pr].already_invoked }
216
+ if val == task || Rake::Task[task].prerequisites.any? { |pr| pr == val && Rake::Task[val].already_invoked }
217
+ return true
218
+ end
213
219
  end
214
220
  end
215
221
  false
216
222
  end
217
223
 
218
224
  def multiple?(val = nil)
219
- already_invoked? multiple, val
225
+ already_invoked?(multiple, val)
220
226
  end
221
227
 
222
228
  def sync?(val = nil)
223
- already_invoked? sync, val
229
+ already_invoked?(sync, val)
224
230
  end
225
231
 
226
232
  def parallel?(val = nil)
227
- already_invoked? parallel, val
233
+ already_invoked?(parallel, val)
228
234
  end
229
235
 
230
236
  def exclude?(key, empty = false)
@@ -233,16 +239,12 @@ module Squared
233
239
 
234
240
  private
235
241
 
236
- def invoked_get(name)
237
- return unless Rake::Task.task_defined?(name) && (ret = Rake::Task[name]).already_invoked
238
-
239
- ret
240
- end
241
-
242
242
  def already_invoked?(list, val)
243
- return Rake::Task.tasks.any? { |obj| obj.already_invoked && list.include?(obj.name) } unless val
244
-
245
- list.include?(val) && !invoked_get(val).nil?
243
+ if val
244
+ list.include?(val) && Rake::Task[val].already_invoked
245
+ else
246
+ Rake::Task.tasks.any? { |obj| obj.already_invoked && list.include?(obj.name) }
247
+ end
246
248
  end
247
249
  end
248
250
 
@@ -3,8 +3,9 @@
3
3
  module Squared
4
4
  module Workspace
5
5
  module Support
6
- RunData = Struct.new('RunData', :run, :block)
7
- ChainData = Struct.new('ChainData', :action, :step, :with, :before, :after, :sync)
6
+ RunData = Struct.new(:run, :block)
7
+ ChainData = Struct.new(:action, :step, :with, :before, :after, :sync)
8
+ BannerData = Struct.new(:command, :order, :styles, :border)
8
9
  end
9
10
  end
10
11
  end
@@ -1,4 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'support/base'
4
3
  require_relative 'support/data'
@@ -10,7 +10,7 @@ module Squared
10
10
  if id.is_a?(Symbol)
11
11
  project id
12
12
  else
13
- __get__(:project).find { |_, val| File.expand_path(val.path) == File.expand_path(id) }
13
+ __get__(:project).find { |_, val| File.expand_path(val.path) == File.expand_path(id, __dir__) }
14
14
  end
15
15
  end
16
16
  ret.size == 1 ? ret.first : ret
data/squared.gemspec CHANGED
@@ -1,17 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- version = File.read(File.join(__dir__, "lib/squared/version.rb"))[/\bVERSION = '(.+)'$/, 1]
3
+ require_relative "lib/squared/version"
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "squared"
7
- spec.version = version
7
+ spec.version = Squared::VERSION
8
8
  spec.authors = ["An Pham"]
9
9
  spec.email = ["anpham6@gmail.com"]
10
10
 
11
11
  spec.summary = %q{Rake task generator for managing multi-language workspaces.}
12
12
  spec.description = %q{Rake task generator for managing multi-language workspaces.}
13
- spec.homepage = "https://github.com/anpham6/squared-ruby"
14
- spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
13
+ spec.homepage = "https://github.com/anpham6/squared"
14
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
15
15
  spec.licenses = ["BSD-3-Clause"]
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.metadata["documentation_uri"] = "https://squared.readthedocs.io"
20
20
 
21
21
  spec.files = Dir["lib/**/*"] +
22
- %w[CHANGELOG.md LICENSE README.md squared.gemspec]
22
+ %w[CHANGELOG.md LICENSE README.md README.ruby.md squared.gemspec]
23
23
  spec.bindir = "exe"
24
24
  spec.executables = []
25
25
  spec.require_paths = ["lib"]
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: squared
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.25
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - An Pham
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-10-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: rake
@@ -76,11 +75,11 @@ files:
76
75
  - CHANGELOG.md
77
76
  - LICENSE
78
77
  - README.md
78
+ - README.ruby.md
79
79
  - lib/squared.rb
80
80
  - lib/squared/app.rb
81
81
  - lib/squared/common.rb
82
82
  - lib/squared/common/base.rb
83
- - lib/squared/common/class.rb
84
83
  - lib/squared/common/format.rb
85
84
  - lib/squared/common/prompt.rb
86
85
  - lib/squared/common/shell.rb
@@ -102,17 +101,15 @@ files:
102
101
  - lib/squared/workspace/repo.rb
103
102
  - lib/squared/workspace/series.rb
104
103
  - lib/squared/workspace/support.rb
105
- - lib/squared/workspace/support/base.rb
106
104
  - lib/squared/workspace/support/data.rb
107
105
  - squared.gemspec
108
- homepage: https://github.com/anpham6/squared-ruby
106
+ homepage: https://github.com/anpham6/squared
109
107
  licenses:
110
108
  - BSD-3-Clause
111
109
  metadata:
112
- homepage_uri: https://github.com/anpham6/squared-ruby
113
- source_code_uri: https://github.com/anpham6/squared-ruby
110
+ homepage_uri: https://github.com/anpham6/squared
111
+ source_code_uri: https://github.com/anpham6/squared
114
112
  documentation_uri: https://squared.readthedocs.io
115
- post_install_message:
116
113
  rdoc_options: []
117
114
  require_paths:
118
115
  - lib
@@ -120,15 +117,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
120
117
  requirements:
121
118
  - - ">="
122
119
  - !ruby/object:Gem::Version
123
- version: 2.4.0
120
+ version: 2.5.0
124
121
  required_rubygems_version: !ruby/object:Gem::Requirement
125
122
  requirements:
126
123
  - - ">="
127
124
  - !ruby/object:Gem::Version
128
125
  version: '0'
129
126
  requirements: []
130
- rubygems_version: 3.2.33
131
- signing_key:
127
+ rubygems_version: 3.6.7
132
128
  specification_version: 4
133
129
  summary: Rake task generator for managing multi-language workspaces.
134
130
  test_files: []
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'set'
4
- require 'forwardable'
5
-
6
- module Squared
7
- module Common
8
- class SymSet
9
- extend Forwardable
10
-
11
- def self.to_s
12
- super[/[^:]+\z/, 0]
13
- end
14
-
15
- def_delegators :@data, :+, :each, :each_with_index, :entries, :merge, :include?
16
-
17
- def initialize(data = [])
18
- @data = Set.new(data)
19
- end
20
-
21
- def add(val)
22
- @data.add(val.to_sym)
23
- end
24
-
25
- def to_a
26
- @data.to_a.freeze
27
- end
28
-
29
- def to_s
30
- @data.to_s.sub('Set', SymSet.to_s)
31
- end
32
-
33
- alias inspect to_s
34
- end
35
-
36
- class JoinSet < Set
37
- def self.to_s
38
- super[/[^:]+\z/, 0]
39
- end
40
-
41
- attr_reader :delim
42
-
43
- def initialize(data = [], delim: ' ')
44
- super(data.compact)
45
- @delim = delim
46
- end
47
-
48
- def last(val, pat)
49
- (@last ||= []).push([val, pat, $1]) if val =~ pat
50
- self << val
51
- end
52
-
53
- def pass(&blk)
54
- ret = to_a.map!(&:to_s).reject(&:empty?)
55
- @last&.each do |val, pat, key|
56
- i = []
57
- j = nil
58
- ret.each_with_index do |opt, index|
59
- if opt == val
60
- j = index
61
- elsif j && opt[pat, 1] == key
62
- i << index
63
- end
64
- end
65
- next unless j && !i.empty?
66
-
67
- val = ret[j]
68
- cur = j
69
- i.each do |k|
70
- ret[cur] = ret[k]
71
- cur = k
72
- end
73
- ret[i.last] = val
74
- end
75
- block_given? ? ret.reject(&blk) : ret
76
- end
77
-
78
- def and(*args)
79
- self << '&&'
80
- merge(args)
81
- end
82
-
83
- def or(*args)
84
- self << '||'
85
- merge(args)
86
- end
87
-
88
- def with(*args, &blk)
89
- temp('&&', *args, &blk)
90
- end
91
-
92
- def temp(*args, &blk)
93
- args.compact!
94
- ret = pass(&blk)
95
- ret = Set.new(ret.concat(args)).to_a unless args.empty?
96
- ret.join(@delim)
97
- end
98
-
99
- def done
100
- ret = to_s
101
- clear
102
- ret
103
- end
104
-
105
- def to_s
106
- pass.join(@delim)
107
- end
108
- end
109
- end
110
- end
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Squared
4
- module Workspace
5
- module Support
6
- class << self
7
- def hashobj
8
- Hash.new { |data, key| data[key] = {} }
9
- end
10
-
11
- def hashlist
12
- Hash.new { |data, key| data[key] = [] }
13
- end
14
-
15
- def hashdup(data, pass: [])
16
- ret = {}
17
- data.each do |key, val|
18
- ret[key] = case val
19
- when Hash
20
- if pass.include?(val)
21
- val
22
- else
23
- pass << val
24
- hashdup(val, pass: pass)
25
- end
26
- when Proc, Method
27
- val
28
- else
29
- val.dup
30
- end
31
- end
32
- ret
33
- end
34
- end
35
- end
36
- end
37
- end