prepper 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +33 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile.lock +1 -1
- data/README.md +7 -4
- data/lib/prepper/tools/apt.rb +16 -18
- data/lib/prepper/tools/file.rb +103 -49
- data/lib/prepper/tools/rbenv.rb +19 -18
- data/lib/prepper/tools/text.rb +13 -11
- data/lib/prepper/tools/users.rb +19 -14
- data/lib/prepper/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77a7a8da09385a93046fcd636b90eaddd34fe8bc4ed9c3a9a103e0f5e383402a
|
4
|
+
data.tar.gz: 4f0ff1b545ba8c603f362ede36ab62bd63f8352c26de97830d2cabf8fabbd3c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d251c3448c12d5accc5ee510b42df6180fcbaa30f2757ec1abdfd36ac25c8875a93f06f07fff6e80350a0809185dea9cba088e5f107c6b090e01088aa2ce8ffe
|
7
|
+
data.tar.gz: 28fd214634b0a79c171c5d9712686e84ebe10084f7e5a9f854918fd4bb0bc998dbcd2b010185d0b738833fc20d18a4dcc60ea1f5e4d595af526a77684b114160
|
@@ -0,0 +1,33 @@
|
|
1
|
+
name: CI
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
pull_request:
|
6
|
+
workflow_dispatch:
|
7
|
+
# schedule:
|
8
|
+
# - cron: '42 5 * * *'
|
9
|
+
|
10
|
+
jobs:
|
11
|
+
test:
|
12
|
+
strategy:
|
13
|
+
fail-fast: false
|
14
|
+
matrix:
|
15
|
+
ruby: [ '3.0' ]
|
16
|
+
|
17
|
+
runs-on: ubuntu-latest
|
18
|
+
name: Ruby ${{matrix.ruby}}
|
19
|
+
container: ruby:${{matrix.ruby}}
|
20
|
+
|
21
|
+
steps:
|
22
|
+
- uses: actions/checkout@v3
|
23
|
+
|
24
|
+
- name: Show ruby Version
|
25
|
+
run: |
|
26
|
+
ruby -v
|
27
|
+
|
28
|
+
- name: Install Modules
|
29
|
+
run: ./bin/setup
|
30
|
+
|
31
|
+
- name: Run tests
|
32
|
+
run: rake test
|
33
|
+
|
data/CHANGELOG.md
ADDED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Prepper
|
2
2
|
|
3
3
|
Prepper is a simple server provisioning tool, built on top of SSHKit. You can
|
4
|
-
use it to script your server build process.
|
4
|
+
use it to script your server build process. It is heavily inspired by (Sprinkle)[https://github.com/sprinkle-tool/sprinkle], but Sprinkle doesn't seem to be maintained anymore, that's why Prepper was born.
|
5
5
|
|
6
6
|
|
7
7
|
## Installation
|
@@ -17,7 +17,8 @@ upload files to the server, etc.
|
|
17
17
|
|
18
18
|
A simple example:
|
19
19
|
|
20
|
-
```
|
20
|
+
```ruby
|
21
|
+
|
21
22
|
server_host "YOUR_SERVER_IP"
|
22
23
|
server_port 22
|
23
24
|
server_user "root"
|
@@ -47,6 +48,8 @@ end
|
|
47
48
|
|
48
49
|
You can see a full example in [examples/config.rb](examples/config.rb). You would run that file with `bundle exec prepper config.rb` to provision the server.
|
49
50
|
|
51
|
+
The API documentation can be found on rubydoc: [https://rubydoc.info/github/gregmolnar/prepper](https://rubydoc.info/github/gregmolnar/prepper).
|
52
|
+
|
50
53
|
## Development
|
51
54
|
|
52
55
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -55,7 +58,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
55
58
|
|
56
59
|
## Contributing
|
57
60
|
|
58
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
61
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/gregmolnar/prepper. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/gregmolnar/prepper/blob/master/CODE_OF_CONDUCT.md).
|
59
62
|
|
60
63
|
|
61
64
|
## License
|
@@ -64,4 +67,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
64
67
|
|
65
68
|
## Code of Conduct
|
66
69
|
|
67
|
-
Everyone interacting in the Prepper project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
70
|
+
Everyone interacting in the Prepper project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/gregmolnar/prepper/blob/master/CODE_OF_CONDUCT.md).
|
data/lib/prepper/tools/apt.rb
CHANGED
@@ -1,27 +1,25 @@
|
|
1
1
|
module Prepper
|
2
2
|
module Tools
|
3
|
+
# Helper methods to interact with Apt
|
3
4
|
module Apt
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def apt_update
|
9
|
-
@commands << Command.new("apt update", sudo: true)
|
10
|
-
end
|
11
|
-
|
12
|
-
# Installs packages
|
13
|
-
# @param packages [Array] array of package names
|
14
|
-
def apt_install(packages)
|
15
|
-
packages.each do |package|
|
16
|
-
@commands << Command.new("apt install --force-yes -qyu #{package}", sudo: true, verify: has_apt_package?(package))
|
17
|
-
end
|
18
|
-
end
|
5
|
+
# Updates apt repositories
|
6
|
+
def apt_update
|
7
|
+
@commands << Command.new("apt update", sudo: true)
|
8
|
+
end
|
19
9
|
|
20
|
-
|
21
|
-
|
22
|
-
|
10
|
+
# Installs packages
|
11
|
+
# @param packages [Array] array of package names
|
12
|
+
def apt_install(packages)
|
13
|
+
packages.each do |package|
|
14
|
+
@commands << Command.new("apt install --force-yes -qyu #{package}", sudo: true, verify: has_apt_package?(package))
|
23
15
|
end
|
24
16
|
end
|
17
|
+
|
18
|
+
# Verifier command to checks if an apt package is installed
|
19
|
+
# @param package [String] name of the package
|
20
|
+
def has_apt_package?(package)
|
21
|
+
Command.new("dpkg --status #{package} | grep 'ok installed'", sudo: true)
|
22
|
+
end
|
25
23
|
end
|
26
24
|
end
|
27
25
|
end
|
data/lib/prepper/tools/file.rb
CHANGED
@@ -2,67 +2,121 @@ require 'erb'
|
|
2
2
|
require 'digest/md5'
|
3
3
|
module Prepper
|
4
4
|
module Tools
|
5
|
+
# Helper methods for file and directory management
|
5
6
|
module File
|
6
|
-
def self.included(base)
|
7
|
-
base.class_eval do
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
# Changes ownership of a path
|
9
|
+
# @param path [String] the path which we want to change the ownership of
|
10
|
+
# @param owner [String] name of the owner, ie: 'root:root'
|
11
|
+
# @param flags [String] flags to pass to chown, ie: '-R' to do it recursively
|
12
|
+
def chown(path, owner, flags = "")
|
13
|
+
@commands << Command.new("chown #{flags} #{owner} #{path}", sudo: true)
|
14
|
+
end
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
# Create a directory unless it already exists
|
17
|
+
# @param path [String] path of the directory
|
18
|
+
# @param [Hash] opts options hash
|
19
|
+
# @option opts [String] :owner Owner of the directory, ie: 'root:root'
|
20
|
+
# @option opts [String] :mode mode bits, ie: '0777'
|
21
|
+
def directory(path, opts = {})
|
22
|
+
@commands << Command.new("mkdir -p #{path}", opts.merge(sudo: true, verifier: has_directory?(path)))
|
23
|
+
@commands << Command.new("chown #{opts[:owner]} #{path}", sudo: true) if opts[:owner]
|
24
|
+
@commands << Command.new("chmod #{opts[:mode]} #{path}", sudo: true) if opts[:mode]
|
25
|
+
end
|
18
26
|
|
19
|
-
|
20
|
-
|
21
|
-
|
27
|
+
# returns a verifier command to test if a directory exists
|
28
|
+
# @param path [String] path to test
|
29
|
+
def has_directory?(path)
|
30
|
+
Command.new("test -d #{path}", sudo: true)
|
31
|
+
end
|
22
32
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
# Create a file unless it already exists. The contents can be set to a
|
34
|
+
# string or a template can be rendered with the provided locals
|
35
|
+
# @param path [String] path of the file
|
36
|
+
# @param [Hash] opts options hash
|
37
|
+
# @option opts [String] :content string content of the file
|
38
|
+
# @option opts [String] :template name of the template for the file
|
39
|
+
# @option opts [String] :locals hash of variables to pass to the template
|
40
|
+
# @option opts [String] :verify_content set to true to verify the file
|
41
|
+
# content is the same in case the file already exists
|
42
|
+
# @option opts [String] :owner Owner of the directory, ie: 'root:root'
|
43
|
+
# @option opts [String] :mode mode bits, ie: '0777'
|
44
|
+
def file(path, opts = {})
|
45
|
+
opts[:locals] ||= {}
|
46
|
+
opts[:verify_content] ||= true
|
47
|
+
content = opts[:content] || render_template(opts[:template], opts[:locals])
|
48
|
+
verifier = if opts[:verify_content]
|
49
|
+
matches_content?(path, content)
|
50
|
+
else
|
51
|
+
has_file?(path)
|
52
|
+
end
|
53
|
+
io = StringIO.new(content)
|
54
|
+
@commands << Command.new("put!", {params: [io, path, {owner: opts[:owner], mode: opts[:mode]}], verifier: verifier})
|
55
|
+
end
|
35
56
|
|
36
|
-
|
37
|
-
|
38
|
-
|
57
|
+
# returns a verifier command to test if a file exists
|
58
|
+
# @param path [String] path to test
|
59
|
+
def has_file?(path)
|
60
|
+
Command.new("test -f #{path}", sudo: true)
|
61
|
+
end
|
39
62
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
63
|
+
# returns a verifier command to test if has a matching content
|
64
|
+
# @param path [String] path to test
|
65
|
+
# @param content [String] expected content
|
66
|
+
def matches_content?(path, content)
|
67
|
+
md5 = Digest::MD5.hexdigest(content)
|
68
|
+
Command.new("md5sum #{path} | cut -f1 -d' '`\" = \"#{md5}\"", sudo: true, verifier: has_file?(path))
|
69
|
+
end
|
44
70
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
71
|
+
# creates a symlink
|
72
|
+
# @param link [String] link
|
73
|
+
# @param target [String] target
|
74
|
+
# @param [Hash] opts options hash
|
75
|
+
def symlink(link, target, opts = {})
|
76
|
+
opts.merge!(
|
77
|
+
sudo: true,
|
78
|
+
verifier: has_symlink?(link)
|
79
|
+
)
|
80
|
+
@commands << Command.new("ln -s #{target} #{link}", opts)
|
81
|
+
end
|
82
|
+
|
83
|
+
# returns a verifier command to test if as a matching content
|
84
|
+
# @param path [String] path to test
|
85
|
+
# @param content [String] expected content
|
86
|
+
def matches_content?(path, content)
|
87
|
+
md5 = Digest::MD5.hexdigest(content)
|
88
|
+
Command.new("md5sum #{path} | cut -f1 -d' '`\" = \"#{md5}\"", sudo: true, verifier: has_file?(path))
|
89
|
+
end
|
52
90
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
91
|
+
# creates a symlink
|
92
|
+
# @param link [String] link
|
93
|
+
# @param target [String] target
|
94
|
+
# @param [Hash] opts options hash
|
95
|
+
def symlink(link, target, opts = {})
|
96
|
+
opts.merge!(
|
97
|
+
sudo: true,
|
98
|
+
verifier: has_symlink?(link)
|
99
|
+
)
|
100
|
+
@commands << Command.new("ln -s #{target} #{link}", opts)
|
101
|
+
end
|
60
102
|
|
61
|
-
|
62
|
-
|
63
|
-
|
103
|
+
# returns a verifier command to test if symlink exists
|
104
|
+
# @param path [String] path to test
|
105
|
+
# @param file [String] optionally check if it points to the correct file
|
106
|
+
def has_symlink?(link, file = nil)
|
107
|
+
if file
|
108
|
+
Command.new("'#{file}' = `readlink #{link}`")
|
109
|
+
else
|
110
|
+
Command.new("test -L #{link}", sudo: true)
|
64
111
|
end
|
65
112
|
end
|
113
|
+
|
114
|
+
# render an ERB template
|
115
|
+
# @param template [String] name of the template
|
116
|
+
# @param locals [Hash] hash of variables to pass to the template
|
117
|
+
def render_template(template, locals)
|
118
|
+
ERB.new(::File.read("./templates/#{template}")).result_with_hash(locals)
|
119
|
+
end
|
66
120
|
end
|
67
121
|
end
|
68
122
|
end
|
data/lib/prepper/tools/rbenv.rb
CHANGED
@@ -1,27 +1,28 @@
|
|
1
1
|
module Prepper
|
2
2
|
module Tools
|
3
|
+
# Helper methods for rbenv
|
3
4
|
module Rbenv
|
4
|
-
|
5
|
-
|
5
|
+
# install rbenv for a given user
|
6
|
+
# @param user [String] name of the user to install rbenv for
|
7
|
+
def install_rbenv(user)
|
8
|
+
apt_install %w{libssl-dev zlib1g zlib1g-dev libreadline-dev}
|
9
|
+
@commands << Command.new("sudo -u #{user} -i git clone https://github.com/sstephenson/rbenv.git /home/#{user}/.rbenv", verifier: has_directory?("/home/#{user}/.rbenv"))
|
10
|
+
@commands << Command.new("sudo -u #{user} -i git clone https://github.com/sstephenson/ruby-build.git /home/#{user}/.rbenv/plugins/ruby-build", verifier: has_directory?("/home/#{user}/.rbenv/plugins/ruby-build"))
|
6
11
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
append_text 'export PATH="$HOME/.rbenv/bin:$PATH"', "/home/#{user}/.profile"
|
13
|
-
append_text 'eval "$(rbenv init -)"', "/home/#{user}/.profile"
|
14
|
-
chown "/home/#{user}/.profile", 'deploy:deploy'
|
15
|
-
end
|
12
|
+
append_text 'export PATH="$HOME/.rbenv/bin:$PATH"', "/home/#{user}/.profile"
|
13
|
+
append_text 'eval "$(rbenv init -)"', "/home/#{user}/.profile"
|
14
|
+
chown "/home/#{user}/.profile", "#{user}:#{user}"
|
15
|
+
end
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
# install a given ruby version for a given user
|
18
|
+
# @param user [String] name of the user
|
19
|
+
# @param version [String] ruby version
|
20
|
+
def install_ruby(user, version, opts = '')
|
21
|
+
@commands << Command.new("sudo -u #{user} -i RUBY_CONFIGURE_OPTS='#{opts}' rbenv install #{version}", verifier: has_directory?("/home/#{user}/.rbenv/versions/#{version}"))
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
end
|
23
|
+
@commands << Command.new("sudo -u #{user} -i rbenv rehash")
|
24
|
+
@commands << Command.new("sudo -u #{user} -i rbenv global #{version}")
|
25
|
+
@commands << Command.new("sudo -u #{user} -i rbenv rehash")
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
data/lib/prepper/tools/text.rb
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
module Prepper
|
2
2
|
module Tools
|
3
|
+
# text related helpers
|
3
4
|
module Text
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
# append text to a file
|
6
|
+
# @param text [String] text to append
|
7
|
+
# @param path [String]
|
8
|
+
def append_text(text, path)
|
9
|
+
@commands << Command.new("/bin/echo -e '#{text}' | sudo tee -a #{path}", verifier: has_text?(text, path))
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
# returns a verifier command to test the presence of a string in a file
|
13
|
+
# @param text [String] text
|
14
|
+
# @param path [String] path to file
|
15
|
+
def has_text?(text, path)
|
16
|
+
regex = Regexp.escape(text)
|
17
|
+
Command.new("grep -qPzo '^#{regex}$' #{path} ||", sudo: true)
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
data/lib/prepper/tools/users.rb
CHANGED
@@ -1,22 +1,27 @@
|
|
1
1
|
module Prepper
|
2
2
|
module Tools
|
3
|
+
# user management related helpers
|
3
4
|
module Users
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
# add a user to the host
|
6
|
+
# @param username [String] name of the user
|
7
|
+
# @param [Hash] opts options has
|
8
|
+
# @option opts [String] :flags flags to pass to adduser
|
9
|
+
def add_user(username, opts = {})
|
10
|
+
opts[:flags] << ' --gecos ,,,'
|
11
|
+
@commands << Command.new("adduser #{username} #{opts[:flags]}", sudo: true, verifier: has_user?(username))
|
12
|
+
end
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
# returns a verifier command to check if a user exists
|
15
|
+
# @param username [String] name of the user
|
16
|
+
# @param [Hash] opts options hash
|
17
|
+
# @option opts [String] :in_group check if the user is in the given group
|
18
|
+
def has_user?(username, opts = {})
|
19
|
+
if opts[:in_group]
|
20
|
+
command = "id -nG #{username} | xargs -n1 echo | grep #{opts[:in_group]}"
|
21
|
+
else
|
22
|
+
command = "id #{username}"
|
19
23
|
end
|
24
|
+
Command.new(command, sudo: true)
|
20
25
|
end
|
21
26
|
end
|
22
27
|
end
|
data/lib/prepper/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prepper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Molnar
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: zeitwerk
|
@@ -74,8 +74,10 @@ executables:
|
|
74
74
|
extensions: []
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
+
- ".github/workflows/ci.yml"
|
77
78
|
- ".gitignore"
|
78
79
|
- ".travis.yml"
|
80
|
+
- CHANGELOG.md
|
79
81
|
- CODE_OF_CONDUCT.md
|
80
82
|
- Gemfile
|
81
83
|
- Gemfile.lock
|