modulesync 2.6.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -4
- data/.github/workflows/release.yml +1 -1
- data/.rubocop.yml +3 -48
- data/.rubocop_todo.yml +124 -19
- data/CHANGELOG.md +13 -0
- data/Gemfile +6 -5
- data/Rakefile +4 -11
- data/lib/modulesync/cli.rb +107 -107
- data/lib/modulesync/git_service/base.rb +2 -2
- data/lib/modulesync/git_service/github.rb +5 -5
- data/lib/modulesync/git_service/gitlab.rb +9 -9
- data/lib/modulesync/renderer.rb +1 -1
- data/lib/modulesync/repository.rb +2 -2
- data/lib/modulesync.rb +21 -21
- data/modulesync.gemspec +5 -9
- data/spec/helpers/faker/puppet_module_remote_repo.rb +4 -4
- data/spec/helpers/faker.rb +1 -0
- data/spec/unit/modulesync/git_service/factory_spec.rb +8 -2
- data/spec/unit/modulesync/git_service/github_spec.rb +23 -25
- data/spec/unit/modulesync/git_service/gitlab_spec.rb +26 -29
- data/spec/unit/modulesync/git_service_spec.rb +48 -47
- data/spec/unit/modulesync/settings_spec.rb +1 -1
- data/spec/unit/modulesync/source_code_spec.rb +5 -7
- metadata +21 -71
@@ -13,12 +13,12 @@ module ModuleSync
|
|
13
13
|
Octokit.configure do |c|
|
14
14
|
c.api_endpoint = endpoint
|
15
15
|
end
|
16
|
-
@api = Octokit::Client.new(:
|
16
|
+
@api = Octokit::Client.new(access_token: token)
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
20
20
|
|
21
|
-
def _open_pull_request(repo_path:, namespace:, title:, message:, source_branch:, target_branch:, labels:, noop:)
|
21
|
+
def _open_pull_request(repo_path:, namespace:, title:, message:, source_branch:, target_branch:, labels:, noop:)
|
22
22
|
head = "#{namespace}:#{source_branch}"
|
23
23
|
|
24
24
|
if noop
|
@@ -28,9 +28,9 @@ module ModuleSync
|
|
28
28
|
end
|
29
29
|
|
30
30
|
pull_requests = @api.pull_requests(repo_path,
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
31
|
+
state: 'open',
|
32
|
+
base: target_branch,
|
33
|
+
head: head)
|
34
34
|
unless pull_requests.empty?
|
35
35
|
# Skip creating the PR if it exists already.
|
36
36
|
$stdout.puts "Skipped! #{pull_requests.length} PRs found for branch '#{source_branch}'"
|
@@ -11,8 +11,8 @@ module ModuleSync
|
|
11
11
|
super()
|
12
12
|
|
13
13
|
@api = Gitlab::Client.new(
|
14
|
-
:
|
15
|
-
:
|
14
|
+
endpoint: endpoint,
|
15
|
+
private_token: token,
|
16
16
|
)
|
17
17
|
end
|
18
18
|
|
@@ -26,7 +26,7 @@ module ModuleSync
|
|
26
26
|
|
27
27
|
private
|
28
28
|
|
29
|
-
def _open_pull_request(repo_path:, namespace:, title:, message:, source_branch:, target_branch:, labels:, noop:) # rubocop:disable
|
29
|
+
def _open_pull_request(repo_path:, namespace:, title:, message:, source_branch:, target_branch:, labels:, noop:) # rubocop:disable Lint/UnusedMethodArgument
|
30
30
|
if noop
|
31
31
|
$stdout.puts "Using no-op. Would submit MR '#{title}' to '#{repo_path}' " \
|
32
32
|
"- merges #{source_branch} into #{target_branch}"
|
@@ -34,9 +34,9 @@ module ModuleSync
|
|
34
34
|
end
|
35
35
|
|
36
36
|
merge_requests = @api.merge_requests(repo_path,
|
37
|
-
:
|
38
|
-
:
|
39
|
-
:
|
37
|
+
state: 'opened',
|
38
|
+
source_branch: source_branch,
|
39
|
+
target_branch: target_branch)
|
40
40
|
unless merge_requests.empty?
|
41
41
|
# Skip creating the MR if it exists already.
|
42
42
|
$stdout.puts "Skipped! #{merge_requests.length} MRs found for branch '#{source_branch}'"
|
@@ -45,9 +45,9 @@ module ModuleSync
|
|
45
45
|
|
46
46
|
mr = @api.create_merge_request(repo_path,
|
47
47
|
title,
|
48
|
-
:
|
49
|
-
:
|
50
|
-
:
|
48
|
+
source_branch: source_branch,
|
49
|
+
target_branch: target_branch,
|
50
|
+
labels: labels)
|
51
51
|
$stdout.puts \
|
52
52
|
"Submitted MR '#{title}' to '#{repo_path}' " \
|
53
53
|
"- merges '#{source_branch}' into '#{target_branch}'"
|
data/lib/modulesync/renderer.rb
CHANGED
@@ -15,7 +15,7 @@ module ModuleSync
|
|
15
15
|
erb_obj = if RUBY_VERSION >= '2.7'
|
16
16
|
ERB.new(template, trim_mode: '-')
|
17
17
|
else
|
18
|
-
ERB.new(template,
|
18
|
+
ERB.new(template, trim_mode: '-')
|
19
19
|
end
|
20
20
|
erb_obj.filename = template_file
|
21
21
|
erb_obj.def_method(ForgeModuleFile, 'render()', template_file)
|
@@ -140,8 +140,8 @@ module ModuleSync
|
|
140
140
|
begin
|
141
141
|
opts_commit = {}
|
142
142
|
opts_push = {}
|
143
|
-
opts_commit = { :
|
144
|
-
opts_push = { :
|
143
|
+
opts_commit = { amend: true } if options[:amend]
|
144
|
+
opts_push = { force: true } if options[:force]
|
145
145
|
if options[:pre_commit_script]
|
146
146
|
script = "#{File.dirname(File.dirname(__FILE__))}/../contrib/#{options[:pre_commit_script]}"
|
147
147
|
`#{script} #{@directory}`
|
data/lib/modulesync.rb
CHANGED
@@ -12,17 +12,17 @@ require 'modulesync/util'
|
|
12
12
|
|
13
13
|
require 'monkey_patches'
|
14
14
|
|
15
|
-
module ModuleSync
|
15
|
+
module ModuleSync
|
16
16
|
class Error < StandardError; end
|
17
17
|
|
18
18
|
include Constants
|
19
19
|
|
20
20
|
def self.config_defaults
|
21
21
|
{
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
22
|
+
project_root: 'modules/',
|
23
|
+
managed_modules_conf: 'managed_modules.yml',
|
24
|
+
configs: '.',
|
25
|
+
tag_pattern: '%s',
|
26
26
|
}
|
27
27
|
end
|
28
28
|
|
@@ -33,7 +33,7 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
|
|
33
33
|
def self.local_file(config_path, file)
|
34
34
|
path = File.join(config_path, MODULE_FILES_DIR, file)
|
35
35
|
if !File.exist?("#{path}.erb") && File.exist?(path)
|
36
|
-
|
36
|
+
warn "Warning: using '#{path}' as template without '.erb' suffix"
|
37
37
|
path
|
38
38
|
else
|
39
39
|
"#{path}.erb"
|
@@ -50,9 +50,9 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
|
|
50
50
|
.collect { |p| p.chomp('.erb') }
|
51
51
|
.to_a
|
52
52
|
else
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
warn "#{local_template_dir} does not exist. " \
|
54
|
+
'Check that you are working in your module configs directory or ' \
|
55
|
+
'that you have passed in the correct directory with -c.'
|
56
56
|
exit 1
|
57
57
|
end
|
58
58
|
end
|
@@ -68,8 +68,8 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
|
|
68
68
|
|
69
69
|
managed_modules = Util.parse_config(config_file)
|
70
70
|
if managed_modules.empty?
|
71
|
-
|
72
|
-
|
71
|
+
warn "No modules found in #{config_file}. " \
|
72
|
+
'Check that you specified the right :configs directory and :managed_modules_conf file.'
|
73
73
|
exit 1
|
74
74
|
end
|
75
75
|
managed_modules.select! { |m| m =~ Regexp.new(filter) } unless filter.nil?
|
@@ -99,16 +99,16 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
|
|
99
99
|
erb = Renderer.build(template_file)
|
100
100
|
# Meta data passed to the template as @metadata[:name]
|
101
101
|
metadata = {
|
102
|
-
:
|
103
|
-
:
|
104
|
-
:
|
105
|
-
:
|
102
|
+
module_name: settings.additional_settings[:puppet_module],
|
103
|
+
namespace: settings.additional_settings[:namespace],
|
104
|
+
workdir: puppet_module.working_directory,
|
105
|
+
target_file: target_file,
|
106
106
|
}
|
107
107
|
template = Renderer.render(erb, configs, metadata)
|
108
108
|
mode = File.stat(template_file).mode
|
109
109
|
Renderer.sync(template, target_file, mode)
|
110
110
|
rescue StandardError
|
111
|
-
|
111
|
+
warn "#{puppet_module.given_name}: Error while rendering file: '#{filename}'"
|
112
112
|
raise
|
113
113
|
end
|
114
114
|
end
|
@@ -128,9 +128,9 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
|
|
128
128
|
defaults,
|
129
129
|
module_configs[GLOBAL_DEFAULTS_KEY] || {},
|
130
130
|
module_configs,
|
131
|
-
:
|
132
|
-
:
|
133
|
-
:
|
131
|
+
puppet_module: puppet_module.repository_name,
|
132
|
+
git_base: options[:git_base],
|
133
|
+
namespace: puppet_module.repository_namespace)
|
134
134
|
|
135
135
|
settings.unmanaged_files(module_files).each do |filename|
|
136
136
|
$stdout.puts "Not managing '#{filename}' in '#{puppet_module.given_name}'"
|
@@ -178,7 +178,7 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
|
|
178
178
|
manage_module(puppet_module, module_files, defaults)
|
179
179
|
rescue ModuleSync::Error, Git::GitExecuteError => e
|
180
180
|
message = e.message || 'Error during `update`'
|
181
|
-
|
181
|
+
warn "#{puppet_module.given_name}: #{message}"
|
182
182
|
exit 1 unless options[:skip_broken]
|
183
183
|
errors = true
|
184
184
|
$stdout.puts "Skipping '#{puppet_module.given_name}' as update process failed"
|
@@ -222,7 +222,7 @@ module ModuleSync # rubocop:disable Metrics/ModuleLength
|
|
222
222
|
raise Thor::Error, message if @options[:fail_fast]
|
223
223
|
|
224
224
|
errors[puppet_module.given_name] = message
|
225
|
-
|
225
|
+
warn message
|
226
226
|
end
|
227
227
|
|
228
228
|
$stdout.puts ''
|
data/modulesync.gemspec
CHANGED
@@ -3,33 +3,29 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |spec|
|
5
5
|
spec.name = 'modulesync'
|
6
|
-
spec.version = '2.
|
6
|
+
spec.version = '2.7.0'
|
7
7
|
spec.authors = ['Vox Pupuli']
|
8
8
|
spec.email = ['voxpupuli@groups.io']
|
9
9
|
spec.summary = 'Puppet Module Synchronizer'
|
10
10
|
spec.description = 'Utility to synchronize common files across puppet modules in Github.'
|
11
11
|
spec.homepage = 'https://github.com/voxpupuli/modulesync'
|
12
12
|
spec.license = 'Apache-2.0'
|
13
|
-
spec.required_ruby_version = '>= 2.
|
13
|
+
spec.required_ruby_version = '>= 2.7.0'
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
17
|
spec.require_paths = ['lib']
|
18
18
|
|
19
19
|
spec.add_development_dependency 'aruba', '~>2.0'
|
20
|
-
spec.add_development_dependency 'bundler'
|
21
20
|
spec.add_development_dependency 'cucumber'
|
22
21
|
spec.add_development_dependency 'rake'
|
23
22
|
spec.add_development_dependency 'rspec'
|
24
|
-
spec.add_development_dependency 'rubocop', '~> 1.28.2'
|
25
|
-
spec.add_development_dependency 'rubocop-performance'
|
26
|
-
spec.add_development_dependency 'rubocop-rake'
|
27
|
-
spec.add_development_dependency 'rubocop-rspec'
|
28
23
|
spec.add_development_dependency 'simplecov'
|
24
|
+
spec.add_development_dependency 'voxpupuli-rubocop', '~> 1.3'
|
29
25
|
|
30
26
|
spec.add_runtime_dependency 'git', '~>1.7'
|
31
27
|
spec.add_runtime_dependency 'gitlab', '~>4.0'
|
32
|
-
spec.add_runtime_dependency 'octokit', '
|
33
|
-
spec.add_runtime_dependency 'puppet-blacksmith', '>= 3.0', '<
|
28
|
+
spec.add_runtime_dependency 'octokit', '>=4', '<7'
|
29
|
+
spec.add_runtime_dependency 'puppet-blacksmith', '>= 3.0', '< 8'
|
34
30
|
spec.add_runtime_dependency 'thor'
|
35
31
|
end
|
@@ -102,21 +102,21 @@ module ModuleSync
|
|
102
102
|
|
103
103
|
def tags
|
104
104
|
FileUtils.chdir(bare_repo_dir) do
|
105
|
-
return run %w
|
105
|
+
return run %w[git tag --list]
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
109
|
def delete_branch(branch)
|
110
110
|
FileUtils.chdir(bare_repo_dir) do
|
111
|
-
run %W
|
111
|
+
run %W[git branch -D #{branch}]
|
112
112
|
end
|
113
113
|
end
|
114
114
|
|
115
115
|
def create_branch(branch, from = nil)
|
116
116
|
from ||= default_branch
|
117
117
|
FileUtils.chdir(tmp_repo_dir) do
|
118
|
-
run %W
|
119
|
-
run %W
|
118
|
+
run %W[git branch -c #{from} #{branch}]
|
119
|
+
run %W[git push --set-upstream origin #{branch}]
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
data/spec/helpers/faker.rb
CHANGED
@@ -4,13 +4,19 @@ require 'modulesync/git_service/factory'
|
|
4
4
|
describe ModuleSync::GitService::Factory do
|
5
5
|
context 'when instantiate a GitHub service without credentials' do
|
6
6
|
it 'raises an error' do
|
7
|
-
expect
|
7
|
+
expect do
|
8
|
+
ModuleSync::GitService::Factory.instantiate(type: :github, endpoint: nil,
|
9
|
+
token: nil)
|
10
|
+
end.to raise_error(ModuleSync::GitService::MissingCredentialsError)
|
8
11
|
end
|
9
12
|
end
|
10
13
|
|
11
14
|
context 'when instantiate a GitLab service without credentials' do
|
12
15
|
it 'raises an error' do
|
13
|
-
expect
|
16
|
+
expect do
|
17
|
+
ModuleSync::GitService::Factory.instantiate(type: :gitlab, endpoint: nil,
|
18
|
+
token: nil)
|
19
|
+
end.to raise_error(ModuleSync::GitService::MissingCredentialsError)
|
14
20
|
end
|
15
21
|
end
|
16
22
|
end
|
@@ -4,8 +4,8 @@ require 'modulesync/git_service/github'
|
|
4
4
|
|
5
5
|
describe ModuleSync::GitService::GitHub do
|
6
6
|
context '::open_pull_request' do
|
7
|
-
before
|
8
|
-
@client = double
|
7
|
+
before do
|
8
|
+
@client = double
|
9
9
|
allow(Octokit::Client).to receive(:new).and_return(@client)
|
10
10
|
@it = ModuleSync::GitService::GitHub.new('test', 'https://api.github.com')
|
11
11
|
end
|
@@ -28,53 +28,51 @@ describe ModuleSync::GitService::GitHub do
|
|
28
28
|
it 'submits PR when --pr is set' do
|
29
29
|
allow(@client).to receive(:pull_requests)
|
30
30
|
.with(args[:repo_path],
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
).and_return([])
|
31
|
+
state: 'open',
|
32
|
+
base: 'master',
|
33
|
+
head: "#{args[:namespace]}:#{args[:source_branch]}").and_return([])
|
35
34
|
expect(@client).to receive(:create_pull_request)
|
36
35
|
.with(args[:repo_path],
|
37
36
|
'master',
|
38
37
|
args[:source_branch],
|
39
38
|
args[:title],
|
40
|
-
args[:message]
|
41
|
-
).and_return({"html_url" => "http://example.com/pulls/22"})
|
39
|
+
args[:message]).and_return({ 'html_url' => 'http://example.com/pulls/22' })
|
42
40
|
expect { @it.open_pull_request(**args) }.to output(/Submitted PR/).to_stdout
|
43
41
|
end
|
44
42
|
|
45
43
|
it 'skips submitting PR if one has already been issued' do
|
46
44
|
pr = {
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
'title' => 'Test title',
|
46
|
+
'html_url' => 'https://example.com/pulls/44',
|
47
|
+
'number' => '44',
|
50
48
|
}
|
51
49
|
|
52
50
|
expect(@client).to receive(:pull_requests)
|
53
51
|
.with(args[:repo_path],
|
54
|
-
:
|
55
|
-
:
|
56
|
-
:
|
57
|
-
).and_return([pr])
|
52
|
+
state: 'open',
|
53
|
+
base: 'master',
|
54
|
+
head: "#{args[:namespace]}:#{args[:source_branch]}").and_return([pr])
|
58
55
|
expect { @it.open_pull_request(**args) }.to output("Skipped! 1 PRs found for branch 'test'\n").to_stdout
|
59
56
|
end
|
60
57
|
|
61
58
|
context 'when labels are set' do
|
62
|
-
let(:labels) { %w
|
59
|
+
let(:labels) { %w[HELLO WORLD] }
|
63
60
|
|
64
61
|
it 'adds labels to PR' do
|
65
|
-
allow(@client).to receive(:create_pull_request).and_return({
|
62
|
+
allow(@client).to receive(:create_pull_request).and_return({ 'html_url' => 'http://example.com/pulls/22',
|
63
|
+
'number' => '44', })
|
66
64
|
allow(@client).to receive(:pull_requests)
|
67
65
|
.with(args[:repo_path],
|
68
|
-
:
|
69
|
-
:
|
70
|
-
:
|
71
|
-
).and_return([])
|
66
|
+
state: 'open',
|
67
|
+
base: 'master',
|
68
|
+
head: "#{args[:namespace]}:#{args[:source_branch]}").and_return([])
|
72
69
|
expect(@client).to receive(:add_labels_to_an_issue)
|
73
70
|
.with(args[:repo_path],
|
74
|
-
|
75
|
-
[
|
76
|
-
|
77
|
-
|
71
|
+
'44',
|
72
|
+
%w[HELLO WORLD])
|
73
|
+
expect do
|
74
|
+
@it.open_pull_request(**args)
|
75
|
+
end.to output(/Attaching the following labels to PR 44: HELLO, WORLD/).to_stdout
|
78
76
|
end
|
79
77
|
end
|
80
78
|
end
|
@@ -4,8 +4,8 @@ require 'modulesync/git_service/gitlab'
|
|
4
4
|
|
5
5
|
describe ModuleSync::GitService::GitLab do
|
6
6
|
context '::open_pull_request' do
|
7
|
-
before
|
8
|
-
@client = double
|
7
|
+
before do
|
8
|
+
@client = double
|
9
9
|
allow(Gitlab::Client).to receive(:new).and_return(@client)
|
10
10
|
@it = ModuleSync::GitService::GitLab.new('test', 'https://gitlab.com/api/v4')
|
11
11
|
end
|
@@ -28,62 +28,59 @@ describe ModuleSync::GitService::GitLab do
|
|
28
28
|
it 'submits MR when --pr is set' do
|
29
29
|
allow(@client).to receive(:merge_requests)
|
30
30
|
.with(args[:repo_path],
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
).and_return([])
|
31
|
+
state: 'opened',
|
32
|
+
source_branch: args[:source_branch],
|
33
|
+
target_branch: 'master').and_return([])
|
35
34
|
|
36
35
|
expect(@client).to receive(:create_merge_request)
|
37
36
|
.with(args[:repo_path],
|
38
37
|
args[:title],
|
39
|
-
:
|
40
|
-
:
|
41
|
-
:
|
42
|
-
).and_return({"html_url" => "http://example.com/pulls/22"})
|
38
|
+
labels: [],
|
39
|
+
source_branch: args[:source_branch],
|
40
|
+
target_branch: 'master').and_return({ 'html_url' => 'http://example.com/pulls/22' })
|
43
41
|
|
44
42
|
expect { @it.open_pull_request(**args) }.to output(/Submitted MR/).to_stdout
|
45
43
|
end
|
46
44
|
|
47
45
|
it 'skips submitting MR if one has already been issued' do
|
48
46
|
mr = {
|
49
|
-
|
50
|
-
|
51
|
-
|
47
|
+
'title' => 'Test title',
|
48
|
+
'html_url' => 'https://example.com/pulls/44',
|
49
|
+
'iid' => '44',
|
52
50
|
}
|
53
51
|
|
54
52
|
expect(@client).to receive(:merge_requests)
|
55
53
|
.with(args[:repo_path],
|
56
|
-
:
|
57
|
-
:
|
58
|
-
:
|
59
|
-
).and_return([mr])
|
54
|
+
state: 'opened',
|
55
|
+
source_branch: args[:source_branch],
|
56
|
+
target_branch: 'master').and_return([mr])
|
60
57
|
|
61
58
|
expect { @it.open_pull_request(**args) }.to output("Skipped! 1 MRs found for branch 'test'\n").to_stdout
|
62
59
|
end
|
63
60
|
|
64
61
|
context 'when labels are set' do
|
65
|
-
let(:labels) { %w
|
62
|
+
let(:labels) { %w[HELLO WORLD] }
|
66
63
|
|
67
64
|
it 'adds labels to MR' do
|
68
|
-
mr = double
|
69
|
-
allow(mr).to receive(:iid).and_return(
|
65
|
+
mr = double
|
66
|
+
allow(mr).to receive(:iid).and_return('42')
|
70
67
|
|
71
68
|
expect(@client).to receive(:create_merge_request)
|
72
69
|
.with(args[:repo_path],
|
73
70
|
args[:title],
|
74
|
-
:
|
75
|
-
:
|
76
|
-
:
|
77
|
-
).and_return(mr)
|
71
|
+
labels: %w[HELLO WORLD],
|
72
|
+
source_branch: args[:source_branch],
|
73
|
+
target_branch: 'master').and_return(mr)
|
78
74
|
|
79
75
|
allow(@client).to receive(:merge_requests)
|
80
76
|
.with(args[:repo_path],
|
81
|
-
:
|
82
|
-
:
|
83
|
-
:
|
84
|
-
).and_return([])
|
77
|
+
state: 'opened',
|
78
|
+
source_branch: args[:source_branch],
|
79
|
+
target_branch: 'master').and_return([])
|
85
80
|
|
86
|
-
expect
|
81
|
+
expect do
|
82
|
+
@it.open_pull_request(**args)
|
83
|
+
end.to output(/Attached the following labels to MR 42: HELLO, WORLD/).to_stdout
|
87
84
|
end
|
88
85
|
end
|
89
86
|
end
|