pups 1.0.0 → 1.1.1
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 +5 -5
- data/.github/workflows/ci.yml +29 -0
- data/.github/workflows/lint.yml +27 -0
- data/.rubocop.yml +3 -0
- data/CHANGELOG +3 -0
- data/Gemfile +2 -0
- data/Guardfile +3 -1
- data/README.md +108 -29
- data/Rakefile +9 -3
- data/bin/pups +4 -4
- data/lib/pups.rb +25 -11
- data/lib/pups/cli.rb +61 -27
- data/lib/pups/command.rb +14 -11
- data/lib/pups/config.rb +139 -83
- data/lib/pups/docker.rb +69 -0
- data/lib/pups/exec_command.rb +93 -81
- data/lib/pups/file_command.rb +28 -28
- data/lib/pups/merge_command.rb +48 -46
- data/lib/pups/replace_command.rb +36 -34
- data/lib/pups/runit.rb +23 -24
- data/lib/pups/version.rb +3 -1
- data/pups.gemspec +21 -16
- data/test/cli_test.rb +117 -0
- data/test/config_test.rb +192 -30
- data/test/docker_test.rb +157 -0
- data/test/exec_command_test.rb +30 -34
- data/test/file_command_test.rb +8 -9
- data/test/merge_command_test.rb +32 -32
- data/test/replace_command_test.rb +42 -44
- data/test/test_helper.rb +4 -2
- metadata +80 -16
data/lib/pups/file_command.rb
CHANGED
@@ -1,37 +1,37 @@
|
|
1
|
-
|
2
|
-
attr_accessor :path, :contents, :params, :type, :chmod
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
command.contents = hash["contents"]
|
8
|
-
command.chmod = hash["chmod"]
|
9
|
-
command.params = params
|
3
|
+
module Pups
|
4
|
+
class FileCommand < Pups::Command
|
5
|
+
attr_accessor :path, :contents, :params, :type, :chmod, :chown
|
10
6
|
|
11
|
-
|
12
|
-
|
7
|
+
def self.from_hash(hash, params)
|
8
|
+
command = new
|
9
|
+
command.path = hash['path']
|
10
|
+
command.contents = hash['contents']
|
11
|
+
command.chmod = hash['chmod']
|
12
|
+
command.chown = hash['chown']
|
13
|
+
command.params = params
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
@type = :bash
|
17
|
-
end
|
15
|
+
command
|
16
|
+
end
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
def initialize
|
19
|
+
@params = {}
|
20
|
+
@type = :bash
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
path = interpolate_params(@path)
|
23
|
+
attr_writer :params
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
25
|
+
def run
|
26
|
+
path = interpolate_params(@path)
|
27
|
+
|
28
|
+
`mkdir -p #{File.dirname(path)}`
|
29
|
+
File.open(path, 'w') do |f|
|
30
|
+
f.write(interpolate_params(contents))
|
31
|
+
end
|
32
|
+
`chmod #{@chmod} #{path}` if @chmod
|
33
|
+
`chown #{@chown} #{path}` if @chown
|
34
|
+
Pups.log.info("File > #{path} chmod: #{@chmod} chown: #{@chown}")
|
32
35
|
end
|
33
|
-
Pups.log.info("File > #{path} chmod: #{@chmod}")
|
34
36
|
end
|
35
|
-
|
36
37
|
end
|
37
|
-
|
data/lib/pups/merge_command.rb
CHANGED
@@ -1,48 +1,50 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Pups
|
4
|
+
class MergeCommand < Pups::Command
|
5
|
+
attr_reader :filename, :merge_hash
|
6
|
+
|
7
|
+
def self.from_str(command, params)
|
8
|
+
new(command, params)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.parse_command(command)
|
12
|
+
split = command.split(' ')
|
13
|
+
raise ArgumentError, "Invalid merge command #{command}" unless split[-1][0] == '$'
|
14
|
+
|
15
|
+
[split[0..-2].join(' '), split[-1][1..-1]]
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(command, params)
|
19
|
+
@params = params
|
20
|
+
|
21
|
+
filename, target_param = Pups::MergeCommand.parse_command(command)
|
22
|
+
@filename = interpolate_params(filename)
|
23
|
+
@merge_hash = params[target_param]
|
24
|
+
end
|
25
|
+
|
26
|
+
def run
|
27
|
+
merged = self.class.deep_merge(YAML.load_file(@filename), @merge_hash)
|
28
|
+
File.open(@filename, 'w') { |f| f.write(merged.to_yaml) }
|
29
|
+
Pups.log.info("Merge: #{@filename} with: \n#{@merge_hash.inspect}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.deep_merge(first, second, *args)
|
33
|
+
args ||= []
|
34
|
+
merge_arrays = args.include? :merge_arrays
|
35
|
+
|
36
|
+
merger = proc { |_key, v1, v2|
|
37
|
+
if v1.is_a?(Hash) && v2.is_a?(Hash)
|
38
|
+
v1.merge(v2, &merger)
|
39
|
+
elsif v1.is_a?(Array) && v2.is_a?(Array)
|
40
|
+
merge_arrays ? v1 + v2 : v2
|
41
|
+
elsif v2.is_a?(NilClass)
|
42
|
+
v1
|
43
|
+
else
|
44
|
+
v2
|
45
|
+
end
|
46
|
+
}
|
47
|
+
first.merge(second, &merger)
|
48
|
+
end
|
28
49
|
end
|
29
|
-
|
30
|
-
def self.deep_merge(first,second, *args)
|
31
|
-
args ||= []
|
32
|
-
merge_arrays = args.include? :merge_arrays
|
33
|
-
|
34
|
-
merger = proc { |key, v1, v2|
|
35
|
-
if Hash === v1 && Hash === v2
|
36
|
-
v1.merge(v2, &merger)
|
37
|
-
elsif Array === v1 && Array === v2
|
38
|
-
merge_arrays ? v1 + v2 : v2
|
39
|
-
elsif NilClass === v2
|
40
|
-
v1
|
41
|
-
else
|
42
|
-
v2
|
43
|
-
end
|
44
|
-
}
|
45
|
-
first.merge(second, &merger)
|
46
|
-
end
|
47
|
-
|
48
50
|
end
|
data/lib/pups/replace_command.rb
CHANGED
@@ -1,43 +1,45 @@
|
|
1
|
-
|
2
|
-
attr_accessor :text, :from, :to, :filename, :direction, :global
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
replacer.to = guess_replace_type(hash["to"])
|
8
|
-
replacer.text = File.read(hash["filename"])
|
9
|
-
replacer.filename = hash["filename"]
|
10
|
-
replacer.direction = hash["direction"].to_sym if hash["direction"]
|
11
|
-
replacer.global = hash["global"].to_s == "true"
|
12
|
-
replacer
|
13
|
-
end
|
3
|
+
module Pups
|
4
|
+
class ReplaceCommand < Pups::Command
|
5
|
+
attr_accessor :text, :from, :to, :filename, :direction, :global
|
14
6
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
7
|
+
def self.from_hash(hash, params)
|
8
|
+
replacer = new(params)
|
9
|
+
replacer.from = guess_replace_type(hash['from'])
|
10
|
+
replacer.to = guess_replace_type(hash['to'])
|
11
|
+
replacer.text = File.read(hash['filename'])
|
12
|
+
replacer.filename = hash['filename']
|
13
|
+
replacer.direction = hash['direction'].to_sym if hash['direction']
|
14
|
+
replacer.global = hash['global'].to_s == 'true'
|
15
|
+
replacer
|
16
|
+
end
|
19
17
|
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
def self.guess_replace_type(item)
|
19
|
+
# evaling to get all the regex flags easily
|
20
|
+
item[0] == '/' ? eval(item) : item
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
if String === to
|
27
|
-
new_to = interpolate_params(to)
|
23
|
+
def initialize(params)
|
24
|
+
@params = params
|
28
25
|
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
26
|
+
|
27
|
+
def replaced_text
|
28
|
+
new_to = to
|
29
|
+
new_to = interpolate_params(to) if to.is_a?(String)
|
30
|
+
if global
|
31
|
+
text.gsub(from, new_to)
|
32
|
+
elsif direction == :reverse
|
33
|
+
index = text.rindex(from)
|
34
|
+
text[0..index - 1] << text[index..-1].sub(from, new_to)
|
35
|
+
else
|
36
|
+
text.sub(from, new_to)
|
37
|
+
end
|
36
38
|
end
|
37
|
-
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
def run
|
41
|
+
Pups.log.info("Replacing #{from} with #{to} in #{filename}")
|
42
|
+
File.open(filename, 'w') { |f| f.write replaced_text }
|
43
|
+
end
|
42
44
|
end
|
43
45
|
end
|
data/lib/pups/runit.rb
CHANGED
@@ -1,40 +1,39 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
module Pups
|
4
|
+
class Runit
|
5
|
+
attr_accessor :env, :exec, :cd, :name
|
4
6
|
|
7
|
+
def initialize(name)
|
8
|
+
@name = name
|
9
|
+
end
|
5
10
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
File.open(run, "w") do |f|
|
14
|
-
f.write(run_script)
|
11
|
+
def setup
|
12
|
+
`mkdir -p /etc/service/#{name}`
|
13
|
+
run = "/etc/service/#{name}/run"
|
14
|
+
File.open(run, 'w') do |f|
|
15
|
+
f.write(run_script)
|
16
|
+
end
|
17
|
+
`chmod +x #{run}`
|
15
18
|
end
|
16
|
-
`chmod +x #{run}`
|
17
|
-
end
|
18
19
|
|
19
|
-
|
20
|
-
"#!/bin/bash
|
20
|
+
def run_script
|
21
|
+
"#!/bin/bash
|
21
22
|
exec 2>&1
|
22
23
|
#{env_script}
|
23
24
|
#{cd_script}
|
24
25
|
#{exec}
|
25
26
|
"
|
26
|
-
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
def cd_script
|
30
|
+
"cd #{@cd}" if @cd
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
@env.map do |k,v|
|
33
|
+
def env_script
|
34
|
+
@env&.map do |k, v|
|
35
35
|
"export #{k}=#{v}"
|
36
|
-
end
|
36
|
+
end&.join("\n")
|
37
37
|
end
|
38
38
|
end
|
39
|
-
|
40
39
|
end
|
data/lib/pups/version.rb
CHANGED
data/pups.gemspec
CHANGED
@@ -1,26 +1,31 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'pups/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
+
spec.name = 'pups'
|
8
9
|
spec.version = Pups::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.description =
|
12
|
-
spec.summary =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
10
|
+
spec.authors = ['Sam Saffron']
|
11
|
+
spec.email = ['sam.saffron@gmail.com']
|
12
|
+
spec.description = 'Simple docker image creator'
|
13
|
+
spec.summary = 'Toolkit for orchestrating a composed docker image'
|
14
|
+
spec.homepage = ''
|
15
|
+
spec.license = 'MIT'
|
15
16
|
|
16
|
-
spec.files = `git ls-files`.split(
|
17
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ['lib']
|
20
21
|
|
21
|
-
spec.add_development_dependency
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
22
|
+
spec.add_development_dependency 'bundler'
|
23
|
+
spec.add_development_dependency 'guard'
|
24
|
+
spec.add_development_dependency 'guard-minitest'
|
25
|
+
spec.add_development_dependency 'minitest'
|
26
|
+
spec.add_development_dependency 'rake'
|
27
|
+
spec.add_development_dependency 'rubocop'
|
28
|
+
spec.add_development_dependency 'rubocop-discourse'
|
29
|
+
spec.add_development_dependency 'rubocop-minitest'
|
30
|
+
spec.add_development_dependency 'rubocop-rake'
|
26
31
|
end
|
data/test/cli_test.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'stringio'
|
6
|
+
|
7
|
+
module Pups
|
8
|
+
class CliTest < MiniTest::Test
|
9
|
+
def test_cli_option_parsing_stdin
|
10
|
+
options = Cli.parse_args(['--stdin'])
|
11
|
+
assert_equal(true, options[:stdin])
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_cli_option_parsing_none
|
15
|
+
options = Cli.parse_args([])
|
16
|
+
assert_nil(options[:stdin])
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_cli_read_config_from_file
|
20
|
+
# for testing output
|
21
|
+
f = Tempfile.new('test_output')
|
22
|
+
f.close
|
23
|
+
|
24
|
+
# for testing input
|
25
|
+
cf = Tempfile.new('test_config')
|
26
|
+
cf.puts <<~YAML
|
27
|
+
params:
|
28
|
+
run: #{f.path}
|
29
|
+
run:
|
30
|
+
- exec: echo hello world >> #{f.path}
|
31
|
+
YAML
|
32
|
+
cf.close
|
33
|
+
|
34
|
+
Cli.run([cf.path])
|
35
|
+
assert_equal('hello world', File.read(f.path).strip)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_cli_ignore_config_element
|
39
|
+
# for testing output
|
40
|
+
f = Tempfile.new('test_output')
|
41
|
+
f.close
|
42
|
+
|
43
|
+
# for testing input
|
44
|
+
cf = Tempfile.new('test_config')
|
45
|
+
cf.puts <<~YAML
|
46
|
+
env:
|
47
|
+
MY_IGNORED_VAR: a_word
|
48
|
+
params:
|
49
|
+
a_param_var: another_word
|
50
|
+
run:
|
51
|
+
- exec: echo repeating $MY_IGNORED_VAR and also $a_param_var >> #{f.path}
|
52
|
+
YAML
|
53
|
+
cf.close
|
54
|
+
|
55
|
+
Cli.run(["--ignore", "env,params", cf.path])
|
56
|
+
assert_equal('repeating and also', File.read(f.path).strip)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_cli_gen_docker_run_args_ignores_other_config
|
60
|
+
# When generating the docker run arguments it should ignore other template configuration
|
61
|
+
# like 'run' directives.
|
62
|
+
|
63
|
+
# for testing output
|
64
|
+
f = Tempfile.new("test_output")
|
65
|
+
f.close
|
66
|
+
|
67
|
+
# for testing input
|
68
|
+
cf = Tempfile.new("test_config")
|
69
|
+
cf.puts <<~YAML
|
70
|
+
env:
|
71
|
+
foo: 1
|
72
|
+
bar: 5
|
73
|
+
baz: 'hello_{{spam}}'
|
74
|
+
env_template:
|
75
|
+
spam: 'eggs'
|
76
|
+
config: my_app
|
77
|
+
params:
|
78
|
+
run: #{f.path}
|
79
|
+
run:
|
80
|
+
- exec: echo hello world >> #{f.path}
|
81
|
+
expose:
|
82
|
+
- "2222:22"
|
83
|
+
- "127.0.0.1:20080:80"
|
84
|
+
- 5555
|
85
|
+
volumes:
|
86
|
+
- volume:
|
87
|
+
host: /var/discourse/shared
|
88
|
+
guest: /shared
|
89
|
+
- volume:
|
90
|
+
host: /bar
|
91
|
+
guest: /baz
|
92
|
+
links:
|
93
|
+
- link:
|
94
|
+
name: postgres
|
95
|
+
alias: postgres
|
96
|
+
- link:
|
97
|
+
name: foo
|
98
|
+
alias: bar
|
99
|
+
labels:
|
100
|
+
monitor: "true"
|
101
|
+
app_name: "{{config}}_discourse"
|
102
|
+
YAML
|
103
|
+
cf.close
|
104
|
+
|
105
|
+
expected = []
|
106
|
+
expected << "--env foo=1 --env bar=5 --env baz=hello_eggs"
|
107
|
+
expected << "--publish 2222:22 --publish 127.0.0.1:20080:80 --expose 5555"
|
108
|
+
expected << "--volume /var/discourse/shared:/shared --volume /bar:/baz"
|
109
|
+
expected << "--link postgres:postgres --link foo:bar"
|
110
|
+
expected << "--label monitor=true --label app_name=my_app_discourse"
|
111
|
+
expected.sort!
|
112
|
+
|
113
|
+
assert_equal("", File.read(f.path).strip)
|
114
|
+
assert_output(expected.join(" ")) { Cli.run(["--gen-docker-run-args", cf.path]) }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|