archlinux 0.0.1 → 0.1.0

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: 0b2ec1f095b419f4c323bee8fa6f9f1f7215ab0e4b785c3922de9db10e5a0413
4
- data.tar.gz: 84fe5ee8d792c03b5a62bac375114c82dcb2d1448dfe55f2a1de33a77f90a8df
3
+ metadata.gz: c5a6439dbf4a18b2f03c70c67dd3707dbe0aa860e147bbb86242a5540173fe09
4
+ data.tar.gz: 2b56713549a7861cf2602e3132f66c94a66d10419de67fe89ea973a32724e6bc
5
5
  SHA512:
6
- metadata.gz: c12e913857365c3390faa570f1ffee57a577628d831efb9d974c83ecf4f5b6784c4fb901b2ed09e63d5c5a1bbc025e7b636add7a7801c31e4f2c0b01c8bf68b0
7
- data.tar.gz: c74297e0f98dd6c9518420e1d28d2193c5a56f97749ae596a1c6be398ea3f1bfc630e814d5fd889315ce39fcac2030024157902bb954a7ffa212bfd827535fa3
6
+ metadata.gz: 69423c894c111c99ca618d9b86e17f10e7b040edd8e2facb06fa6dd599fb37d1b4011162da28a13dc73e2f15859437c36cb5284e1b0e393f25c014da5543ea9d
7
+ data.tar.gz: 4823b44bfdb3ca749657bf81d1b003cd5abb72f08d822d5880890a8cd6b561855f05f5df46b618b48ede8cbe3874f999e3f2372016d50e200a988ca3c73196bf
data/.rubocop.yml ADDED
@@ -0,0 +1,22 @@
1
+ AllCops:
2
+ NewCops: enable
3
+ TargetRubyVersion: 3.0
4
+
5
+ Style/StringLiterals:
6
+ Enabled: false
7
+ Style/FrozenStringLiteralComment:
8
+ Enabled: false
9
+ Style/StringLiteralsInInterpolation:
10
+ Enabled: false
11
+ Metrics/MethodLength:
12
+ Max: 100
13
+ Lint/ShadowingOuterLocalVariable:
14
+ Enabled: false
15
+ Style/RescueModifier:
16
+ Enabled: false
17
+ Metrics/AbcSize:
18
+ Enabled: false
19
+ Metrics/CyclomaticComplexity:
20
+ Enabled: false
21
+ Metrics/PerceivedComplexity:
22
+ Enabled: false
data/README.md CHANGED
@@ -116,4 +116,10 @@ It will do the following:
116
116
 
117
117
  ## Declarations:
118
118
 
119
- Functions the user will run to declare the state of the system like packages to be present, files, services, user, group...etc
119
+ Functions the user will run to declare the state of the system like packages to
120
+ be present, files, services, user, group...etc
121
+
122
+ ## Utilities:
123
+
124
+ Methods for logging and small predicates, technically any ruby method is a
125
+ utility. calling it executes the code directly instead of declaring a state.
data/archlinux.gemspec CHANGED
@@ -4,6 +4,8 @@ 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.1'
8
- s.licenses = ["GPL-3.0-or-later"]
7
+ s.version = '0.1.0'
8
+ s.licenses = ["GPL-3.0-or-later"]
9
+ s.metadata['rubygems_mfa_required'] = 'true'
10
+ s.required_ruby_version = '>=3.0'
9
11
  end
data/lib/archlinux.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  def require_relative_dir(dir)
2
- Dir["#{File.dirname(__FILE__)}/#{dir}/**/*.rb"].sort.each { |f| require f }
2
+ Dir["#{File.dirname(__FILE__)}/#{dir}/**/*.rb"].each { |f| require f }
3
3
  end
4
4
 
5
5
  require_relative 'core'
data/lib/core.rb CHANGED
@@ -3,35 +3,35 @@
3
3
  # of this class
4
4
  class State
5
5
  def apply(block)
