prepper 0.1.0 → 0.2.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 +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
|