Skip to content

Commit 79c13b2

Browse files
committed
python/root: Add Alpine setup script
Signed-off-by: Nathan Chancellor <[email protected]>
1 parent cfa3909 commit 79c13b2

File tree

2 files changed

+162
-0
lines changed

2 files changed

+162
-0
lines changed

python/root/alpine.py

+158
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#!/usr/bin/env python3
2+
# SPDX-License-Identifier: MIT
3+
# Copyright (C) 2022 Nathan Chancellor
4+
5+
from argparse import ArgumentParser
6+
import os
7+
from pathlib import Path
8+
import re
9+
import shutil
10+
import subprocess
11+
import time
12+
13+
import lib_root
14+
15+
16+
def parse_arguments():
17+
parser = ArgumentParser(description='Set up an Alpine installation')
18+
19+
parser.add_argument('-n',
20+
'--user-name',
21+
default=lib_root.get_user(),
22+
help='Name of user account')
23+
parser.add_argument('-p', '--user-password', help='Password for user account', required=True)
24+
25+
return parser.parse_args()
26+
27+
28+
def enable_community_repo():
29+
repo_conf = Path('/etc/apk/repositories')
30+
repo_txt = repo_conf.read_text(encoding='utf-8')
31+
32+
# Get the repository URL to create the community repo from (build from the
33+
# first uncommented line ending in main).
34+
if not (repo_url := re.search('^([^#].*/)main$', repo_txt, flags=re.M).groups()[0]):
35+
raise Exception(f"Could not find main repo in {repo_conf}?")
36+
community_repo = repo_url + 'community'
37+
38+
# If the community repo is already enabled (uncommented), we do not need to
39+
# do anything.
40+
if (match := re.search(f"^#{community_repo}$", repo_txt, flags=re.M)):
41+
repo_conf.write_text(re.sub(match.group(0), community_repo, repo_txt), encoding='utf-8')
42+
43+
44+
def update_and_install_packages():
45+
packages = [
46+
# Development
47+
'hyperfine',
48+
'podman',
49+
50+
# env
51+
'curl',
52+
'fish',
53+
'fzf',
54+
'jq',
55+
'neofetch',
56+
'stow',
57+
'tmux',
58+
'vim',
59+
'zoxide',
60+
61+
# git
62+
'delta',
63+
'git',
64+
65+
# Nicer GNU utilities
66+
'bat',
67+
'exa',
68+
'fd',
69+
'ripgrep',
70+
71+
# System management
72+
'doas',
73+
]
74+
lib_root.apk(['update'])
75+
lib_root.apk(['upgrade'])
76+
lib_root.apk(['add', *packages])
77+
78+
79+
def setup_user(user_name, user_password):
80+
if not lib_root.user_exists(user_name):
81+
useradd_cmd = [
82+
'adduser',
83+
'--disabled-password',
84+
'--gecos', 'Nathan Chancellor',
85+
'--shell', shutil.which('fish'),
86+
user_name
87+
] # yapf: disable
88+
subprocess.run(useradd_cmd, check=True)
89+
lib_root.chpasswd(user_name, user_password)
90+
91+
user_groups = [
92+
# Default setup-alpine
93+
'audio',
94+
'netdev',
95+
'video',
96+
# Mine
97+
'kvm',
98+
'wheel',
99+
]
100+
for group in user_groups:
101+
subprocess.run(['addgroup', user_name, group], check=True)
102+
103+
# Setup doas
104+
doas_conf = Path('/etc/doas.d/doas.conf')
105+
doas_wheel = 'permit persist :wheel'
106+
if not re.search(doas_wheel, doas_conf.read_text(encoding='utf-8')):
107+
with open(doas_conf, 'a', encoding='utf-8') as file:
108+
file.write(doas_wheel + '\n')
109+
110+
# Authorize my ssh key
111+
if not (ssh_authorized_keys := Path('/home', user_name, '.ssh', 'authorized_keys')).exists():
112+
old_umask = os.umask(0o077)
113+
ssh_authorized_keys.parent.mkdir(exist_ok=True, parents=True)
114+
ssh_key = subprocess.run(['wget', '-q', '-O-', 'https://github.com/nathanchance.keys'],
115+
capture_output=True,
116+
check=True).stdout
117+
ssh_authorized_keys.write_bytes(ssh_key)
118+
os.umask(old_umask)
119+
lib_root.chown(user_name, ssh_authorized_keys.parent)
120+
121+
122+
# https://wiki.alpinelinux.org/wiki/Podman
123+
def setup_podman(user_name):
124+
# Set up cgroupsv2
125+
rc_conf = Path('/etc/rc.conf')
126+
rc_conf_txt = rc_conf.read_text(encoding='utf-8')
127+
rc_cgroup_mode = 'rc_cgroup_mode="unified"'
128+
if not re.search(f"^{rc_cgroup_mode}$", rc_conf_txt, flags=re.M):
129+
rc_cgroup_mode_line = re.search('^#?rc_cgroup_mode=.*$', rc_conf_txt, flags=re.M).group(0)
130+
rc_conf.write_text(re.sub(rc_cgroup_mode_line, rc_cgroup_mode, rc_conf_txt),
131+
encoding='utf-8')
132+
133+
subprocess.run(['rc-update', 'add', 'cgroups'], check=True)
134+
subprocess.run(['rc-service', 'cgroups', 'start'], check=True)
135+
136+
subprocess.run(['modprobe', 'tun'], check=True)
137+
etc_modules = Path('/etc/modules')
138+
if not re.search('tun', etc_modules.read_text(encoding='utf-8')):
139+
with open(etc_modules, mode='a', encoding='utf-8') as file:
140+
file.write('tun\n')
141+
142+
lib_root.podman_setup(user_name)
143+
144+
145+
if __name__ == '__main__':
146+
args = parse_arguments()
147+
148+
lib_root.check_root()
149+
enable_community_repo()
150+
update_and_install_packages()
151+
setup_user(args.user_name, args.user_password)
152+
setup_podman(args.user_name)
153+
lib_root.setup_sudo_symlink()
154+
lib_root.setup_initial_fish_config(args.user_name)
155+
156+
print("[INFO] Powering off machine in 10 seconds, hit Ctrl-C to cancel...")
157+
time.sleep(10)
158+
subprocess.run('poweroff', check=True)

python/root/lib_root.py

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ def add_user_to_group_if_exists(groupname, username):
2424
add_user_to_group(groupname, username)
2525

2626

27+
def apk(apk_arguments):
28+
subprocess.run(['apk', *apk_arguments], check=True)
29+
30+
2731
def apt(apt_arguments):
2832
subprocess.run(['apt', *apt_arguments], check=True)
2933

0 commit comments

Comments
 (0)