6
- instance_eval &block
6
+ instance_eval(&block)
7
7
  end
8
8
 
9
9
  # Run block on prepare step. id identifies the block uniqueness in the steps.
10
10
  # registering a block with same id multiple times replaces old block by new
11
11
  # one. if id is nil the block location in source code is used as an id
12
- def on_prepare(id=nil, &block)
13
- id ||= caller_locations(1,1).first.to_s
12
+ def on_prepare(id = nil, &block)
13
+ id ||= caller_locations(1, 1).first.to_s
14
14
  @prepare_steps ||= {}
15
15
  @prepare_steps[id] = block
16
16
  end
17
17
 
18
18
  # Same as {#on_prepare} but for install step
19
- def on_install(id=nil, &block)
20
- id ||= caller_locations(1,1).first.to_s
19
+ def on_install(id = nil, &block)
20
+ id ||= caller_locations(1, 1).first.to_s
21
21
  @install_steps ||= {}
22
22
  @install_steps[id] = block
23
23
  end
24
24
 
25
25
  # Same as {.on_prepare} but for configure step
26
- def on_configure(id=nil, &block)
27
- id ||= caller_locations(1,1).first.to_s
26
+ def on_configure(id = nil, &block)
27
+ id ||= caller_locations(1, 1).first.to_s
28
28
  @configure_steps ||= {}
29
29
  @configure_steps[id] = block
30
30
  end
31
31
 
32
32
  # Same as {.on_prepare} but for configure step
33
- def on_finalize(id=nil, &block)
34
- id ||= caller_locations(1,1).first.to_s
33
+ def on_finalize(id = nil, &block)
34
+ id ||= caller_locations(1, 1).first.to_s
35
35
  @finalize_steps ||= {}
36
36
  @finalize_steps[id] = block
37
37
  end
@@ -51,7 +51,7 @@ def run_step(name, step)
51
51
  return unless step&.any?
52
52
 
53
53
  log "=> #{name}"
54
- step.each { |_, s| apply(s) }
54
+ step.each_value { |s| apply(s) }
55
55
  end
56
56
 
57
57
  # @group Core:
@@ -21,7 +21,7 @@ end
21
21
  # Replace a regex pattern with replacement string in a file during configure step
22
22
  def replace(file, pattern, replacement)
23
23
  @replace ||= []
24
- @replace << {file: file, pattern: pattern, replacement: replacement}
24
+ @replace << { file: file, pattern: pattern, replacement: replacement }
25
25
 
26
26
  on_configure do
27
27
  @replace.each do |params|
@@ -35,18 +35,23 @@ end
35
35
  # link file to destination
36
36
  def symlink(target, link_name)
37
37
  @symlink ||= Set.new
38
- @symlink << {target: target, link_name: link_name}
38
+ @symlink << { target: target, link_name: link_name }
39
39
 
40
40
  on_configure do
41
-
42
41
  @symlink.each do |params|
43
42
  target = File.expand_path params[:target]
44
43
  link_name = File.expand_path params[:link_name]
44
+
45
+ if File.directory?(target)
46
+ log "Can't link directories", target: target, link_name: link_name
47
+ exit
48
+ end
49
+
45
50
  log "Linking", target: target, link_name: link_name
46
51
 
47
52
  # make the parent if it doesn't exist
48
53
  dest_dir = File.dirname(link_name)
49
- FileUtils.mkdir_p(dest_dir) unless File.exist?(dest_dir)
54
+ FileUtils.mkdir_p(dest_dir)
50
55
 
51
56
  # link with force
52
57
  FileUtils.ln_s(target, link_name, force: true)
@@ -74,7 +79,10 @@ def file(path, content)
74
79
 
75
80
  on_configure do
76
81
  @files.each do |path, content|
82
+ FileUtils.mkdir_p File.dirname(path)
77
83
  File.write(path, content)
