archlinux 0.0.1 → 0.2.0
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/.rubocop.yml +22 -0
- data/README.md +7 -1
- data/archlinux.gemspec +4 -2
- data/lib/archlinux.rb +1 -1
- data/lib/core.rb +10 -10
- data/lib/declarations/file.rb +21 -4
- data/lib/declarations/git.rb +1 -1
- data/lib/declarations/pacman.rb +26 -20
- data/lib/declarations/systemd.rb +16 -9
- data/lib/declarations/user.rb +12 -2
- data/lib/utils.rb +2 -2
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a503802bfcc8eecbd6b5605d96f6cf9de4ded00319ec74c9a091d56840fd86b
|
4
|
+
data.tar.gz: 31a14e3c70aed70bad24a5a716fb3088093866ea2db7c7e46b87ca3c101aad24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af974b69974f277bf426b912c7619ba7fc6d87ea4fc7dfa86b5858c6abe9b2a6b862d8d32359344d566c2d4a59fd92a74be14ef3fa4bb1cbe955887c07321e80
|
7
|
+
data.tar.gz: 712fc0f6b8035b038facd41ea33df1185be0819d77f0b11caf91cc08440d3432d1765e1eb652aa83865037d6e8d1f0d464475222dcf77264d6acd5493bfe91e2
|
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
|
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
|
8
|
-
s.licenses
|
7
|
+
s.version = '0.2.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
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
|
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 ||=
|
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 ||=
|
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 ||=
|
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 ||=
|
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.
|
54
|
+
step.each_value { |s| apply(s) }
|
55
55
|
end
|
56
56
|
|
57
57
|
# @group Core:
|
data/lib/declarations/file.rb
CHANGED
@@ -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,32 @@ 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
|
+
|
50
|
+
# Check if file exists and is a symlink
|
51
|
+
if File.exist?(link_name) && File.symlink?(link_name)
|
52
|
+
links_to = File.readlink(link_name)
|
53
|
+
next if links_to == target # skip creating link if it's already exists
|
54
|
+
|
55
|
+
# Remove the link if it links to something else
|
56
|
+
File.unlink link_name
|
57
|
+
end
|
58
|
+
|
45
59
|
log "Linking", target: target, link_name: link_name
|
46
60
|
|
47
61
|
# make the parent if it doesn't exist
|
48
62
|
dest_dir = File.dirname(link_name)
|
49
|
-
FileUtils.mkdir_p(dest_dir)
|
63
|
+
FileUtils.mkdir_p(dest_dir)
|
50
64
|
|
51
65
|
# link with force
|
52
66
|
FileUtils.ln_s(target, link_name, force: true)
|
@@ -74,7 +88,10 @@ def file(path, content)
|
|
74
88
|
|
75
89
|
on_configure do
|
76
90
|
@files.each do |path, content|
|
91
|
+
FileUtils.mkdir_p File.dirname(path)
|
77
92
|
File.write(path, content)
|
93
|
+
rescue Errno::ENOENT => e
|
94
|
+
log "Error: Can't write file", file: path, error: e
|
78
95
|
end
|
79
96
|
end
|
80
97
|
end
|
data/lib/declarations/git.rb
CHANGED
data/lib/declarations/pacman.rb
CHANGED
@@ -19,29 +19,28 @@ def package(*names)
|
|
19
19
|
|
20
20
|
# install step to install packages required and remove not required
|
21
21
|
on_install do
|
22
|
-
#
|
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
|
22
|
+
# Expand groups to packages
|
31
23
|
packages_args = @packages.join(" ")
|
32
24
|
group_packages = Set.new(`pacman --quiet -Sg #{packages_args}`.lines.map(&:strip))
|
33
25
|
|
34
|
-
# full list of packages that should exist on the system
|
35
|
-
all = @packages + group_packages
|
26
|
+
all = @packages + group_packages # full list of packages that should exist on the system
|
36
27
|
|
37
28
|
# actual list on the system
|
38
29
|
installed = Set.new(`pacman -Q --quiet --explicit --unrequired --native`.lines.map(&:strip))
|
39
30
|
|
40
31
|
unneeded = installed - all
|
41
|
-
|
32
|
+
if unneeded.any?
|
33
|
+
log "Removing packages", packages: unneeded
|
34
|
+
sudo("pacman -Rsu #{unneeded.join(" ")}")
|
35
|
+
end
|
42
36
|
|
43
|
-
|
44
|
-
|
37
|
+
# install missing packages
|
38
|
+
need_install = @packages.reject { |p| package? p }
|
39
|
+
need_install_args = need_install.join(" ")
|
40
|
+
if need_install.any?
|
41
|
+
log "Installing packages", packages: need_install
|
42
|
+
sudo "pacman --noconfirm --needed -S #{need_install_args}"
|
43
|
+
end
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
@@ -52,23 +51,30 @@ def aur(*names)
|
|
52
51
|
@aurs += names.map(&:to_s)
|
53
52
|
|
54
53
|
on_install do
|
55
|
-
|
56
|
-
log "Install AUR packages", packages: names
|
54
|
+
log "Install AUR packages", packages: @aurs
|
57
55
|
cache = "./cache/aur"
|
58
56
|
FileUtils.mkdir_p cache
|
59
57
|
Dir.chdir cache do
|
60
|
-
|
61
|
-
|
58
|
+
@aurs.each do |package|
|
59
|
+
unless Dir.exist?(package)
|
60
|
+
system("git clone --depth 1 --shallow-submodules https://aur.archlinux.org/#{package}.git")
|
61
|
+
end
|
62
62
|
Dir.chdir package do
|
63
|
-
|
64
63
|
pkgbuild = File.readlines('PKGBUILD')
|
65
64
|
pkgver = pkgbuild.find { |l| l.start_with?('pkgver=') }.split('=')[1].strip.chomp('"')
|
66
|
-
package_info = `pacman -Qi #{package}`.strip.lines.
|
65
|
+
package_info = `pacman -Qi #{package}`.strip.lines.to_h { |l| l.strip.split(/\s*:\s*/, 2) }
|
67
66
|
installed = package_info["Version"].to_s.split("-")[0] == pkgver
|
68
67
|
|
69
68
|
system("makepkg --syncdeps --install --noconfirm --needed") unless installed
|
70
69
|
end
|
71
70
|
end
|
72
71
|
end
|
72
|
+
|
73
|
+
foreign = Set.new(`pacman -Qm`.lines.map { |l| l.split(/\s+/, 2).first })
|
74
|
+
unneeded = foreign - @aurs
|
75
|
+
next if unneeded.empty?
|
76
|
+
|
77
|
+
log "Foreign packages to remove", packages: unneeded
|
78
|
+
sudo("pacman -Rsu #{unneeded.join(" ")}")
|
73
79
|
end
|
74
80
|
end
|
data/lib/declarations/systemd.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/declarations/user.rb
CHANGED
@@ -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][:
|
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
|
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
|
4
|
+
version: 0.2.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-
|
11
|
+
date: 2024-04-20 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
|
- - ">="
|