akabei 0.1.0 → 0.2.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/CHANGELOG.md +15 -0
- data/README.md +77 -0
- data/Rakefile +28 -0
- data/akabei.gemspec +2 -0
- data/lib/akabei/build_helper.rb +15 -0
- data/lib/akabei/builder.rb +7 -0
- data/lib/akabei/chroot_tree.rb +1 -0
- data/lib/akabei/cli.rb +18 -29
- data/lib/akabei/omakase/cli.rb +116 -0
- data/lib/akabei/omakase/config.rb +113 -0
- data/lib/akabei/omakase/s3.rb +71 -0
- data/lib/akabei/omakase/templates/.akabei.yml.tt +24 -0
- data/lib/akabei/omakase/templates/makepkg.i686.conf +140 -0
- data/lib/akabei/omakase/templates/makepkg.x86_64.conf +140 -0
- data/lib/akabei/omakase/templates/pacman.i686.conf +90 -0
- data/lib/akabei/omakase/templates/pacman.x86_64.conf +99 -0
- data/lib/akabei/repository.rb +5 -4
- data/lib/akabei/signer.rb +6 -2
- data/lib/akabei/version.rb +1 -1
- data/spec/akabei/builder_spec.rb +1 -5
- data/spec/akabei/cli_spec.rb +135 -0
- data/spec/akabei/omakase/cli_spec.rb +154 -0
- data/spec/akabei/repository_spec.rb +1 -2
- metadata +42 -2
@@ -0,0 +1,99 @@
|
|
1
|
+
#
|
2
|
+
# /etc/pacman.conf
|
3
|
+
#
|
4
|
+
# See the pacman.conf(5) manpage for option and repository directives
|
5
|
+
|
6
|
+
#
|
7
|
+
# GENERAL OPTIONS
|
8
|
+
#
|
9
|
+
[options]
|
10
|
+
# The following paths are commented out with their default values listed.
|
11
|
+
# If you wish to use different paths, uncomment and update the paths.
|
12
|
+
#RootDir = /
|
13
|
+
#DBPath = /var/lib/pacman/
|
14
|
+
#CacheDir = /var/cache/pacman/pkg/
|
15
|
+
#LogFile = /var/log/pacman.log
|
16
|
+
#GPGDir = /etc/pacman.d/gnupg/
|
17
|
+
HoldPkg = pacman glibc
|
18
|
+
#XferCommand = /usr/bin/curl -C - -f %u > %o
|
19
|
+
#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
|
20
|
+
#CleanMethod = KeepInstalled
|
21
|
+
#UseDelta = 0.7
|
22
|
+
Architecture = auto
|
23
|
+
|
24
|
+
# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
|
25
|
+
#IgnorePkg =
|
26
|
+
#IgnoreGroup =
|
27
|
+
|
28
|
+
#NoUpgrade =
|
29
|
+
#NoExtract =
|
30
|
+
|
31
|
+
# Misc options
|
32
|
+
#UseSyslog
|
33
|
+
#Color
|
34
|
+
#TotalDownload
|
35
|
+
CheckSpace
|
36
|
+
#VerbosePkgLists
|
37
|
+
|
38
|
+
# By default, pacman accepts packages signed by keys that its local keyring
|
39
|
+
# trusts (see pacman-key and its man page), as well as unsigned packages.
|
40
|
+
SigLevel = Required DatabaseOptional
|
41
|
+
LocalFileSigLevel = Optional
|
42
|
+
#RemoteFileSigLevel = Required
|
43
|
+
|
44
|
+
# NOTE: You must run `pacman-key --init` before first using pacman; the local
|
45
|
+
# keyring can then be populated with the keys of all official Arch Linux
|
46
|
+
# packagers with `pacman-key --populate archlinux`.
|
47
|
+
|
48
|
+
#
|
49
|
+
# REPOSITORIES
|
50
|
+
# - can be defined here or included from another file
|
51
|
+
# - pacman will search repositories in the order defined here
|
52
|
+
# - local/custom mirrors can be added here or in separate files
|
53
|
+
# - repositories listed first will take precedence when packages
|
54
|
+
# have identical names, regardless of version number
|
55
|
+
# - URLs will have $repo replaced by the name of the current repo
|
56
|
+
# - URLs will have $arch replaced by the name of the architecture
|
57
|
+
#
|
58
|
+
# Repository entries are of the format:
|
59
|
+
# [repo-name]
|
60
|
+
# Server = ServerName
|
61
|
+
# Include = IncludePath
|
62
|
+
#
|
63
|
+
# The header [repo-name] is crucial - it must be present and
|
64
|
+
# uncommented to enable the repo.
|
65
|
+
#
|
66
|
+
|
67
|
+
# The testing repositories are disabled by default. To enable, uncomment the
|
68
|
+
# repo name header and Include lines. You can add preferred servers immediately
|
69
|
+
# after the header, and they will be used before the default mirrors.
|
70
|
+
|
71
|
+
#[testing]
|
72
|
+
#Include = /etc/pacman.d/mirrorlist
|
73
|
+
|
74
|
+
[core]
|
75
|
+
Include = /etc/pacman.d/mirrorlist
|
76
|
+
|
77
|
+
[extra]
|
78
|
+
Include = /etc/pacman.d/mirrorlist
|
79
|
+
|
80
|
+
#[community-testing]
|
81
|
+
#Include = /etc/pacman.d/mirrorlist
|
82
|
+
|
83
|
+
[community]
|
84
|
+
Include = /etc/pacman.d/mirrorlist
|
85
|
+
|
86
|
+
# If you want to run 32 bit applications on your x86_64 system,
|
87
|
+
# enable the multilib repositories as required here.
|
88
|
+
|
89
|
+
#[multilib-testing]
|
90
|
+
#Include = /etc/pacman.d/mirrorlist
|
91
|
+
|
92
|
+
#[multilib]
|
93
|
+
#Include = /etc/pacman.d/mirrorlist
|
94
|
+
|
95
|
+
# An example of a custom package repository. See the pacman manpage for
|
96
|
+
# tips on creating your own repositories.
|
97
|
+
#[custom]
|
98
|
+
#SigLevel = Optional TrustAll
|
99
|
+
#Server = file:///home/custompkgs
|
data/lib/akabei/repository.rb
CHANGED
@@ -9,9 +9,10 @@ module Akabei
|
|
9
9
|
class Repository
|
10
10
|
attr_accessor :signer, :include_files
|
11
11
|
|
12
|
-
def initialize
|
12
|
+
def initialize(opts = {})
|
13
13
|
@db = {}
|
14
|
-
@include_files = false
|
14
|
+
@include_files = opts[:include_files] || false
|
15
|
+
@signer = opts[:signer]
|
15
16
|
end
|
16
17
|
|
17
18
|
extend Forwardable
|
@@ -55,8 +56,8 @@ module Akabei
|
|
55
56
|
nil
|
56
57
|
end
|
57
58
|
|
58
|
-
def self.load(path)
|
59
|
-
new.tap do |repo|
|
59
|
+
def self.load(path, opts = {})
|
60
|
+
new(opts).tap do |repo|
|
60
61
|
repo.load(path)
|
61
62
|
end
|
62
63
|
end
|
data/lib/akabei/signer.rb
CHANGED
@@ -36,9 +36,13 @@ module Akabei
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
39
|
+
def self.get(gpg_key, crypto = nil)
|
40
|
+
gpg_key && new(gpg_key, crypto)
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(gpg_key, crypto = nil)
|
40
44
|
@gpg_key = find_secret_key(gpg_key)
|
41
|
-
@crypto = crypto
|
45
|
+
@crypto = crypto || GPGME::Crypto.new
|
42
46
|
end
|
43
47
|
|
44
48
|
def detach_sign(path)
|
data/lib/akabei/version.rb
CHANGED
data/spec/akabei/builder_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'akabei/builder'
|
|
3
3
|
require 'akabei/signer'
|
4
4
|
|
5
5
|
describe Akabei::Builder do
|
6
|
-
let(:builder) { described_class.new }
|
6
|
+
let(:builder) { described_class.new(pkgdest: pkgdest) }
|
7
7
|
let(:pkgdest) { test_dest('packages').tap(&:mkpath) }
|
8
8
|
let(:package_dir) { test_dest('nkf').tap(&:mkpath) }
|
9
9
|
let(:pkgname) { 'nkf-2.1.3-1' }
|
@@ -14,10 +14,6 @@ describe Akabei::Builder do
|
|
14
14
|
let(:log_package_fname) { "#{pkgname}-#{arch}-package.log" }
|
15
15
|
let(:srcpkg_fname) { "#{pkgname}.src.tar.gz" }
|
16
16
|
|
17
|
-
before do
|
18
|
-
builder.pkgdest = pkgdest
|
19
|
-
end
|
20
|
-
|
21
17
|
describe '#build_package' do
|
22
18
|
let(:chroot) { double('ChrootTree') }
|
23
19
|
|
data/spec/akabei/cli_spec.rb
CHANGED
@@ -2,4 +2,139 @@ require 'spec_helper'
|
|
2
2
|
require 'akabei/cli'
|
3
3
|
|
4
4
|
describe Akabei::CLI do
|
5
|
+
let(:cli) { described_class.new }
|
6
|
+
let(:package_dir) { test_dest('nkf') }
|
7
|
+
let(:srcpkg_path) { test_input('nkf.tar.gz') }
|
8
|
+
let(:package_path) { test_input('nkf-2.1.3-1-x86_64.pkg.tar.xz') }
|
9
|
+
|
10
|
+
describe '#build' do
|
11
|
+
let(:repo_dir) { test_dest('repo').tap(&:mkpath) }
|
12
|
+
let(:base_opts) { { arch: 'x86_64', repo_dir: repo_dir.to_s, repo_name: 'test' } }
|
13
|
+
let(:package) { double('built package') }
|
14
|
+
let(:entry) { Akabei::PackageEntry.new }
|
15
|
+
let(:chroot_expectations) { lambda { |chroot| } }
|
16
|
+
|
17
|
+
before do
|
18
|
+
tar('xf', test_input('nkf.tar.gz').to_s, '-C', package_dir.parent.to_s)
|
19
|
+
# Disable warning
|
20
|
+
entry.add('files', 'usr/bin/nkf')
|
21
|
+
|
22
|
+
allow(package).to receive(:db_name).and_return('nkf-2.1.3-1')
|
23
|
+
allow(package).to receive(:to_entry).and_return(entry)
|
24
|
+
|
25
|
+
allow_any_instance_of(Akabei::ChrootTree).to receive(:with_chroot) { |chroot, &block|
|
26
|
+
chroot_expectations.call(chroot)
|
27
|
+
block.call
|
28
|
+
}
|
29
|
+
allow_any_instance_of(Akabei::Builder).to receive(:build_package) { |builder, dir, chroot|
|
30
|
+
expect(builder).to receive(:with_source_package).with(package_dir.to_s).and_yield(srcpkg_path)
|
31
|
+
[package]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'calls Builder#build_package in chroot and create repository database' do
|
36
|
+
cli.invoke(:build, [package_dir.to_s], base_opts)
|
37
|
+
expect(repo_dir.join('test.db')).to be_file
|
38
|
+
expect(repo_dir.join('test.files')).to be_file
|
39
|
+
expect(repo_dir.join('test.abs.tar.gz')).to be_file
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'with --makepkg-config' do
|
43
|
+
let(:makepkg_path) { test_input('makepkg.x86_64.conf') }
|
44
|
+
let(:chroot_expectations) {
|
45
|
+
lambda do |chroot|
|
46
|
+
expect(chroot.makepkg_config).to eq(makepkg_path)
|
47
|
+
end
|
48
|
+
}
|
49
|
+
|
50
|
+
it 'calls ChrootTree#with_chroot with makepkg_config' do
|
51
|
+
cli.invoke(:build, [package_dir.to_s], base_opts.merge(makepkg_config: makepkg_path.to_s))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with --pacman-config' do
|
56
|
+
let(:pacman_path) { test_input('pacman.x86_64.conf') }
|
57
|
+
let(:chroot_expectations) {
|
58
|
+
lambda do |chroot|
|
59
|
+
expect(chroot.pacman_config).to eq(pacman_path)
|
60
|
+
end
|
61
|
+
}
|
62
|
+
|
63
|
+
it 'calls ChrootTree#with_chroot with pacman_config' do
|
64
|
+
cli.invoke(:build, [package_dir.to_s], base_opts.merge(pacman_config: pacman_path.to_s))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#abs_add' do
|
70
|
+
let(:repo_name) { 'test' }
|
71
|
+
let(:abs_path) { test_dest('abs.tar.gz') }
|
72
|
+
|
73
|
+
it 'creates abs tarball' do
|
74
|
+
allow_any_instance_of(Akabei::Builder).to receive(:with_source_package).with(package_dir.to_s).and_yield(srcpkg_path)
|
75
|
+
cli.invoke(:abs_add, [package_dir.to_s, abs_path.to_s], repo_name: repo_name)
|
76
|
+
expect(abs_path).to be_file
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe '#abs_remove' do
|
81
|
+
let(:repo_name) { 'test' }
|
82
|
+
let(:abs_path) { test_dest('abs.tar.gz') }
|
83
|
+
|
84
|
+
before do
|
85
|
+
FileUtils.cp(test_input('abs.tar.gz'), abs_path)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'removes package from abs tarball' do
|
89
|
+
cli.invoke(:abs_remove, ['htop-vi', abs_path.to_s], repo_name: repo_name)
|
90
|
+
expect(abs_path).to be_file
|
91
|
+
expect(tar('tf', abs_path.to_s)).to_not include('test/htop-vi/PKGBUILD')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '#repo_add' do
|
96
|
+
let(:db_path) { test_dest('test.db') }
|
97
|
+
|
98
|
+
it 'creates repository database' do
|
99
|
+
cli.invoke(:repo_add, [package_path.to_s, db_path.to_s])
|
100
|
+
expect(db_path).to be_file
|
101
|
+
expect(tar('tf', db_path.to_s)).to include('nkf-2.1.3-1/desc')
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '#repo_remove' do
|
106
|
+
let(:db_path) { test_dest('test.db') }
|
107
|
+
|
108
|
+
before do
|
109
|
+
FileUtils.cp(test_input('test.db'), db_path)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'removes package from repository database' do
|
113
|
+
cli.invoke(:repo_remove, ['htop-vi', db_path.to_s])
|
114
|
+
expect(tar('tf', db_path.to_s)).to_not include('htop-vi-1.0.2-4/desc')
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#files_add' do
|
119
|
+
let(:files_path) { test_dest('test.files') }
|
120
|
+
|
121
|
+
it 'creates files database' do
|
122
|
+
cli.invoke(:files_add, [package_path.to_s, files_path.to_s])
|
123
|
+
expect(files_path).to be_file
|
124
|
+
expect(tar('tf', files_path.to_s)).to include('nkf-2.1.3-1/files')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe '#files_remove' do
|
129
|
+
let(:files_path) { test_dest('test.files') }
|
130
|
+
|
131
|
+
before do
|
132
|
+
FileUtils.cp(test_input('test.files'), files_path)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'removes package from files database' do
|
136
|
+
cli.invoke(:files_remove, ['htop-vi', files_path.to_s])
|
137
|
+
expect(tar('tf', files_path.to_s)).to_not include('htop-vi-1.0.2-4/files')
|
138
|
+
end
|
139
|
+
end
|
5
140
|
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'akabei/cli'
|
3
|
+
|
4
|
+
class TestShell < Thor::Shell::Basic
|
5
|
+
attr_reader :stdout
|
6
|
+
|
7
|
+
def initialize(stdout)
|
8
|
+
@stdout = stdout
|
9
|
+
super()
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Akabei::Omakase::CLI do
|
14
|
+
let(:stdout) { StringIO.new }
|
15
|
+
let(:cli) { described_class.new }
|
16
|
+
|
17
|
+
before do
|
18
|
+
cli.shell = TestShell.new(stdout)
|
19
|
+
end
|
20
|
+
|
21
|
+
around do |example|
|
22
|
+
Dir.mktmpdir do |dir|
|
23
|
+
cli.inside(dir) do
|
24
|
+
Dir.chdir(dir) do
|
25
|
+
example.run
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#init' do
|
32
|
+
it 'creates template directories' do
|
33
|
+
cli.invoke(:init, ['test'])
|
34
|
+
here = Pathname.new('.')
|
35
|
+
expect(here.join('.akabei.yml')).to be_file
|
36
|
+
expect(here.join('test')).to be_directory
|
37
|
+
expect(here.join('sources')).to be_directory
|
38
|
+
expect(here.join('logs')).to be_directory
|
39
|
+
expect(here.join('PKGBUILDs')).to be_directory
|
40
|
+
%w[i686 x86_64].each do |arch|
|
41
|
+
%w[makepkg pacman].each do |conf|
|
42
|
+
expect(here.join('etc', "#{conf}.#{arch}.conf")).to be_file
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'creates valid config' do
|
48
|
+
cli.invoke(:init, ['test'])
|
49
|
+
config = Akabei::Omakase::Config.load
|
50
|
+
expect { config.validate! }.to_not raise_error
|
51
|
+
expect(config.name).to eq('test')
|
52
|
+
expect(config.srcdest).to be_directory
|
53
|
+
expect(config.logdest).to be_directory
|
54
|
+
expect(config.pkgbuild).to be_directory
|
55
|
+
config.builds.each do |arch, config_file|
|
56
|
+
expect(Pathname.new(config_file['makepkg'])).to be_file
|
57
|
+
expect(Pathname.new(config_file['pacman'])).to be_file
|
58
|
+
end
|
59
|
+
expect(config['s3']).to be_nil
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'with --s3' do
|
63
|
+
it 'creates config with s3' do
|
64
|
+
cli.invoke(:init, ['test'], s3: true)
|
65
|
+
config = Akabei::Omakase::Config.load
|
66
|
+
expect { config.validate! }.to_not raise_error
|
67
|
+
expect(config['s3']).to_not be_nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '#build' do
|
73
|
+
let(:config) { Akabei::Omakase::Config.load }
|
74
|
+
let(:packages) { { 'i686' => double('built package (i686)'), 'x86_64' => double('built package (x86_64)') } }
|
75
|
+
let(:entry) { Akabei::PackageEntry.new }
|
76
|
+
let(:init_opts) { {} }
|
77
|
+
|
78
|
+
before do
|
79
|
+
cli.invoke(:init, ['test'], init_opts)
|
80
|
+
tar('xf', test_input('nkf.tar.gz').to_s, '-C', config.pkgbuild.to_s)
|
81
|
+
|
82
|
+
packages.each do |arch, package|
|
83
|
+
allow(package).to receive(:db_name).and_return('nkf-2.1.3-1')
|
84
|
+
allow(package).to receive(:to_entry).and_return(entry)
|
85
|
+
allow(package).to receive(:path).and_return(Pathname.new("test/os/#{arch}/nkf-2.1.3-1-#{arch}.pkg.tar.xz"))
|
86
|
+
end
|
87
|
+
entry.add('files', 'usr/bin/nkf')
|
88
|
+
|
89
|
+
allow_any_instance_of(Akabei::ChrootTree).to receive(:with_chroot) { |chroot, &block|
|
90
|
+
expect(chroot.makepkg_config.to_s).to eq("etc/makepkg.#{chroot.arch}.conf")
|
91
|
+
expect(chroot.pacman_config.to_s).to eq("etc/pacman.#{chroot.arch}.conf")
|
92
|
+
block.call
|
93
|
+
}
|
94
|
+
expect(config['builds'].size).to eq(2)
|
95
|
+
|
96
|
+
allow_any_instance_of(Akabei::Builder).to receive(:build_package) { |builder, dir, chroot|
|
97
|
+
expect(builder).to receive(:with_source_package).with(config.package_dir('nkf')).and_yield(test_input('nkf.tar.gz'))
|
98
|
+
[packages[chroot.arch]]
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'builds a package and add it to repository' do
|
103
|
+
cli.invoke(:build, ['nkf'])
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'with --s3' do
|
107
|
+
let(:init_opts) { { s3: true } }
|
108
|
+
let(:access_key_id) { 'ACCESS/KEY' }
|
109
|
+
let(:secret_access_key) { 'SECRET/ACCESS/KEY' }
|
110
|
+
let(:bucket_name) { 'test.bucket.name' }
|
111
|
+
let(:region) { 'ap-northeast-1' }
|
112
|
+
|
113
|
+
let(:buckets) { double('S3::BucketCollection') }
|
114
|
+
let(:bucket) { double('S3::Bucket') }
|
115
|
+
let(:objects) { double('S3::ObjectCollection') }
|
116
|
+
let(:write_options) { { reduced_redundancy: true } }
|
117
|
+
|
118
|
+
before do
|
119
|
+
c = SafeYAML.load_file('.akabei.yml')
|
120
|
+
c['s3']['access_key_id'] = access_key_id
|
121
|
+
c['s3']['secret_access_key'] = secret_access_key
|
122
|
+
c['s3']['bucket'] = bucket_name
|
123
|
+
c['s3']['region'] = region
|
124
|
+
c['s3']['write_options'] = write_options
|
125
|
+
open('.akabei.yml', 'w') { |f| YAML.dump(c, f) }
|
126
|
+
|
127
|
+
allow_any_instance_of(AWS::S3).to receive(:buckets).and_return(buckets)
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'uploads built packages and update repositories' do
|
131
|
+
expect(buckets).to receive(:[]).with(bucket_name).and_return(bucket)
|
132
|
+
allow(bucket).to receive(:objects).and_return(objects)
|
133
|
+
|
134
|
+
%w[i686 x86_64].each do |arch|
|
135
|
+
%w[test.db test.files test.abs.tar.gz].each do |fname|
|
136
|
+
obj = double("S3::Object #{fname}")
|
137
|
+
# download and upload
|
138
|
+
expect(objects).to receive(:[]).with("test/os/#{arch}/#{fname}").twice.and_return(obj)
|
139
|
+
expect(obj).to receive(:read).and_yield('')
|
140
|
+
expect(obj).to receive(:write)
|
141
|
+
end
|
142
|
+
|
143
|
+
# upload only
|
144
|
+
pkg = double("S3::Object built package (#{arch})")
|
145
|
+
db_name = "nkf-2.1.3-1-#{arch}.pkg.tar.xz"
|
146
|
+
expect(objects).to receive(:[]).with("test/os/#{arch}/#{db_name}").and_return(pkg)
|
147
|
+
expect(pkg).to receive(:write).with(anything, hash_including(write_options))
|
148
|
+
end
|
149
|
+
|
150
|
+
cli.invoke(:build, ['nkf'])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|