84
+ rescue Errno::ENOENT => e
85
+ log "Error: Can't write file", file: path, error: e
78
86
  end
79
87
  end
80
88
  end
@@ -5,7 +5,7 @@ require 'set'
5
5
  # on prepare make sure a git repository is cloned to directory
6
6
  def git_clone(from:, to: nil)
7
7
  @git_clone ||= Set.new
8
- @git_clone << {from: from, to: to}
8
+ @git_clone << { from: from, to: to }
9
9
 
10
10
  on_install do
11
11
  @git_clone.each do |item|
@@ -41,7 +41,7 @@ def package(*names)
41
41
  next if unneeded.empty?
42
42
 
43
43
  log "Removing packages", packages: unneeded
44
- sudo("pacman -Rs #{unneeded.join(" ")}")
44
+ sudo("pacman -Rsu #{unneeded.join(" ")}")
45
45
  end
46
46
  end
47
47
 
@@ -52,23 +52,30 @@ def aur(*names)
52
52
  @aurs += names.map(&:to_s)
53
53
 
54
54
  on_install do
55
- names = @aurs || []
56
- log "Install AUR packages", packages: names
55
+ log "Install AUR packages", packages: @aurs
57
56
  cache = "./cache/aur"
58
57
  FileUtils.mkdir_p cache
59
58
  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)
59
+ @aurs.each do |package|
60
+ unless Dir.exist?(package)
61
+ system("git clone --depth 1 --shallow-submodules https://aur.archlinux.org/#{package}.git")
62
+ end
62
63
  Dir.chdir package do
63
-
64
64
  pkgbuild = File.readlines('PKGBUILD')
65
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
66
+ package_info = `pacman -Qi #{package}`.strip.lines.to_h { |l| l.strip.split(/\s*:\s*/, 2) }
67
67
  installed = package_info["Version"].to_s.split("-")[0] == pkgver
68
68
 
69
69
  system("makepkg --syncdeps --install --noconfirm --needed") unless installed
70
70
  end
71
71
  end
72
72
  end
73
+
74
+ foreign = Set.new(`pacman -Qm`.lines.map { |l| l.split(/\s+/, 2).first })
75
+ unneeded = foreign - @aurs
76
+ next if unneeded.empty?
77
+
78
+ log "Foreign packages to remove", packages: unneeded
79
+ sudo("pacman -Rsu #{unneeded.join(" ")}")
73
80
  end
74
81
  end
@@ -4,7 +4,7 @@ require 'set'
4
4
 
5
5
  # set timezone and NTP settings during prepare step
6
6
  def timedate(timezone: 'UTC', ntp: true)
7
- @timedate = {timezone: timezone, ntp: ntp}
7
+ @timedate = { timezone: timezone, ntp: ntp }
8
8
 
9
9
  on_configure do
10
10
  log "Set timedate", @timedate
@@ -20,21 +20,28 @@ def service(*names)
20
20
  @services += names.map(&:to_s)
21
21
 
22
22
  on_finalize do
23
- log "Enable services", services: @services
24
23
  user_flags = root? ? "" : "--user"
25
24
 
26
- system "systemctl enable #{user_flags} #{@services.join(" ")}"
25
+ services = `systemctl list-unit-files #{user_flags} --state=enabled --type=service --no-legend --no-pager`
26
+ enabled = services.lines
27
+ enabled.map! { |l| l.strip.split(/\s+/) }
28
+ enabled.each { |l| l[0].delete_suffix!(".service") }
29
+
30
+ to_enable = @services - enabled.map(&:first)
31
+
32
+ if to_enable.any?
33
+ log "Enable services", services: to_enable
34
+ system "systemctl enable #{user_flags} #{to_enable.join(" ")}"
35
+ end
27
36
 
28
37
  # 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
38
+ enabled_manually = enabled.select! { |l| l[2] == 'disabled' }.map(&:first)
33
39
 
40
+ to_disable = enabled_manually - @services.to_a
34
41
  next if to_disable.empty?
