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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d68d0ee338e47bb82505f4b37da819ea323e77de3f3e49cba9bbec6d4d5f6d04
4
- data.tar.gz: 32e04ba18f7dbaf9397ee08a3dfb6a72e18026fb54b157c3b3649ce1ab792f54
3
+ metadata.gz: 77a7a8da09385a93046fcd636b90eaddd34fe8bc4ed9c3a9a103e0f5e383402a
4
+ data.tar.gz: 4f0ff1b545ba8c603f362ede36ab62bd63f8352c26de97830d2cabf8fabbd3c0
5
5
  SHA512:
6
- metadata.gz: f71e24560ab3572628fdc97e663da08142af71ef848ac096dc988d37b0bf2c3c7abe712520b4a2a418d7d29d5676b3ff6a27577a8cc559b5fd3151c6ca220ec2
7
- data.tar.gz: ecef0450a23ff26eef8ea203638c3b9ca22f19cbd9648795246d7cdb242e9be0fcf4378fb91a0e68535ede0c9b2e2117509e7e84c4711defbc7c127fb728709a
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
@@ -0,0 +1,11 @@
1
+ # Change log
2
+
3
+ ## Unreleased
4
+
5
+ ## 0.2.0
6
+
7
+ * better docs
8
+
9
+ ## 0.1.0
10
+
11
+ * First release
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- prepper (0.1.0)
4
+ prepper (0.2.1)
5
5
  sshkit
6
6
  tty-option
7
7
  zeitwerk
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/[USERNAME]/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/[USERNAME]/prepper/blob/master/CODE_OF_CONDUCT.md).
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/[USERNAME]/prepper/blob/master/CODE_OF_CONDUCT.md).
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).
@@ -1,27 +1,25 @@
1
1
  module Prepper
2
2
  module Tools
3
+ # Helper methods to interact with Apt
3
4
  module Apt
4
- def self.included(base)
5
- base.class_eval do
6
-
7
- # Updates apt repositories
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
- def has_apt_package?(package)
21
- Command.new("dpkg --status #{package} | grep 'ok installed'", sudo: true)
22
- end
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
@@ -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
- def chown(path, owner, flags = "")
10
- @commands << Command.new("chown #{flags} #{owner} #{path}", sudo: true)
11
- end
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
- def directory(path, opts = {})
14
- @commands << Command.new("mkdir -p #{path}", opts.merge(sudo: true, verifier: has_directory?(path)))
15
- @commands << Command.new("chown #{opts[:owner]} #{path}", sudo: true) if opts[:owner]
16
- @commands << Command.new("chmod #{opts[:mode]} #{path}", sudo: true) if opts[:mode]
17
- end
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
- def has_directory?(path)
20
- Command.new("test -d #{path}", sudo: true)
21
- end
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
- def file(path, opts = {})
24
- opts[:locals] ||= {}
25
- opts[:verify_content] ||= true
26
- content = opts[:content] || render_template(opts[:template], opts[:locals])
27
- verifier = if opts[:verify_content]
28
- matches_content?(path, content)
29
- else
30
- has_file?(path)
31
- end
32
- io = StringIO.new(content)
33
- @commands << Command.new("put!", {params: [io, path, {owner: opts[:owner], mode: opts[:mode]}], verifier: verifier})
34
- end
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
- def has_file?(path)
37
- Command.new("test -f #{path}", sudo: true)
38
- end
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
- def matches_content?(path, content)
41
- md5 = Digest::MD5.hexdigest(content)
42
- Command.new("md5sum #{path} | cut -f1 -d' '`\" = \"#{md5}\"", sudo: true, verifier: has_file?(path))
43
- end
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
- def symlink(link, target, opts = {})
46
- opts.merge!(
47
- sudo: true,
48
- verifier: has_symlink?(link)
49
- )
50
- @commands << Command.new("ln -s #{target} #{link}", opts)
51
- end
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
- def has_symlink?(link, file = nil)
54
- if file
55
- Command.new("'#{file}' = `readlink #{link}`")
56
- else
57
- Command.new("test -L #{link}", sudo: true)
58
- end
59
- end
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
- def render_template(template, locals)
62
- ERB.new(::File.read("./templates/#{template}")).result_with_hash(locals)
63
- end
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
@@ -1,27 +1,28 @@
1
1
  module Prepper
2
2
  module Tools
3
+ # Helper methods for rbenv
3
4
  module Rbenv
4
- def self.included(base)
5
- base.class_eval do
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
- 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"))
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
- def install_ruby(user, version, opts = '')
18
- @commands << Command.new("sudo -u #{user} -i RUBY_CONFIGURE_OPTS='#{opts}' rbenv install #{version}", verifier: has_directory?("/home/#{user}/.rbenv/versions/#{version}"))
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
- @commands << Command.new("sudo -u #{user} -i rbenv rehash")
21
- @commands << Command.new("sudo -u #{user} -i rbenv global #{version}")
22
- @commands << Command.new("sudo -u #{user} -i rbenv rehash")
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
@@ -1,18 +1,20 @@
1
1
  module Prepper
2
2
  module Tools
3
+ # text related helpers
3
4
  module Text
4
- def self.included(base)
5
- base.class_eval do
6
-
7
- def append_text(text, path)
8
- @commands << Command.new("/bin/echo -e '#{text}' | sudo tee -a #{path}", verifier: has_text?(text, path))
9
- end
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
- def has_text?(text, path)
12
- regex = Regexp.escape(text)
13
- Command.new("grep -qPzo '^#{regex}$' #{path} ||", sudo: true)
14
- end
15
- end
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
@@ -1,22 +1,27 @@
1
1
  module Prepper
2
2
  module Tools
3
+ # user management related helpers
3
4
  module Users
4
- def self.included(base)
5
- base.class_eval do
6
- def add_user(username, opts = {})
7
- opts[:flags] << ' --gecos ,,,'
8
- @commands << Command.new("adduser #{username} #{opts[:flags]}", sudo: true, verifier: has_user?(username))
9
- end
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
- def has_user?(username, opts = {})
12
- if opts[:in_group]
13
- command = "id -nG #{username} | xargs -n1 echo | grep #{opts[:in_group]}"
14
- else
15
- command = "id #{username}"
16
- end
17
- Command.new(command, sudo: true)
18
- end
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
@@ -1,3 +1,3 @@
1
1
  module Prepper
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.1"
3
3
  end
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.0
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: 2022-12-16 00:00:00.000000000 Z
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