binpkgbot 0.1.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 +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +161 -0
- data/Rakefile +6 -0
- data/bin/binpkgbot +4 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/binpkgbot.gemspec +26 -0
- data/lib/binpkgbot.rb +2 -0
- data/lib/binpkgbot/cli.rb +59 -0
- data/lib/binpkgbot/config.rb +58 -0
- data/lib/binpkgbot/container.rb +111 -0
- data/lib/binpkgbot/emerge_runner.rb +137 -0
- data/lib/binpkgbot/tasks.rb +42 -0
- data/lib/binpkgbot/tasks/base.rb +23 -0
- data/lib/binpkgbot/tasks/concern/emerge.rb +26 -0
- data/lib/binpkgbot/tasks/include.rb +22 -0
- data/lib/binpkgbot/tasks/install.rb +19 -0
- data/lib/binpkgbot/tasks/run.rb +28 -0
- data/lib/binpkgbot/tasks/upgrade.rb +19 -0
- data/lib/binpkgbot/utils.rb +14 -0
- data/lib/binpkgbot/version.rb +3 -0
- data/share/libexec/modify-etc-portage +51 -0
- data/share/libexec/modify-etc-portage.sh +29 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f2648e9c2366abff1f8551eb3c8f7c112f577345
|
4
|
+
data.tar.gz: 2e4e135f112ac49dd4d9427d765c902061f362cf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3407003e296eb9a3be40cfe9b295d2c0e5a128978b55f81009af033e6eff9f8dd212fc8fe8dbc011d772adac7e1e22dda3e00c2629e9d8537c03a3c1e5603da4
|
7
|
+
data.tar.gz: 721140a0c13b88a03a50f018adf074aa7de6b4d9316185a9a83242a6431c1c26cc484fab5d397b8eadc3845025532151670c03b478529db9c8fbb4088f0066d9
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Sorah Fukumori
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
# binpkgbot: Robot builds Gentoo binpkg in clean environment, automatically and continuously.
|
2
|
+
|
3
|
+
When you manage many Gentoo servers, I guess you're tired to run `emerge -uDN @world` and wait for all servers, or to install same packages in your all servers. Portage has binpkg feature, which allows to re-use prebuilt binaries when `emerge`ing packages.
|
4
|
+
|
5
|
+
Binpkgbot allows you to build binpkg in clean environment, automatically and continuously! Continuous building of the latest packages also allows you to run `-uDN` anytime you want.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- Build in clean environment
|
10
|
+
- Runs emerge on clean stage3 using systemd-nspawn every run
|
11
|
+
- Fetch and install existing binpkg of build dependencies
|
12
|
+
- Builds binpkg of new dependencies
|
13
|
+
- 2 build modes:
|
14
|
+
- Attempt to upgrade existing stage3 directory
|
15
|
+
- Just install additional packages
|
16
|
+
|
17
|
+
## Prerequisites
|
18
|
+
|
19
|
+
- systemd based Linux box
|
20
|
+
- systemd-nspawn
|
21
|
+
- with btrfs filesystem
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
$ gem install binpkgbot
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
### Set up
|
30
|
+
|
31
|
+
1. Prepare stage3 (or 4) directory (e.g. `/mnt/vol/stage`)
|
32
|
+
- binpkgbot adds modification when running upgrades. Otherwise it's used as base of ephemeral container
|
33
|
+
- this directory should be in a btrfs filesystem
|
34
|
+
2. `/etc/portage` directory outside of (1) (e.g. `/mnt/vol/etc-portage`)
|
35
|
+
- binpkgbot always copies the entire directory into a container
|
36
|
+
3. gentoo portage repository (e.g. `/usr/portage`)
|
37
|
+
- binpkgbot syncs the specified directory; if this behavior is acceptable, you can specify `/usr/portage`.
|
38
|
+
|
39
|
+
then write some yml:
|
40
|
+
|
41
|
+
``` yaml
|
42
|
+
# binpkgbot.yml
|
43
|
+
stage: /mnt/vol/stage
|
44
|
+
etc_portage: /mnt/vol/etc-portage
|
45
|
+
portage_repo: /usr/portage
|
46
|
+
|
47
|
+
# use_sudo_for_nspawn: true
|
48
|
+
|
49
|
+
# emerge_options:
|
50
|
+
# - '-v'
|
51
|
+
# config_protect_mask: false
|
52
|
+
|
53
|
+
## bind mounts for a container
|
54
|
+
# binds:
|
55
|
+
# - /opt/my-overlay # default to read-only
|
56
|
+
# - rw: /tmp/something_writable # or 'ro:'
|
57
|
+
|
58
|
+
# - from: /mnt/vol/packages
|
59
|
+
# to: /usr/portage/packages
|
60
|
+
# writable: true
|
61
|
+
# - from: /mnt/vol/elog
|
62
|
+
# to: /var/log/portage/elog
|
63
|
+
# writable: true
|
64
|
+
|
65
|
+
tasks:
|
66
|
+
# Upgrade the stage (-uDN)
|
67
|
+
- upgrade: '@world' # emerge -uDN @world
|
68
|
+
- install:
|
69
|
+
atom: '@preserve-rebuild'
|
70
|
+
persist: true # Run sd-nspawn without --ephemeral option, default to false
|
71
|
+
|
72
|
+
|
73
|
+
# Simple way
|
74
|
+
- install: 'sys-apps/dstat'
|
75
|
+
# Complex way
|
76
|
+
- install:
|
77
|
+
atom: 'media-apps/ffmpeg'
|
78
|
+
use: x265
|
79
|
+
## or
|
80
|
+
# use:
|
81
|
+
# - x265
|
82
|
+
# # to other packages
|
83
|
+
# - media-libs/x265 numa
|
84
|
+
accept_keywords: true # default to ~*
|
85
|
+
# accept_keywords:
|
86
|
+
# - true
|
87
|
+
# # - "~amd64"
|
88
|
+
# ## to other packages
|
89
|
+
# - "media-libs/x265 ~amd64"
|
90
|
+
# - media-libs/x265 # or default: ~*
|
91
|
+
unmask: true # unmask a specified atom itself
|
92
|
+
# unmask:
|
93
|
+
# - true
|
94
|
+
# - media-libs/x265
|
95
|
+
# mask:
|
96
|
+
# - media-libs/x264
|
97
|
+
|
98
|
+
- include: ./task.yml # use file instead
|
99
|
+
- include: ./task.d/* # or glob files and run all of them (order by filename)
|
100
|
+
|
101
|
+
## run something inside or outside a container
|
102
|
+
# - run:
|
103
|
+
# ## run outside of container? (default to false)
|
104
|
+
# # host: true
|
105
|
+
# ## persist change in a container (default to true)
|
106
|
+
# # persist: false
|
107
|
+
# script:
|
108
|
+
# - emerge-webrsync
|
109
|
+
```
|
110
|
+
|
111
|
+
```
|
112
|
+
# ./task.yml
|
113
|
+
- install: ...
|
114
|
+
```
|
115
|
+
|
116
|
+
### Running
|
117
|
+
|
118
|
+
```
|
119
|
+
$ binpkgbot
|
120
|
+
$ binpkgbot -c path/to/binpkgbot.yml
|
121
|
+
|
122
|
+
$ binpkgbot --help
|
123
|
+
$ binpkgbot --version
|
124
|
+
```
|
125
|
+
|
126
|
+
## Recommended practices
|
127
|
+
|
128
|
+
- Turn on `binpkg-multi-instance` and `getbinpkg` in `$FEATURES`
|
129
|
+
- These are optional. Put them by yourself into `make.conf`.
|
130
|
+
- Maintain different `/etc/portage` directory then put all common use flags, keywords, and unmaskings in the directory.
|
131
|
+
- minimize USE flag difference. Use `use:` `accept_keywords:` `unmask:` for a case needs to make a variant of builds.
|
132
|
+
|
133
|
+
(e.g. `app-analyzer/zabbix` has a flag to build `server` or go only with `agent`. Server needs both, but most servers are okay with having `agent` only. Then we need 2 variants in `zabbix` binpkg, so use `use:` configuration to build variants.)
|
134
|
+
|
135
|
+
``` yaml
|
136
|
+
- install:
|
137
|
+
atom: app-analyzer/zabbix
|
138
|
+
use:
|
139
|
+
- agent
|
140
|
+
- server
|
141
|
+
- install:
|
142
|
+
atom: app-analyzer/zabbix
|
143
|
+
use:
|
144
|
+
- agent
|
145
|
+
```
|
146
|
+
|
147
|
+
## Development
|
148
|
+
|
149
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
150
|
+
|
151
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
152
|
+
|
153
|
+
## Contributing
|
154
|
+
|
155
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sorah/binpkgbot.
|
156
|
+
|
157
|
+
|
158
|
+
## License
|
159
|
+
|
160
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
161
|
+
|
data/Rakefile
ADDED
data/bin/binpkgbot
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "binpkgbot"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/binpkgbot.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'binpkgbot/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "binpkgbot"
|
8
|
+
spec.version = Binpkgbot::VERSION
|
9
|
+
spec.authors = ["Sorah Fukumori"]
|
10
|
+
spec.email = ["her@sorah.jp"]
|
11
|
+
|
12
|
+
spec.summary = %q{Your robot to build Gentoo binpkgs in clean environment, continously}
|
13
|
+
spec.homepage = "https://github.com/sorah/binpkgbot"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
26
|
+
end
|
data/lib/binpkgbot.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'binpkgbot/config'
|
3
|
+
require 'binpkgbot/version'
|
4
|
+
|
5
|
+
module Binpkgbot
|
6
|
+
class Cli
|
7
|
+
def initialize(argv)
|
8
|
+
@argv = argv.dup
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
optparse.parse!(@argv)
|
13
|
+
case options[:mode]
|
14
|
+
when :version
|
15
|
+
do_version
|
16
|
+
when :run
|
17
|
+
do_run
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def do_version
|
22
|
+
puts "binpkgbot #{Binpkgbot::VERSION}"
|
23
|
+
0
|
24
|
+
end
|
25
|
+
|
26
|
+
def do_run
|
27
|
+
config.tasks.each do |task|
|
28
|
+
task.execute
|
29
|
+
end
|
30
|
+
0
|
31
|
+
end
|
32
|
+
|
33
|
+
def options
|
34
|
+
@options ||= {
|
35
|
+
config: nil,
|
36
|
+
mode: :run,
|
37
|
+
debug: false,
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def optparse
|
42
|
+
@optparse ||= OptionParser.new do |opt|
|
43
|
+
opt.on('-v', '--version') { options[:mode] = :version }
|
44
|
+
|
45
|
+
opt.on('-c PATH', '--config PATH', 'config file to use (default: ./binpkgbot.yml)') do |file|
|
46
|
+
options[:config] = file
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def config_path
|
52
|
+
options[:config] || './binpkgbot.yml'
|
53
|
+
end
|
54
|
+
|
55
|
+
def config
|
56
|
+
@config ||= Config.load_yaml(config_path)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'binpkgbot/utils'
|
2
|
+
require 'binpkgbot/tasks'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module Binpkgbot
|
6
|
+
class Config
|
7
|
+
def self.from_yaml(yaml)
|
8
|
+
new(YAML.load(yaml))
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.load_yaml(path)
|
12
|
+
new(YAML.load_file(path))
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(doc={})
|
16
|
+
@doc = Utils.symbolize_keys(doc)
|
17
|
+
end
|
18
|
+
|
19
|
+
def stage
|
20
|
+
@doc[:stage]
|
21
|
+
end
|
22
|
+
|
23
|
+
def etc_portage
|
24
|
+
@doc[:etc_portage]
|
25
|
+
end
|
26
|
+
|
27
|
+
def portage_repo
|
28
|
+
@doc[:portage_repo]
|
29
|
+
end
|
30
|
+
|
31
|
+
def emerge_options
|
32
|
+
@doc[:emerge_options]
|
33
|
+
end
|
34
|
+
|
35
|
+
def binds
|
36
|
+
@doc[:binds]
|
37
|
+
end
|
38
|
+
|
39
|
+
def tasks
|
40
|
+
(@doc[:tasks] || []).map do |defi|
|
41
|
+
Tasks.from_definition(defi, config: self)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def config_protect_mask?
|
46
|
+
@doc.key?(:config_protect_mask)
|
47
|
+
end
|
48
|
+
|
49
|
+
def config_protect_mask
|
50
|
+
@doc[:config_protect_mask]
|
51
|
+
end
|
52
|
+
|
53
|
+
def use_sudo_for_nspawn?
|
54
|
+
@doc.fetch(:use_sudo_for_nspawn, false)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'binpkgbot/utils'
|
2
|
+
require 'tmpdir'
|
3
|
+
require 'shellwords'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
module Binpkgbot
|
7
|
+
class Container
|
8
|
+
class ContainerFailure < StandardError; end
|
9
|
+
|
10
|
+
COPY_TMPDIR = "/.binpkgbot.copies"
|
11
|
+
WORKDIR = "/.binpkgbot.work"
|
12
|
+
SHAREDIR = "/.binpkgbot.share"
|
13
|
+
|
14
|
+
SHAREDIR_SRC = File.expand_path("../../share", __dir__)
|
15
|
+
def initialize(directory, ephemeral: true, binds: [], copies: [], env: {}, script:, config: nil)
|
16
|
+
@directory = directory
|
17
|
+
@ephemeral = ephemeral
|
18
|
+
@env = env
|
19
|
+
@binds = normalize_binds(binds)
|
20
|
+
@copies = normalize_copies(copies)
|
21
|
+
@script = script
|
22
|
+
@config = config
|
23
|
+
end
|
24
|
+
|
25
|
+
def workdir
|
26
|
+
@workdir ||= Pathname.new(Dir.mktmpdir)
|
27
|
+
end
|
28
|
+
|
29
|
+
def script
|
30
|
+
parts = ['set -e']
|
31
|
+
@copies.each do |copy|
|
32
|
+
parts.push <<-EOF
|
33
|
+
if [ -e #{copy[:to].shellescape} ]; then
|
34
|
+
rm -rf #{copy[:to].shellescape}
|
35
|
+
fi
|
36
|
+
cp -pr #{COPY_TMPDIR}-#{copy[:id]} #{copy[:to].shellescape}
|
37
|
+
EOF
|
38
|
+
end
|
39
|
+
parts.push @env.map { |k, v| "export #{k}=#{v.shellescape}" }.join("\n")
|
40
|
+
parts << @script
|
41
|
+
parts.join("\n\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
def binds
|
45
|
+
@binds + \
|
46
|
+
(@config&.binds || []) + \
|
47
|
+
@copies.map { |copy| {from: copy[:from], to: "/#{COPY_TMPDIR}-#{copy[:id]}", writable: false} } + \
|
48
|
+
[
|
49
|
+
{from: workdir, to: WORKDIR, writable: true},
|
50
|
+
{from: SHAREDIR_SRC, to: SHAREDIR, writable: true},
|
51
|
+
]
|
52
|
+
end
|
53
|
+
|
54
|
+
def command_line
|
55
|
+
[
|
56
|
+
@config.use_sudo_for_nspawn? ? 'sudo' : nil,
|
57
|
+
'systemd-nspawn',
|
58
|
+
"--directory=#{@directory}",
|
59
|
+
@ephemeral ? "--ephemeral" : nil,
|
60
|
+
binds.map { |_| "--bind#{_[:writable] ? nil : '-ro'}=#{_[:from]}:#{_[:to]}" },
|
61
|
+
'/bin/bash'
|
62
|
+
].flatten.compact
|
63
|
+
end
|
64
|
+
|
65
|
+
def run(error: true)
|
66
|
+
puts script.each_line.map.with_index { |_,i| _.strip.empty? ? nil : "#{(i.zero? ? "$ " : " ")}#{_.chomp}" }.compact.join(?\n)
|
67
|
+
r,w = IO.pipe
|
68
|
+
w.puts script
|
69
|
+
pid = spawn(*command_line, in: r)
|
70
|
+
r.close
|
71
|
+
w.close
|
72
|
+
_, status = Process.waitpid2(pid)
|
73
|
+
if error && !status.success?
|
74
|
+
raise ContainerFailure, "container failed #{status.inspect}, #{command_line.inspect}"
|
75
|
+
end
|
76
|
+
status
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def normalize_binds(binds)
|
82
|
+
Utils.symbolize_keys(binds || []).map do |bind|
|
83
|
+
case
|
84
|
+
when bind.kind_of?(String)
|
85
|
+
{from: bind, to: bind, writable: false}
|
86
|
+
when bind[:ro]
|
87
|
+
{from: bind[:ro], to: bind[:ro], writable: false}
|
88
|
+
when bind[:rw]
|
89
|
+
{from: bind[:rw], to: bind[:rw], writable: true}
|
90
|
+
when bind.kind_of?(Hash)
|
91
|
+
bind
|
92
|
+
else
|
93
|
+
raise ArgumentError, "Unknown --bind specification: #{bind.inspect}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def normalize_copies(copies)
|
99
|
+
Utils.symbolize_keys(copies || []).map.with_index do |copy, idx|
|
100
|
+
case
|
101
|
+
when copy.kind_of?(String)
|
102
|
+
{id: idx, from: copy, to: copy}
|
103
|
+
when copy.kind_of?(Hash)
|
104
|
+
copy.merge(id: idx)
|
105
|
+
else
|
106
|
+
raise ArgumentError, "Unknown copy specification: #{copy.inspect}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'binpkgbot/container'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module Binpkgbot
|
5
|
+
class EmergeRunner
|
6
|
+
def initialize(atom, *args, config:, ephemeral: true, use: [], accept_keywords: [], unmasks: [], masks: [], script: nil)
|
7
|
+
@atom = atom
|
8
|
+
@args = args
|
9
|
+
@config = config
|
10
|
+
@ephemeral = ephemeral
|
11
|
+
@use = use
|
12
|
+
@accept_keywords = accept_keywords
|
13
|
+
@unmasks = unmasks
|
14
|
+
@masks = masks
|
15
|
+
@script = script
|
16
|
+
end
|
17
|
+
|
18
|
+
def binds
|
19
|
+
[
|
20
|
+
{from: @config.portage_repo, to: '/usr/portage', writable: true},
|
21
|
+
*@config.binds
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
def copies
|
26
|
+
[
|
27
|
+
{from: @config.etc_portage, to: '/etc/portage'},
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
def env
|
32
|
+
{
|
33
|
+
FEATURES: "buildpkg",
|
34
|
+
PORTAGE_ELOG_CLASSES: 'log warn error qa',
|
35
|
+
PORTAGE_ELOG_SYSTEM: 'save save_summary',
|
36
|
+
CONFIG_PROTECT_MASK: @config.config_protect_mask? ? @config.config_protect_mask : '/etc',
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def script
|
41
|
+
@script || <<-EOF
|
42
|
+
set -x
|
43
|
+
/.binpkgbot.share/libexec/modify-etc-portage /.binpkgbot.work/package.use /etc/portage/package.use
|
44
|
+
/.binpkgbot.share/libexec/modify-etc-portage /.binpkgbot.work/package.accept_keywords /etc/portage/package.accept_keywords
|
45
|
+
/.binpkgbot.share/libexec/modify-etc-portage /.binpkgbot.work/package.unmasks /etc/portage/package.unmasks
|
46
|
+
/.binpkgbot.share/libexec/modify-etc-portage /.binpkgbot.work/package.masks /etc/portage/package.masks
|
47
|
+
emerge #{@config.emerge_options.shelljoin} #{@args.shelljoin} #{@atom.shellescape}
|
48
|
+
EOF
|
49
|
+
end
|
50
|
+
|
51
|
+
def container
|
52
|
+
@container ||= Container.new(
|
53
|
+
@config.stage,
|
54
|
+
ephemeral: @ephemeral,
|
55
|
+
binds: binds,
|
56
|
+
copies: copies,
|
57
|
+
env: env,
|
58
|
+
script: script,
|
59
|
+
config: @config,
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def prepare_etc_portage_overrides
|
64
|
+
{masks: @masks, unmasks: @unmasks}.each do |k,v|
|
65
|
+
file = container.workdir.join("package.#{k}")
|
66
|
+
content = []
|
67
|
+
[v].flatten.each do |x|
|
68
|
+
case x
|
69
|
+
when true
|
70
|
+
content << @atom
|
71
|
+
when false
|
72
|
+
# do nothing
|
73
|
+
when String
|
74
|
+
content << x
|
75
|
+
when nil
|
76
|
+
else
|
77
|
+
raise TypeError, "Invalid type for #{k.inspect} specification on #{@atom}, it should be true or false or string: #{x.inspect}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
File.write file, "#{content.join(?\n)}\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
begin
|
84
|
+
file = container.workdir.join("package.accept_keywords")
|
85
|
+
content = []
|
86
|
+
[@accept_keywords].flatten.each do |x|
|
87
|
+
case x
|
88
|
+
when true
|
89
|
+
content << "#{@atom} ~*"
|
90
|
+
when false
|
91
|
+
# do nothing
|
92
|
+
when String
|
93
|
+
if x.split(/\s+/, 2).size > 1
|
94
|
+
content << x
|
95
|
+
else
|
96
|
+
content << "#{x} ~*"
|
97
|
+
end
|
98
|
+
when nil
|
99
|
+
else
|
100
|
+
raise TypeError, "Invalid type for accept_keywords specification on #{@atom}, it should be true or false or string: #{x.inspect}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
File.write file, "#{content.join(?\n)}\n"
|
104
|
+
end
|
105
|
+
|
106
|
+
begin
|
107
|
+
file = container.workdir.join("package.use")
|
108
|
+
content = []
|
109
|
+
[@use].flatten.each do |x|
|
110
|
+
case x
|
111
|
+
when true
|
112
|
+
content << "#{@atom} ~*"
|
113
|
+
when false
|
114
|
+
# do nothing
|
115
|
+
when String
|
116
|
+
if x.split(/\s+/, 2).size > 1
|
117
|
+
content << x
|
118
|
+
else
|
119
|
+
content << "#{@atom} #{x}"
|
120
|
+
end
|
121
|
+
when nil
|
122
|
+
else
|
123
|
+
raise TypeError, "Invalid type for :use specification on #{@atom}, it should be true or false or string: #{x.inspect}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
File.write file, "#{content.join(?\n)}\n"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def run
|
131
|
+
prepare_etc_portage_overrides
|
132
|
+
container.run
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'binpkgbot/utils'
|
2
|
+
|
3
|
+
module Binpkgbot
|
4
|
+
module Tasks
|
5
|
+
def self.from_definition(defi, config: nil)
|
6
|
+
case defi
|
7
|
+
when String, Symbol
|
8
|
+
self.find(defi).new(config: config)
|
9
|
+
when Hash
|
10
|
+
raise ArgumentError, "task defiification should not have more than 2 keys when it's a Hash" if defi.size > 1
|
11
|
+
kind = defi.keys.first
|
12
|
+
options = defi.values.first
|
13
|
+
options = {name: options} unless options.kind_of?(Hash)
|
14
|
+
self.find(kind).new(config: config, **Utils.symbolize_keys(options))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.find(name)
|
19
|
+
const = Binpkgbot::Tasks
|
20
|
+
prefix = 'binpkgbot/tasks'
|
21
|
+
|
22
|
+
retried = false
|
23
|
+
constant_name = name.to_s.gsub(/\A.|_./) { |s| s[-1].upcase }
|
24
|
+
|
25
|
+
begin
|
26
|
+
const.const_get constant_name, false
|
27
|
+
rescue NameError
|
28
|
+
unless retried
|
29
|
+
begin
|
30
|
+
require "#{prefix}/#{name}"
|
31
|
+
rescue LoadError
|
32
|
+
end
|
33
|
+
|
34
|
+
retried = true
|
35
|
+
retry
|
36
|
+
end
|
37
|
+
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Binpkgbot
|
2
|
+
module Tasks
|
3
|
+
class Base
|
4
|
+
def initialize(config: nil, **options)
|
5
|
+
@config = config
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :config, :options
|
10
|
+
|
11
|
+
def execute
|
12
|
+
puts "==> #{self.class}: #{options.inspect}"
|
13
|
+
run
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def run
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'binpkgbot/emerge_runner'
|
2
|
+
|
3
|
+
module Binpkgbot
|
4
|
+
module Tasks
|
5
|
+
module Concern
|
6
|
+
module Emerge
|
7
|
+
def emerge_runner(script, **options)
|
8
|
+
emerge(nil, script: script, **options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def emerge(atom, *args, ephemeral: !@options[:persist], use: @options[:use], accept_keywords: @options[:accept_keywords], unmasks: @options[:unmasks], masks: @options[:masks], script: nil)
|
12
|
+
EmergeRunner.new(
|
13
|
+
atom, *args,
|
14
|
+
ephemeral: ephemeral,
|
15
|
+
use: use,
|
16
|
+
accept_keywords: accept_keywords,
|
17
|
+
unmasks: unmasks,
|
18
|
+
masks: masks,
|
19
|
+
config: @config,
|
20
|
+
script: script,
|
21
|
+
).run
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'binpkgbot/tasks/base'
|
2
|
+
require 'binpkgbot/tasks'
|
3
|
+
|
4
|
+
module Binpkgbot
|
5
|
+
module Tasks
|
6
|
+
class Include < Base
|
7
|
+
def run
|
8
|
+
name = @options[:name]
|
9
|
+
raise ArgumentError, "include task is missing :name -- what to include?" unless name
|
10
|
+
Dir.glob[name].sort.each do |path|
|
11
|
+
yaml = YAML.load_file(path)
|
12
|
+
if yaml.kind_of?(Array)
|
13
|
+
raise ArgumentError, "#{path} should be an array of task definitions"
|
14
|
+
end
|
15
|
+
yaml.each do |task_def|
|
16
|
+
Tasks.from_definition(task_def, config: @config).execute
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'binpkgbot/tasks/base'
|
2
|
+
require 'binpkgbot/tasks/concern/emerge'
|
3
|
+
|
4
|
+
module Binpkgbot
|
5
|
+
module Tasks
|
6
|
+
class Install < Base
|
7
|
+
include Concern::Emerge
|
8
|
+
|
9
|
+
def run
|
10
|
+
name = @options[:name] || @options[:atom]
|
11
|
+
unless name
|
12
|
+
raise ArgumentError, "install task is missing :name and :atom -- what to install?"
|
13
|
+
end
|
14
|
+
|
15
|
+
emerge(name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'binpkgbot/tasks/base'
|
2
|
+
require 'binpkgbot/tasks/concern/emerge'
|
3
|
+
|
4
|
+
module Binpkgbot
|
5
|
+
module Tasks
|
6
|
+
class Run < Base
|
7
|
+
include Concern::Emerge
|
8
|
+
|
9
|
+
def run
|
10
|
+
script = [*@options[:script],*@options[:scripts]].join("\n")
|
11
|
+
if @options[:host]
|
12
|
+
puts script.each_line.map.with_index { |_,i| "#{(i.zero? ? "$ " : " ")}#{_}" }.join(?\n)
|
13
|
+
r,w = IO.pipe
|
14
|
+
w.puts script
|
15
|
+
pid = spawn('bash', in: r)
|
16
|
+
r.close
|
17
|
+
w.close
|
18
|
+
_, status = Process.waitpid2(pid)
|
19
|
+
if !status.success?
|
20
|
+
raise "host run failed #{status.inspect}, #{command_line.inspect}"
|
21
|
+
end
|
22
|
+
else
|
23
|
+
emerge_runner(script, ephemeral: @options.key?(:persist) ? !@options[:persist] : false)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'binpkgbot/tasks/base'
|
2
|
+
require 'binpkgbot/tasks/concern/emerge'
|
3
|
+
|
4
|
+
module Binpkgbot
|
5
|
+
module Tasks
|
6
|
+
class Upgrade < Base
|
7
|
+
include Concern::Emerge
|
8
|
+
|
9
|
+
def run
|
10
|
+
name = @options[:name] || @options[:atom]
|
11
|
+
unless name
|
12
|
+
raise ArgumentError, "upgrade task is missing :name -- what to upgrade? e.g. @world"
|
13
|
+
end
|
14
|
+
|
15
|
+
emerge(name, '-uDN', ephemeral: @options.key?(:persist) ? !@options[:persist] : false)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Binpkgbot
|
2
|
+
module Utils
|
3
|
+
def self.symbolize_keys(obj)
|
4
|
+
case obj
|
5
|
+
when Hash
|
6
|
+
Hash[obj.map { |k, v| [k.is_a?(String) ? k.to_sym : k, symbolize_keys(v)] }]
|
7
|
+
when Array
|
8
|
+
obj.map { |v| symbolize_keys(v) }
|
9
|
+
else
|
10
|
+
obj
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/python3
|
2
|
+
import re
|
3
|
+
import sys
|
4
|
+
import os
|
5
|
+
import glob
|
6
|
+
|
7
|
+
if len(sys.argv) != 3:
|
8
|
+
print("Usage: modify-etc-portage file-to-add target", file=sys.stderr)
|
9
|
+
sys.exit(1)
|
10
|
+
|
11
|
+
source = sys.argv[1]
|
12
|
+
target = sys.argv[2]
|
13
|
+
|
14
|
+
if not os.path.exists(source):
|
15
|
+
sys.exit(0)
|
16
|
+
|
17
|
+
source_content = []
|
18
|
+
with open(source, 'r') as io:
|
19
|
+
for line in io:
|
20
|
+
if not re.match("^#", line):
|
21
|
+
source_content.append(re.split("\s+", line.rstrip()))
|
22
|
+
|
23
|
+
target_is_directory = os.path.isdir(target)
|
24
|
+
|
25
|
+
target_content = []
|
26
|
+
if target_is_directory:
|
27
|
+
paths = sorted(glob.glob(os.path.join(target, "*")))
|
28
|
+
for path in paths:
|
29
|
+
with open(path, 'r') as io:
|
30
|
+
for line in io:
|
31
|
+
if not re.match("^#", line):
|
32
|
+
target_content.append(re.split("\s+", line.rstrip()))
|
33
|
+
elif os.path.exists(target):
|
34
|
+
with open(target, 'r') as io:
|
35
|
+
for line in io:
|
36
|
+
if not re.match("^#", line):
|
37
|
+
target_content.append(re.split("\s+", line.rstrip()))
|
38
|
+
|
39
|
+
|
40
|
+
source_atoms = [x[0] for x in source_content]
|
41
|
+
filtered_target_content = [x for x in target_content if not x[0] in source_atoms]
|
42
|
+
|
43
|
+
new_content = filtered_target_content + source_content
|
44
|
+
new_content_str = "\n".join([" ".join(x) for x in new_content]) + "\n"
|
45
|
+
|
46
|
+
if target_is_directory:
|
47
|
+
os.rename(target, target + "." + str(os.getpid()))
|
48
|
+
|
49
|
+
with open(target, 'w') as io:
|
50
|
+
io.write(new_content_str)
|
51
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
if [ $# -lt 2 ]; then
|
3
|
+
echo "usage: $0 file-to-add target" 1>&2
|
4
|
+
exit 1
|
5
|
+
fi
|
6
|
+
set -e
|
7
|
+
|
8
|
+
file_to_add="$1"
|
9
|
+
target="$2"
|
10
|
+
|
11
|
+
if [ ! -e "${file_to_add}" ]; then
|
12
|
+
exit 0
|
13
|
+
fi
|
14
|
+
|
15
|
+
if [ -d "${target}" ]; then
|
16
|
+
target="${target}/binpkgbot"
|
17
|
+
fi
|
18
|
+
|
19
|
+
echo "modify-etc-portage: modifying ${target}"
|
20
|
+
|
21
|
+
temp="$(mktemp)"
|
22
|
+
if [ -f "${target}" ]; then
|
23
|
+
cat "${target}" > "${temp}"
|
24
|
+
echo >> "${temp}"
|
25
|
+
elif [ -d "${target}" ]; then
|
26
|
+
fi
|
27
|
+
cat "${file_to_add}" >> "${temp}"
|
28
|
+
|
29
|
+
mv "${temp}" "${target}"
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: binpkgbot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sorah Fukumori
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.13'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description:
|
56
|
+
email:
|
57
|
+
- her@sorah.jp
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".rspec"
|
64
|
+
- ".travis.yml"
|
65
|
+
- Gemfile
|
66
|
+
- LICENSE.txt
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- bin/binpkgbot
|
70
|
+
- bin/console
|
71
|
+
- bin/setup
|
72
|
+
- binpkgbot.gemspec
|
73
|
+
- lib/binpkgbot.rb
|
74
|
+
- lib/binpkgbot/cli.rb
|
75
|
+
- lib/binpkgbot/config.rb
|
76
|
+
- lib/binpkgbot/container.rb
|
77
|
+
- lib/binpkgbot/emerge_runner.rb
|
78
|
+
- lib/binpkgbot/tasks.rb
|
79
|
+
- lib/binpkgbot/tasks/base.rb
|
80
|
+
- lib/binpkgbot/tasks/concern/emerge.rb
|
81
|
+
- lib/binpkgbot/tasks/include.rb
|
82
|
+
- lib/binpkgbot/tasks/install.rb
|
83
|
+
- lib/binpkgbot/tasks/run.rb
|
84
|
+
- lib/binpkgbot/tasks/upgrade.rb
|
85
|
+
- lib/binpkgbot/utils.rb
|
86
|
+
- lib/binpkgbot/version.rb
|
87
|
+
- share/libexec/modify-etc-portage
|
88
|
+
- share/libexec/modify-etc-portage.sh
|
89
|
+
homepage: https://github.com/sorah/binpkgbot
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.6.8
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: Your robot to build Gentoo binpkgs in clean environment, continously
|
113
|
+
test_files: []
|