35
42
 
36
43
  log "Services to disable", packages: to_disable
37
- # system "systemctl disable #{user_flags} #{to_disable.join(" ")}"
44
+ system "systemctl disable #{user_flags} #{to_disable.join(" ")}"
38
45
  end
39
46
  end
40
47
 
@@ -46,7 +53,7 @@ def timer(*names)
46
53
 
47
54
  on_finalize do
48
55
  log "Enable timers", timers: @timers
49
- timers = @timers.map{ |t| "#{t}.timer" }.join(" ")
56
+ timers = @timers.map { |t| "#{t}.timer" }.join(" ")
50
57
  if root?
51
58
  sudo "systemctl enable #{timers}"
52
59
  else
@@ -5,14 +5,15 @@ require 'etc'
5
5
 
6
6
  # create a user and assign a set of group. if block is passes the block will run
7
7
  # in as this user. block will run during the configure step
8
- def user(name, groups: [], &block)
8
+ def user(name, groups: [], autologin: nil, &block)
9
9
  name = name.to_s
10
10
 
11
11
  @user ||= {}
12
12
  @user[name] ||= {}
13
13
  @user[name][:groups] ||= []
14
14
  @user[name][:groups] += groups.map(&:to_s)
15
- @user[name][:state] = State.new
15
+ @user[name][:autologin] = autologin unless autologin.nil?
16
+ @user[name][:state] ||= State.new
16
17
  @user[name][:state].apply(block) if block_given?
17
18
 
18
19
  on_configure do
@@ -21,6 +22,15 @@ def user(name, groups: [], &block)
21
22
  sudo "useradd #{name}" unless exists
22
23
  sudo "usermod --groups #{groups.join(",")} #{name}" if groups.any?
23
24
 
25
+ if conf[:autologin]
26
+ FileUtils.mkdir_p '/etc/systemd/system/getty@tty1.service.d'
27
+ file '/etc/systemd/system/getty@tty1.service.d/autologin.conf', <<~FILE
28
+ [Service]
29
+ ExecStart=
30
+ ExecStart=-/sbin/agetty -o '-p -f -- \\u' --noclear --autologin #{name} %I $TERM
31
+ FILE
32
+ end
33
+
24
34
  fork do
25
35
  currentuser = Etc.getpwnam(name)
26
36
  Process::GID.change_privilege(currentuser.gid)
data/lib/utils.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  require 'etc'
2
2
 
3
- # @group Utilities: Methods for logging and small predicates
3
+ # @group Utilities
4
4
 
5
5
  # Prints a message to the STDOUT
6
6
  # @param msg [String] a log message to print
7
7
  # @param args [Hash<String, Object>] prints each key and value in separate lines after message
8
- def log(msg, args={})
8
+ def log(msg, args = {})
9
9
  puts msg
10
10
 
11
11
  return unless args.any?
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.1
4
+ version: 0.1.0
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-03-04 00:00:00.000000000 Z
11
+ date: 2024-03-28 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -16,6 +16,7 @@ executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []
18
18
  files:
19
+ - ".rubocop.yml"
19
20
  - README.md
20
21
  - archlinux.gemspec
21
22
  - lib/applications/ufw.rb
@@ -30,7 +31,8 @@ files:
30
31
  homepage: https://github.com/emad-elsaid/archlinux
31
32
  licenses:
32
33
  - GPL-3.0-or-later
33
- metadata: {}
34
+ metadata:
35
+ rubygems_mfa_required: 'true'
34
36
  post_install_message:
35
37
  rdoc_options: []
36
38
  require_paths:
@@ -39,7 +41,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
39
41
  requirements:
40
42
  - - ">="
41
43
  - !ruby/object:Gem::Version
42
- version: '0'
44
+ version: '3.0'
43
45
  required_rubygems_version: !ruby/object:Gem::Requirement
44
46
  requirements:
45
47
  - - ">="