archlinux 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f95fc88d318bc67d0abe5281c5eb940ed50e024f9f064f3af4c3d6a72edbcc87
4
- data.tar.gz: 55e6b4029e5920f2e659f4cf4c0e532cc1216f7570bb9064836284027697d397
3
+ metadata.gz: 0b2ec1f095b419f4c323bee8fa6f9f1f7215ab0e4b785c3922de9db10e5a0413
4
+ data.tar.gz: 84fe5ee8d792c03b5a62bac375114c82dcb2d1448dfe55f2a1de33a77f90a8df
5
5
  SHA512:
6
- metadata.gz: cf9d3e93c200ec678bcb960dddb13c16fa534387104e22a02f64d0a66f1bd740c92bcf1fd318cbe4cd7c8864bece2ca709b5acdee667cb196ce9a106f9453542
7
- data.tar.gz: d7b4b04d3412daf906d20fb94c7f4d9451dc4007dae41b3c23c3d22c857a92e0a5b2e7c4e63f709a8a32f41e255ec532c795d1328e75c5fe749c88a42c51cbe3
6
+ metadata.gz: c12e913857365c3390faa570f1ffee57a577628d831efb9d974c83ecf4f5b6784c4fb901b2ed09e63d5c5a1bbc025e7b636add7a7801c31e4f2c0b01c8bf68b0
7
+ data.tar.gz: c74297e0f98dd6c9518420e1d28d2193c5a56f97749ae596a1c6be398ea3f1bfc630e814d5fd889315ce39fcac2030024157902bb954a7ffa212bfd827535fa3
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Archlinux
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/archlinux.svg)](https://badge.fury.io/rb/archlinux)
4
+
3
5
  > [!WARNING]
4
6
  > this can break your system, don't use it on your running system
5
7
 
@@ -108,3 +110,10 @@ It will do the following:
108
110
  - Make sure services and timers are running
109
111
  - Do other configurations like locale, X11 keyboard settings, hostname
110
112
  - Ensure users are created and in specified groups
113
+
114
+
115
+ # Concepts
116
+
117
+ ## Declarations:
118
+
119
+ Functions the user will run to declare the state of the system like packages to be present, files, services, user, group...etc
data/archlinux.gemspec CHANGED
@@ -4,6 +4,6 @@ Gem::Specification.new do |s|
4
4
  s.files = `git ls-files`.lines.map(&:chomp)
5
5
  s.name = 'archlinux'
6
6
  s.summary = "Archlinux DSL to manage whole system state"
7
- s.version = '0.0.0'
7
+ s.version = '0.0.1'
8
8
  s.licenses = ["GPL-3.0-or-later"]
9
9
  end
@@ -0,0 +1,16 @@
1
+ require 'set'
2
+
3
+ # @group Declarations:
4
+
5
+ # setup add ufw enable it and allow ports during configure step
6
+ def ufw(*allow)
7
+ @ufw ||= Set.new
8
+ @ufw += allow.map(&:to_s)
9
+
10
+ package :ufw
11
+ service :ufw
12
+
13
+ on_configure do
14
+ sudo "ufw allow #{@ufw.join(' ')}"
15
+ end
16
+ end
data/lib/archlinux.rb CHANGED
@@ -1,5 +1,10 @@
1
+ def require_relative_dir(dir)
2
+ Dir["#{File.dirname(__FILE__)}/#{dir}/**/*.rb"].sort.each { |f| require f }
3
+ end
4
+
1
5
  require_relative 'core'
2
6
  require_relative 'utils'
3
- require_relative 'declarations'
7
+ require_relative_dir 'declarations'
8
+ require_relative_dir 'applications'
4
9
 
5
10
  Signal.trap("INT") { exit } # Suppress stack trace on Ctrl-C
data/lib/core.rb CHANGED
@@ -1,9 +1,6 @@
1
- # ==============================================================
2
- # CORE:
3
1
  # State of the system It should hold all the information we need to build the
4
2
  # system, packages, files, changes...etc. everything will run inside an instance
5
3
  # of this class
6
- # ==============================================================
7
4
  class State
8
5
  def apply(block)
9
6
  instance_eval &block
@@ -18,21 +15,21 @@ class State
18
15
  @prepare_steps[id] = block
19
16
  end
20
17
 
21
- # Same as on_prepare but for install step
18
+ # Same as {#on_prepare} but for install step
22
19
  def on_install(id=nil, &block)
23
20
  id ||= caller_locations(1,1).first.to_s
24
21
  @install_steps ||= {}
25
22
  @install_steps[id] = block
26
23
  end
27
24
 
28
- # Same as on_prepare but for configure step
25
+ # Same as {.on_prepare} but for configure step
29
26
  def on_configure(id=nil, &block)
30
27
  id ||= caller_locations(1,1).first.to_s
31
28
  @configure_steps ||= {}
32
29
  @configure_steps[id] = block
33
30
  end
34
31
 
35
- # Same as on_finalize but for configure step
32
+ # Same as {.on_prepare} but for configure step
36
33
  def on_finalize(id=nil, &block)
37
34
  id ||= caller_locations(1,1).first.to_s
38
35
  @finalize_steps ||= {}
@@ -41,29 +38,25 @@ class State
41
38
 
42
39
  # Run all registered code blocks in the following order: Prepare, Install, Configure, Finalize
43
40
  def run_steps
44
- if @prepare_steps&.any?
45
- log "=> Prepare"
46
- @prepare_steps.each { |_, step| apply(step) }
47
- end
41
+ run_step("Prepare", @prepare_steps)
42
+ run_step("Install", @install_steps)
43
+ run_step("Configure", @configure_steps)
44
+ run_step("Finalize", @finalize_steps)
45
+ end
46
+ end
48
47
 
49
- if @install_steps&.any?
50
- log "=> Install"
51
- @install_steps.each { |_, step| apply(step) }
52
- end
48
+ private
53
49
 
54
- if @configure_steps&.any?
55
- log "=> Configure"
56
- @configure_steps.each { |_, step| apply(step) }
57
- end
50
+ def run_step(name, step)
51
+ return unless step&.any?
58
52
 
59
- if @finalize_steps&.any?
60
- log "=> Finalize"
61
- @finalize_steps.each { |_, step| apply(step) }
62
- end
63
- end
53
+ log "=> #{name}"
54
+ step.each { |_, s| apply(s) }
64
55
  end
65
56
 
66
- # passed block will run in the context of a State instance and then a builder
57
+ # @group Core:
58
+
59
+ # passed block will run in the context of a {State} instance and then a builder
67
60
  # will build this state
68
61
  def linux(&block)
69
62
  s = State.new
@@ -0,0 +1,80 @@
1
+ require 'fileutils'
2
+
3
+ # @group Declarations:
4
+
5
+ # Copy src inside dest during configure step, if src/. will copy src content to dest
6
+ def copy(src, dest)
7
+ @copy ||= []
8
+ @copy << { src: src, dest: dest }
9
+
10
+ on_configure do
11
+ next unless @copy
12
+ next if @copy.empty?
13
+
14
+ @copy.each do |item|
15
+ log "Copying", item
16
+ FileUtils.cp_r item[:src], item[:dest]
17
+ end
18
+ end
19
+ end
20
+
21
+ # Replace a regex pattern with replacement string in a file during configure step
22
+ def replace(file, pattern, replacement)
23
+ @replace ||= []
24
+ @replace << {file: file, pattern: pattern, replacement: replacement}
25
+
26
+ on_configure do
27
+ @replace.each do |params|
28
+ input = File.read(params[:file])
29
+ output = input.gsub(params[:pattern], params[:replacement])
30
+ File.write(params[:file], output)
31
+ end
32
+ end
33
+ end
34
+
35
+ # link file to destination
36
+ def symlink(target, link_name)
37
+ @symlink ||= Set.new
38
+ @symlink << {target: target, link_name: link_name}
39
+
40
+ on_configure do
41
+
42
+ @symlink.each do |params|
43
+ target = File.expand_path params[:target]
44
+ link_name = File.expand_path params[:link_name]
45
+ log "Linking", target: target, link_name: link_name
46
+
47
+ # make the parent if it doesn't exist
48
+ dest_dir = File.dirname(link_name)
49
+ FileUtils.mkdir_p(dest_dir) unless File.exist?(dest_dir)
50
+
51
+ # link with force
52
+ FileUtils.ln_s(target, link_name, force: true)
53
+ end
54
+ end
55
+ end
56
+
57
+ # on prepare make sure the directory exists
58
+ def mkdir(*path)
59
+ path.flatten!
60
+ @mkdir ||= Set.new
61
+ @mkdir += path
62
+
63
+ on_prepare do
64
+ @mkdir.each do |path|
65
+ FileUtils.mkdir_p File.expand_path(path)
66
+ end
67
+ end
68
+ end
69
+
70
+ # Write a file during configure step
71
+ def file(path, content)
72
+ @files ||= {}
73
+ @files[path] = content
74
+
75
+ on_configure do
76
+ @files.each do |path, content|
77
+ File.write(path, content)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,22 @@
1
+ require 'set'
2
+
3
+ # @group Declarations:
4
+
5
+ # on prepare make sure a git repository is cloned to directory
6
+ def git_clone(from:, to: nil)
7
+ @git_clone ||= Set.new
8
+ @git_clone << {from: from, to: to}
9
+
10
+ on_install do
11
+ @git_clone.each do |item|
12
+ from = item[:from]
13
+ to = item[:to]
14
+ system "git clone #{from} #{to}" unless File.exist?(File.expand_path(to))
15
+ end
16
+ end
17
+ end
18
+
19
+ # git clone for github repositories
20
+ def github_clone(from:, to: nil)
21
+ git_clone(from: "https://github.com/#{from}", to: to)
22
+ end
@@ -0,0 +1,74 @@
1
+ require 'set'
2
+ require 'fileutils'
3
+
4
+ # @group Utilities
5
+
6
+ # Utility function, returns true of package is installed
7
+ def package?(name)
8
+ system("pacman -Qi #{name} &> /dev/null")
9
+ end
10
+
11
+ # @group Declarations:
12
+
13
+ # Install a package on install step and remove packages not registered with this
14
+ # function
15
+ def package(*names)
16
+ names.flatten!
17
+ @packages ||= Set.new
18
+ @packages += names.map(&:to_s)
19
+
20
+ # install step to install packages required and remove not required
21
+ on_install do
22
+ # install missing packages
23
+ need_install = @packages.reject { |p| package? p }
24
+ need_install_args = need_install.join(" ")
25
+ if need_install.any?
26
+ log "Installing packages", packages: need_install
27
+ sudo "pacman --noconfirm --needed -S #{need_install_args}"
28
+ end
29
+
30
+ # expand groups to packages
31
+ packages_args = @packages.join(" ")
32
+ group_packages = Set.new(`pacman --quiet -Sg #{packages_args}`.lines.map(&:strip))
33
+
34
+ # full list of packages that should exist on the system
35
+ all = @packages + group_packages
36
+
37
+ # actual list on the system
38
+ installed = Set.new(`pacman -Q --quiet --explicit --unrequired --native`.lines.map(&:strip))
39
+
40
+ unneeded = installed - all
41
+ next if unneeded.empty?
42
+
43
+ log "Removing packages", packages: unneeded
44
+ sudo("pacman -Rs #{unneeded.join(" ")}")
45
+ end
46
+ end
47
+
48
+ # aur command to install packages from aur on install step
49
+ def aur(*names)
50
+ names.flatten!
51
+ @aurs ||= Set.new
52
+ @aurs += names.map(&:to_s)
53
+
54
+ on_install do
55
+ names = @aurs || []
56
+ log "Install AUR packages", packages: names
57
+ cache = "./cache/aur"
58
+ FileUtils.mkdir_p cache
59
+ Dir.chdir cache do
60
+ names.each do |package|
61
+ system("git clone --depth 1 --shallow-submodules https://aur.archlinux.org/#{package}.git") unless Dir.exist?(package)
62
+ Dir.chdir package do
63
+
64
+ pkgbuild = File.readlines('PKGBUILD')
65
+ pkgver = pkgbuild.find { |l| l.start_with?('pkgver=') }.split('=')[1].strip.chomp('"')
66
+ package_info = `pacman -Qi #{package}`.strip.lines.map{|l| l.strip.split(/\s*:\s*/, 2) }.to_h
67
+ installed = package_info["Version"].to_s.split("-")[0] == pkgver
68
+
69
+ system("makepkg --syncdeps --install --noconfirm --needed") unless installed
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,100 @@
1
+ require 'set'
2
+
3
+ # @group Declarations:
4
+
5
+ # set timezone and NTP settings during prepare step
6
+ def timedate(timezone: 'UTC', ntp: true)
7
+ @timedate = {timezone: timezone, ntp: ntp}
8
+
9
+ on_configure do
10
+ log "Set timedate", @timedate
11
+ sudo "timedatectl set-timezone #{@timedate[:timezone]}"
12
+ sudo "timedatectl set-ntp #{@timedate[:ntp]}"
13
+ end
14
+ end
15
+
16
+ # enable system service if root or user service if not during finalize step
17
+ def service(*names)
18
+ names.flatten!
19
+ @services ||= Set.new
20
+ @services += names.map(&:to_s)
21
+
22
+ on_finalize do
23
+ log "Enable services", services: @services
24
+ user_flags = root? ? "" : "--user"
25
+
26
+ system "systemctl enable #{user_flags} #{@services.join(" ")}"
27
+
28
+ # Disable services that were enabled manually and not in the list we have
29
+ services = `systemctl list-unit-files #{user_flags} --state=enabled --type=service --no-legend --no-pager`
30
+ enabled_manually = services.lines.map{|l| l.strip.split(/\s+/) }.select{|l| (l[1] == 'enabled') && (l[2] == 'disabled')}
31
+ names_without_extension = enabled_manually.map{|l| l.first.delete_suffix(".service") }
32
+ to_disable = names_without_extension - @services.to_a
33
+
34
+ next if to_disable.empty?
35
+
36
+ log "Services to disable", packages: to_disable
37
+ # system "systemctl disable #{user_flags} #{to_disable.join(" ")}"
38
+ end
39
+ end
40
+
41
+ # enable system timer if root or user timer if not during finalize step
42
+ def timer(*names)
43
+ names.flatten!
44
+ @timers ||= Set.new
45
+ @timers += names.map(&:to_s)
46
+
47
+ on_finalize do
48
+ log "Enable timers", timers: @timers
49
+ timers = @timers.map{ |t| "#{t}.timer" }.join(" ")
50
+ if root?
51
+ sudo "systemctl enable #{timers}"
52
+ else
53
+ system "systemctl enable --user #{timers}"
54
+ end
55
+ # disable all other timers
56
+ end
57
+ end
58
+
59
+ # set keyboard settings during prepare step
60
+ def keyboard(keymap: nil, layout: nil, model: nil, variant: nil, options: nil)
61
+ @keyboard ||= {}
62
+ values = {
63
+ keymap: keymap,
64
+ layout: layout,
65
+ model: model,
66
+ variant: variant,
67
+ options: options
68
+ }.compact
69
+ @keyboard.merge!(values)
70
+
71
+ on_prepare do
72
+ next unless @keyboard[:keymap]
73
+
74
+ sudo "localectl set-keymap #{@keyboard[:keymap]}"
75
+
76
+ m = @keyboard.to_h.slice(:layout, :model, :variant, :options)
77
+ sudo "localectl set-x11-keymap \"#{m[:layout]}\" \"#{m[:model]}\" \"#{m[:variant]}\" \"#{m[:options]}\""
78
+ end
79
+ end
80
+
81
+ # Sets locale using localectl
82
+ def locale(value)
83
+ @locale = value
84
+
85
+ on_prepare do
86
+ sudo "localectl set-locale #{@locale}"
87
+ end
88
+ end
89
+
90
+ # Sets the machine hostname
91
+ def hostname(name)
92
+ @hostname = name
93
+
94
+ file '/etc/hostname', "#{@hostname}\n"
95
+
96
+ on_configure do
97
+ log "Setting hostname", hostname: @hostname
98
+ sudo "hostnamectl set-hostname #{@hostname}"
99
+ end
100
+ end
@@ -0,0 +1,38 @@
1
+ require 'set'
2
+ require 'etc'
3
+
4
+ # @group Declarations:
5
+
6
+ # create a user and assign a set of group. if block is passes the block will run
7
+ # in as this user. block will run during the configure step
8
+ def user(name, groups: [], &block)
9
+ name = name.to_s
10
+
11
+ @user ||= {}
12
+ @user[name] ||= {}
13
+ @user[name][:groups] ||= []
14
+ @user[name][:groups] += groups.map(&:to_s)
15
+ @user[name][:state] = State.new
16
+ @user[name][:state].apply(block) if block_given?
17
+
18
+ on_configure do
19
+ @user.each do |name, conf|
20
+ exists = Etc.getpwnam name rescue nil
21
+ sudo "useradd #{name}" unless exists
22
+ sudo "usermod --groups #{groups.join(",")} #{name}" if groups.any?
23
+
24
+ fork do
25
+ currentuser = Etc.getpwnam(name)
26
+ Process::GID.change_privilege(currentuser.gid)
27
+ Process::UID.change_privilege(currentuser.uid)
28
+ ENV['XDG_RUNTIME_DIR'] = "/run/user/#{currentuser.uid}"
29
+ ENV['HOME'] = currentuser.dir
30
+ ENV['USER'] = currentuser.name
31
+ ENV['LOGNAME'] = currentuser.name
32
+ conf[:state].run_steps
33
+ end
34
+
35
+ Process.wait
36
+ end
37
+ end
38
+ end
data/lib/utils.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  require 'etc'
2
2
 
3
+ # @group Utilities: Methods for logging and small predicates
4
+
3
5
  # Prints a message to the STDOUT
4
- #
5
- # @param [String] msg a log message to print
6
- #
7
- # @param [Hash<String, Object>] args prints each key and value in separate lines after message
6
+ # @param msg [String] a log message to print
7
+ # @param args [Hash<String, Object>] prints each key and value in separate lines after message
8
8
  def log(msg, args={})
9
9
  puts msg
10
10
 
@@ -22,10 +22,13 @@ def log(msg, args={})
22
22
  end
23
23
  end
24
24
 
25
+ # Checks if current user is the root
26
+ # @return [Boolean] true if current user is root and false otherwise
25
27
  def root?
26
28
  Process.uid == Etc.getpwnam('root').uid
27
29
  end
28
30
 
31
+ # Runs the command with sudo if current user is not root
29
32
  def sudo(command)
30
33
  root? ? system(command) : system("sudo #{command}")
31
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: archlinux
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emad Elsaid
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-18 00:00:00.000000000 Z
11
+ date: 2024-03-04 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -18,9 +18,14 @@ extra_rdoc_files: []
18
18
  files:
19
19
  - README.md
20
20
  - archlinux.gemspec
21
+ - lib/applications/ufw.rb
21
22
  - lib/archlinux.rb
22
23
  - lib/core.rb
23
- - lib/declarations.rb
24
+ - lib/declarations/file.rb
25
+ - lib/declarations/git.rb
26
+ - lib/declarations/pacman.rb
27
+ - lib/declarations/systemd.rb
28
+ - lib/declarations/user.rb
24
29
  - lib/utils.rb
25
30
  homepage: https://github.com/emad-elsaid/archlinux
26
31
  licenses:
data/lib/declarations.rb DELETED
@@ -1,243 +0,0 @@
1
- require 'set'
2
- require 'fileutils'
3
-
4
- # ==============================================================
5
- # DECLARATIONS:
6
- # Functions the user will run to declare the state of the system
7
- # like packages to be present, files, services, user, group...etc
8
- # ==============================================================
9
-
10
- # Install a package on install step and remove packages not registered with this
11
- # function
12
- def package(*names)
13
- names.flatten!
14
- @packages ||= Set.new
15
- @packages += names.map(&:to_s)
16
-
17
- # install step to install packages required and remove not required
18
- on_install do
19
- # install packages list as is
20
- names = @packages.join(" ")
21
- log "Installing packages", packages: @packages
22
- sudo "pacman --noconfirm --needed -S #{names}" unless @packages.empty?
23
-
24
- # expand groups to packages
25
- group_packages = Set.new(`pacman --quiet -Sg #{names}`.lines.map(&:strip))
26
-
27
- # full list of packages that should exist on the system
28
- all = @packages + group_packages
29
-
30
- # actual list on the system
31
- installed = Set.new(`pacman -Q --quiet --explicit --unrequired --native`.lines.map(&:strip))
32
-
33
- unneeded = installed - all
34
- next if unneeded.empty?
35
-
36
- log "Removing packages", packages: unneeded
37
- sudo("pacman -Rs #{unneeded.join(" ")}")
38
- end
39
-
40
- end
41
-
42
- # aur command to install packages from aur on install step
43
- def aur(*names)
44
- names.flatten!
45
- @aurs ||= Set.new
46
- @aurs += names.map(&:to_s)
47
-
48
- on_install do
49
- names = @aurs || []
50
- log "Install AUR packages", packages: names
51
- cache = "./cache/aur"
52
- FileUtils.mkdir_p cache
53
- Dir.chdir cache do
54
- names.each do |package|
55
- system("git clone --depth 1 --shallow-submodules https://aur.archlinux.org/#{package}.git") unless Dir.exists?(package)
56
- Dir.chdir package do
57
- system("makepkg --syncdeps --install --noconfirm --needed")
58
- end
59
- end
60
- end
61
- end
62
- end
63
-
64
- # set timezone and NTP settings during prepare step
65
- def timedate(timezone: 'UTC', ntp: true)
66
- @timedate = {timezone: timezone, ntp: ntp}
67
-
68
- on_configure do
69
- log "Set timedate", @timedate
70
- sudo "timedatectl set-timezone #{@timedate[:timezone]}"
71
- sudo "timedatectl set-ntp #{@timedate[:ntp]}"
72
- end
73
- end
74
-
75
- # enable system service if root or user service if not during finalize step
76
- def service(*names)
77
- names.flatten!
78
- @services ||= Set.new
79
- @services += names.map(&:to_s)
80
-
81
- on_finalize do
82
- log "Enable services", services: @services
83
- user_flags = root? ? "" : "--user"
84
-
85
- system "systemctl enable #{user_flags} #{@services.join(" ")}"
86
-
87
- # Disable services that were enabled manually and not in the list we have
88
- services = `systemctl list-unit-files #{user_flags} --state=enabled --type=service --no-legend --no-pager`
89
- enabled_manually = services.lines.map{|l| l.strip.split(/\s+/) }.select{|l| (l[1] == 'enabled') && (l[2] == 'disabled')}
90
- names_without_extension = enabled_manually.map{|l| l.first.delete_suffix(".service") }
91
- to_disable = names_without_extension - @services.to_a
92
-
93
- next if to_disable.empty?
94
-
95
- log "Services to disable", packages: to_disable
96
- # system "systemctl disable #{user_flags} #{to_disable.join(" ")}"
97
- end
98
- end
99
-
100
- # enable system timer if root or user timer if not during finalize step
101
- def timer(*names)
102
- names.flatten!
103
- @timers ||= Set.new
104
- @timers += names.map(&:to_s)
105
-
106
- on_finalize do
107
- log "Enable timers", timers: @timers
108
- timers = @timers.map{ |t| "#{t}.timer" }.join(" ")
109
- if root?
110
- sudo "systemctl enable #{timers}"
111
- else
112
- system "systemctl enable --user #{timers}"
113
- end
114
- # disable all other timers
115
- end
116
- end
117
-
118
- # set keyboard settings during prepare step
119
- def keyboard(keymap: nil, layout: nil, model: nil, variant: nil, options: nil)
120
- @keyboard ||= {}
121
- values = {
122
- keymap: keymap,
123
- layout: layout,
124
- model: model,
125
- variant: variant,
126
- options: options
127
- }.compact
128
- @keyboard.merge!(values)
129
-
130
- on_prepare do
131
- next unless @keyboard[:keymap]
132
-
133
- sudo "localectl set-keymap #{@keyboard[:keymap]}"
134
-
135
- m = @keyboard.to_h.slice(:layout, :model, :variant, :options)
136
- sudo "localectl set-x11-keymap \"#{m[:layout]}\" \"#{m[:model]}\" \"#{m[:variant]}\" \"#{m[:options]}\""
137
- end
138
- end
139
-
140
- def locale(value)
141
- @locale = value
142
-
143
- on_prepare do
144
- sudo "localectl set-locale #{@locale}"
145
- end
146
- end
147
-
148
- # create a user and assign a set of group. if block is passes the block will run
149
- # in as this user. block will run during the configure step
150
- def user(name, groups: [], &block)
151
- name = name.to_s
152
-
153
- @user ||= {}
154
- @user[name] ||= {}
155
- @user[name][:groups] ||= []
156
- @user[name][:groups] += groups.map(&:to_s)
157
- @user[name][:state] = State.new
158
- @user[name][:state].apply(block) if block_given?
159
-
160
- on_configure do
161
- @user.each do |name, conf|
162
- exists = Etc.getpwnam name rescue nil
163
- sudo "useradd #{name}" if exists
164
- sudo "usermod --groups #{groups.join(",")} #{name}" if groups.any?
165
-
166
- fork do
167
- currentuser = Etc.getpwnam(name)
168
- Process::GID.change_privilege(currentuser.gid)
169
- Process::UID.change_privilege(currentuser.uid)
170
- ENV['XDG_RUNTIME_DIR'] = "/run/user/#{currentuser.uid}"
171
- conf[:state].run_steps
172
- end
173
-
174
- Process.wait
175
- end
176
- end
177
- end
178
-
179
- # Copy src inside dest during configure step, if src/. will copy src content to dest
180
- def copy(src, dest)
181
- @copy ||= []
182
- @copy << { src: src, dest: dest }
183
-
184
- on_configure do
185
- next unless @copy
186
- next if @copy.empty?
187
-
188
- @copy.each do |item|
189
- log "Copying", item
190
- FileUtils.cp_r item[:src], item[:dest]
191
- end
192
- end
193
- end
194
-
195
- # Replace a regex pattern with replacement string in a file during configure step
196
- def replace(file, pattern, replacement)
197
- @replace ||= []
198
- @replace << {file: file, pattern: pattern, replacement: replacement}
199
-
200
- on_configure do
201
- @replace.each do |params|
202
- input = File.read(params[:file])
203
- output = input.gsub(params[:pattern], params[:replacement])
204
- File.write(params[:file], output)
205
- end
206
- end
207
- end
208
-
209
- # setup add ufw enable it and allow ports during configure step
210
- def firewall(*allow)
211
- @firewall ||= Set.new
212
- @firewall += allow.map(&:to_s)
213
-
214
- package :ufw
215
- service :ufw
216
-
217
- on_configure do
218
- sudo "ufw allow #{@firewall.join(' ')}"
219
- end
220
- end
221
-
222
- # Write a file during configure step
223
- def file(path, content)
224
- @files ||= {}
225
- @files[path] = content
226
-
227
- on_configure do
228
- @files.each do |path, content|
229
- File.write(path, content)
230
- end
231
- end
232
- end
233
-
234
- def hostname(name)
235
- @hostname = name
236
-
237
- file '/etc/hostname', "#{@hostname}\n"
238
-
239
- on_configure do
240
- log "Setting hostname", hostname: @hostname
241
- sudo "hostnamectl set-hostname #{@hostname}"
242
- end
243
- end