archlinux 0.0.0 → 0.0.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: 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