Compare commits
No commits in common. "main" and "packaging" have entirely different histories.
9 changed files with 41 additions and 661 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
*
|
||||||
|
|
||||||
|
!.gitignore
|
||||||
|
!PKGBUILD
|
||||||
|
!.SRCINFO
|
36
PKGBUILD
Normal file
36
PKGBUILD
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# Maintainer: Rudra Saraswat <rs2009@ubuntu.com>
|
||||||
|
|
||||||
|
pkgname='akshara-git'
|
||||||
|
pkgver=r59.9f2f25b
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="An update system for operating systems"
|
||||||
|
arch=('x86_64' 'i686')
|
||||||
|
url="https://git.askiiart.net/askiiart-blendos/akshara"
|
||||||
|
license=('GPL3')
|
||||||
|
makedepends=('git' 'base-devel')
|
||||||
|
source=('git+https://git.askiiart.net/askiiart-blendos/akshara')
|
||||||
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
|
depends=('bash' 'python' 'python-lockfile' 'python-psutil' 'python-fasteners' 'python-yaml' 'squashfs-tools' 'wget' 'python-requests' 'arch-install-scripts')
|
||||||
|
provides=("${pkgname%-git}")
|
||||||
|
conflicts=("${pkgname%-git}")
|
||||||
|
|
||||||
|
pkgver() {
|
||||||
|
cd "${srcdir}/${pkgbase%-git}"
|
||||||
|
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "${srcdir}/${pkgbase%-git}"
|
||||||
|
install -Dm755 \
|
||||||
|
"${pkgname%-git}" \
|
||||||
|
-t "${pkgdir}"/usr/bin/
|
||||||
|
install -Dm644 "${pkgname%-git}.service" -t \
|
||||||
|
"${pkgdir}"/usr/lib/systemd/system/
|
||||||
|
install -Dm644 "${pkgname%-git}-system-update.service" -t \
|
||||||
|
"${pkgdir}"/usr/lib/systemd/system/
|
||||||
|
install -Dm644 "${pkgname%-git}.hook" \
|
||||||
|
"${pkgdir}/usr/lib/initcpio/hooks/${pkgname%-git}"
|
||||||
|
install -Dm644 "${pkgname%-git}.install" \
|
||||||
|
"${pkgdir}/usr/lib/initcpio/install/${pkgname%-git}"
|
||||||
|
}
|
15
README.md
15
README.md
|
@ -1,15 +0,0 @@
|
||||||
# akshara
|
|
||||||
|
|
||||||
A simple system builder and immutability layer.
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
To test a modified copy of `akshara`, run the following on a working blendOS install:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
umount -l /usr && sudo mv ./akshara /usr/bin/akshara && sudo chmod +x /usr/bin/akshara
|
|
||||||
```
|
|
||||||
|
|
||||||
Replace `./akshara` with wherever your modified copy of `akshara` is.
|
|
||||||
|
|
||||||
⚠ **ANY CHANGES TO `/usr/bin/akshara` WILL BE REVERTED AFTER EVERY UPDATE!** ⚠
|
|
532
akshara
532
akshara
|
@ -1,532 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
# Copyright (C) 2023 Rudra Saraswat
|
|
||||||
#
|
|
||||||
# This file is part of akshara.
|
|
||||||
#
|
|
||||||
# akshara is free software: you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# akshara is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with akshara. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import yaml
|
|
||||||
import filecmp
|
|
||||||
import argparse
|
|
||||||
import requests
|
|
||||||
import fasteners
|
|
||||||
import subprocess
|
|
||||||
from datetime import datetime
|
|
||||||
__version = '1.0.0'
|
|
||||||
|
|
||||||
|
|
||||||
# Colors
|
|
||||||
|
|
||||||
|
|
||||||
class colors:
|
|
||||||
reset = '\033[0m'
|
|
||||||
bold = '\033[01m'
|
|
||||||
disable = '\033[02m'
|
|
||||||
underline = '\033[04m'
|
|
||||||
reverse = '\033[07m'
|
|
||||||
strikethrough = '\033[09m'
|
|
||||||
invisible = '\033[08m'
|
|
||||||
|
|
||||||
class fg:
|
|
||||||
black = '\033[30m'
|
|
||||||
red = '\033[31m'
|
|
||||||
green = '\033[32m'
|
|
||||||
orange = '\033[33m'
|
|
||||||
blue = '\033[34m'
|
|
||||||
purple = '\033[35m'
|
|
||||||
cyan = '\033[36m'
|
|
||||||
lightgrey = '\033[37m'
|
|
||||||
darkgrey = '\033[90m'
|
|
||||||
lightred = '\033[91m'
|
|
||||||
lightgreen = '\033[92m'
|
|
||||||
yellow = '\033[93m'
|
|
||||||
lightblue = '\033[94m'
|
|
||||||
pink = '\033[95m'
|
|
||||||
lightcyan = '\033[96m'
|
|
||||||
|
|
||||||
class bg:
|
|
||||||
black = '\033[40m'
|
|
||||||
red = '\033[41m'
|
|
||||||
green = '\033[42m'
|
|
||||||
orange = '\033[43m'
|
|
||||||
blue = '\033[44m'
|
|
||||||
purple = '\033[45m'
|
|
||||||
cyan = '\033[46m'
|
|
||||||
lightgrey = '\033[47m'
|
|
||||||
|
|
||||||
|
|
||||||
def exec(*cmd, **kwargs):
|
|
||||||
return subprocess.call(cmd, shell=False, stdout=sys.stdout, stderr=sys.stderr, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def exec_chroot(*cmd, **kwargs):
|
|
||||||
exec('mount', '--bind', '/.new_rootfs', '/.new_rootfs')
|
|
||||||
ret = exec('arch-chroot', '/.new_rootfs', *cmd, **kwargs)
|
|
||||||
exec('umount', '-l', '/.new_rootfs')
|
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
fg = colors.fg()
|
|
||||||
|
|
||||||
|
|
||||||
def info(msg):
|
|
||||||
print(colors.bold + fg.cyan + '[INFO] ' +
|
|
||||||
colors.reset + msg + colors.reset)
|
|
||||||
|
|
||||||
|
|
||||||
def warn(warning):
|
|
||||||
print(colors.bold + fg.yellow + '[WARNING] ' +
|
|
||||||
colors.reset + warning + colors.reset)
|
|
||||||
|
|
||||||
|
|
||||||
def error(err):
|
|
||||||
print(colors.bold + fg.red + '[ERROR] ' +
|
|
||||||
colors.reset + err + colors.reset)
|
|
||||||
|
|
||||||
|
|
||||||
def interpret_track(blend_release):
|
|
||||||
result = yaml.safe_load(requests.get(blend_release.get('impl') + '/' + blend_release.get('track') + '.yaml', allow_redirects=True).content.decode())
|
|
||||||
|
|
||||||
if (type(result.get('impl')) == str and
|
|
||||||
type(result.get('track')) != 'custom'):
|
|
||||||
res = interpret_track(result)
|
|
||||||
|
|
||||||
for i in res.keys():
|
|
||||||
if type(res[i]) is list:
|
|
||||||
if type(result.get(i)) is list:
|
|
||||||
result[i] = res[i] + result[i]
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def update_system():
|
|
||||||
benchmark = True
|
|
||||||
|
|
||||||
if os.path.isdir('/.update_rootfs'):
|
|
||||||
error('update already downloaded, you must reboot first')
|
|
||||||
sys.exit(75)
|
|
||||||
|
|
||||||
os.chdir('/')
|
|
||||||
|
|
||||||
if exec('rm', '-rf', '/.new_rootfs', '/.new.etc', '/.new.var.lib') != 0:
|
|
||||||
exec('umount', '-l', '/.new_rootfs')
|
|
||||||
exec('rm', '-rf', '/.new_rootfs', '/.new.etc', '/.new.var.lib')
|
|
||||||
|
|
||||||
# Check if update is available
|
|
||||||
if not os.path.isfile('/system.yaml'):
|
|
||||||
error('system.yaml does not exist in /')
|
|
||||||
sys.exit(100)
|
|
||||||
|
|
||||||
with open('/system.yaml') as blend_release_file:
|
|
||||||
blend_release = yaml.load(
|
|
||||||
blend_release_file, Loader=yaml.FullLoader)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Add check that all packages actually exist
|
|
||||||
|
|
||||||
info('downloading Arch tarball...')
|
|
||||||
|
|
||||||
# TODO: currently it errors if it doesn't have arch-repo anyways, so this doesn't need any extra checking, maybe add a check for that later though
|
|
||||||
# The mirror to use for downloading the bootstrap image
|
|
||||||
# For example, for the Arch mirror at mirrors.acm.wpi.edu, you'd use https://mirrors.acm.wpi.edu/archlinux
|
|
||||||
# Not sure why this wouldn't just use `arch-repo` but whatever
|
|
||||||
bootstrap_mirror = blend_release.get("arch-repo")
|
|
||||||
|
|
||||||
if not os.path.isfile('/.update.tar.zst'):
|
|
||||||
if exec('wget', '-q', '--show-progress', f'{bootstrap_mirror}/iso/latest/archlinux-bootstrap-x86_64.tar.zst', '-O', '/.update.tar.zst') != 0:
|
|
||||||
warn('failed download')
|
|
||||||
print()
|
|
||||||
info('trying download again...')
|
|
||||||
print()
|
|
||||||
exec('rm', '-f', '/.update.tar.zst')
|
|
||||||
if exec('wget', '-q', '--show-progress', f'{bootstrap_mirror}/iso/latest/archlinux-bootstrap-x86_64.tar.zst', '-O', '/.update.tar.zst') != 0:
|
|
||||||
error('failed download')
|
|
||||||
print()
|
|
||||||
error('update failed')
|
|
||||||
sys.exit(50)
|
|
||||||
|
|
||||||
if exec('bash', '-c', f'sha256sum -c --ignore-missing <(wget -qO- {bootstrap_mirror}/iso/latest/sha256sums.txt | sed "s/archlinux-bootstrap-x86_64\\.tar\\.zst/.update.tar.zst/g") 2>/dev/null') != 0:
|
|
||||||
error('failed checksum verification')
|
|
||||||
print()
|
|
||||||
info('trying download again...')
|
|
||||||
exec('rm', '-f', '/.update.tar.zst')
|
|
||||||
if exec('wget', '-q', '--show-progress', f'{bootstrap_mirror}/iso/latest/archlinux-bootstrap-x86_64.tar.zst', '-O', '/.update.tar.zst') != 0:
|
|
||||||
error('failed download')
|
|
||||||
print()
|
|
||||||
error('update failed')
|
|
||||||
sys.exit(50)
|
|
||||||
return
|
|
||||||
if exec('bash', '-c', f'sha256sum -c --ignore-missing <(wget -qO- {bootstrap_mirror}/iso/latest/sha256sums.txt | sed "s/archlinux-bootstrap-x86_64\\.tar\\.zst/.update.tar.zst/g") 2>/dev/null') != 0:
|
|
||||||
error('failed checksum verification')
|
|
||||||
print()
|
|
||||||
error('update failed')
|
|
||||||
sys.exit(25)
|
|
||||||
return
|
|
||||||
|
|
||||||
info('checksum verification was successful')
|
|
||||||
|
|
||||||
print()
|
|
||||||
|
|
||||||
info('generating new system...')
|
|
||||||
|
|
||||||
before = datetime.now()
|
|
||||||
exec('tar', '--acls', '--xattrs', '-xf', '.update.tar.zst')
|
|
||||||
after = datetime.now()
|
|
||||||
if benchmark:
|
|
||||||
print(f'[BENCH]: {(before - after).seconds} seconds to extract tarball')
|
|
||||||
exec('mv', 'root.x86_64', '.new_rootfs')
|
|
||||||
exec('rm', '-f', '/.new_rootfs/pkglist.x86_64.txt')
|
|
||||||
exec('rm', '-f', '/.new_rootfs/version')
|
|
||||||
|
|
||||||
packages = [
|
|
||||||
'akshara',
|
|
||||||
'blend'
|
|
||||||
]
|
|
||||||
|
|
||||||
aur_packages = []
|
|
||||||
|
|
||||||
services = [
|
|
||||||
'akshara'
|
|
||||||
]
|
|
||||||
|
|
||||||
user_services = [
|
|
||||||
'blend-files'
|
|
||||||
]
|
|
||||||
|
|
||||||
persistent_files = []
|
|
||||||
|
|
||||||
if (type(blend_release.get('impl')) == str and
|
|
||||||
type(blend_release.get('track')) != 'custom'):
|
|
||||||
res = interpret_track(blend_release)
|
|
||||||
|
|
||||||
for i in res.keys():
|
|
||||||
if type(res[i]) is list:
|
|
||||||
if type(blend_release.get(i)) is list:
|
|
||||||
blend_release[i] += res[i]
|
|
||||||
else:
|
|
||||||
blend_release[i] = res[i]
|
|
||||||
|
|
||||||
if type(blend_release.get('packages')) == list:
|
|
||||||
packages += blend_release.get('packages')
|
|
||||||
|
|
||||||
if type(blend_release.get('aur-packages')) == list:
|
|
||||||
packages += ['fakeroot', 'paru']
|
|
||||||
aur_packages += blend_release.get('aur-packages')
|
|
||||||
|
|
||||||
if type(blend_release.get('services')) == list:
|
|
||||||
services += blend_release.get('services')
|
|
||||||
|
|
||||||
if type(blend_release.get('user-services')) == list:
|
|
||||||
user_services += blend_release.get('user-services')
|
|
||||||
|
|
||||||
if type(blend_release.get('persistent-files')) == list:
|
|
||||||
persistent_files += blend_release.get('persistent-files')
|
|
||||||
|
|
||||||
exec_chroot('rm', '-f', '/.new_rootfs/etc/resolv.conf')
|
|
||||||
|
|
||||||
with open('/.new_rootfs/etc/resolv.conf', 'w') as pacman_mirrorlist_conf:
|
|
||||||
pacman_mirrorlist_conf.write('nameserver 1.1.1.1\n')
|
|
||||||
|
|
||||||
with open('/.new_rootfs/etc/pacman.d/mirrorlist', 'w') as pacman_mirrorlist_conf:
|
|
||||||
if type(blend_release.get('arch-repo')) == str:
|
|
||||||
pacman_mirrorlist_conf.write(f'Server = {blend_release.get("arch-repo")}/$repo/os/$arch\n')
|
|
||||||
else:
|
|
||||||
pacman_mirrorlist_conf.write('Server = https://geo.mirror.pkgbuild.com/$repo/os/$arch\n')
|
|
||||||
|
|
||||||
exec_chroot('mkdir', '-p', '/var/cache/pacman/pkg')
|
|
||||||
exec_chroot('rm', '-rf', '/var/cache/pacman/pkg')
|
|
||||||
exec('cp', '-r', '/var/cache/pacman/pkg', '/.new_rootfs/var/cache/pacman')
|
|
||||||
|
|
||||||
# update packages
|
|
||||||
exec_chroot('pacman-key', '--init')
|
|
||||||
exec_chroot('pacman-key', '--populate')
|
|
||||||
|
|
||||||
# If the GitHub API is down or something, this completely breaks
|
|
||||||
# also it's broken currently and reflector is working now anyways soooooo
|
|
||||||
# commented out
|
|
||||||
#exec_chroot('sh', '-c', 'mkdir /tmp/rate-mirrors/; cd /tmp/rate-mirrors/; curl -LO $(curl -s https://api.github.com/repos/westandskif/rate-mirrors/releases/latest | grep "browser_download_url.*rate-mirrors-v.*-x86_64-unknown-linux-musl.tar.gz" | cut -d : -f 2,3 | tr -d \\" | tr -d " ")')
|
|
||||||
#exec_chroot('bash', '-c', 'cd /tmp/rate-mirrors/; tar -xzf rate-mirrors*; cd $(find /tmp/rate-mirrors/ -mindepth 1 -maxdepth 1 -type d); ./rate_mirrors --disable-comments-in-file --entry-country=US --protocol=https arch --max-delay 7200 > /etc/pacman.d/mirrorlist')
|
|
||||||
|
|
||||||
#exec_chroot('sed', 's/#//g', '-i', '/etc/pacman.d/mirrorlist')
|
|
||||||
#exec_chroot('bash', '-c', 'grep "^Server =" /etc/pacman.d/mirrorlist > /etc/pacman.d/mirrorlist.tmp; mv /etc/pacman.d/mirrorlist.tmp /etc/pacman.d/mirrorlist')
|
|
||||||
|
|
||||||
with open('/.new_rootfs/etc/pacman.conf', 'r') as original: data = original.read()
|
|
||||||
with open('/.new_rootfs/etc/pacman.conf', 'w') as modified: modified.write(data.replace("[options]", "[options]\nParallelDownloads = 32\n"))
|
|
||||||
with open('/.new_rootfs/etc/pacman.conf', 'w') as modified: modified.write(data.replace("#[multilib]\n#Include = /etc/pacman.d/mirrorlist", "[multilib]\nInclude = /etc/pacman.d/mirrorlist"))
|
|
||||||
|
|
||||||
with open('/.new_rootfs/etc/pacman.conf', 'a') as pacman_conf:
|
|
||||||
pacman_conf.write(f'''
|
|
||||||
[breakfast]
|
|
||||||
SigLevel = Never
|
|
||||||
Server = {blend_release['repo']}
|
|
||||||
''')
|
|
||||||
|
|
||||||
if type(blend_release.get('package-repos')) == list:
|
|
||||||
for package_repo in blend_release.get('package-repos'):
|
|
||||||
if (type(package_repo.get('name')) == str and
|
|
||||||
type(package_repo.get('repo-url')) == str):
|
|
||||||
pacman_conf.write(f'''
|
|
||||||
[{package_repo["name"]}]
|
|
||||||
SigLevel = Never
|
|
||||||
Server = {package_repo["repo-url"]}
|
|
||||||
''')
|
|
||||||
|
|
||||||
before = datetime.now()
|
|
||||||
counter = 0
|
|
||||||
while True:
|
|
||||||
return_val = exec_chroot('pacman', '-Syu', '--noconfirm')
|
|
||||||
counter += 1
|
|
||||||
if counter > 30:
|
|
||||||
error('failed to download packages')
|
|
||||||
exit(50)
|
|
||||||
if return_val == 0:
|
|
||||||
break
|
|
||||||
after = datetime.now()
|
|
||||||
if benchmark:
|
|
||||||
print(f'[BENCH]: {(before - after).seconds} seconds to update packages')
|
|
||||||
|
|
||||||
exec('cp', '/etc/mkinitcpio.conf', '/.new_rootfs/etc/mkinitcpio.conf')
|
|
||||||
|
|
||||||
before = datetime.now()
|
|
||||||
counter = 0
|
|
||||||
while True:
|
|
||||||
print('running packages again')
|
|
||||||
return_val = exec_chroot('pacman', '-S', '--ask=4', *packages)
|
|
||||||
counter += 1
|
|
||||||
if counter > 30:
|
|
||||||
error('failed to download packages')
|
|
||||||
exit(50)
|
|
||||||
if return_val == 0:
|
|
||||||
break
|
|
||||||
after = datetime.now()
|
|
||||||
if benchmark:
|
|
||||||
print(f'[BENCH]: {(before - after).seconds} seconds to install packages')
|
|
||||||
|
|
||||||
counter = 0
|
|
||||||
if aur_packages != []:
|
|
||||||
while True:
|
|
||||||
print('running aur_packages again')
|
|
||||||
exec_chroot('useradd', '-m', '-G', 'wheel', '-s', '/bin/bash', 'aur')
|
|
||||||
exec_chroot('bash', '-c', 'echo "aur ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/aur')
|
|
||||||
before = datetime.now()
|
|
||||||
return_val = exec_chroot(
|
|
||||||
'runuser', '-u', 'aur', '--', 'paru', '-Sy', '--noconfirm', '--needed',
|
|
||||||
'--noprogressbar', '--skipreview', '--removemake', '--cleanafter', '--ask=4',
|
|
||||||
*aur_packages)
|
|
||||||
after = datetime.now()
|
|
||||||
if benchmark:
|
|
||||||
print(f'[BENCH]: {(before - after).seconds} seconds to install AUR packages')
|
|
||||||
exec_chroot('userdel', '-r', 'aur')
|
|
||||||
exec_chroot('rm', '-f', '/etc/sudoers.d/aur')
|
|
||||||
counter += 1
|
|
||||||
if counter > 30:
|
|
||||||
error('failed to download AUR packages')
|
|
||||||
exit(50)
|
|
||||||
if return_val == 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
for service in services:
|
|
||||||
if type(service) is str:
|
|
||||||
exec_chroot('systemctl', 'enable', service)
|
|
||||||
|
|
||||||
for user_service in user_services:
|
|
||||||
if type(user_service) is str:
|
|
||||||
exec_chroot('systemctl', 'enable', '--global', user_service)
|
|
||||||
|
|
||||||
for persistent_file in persistent_files:
|
|
||||||
if type(persistent_file) is str:
|
|
||||||
if os.path.exists(persistent_file):
|
|
||||||
exec('mkdir', '-p', '/.new_rootfs/'+ os.path.dirname(persistent_file))
|
|
||||||
exec('cp', persistent_file, '/.new_rootfs/'+ persistent_file)
|
|
||||||
|
|
||||||
if type(blend_release.get('commands')) == list:
|
|
||||||
for command in blend_release.get('commands'):
|
|
||||||
if type(command) == str:
|
|
||||||
exec_chroot('bash', '-c', command)
|
|
||||||
elif type(command) == list:
|
|
||||||
exec_chroot(*command)
|
|
||||||
|
|
||||||
kernel_exists = False
|
|
||||||
|
|
||||||
for f in os.listdir('/.new_rootfs/boot'):
|
|
||||||
if f.startswith('vmlinuz'):
|
|
||||||
kernel_exists = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not kernel_exists:
|
|
||||||
error('no Linux kernel found in new system')
|
|
||||||
error('cancelling update so as not to render the system unbootable')
|
|
||||||
sys.exit(10)
|
|
||||||
|
|
||||||
exec_chroot('pacman', '-S', '--noconfirm', 'shadow')
|
|
||||||
|
|
||||||
exec_chroot('mkinitcpio', '-P')
|
|
||||||
|
|
||||||
exec('cp', '-ax', '/etc/locale.gen', '/.new_rootfs/etc/locale.gen')
|
|
||||||
exec_chroot('locale-gen')
|
|
||||||
|
|
||||||
exec_chroot('rm', '-f', '/.new_rootfs/etc/resolv.conf')
|
|
||||||
|
|
||||||
exec('cp', '-ax', '/.new_rootfs/etc', '/.new.etc')
|
|
||||||
|
|
||||||
if os.environ.get('AKSHARA_INSTALL') == '1':
|
|
||||||
exec('rm', '-rf', '/usr/etc')
|
|
||||||
exec('cp', '-ax', '/.new_rootfs/etc', '/usr/etc')
|
|
||||||
|
|
||||||
etc_diff = filecmp.dircmp('/etc/', '/usr/etc/')
|
|
||||||
|
|
||||||
def get_diff_etc_files(dcmp):
|
|
||||||
dir_name = dcmp.left.replace('/etc/', '/.new.etc/', 1)
|
|
||||||
for name in dcmp.left_only:
|
|
||||||
exec('mkdir', '-p', dir_name)
|
|
||||||
exec('cp', '-ax', os.path.join(dcmp.left, name), dir_name)
|
|
||||||
for name in dcmp.diff_files:
|
|
||||||
exec('cp', '-ax', os.path.join(dcmp.left, name), dir_name)
|
|
||||||
for sub_dcmp in dcmp.subdirs.values():
|
|
||||||
get_diff_etc_files(sub_dcmp)
|
|
||||||
|
|
||||||
get_diff_etc_files(etc_diff)
|
|
||||||
|
|
||||||
exec('cp', '-ax', '/var/lib', '/.new.var.lib')
|
|
||||||
|
|
||||||
var_lib_diff = filecmp.dircmp('/.new_rootfs/var/lib/', '/.new.var.lib/')
|
|
||||||
|
|
||||||
dir_name = '/.new.var.lib/'
|
|
||||||
for name in var_lib_diff.left_only:
|
|
||||||
if os.path.isdir(os.path.join(var_lib_diff.left, name)):
|
|
||||||
exec('cp', '-ax', os.path.join(var_lib_diff.left, name), dir_name)
|
|
||||||
|
|
||||||
exec('cp', '/etc/passwd', '/.new_rootfs/etc')
|
|
||||||
exec('cp', '/etc/group', '/.new_rootfs/etc')
|
|
||||||
exec('cp', '/etc/shadow', '/.new_rootfs/etc')
|
|
||||||
exec('cp', '/etc/gshadow', '/.new_rootfs/etc')
|
|
||||||
|
|
||||||
exec_chroot('systemd-sysusers')
|
|
||||||
|
|
||||||
exec('cp', '/.new_rootfs/etc/passwd', '/.new.etc')
|
|
||||||
exec('cp', '/.new_rootfs/etc/group', '/.new.etc')
|
|
||||||
exec('cp', '/.new_rootfs/etc/shadow', '/.new.etc')
|
|
||||||
exec('cp', '/.new_rootfs/etc/gshadow', '/.new.etc')
|
|
||||||
|
|
||||||
exec('cp', '/.new_rootfs/etc/pacman.conf', '/.new.etc')
|
|
||||||
exec('rm', '-rf', '/.new.etc/systemd/system')
|
|
||||||
exec('cp', '-ax', '/.new_rootfs/etc/systemd/system', '/.new.etc/systemd')
|
|
||||||
exec('rm', '-rf', '/.new.var.lib/pacman')
|
|
||||||
exec('cp', '-ax', '/.new_rootfs/var/lib/pacman', '/.new.var.lib/pacman')
|
|
||||||
|
|
||||||
exec('mv', '.new_rootfs', '.update_rootfs')
|
|
||||||
# don't move /etc/shells to /usr/etc
|
|
||||||
exec('bash', '-c', 'shopt -s extglob; cd /.update_rootfs/etc; cp -ax !(shells) /.update_rootfs/usr/etc; cd -')
|
|
||||||
|
|
||||||
new_boot_files = []
|
|
||||||
|
|
||||||
for f in os.listdir('/.update_rootfs/boot'):
|
|
||||||
if not os.path.isdir(f'/.update_rootfs/boot/{f}'):
|
|
||||||
exec('mv', f'/.update_rootfs/boot/{f}', '/boot')
|
|
||||||
new_boot_files.append(f)
|
|
||||||
|
|
||||||
for f in os.listdir('/boot'):
|
|
||||||
if not os.path.isdir(f'/boot/{f}'):
|
|
||||||
if f not in new_boot_files:
|
|
||||||
exec('rm', '-f', f'/boot/{f}')
|
|
||||||
|
|
||||||
exec('grub-mkconfig', '-o', '/boot/grub/grub.cfg')
|
|
||||||
|
|
||||||
exec('touch', '/.update')
|
|
||||||
|
|
||||||
info('downloaded update and generated new rootfs')
|
|
||||||
info('you may reboot now')
|
|
||||||
|
|
||||||
|
|
||||||
def daemon():
|
|
||||||
for dir in os.listdir('/'):
|
|
||||||
if dir.startswith('.old.'):
|
|
||||||
exec('rm', '-rf', '/' + dir)
|
|
||||||
|
|
||||||
|
|
||||||
description = f'''
|
|
||||||
{colors.bold}{colors.fg.cyan}usage:{colors.reset}
|
|
||||||
{os.path.basename(sys.argv[0])} [command] [options] [arguments]
|
|
||||||
|
|
||||||
{colors.bold}{colors.fg.cyan}version:{colors.reset} {__version}{colors.bold}
|
|
||||||
|
|
||||||
{colors.bold}{colors.fg.cyan}available commands{colors.reset}:
|
|
||||||
{colors.bold}help{colors.reset} Show this help message and exit.
|
|
||||||
{colors.bold}update{colors.reset} Update your blendOS system.
|
|
||||||
{colors.bold}version{colors.reset} Show version information and exit.
|
|
||||||
|
|
||||||
{colors.bold}{colors.fg.cyan}options for commands{colors.reset}:
|
|
||||||
{colors.bold}-v, --version{colors.reset} show version information and exit
|
|
||||||
'''
|
|
||||||
|
|
||||||
epilog = f'''
|
|
||||||
{colors.bold}Made with {colors.fg.red}\u2764{colors.reset}{colors.bold} by Rudra Saraswat.{colors.reset}
|
|
||||||
'''
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description=description, usage=argparse.SUPPRESS,
|
|
||||||
epilog=epilog, formatter_class=argparse.RawTextHelpFormatter)
|
|
||||||
command_map = {'help': 'help',
|
|
||||||
'version': 'version',
|
|
||||||
'update': update_system,
|
|
||||||
'daemon': daemon}
|
|
||||||
parser.add_argument('command', choices=command_map.keys(),
|
|
||||||
help=argparse.SUPPRESS)
|
|
||||||
parser.add_argument('pkg', action='store', type=str,
|
|
||||||
nargs='*', help=argparse.SUPPRESS)
|
|
||||||
parser.add_argument('--headless',
|
|
||||||
action='store_true', help=argparse.SUPPRESS)
|
|
||||||
parser.add_argument('-v', '--version', action='version',
|
|
||||||
version=f'%(prog)s {__version}', help=argparse.SUPPRESS)
|
|
||||||
|
|
||||||
if len(sys.argv) == 1:
|
|
||||||
parser.print_help()
|
|
||||||
exit()
|
|
||||||
|
|
||||||
if os.geteuid() != 0 and not sys.argv[1] in ('help', 'version', '-v', '--version'):
|
|
||||||
error('requires root')
|
|
||||||
exit(1)
|
|
||||||
|
|
||||||
args = parser.parse_intermixed_args()
|
|
||||||
|
|
||||||
command = command_map[args.command]
|
|
||||||
|
|
||||||
try:
|
|
||||||
if command == 'help':
|
|
||||||
parser.print_help()
|
|
||||||
elif command == 'version':
|
|
||||||
parser.parse_args(['--version'])
|
|
||||||
elif command == update_system:
|
|
||||||
exec('touch', '/var/lib/.akshara-system-lock')
|
|
||||||
system_lock = fasteners.InterProcessLock('/var/lib/.akshara-system-lock')
|
|
||||||
info('attempting to acquire system lock')
|
|
||||||
with system_lock:
|
|
||||||
command()
|
|
||||||
else:
|
|
||||||
command()
|
|
||||||
except:
|
|
||||||
error('aborting')
|
|
||||||
# remove update and akshara stuff if the program is exited
|
|
||||||
exec('umount', '-rf', '/.new_rootfs/')
|
|
||||||
exec('rmdir', '/.new_rootfs/')
|
|
||||||
exec('rm', '-rf', '/.new_rootfs')
|
|
||||||
exec('rm', '-rf', '/.update_rootfs')
|
|
||||||
# it's basically impossible to ^C before akshara has exited but after it's created this file
|
|
||||||
# and i don't *think* that "update" would be destructive at all (unless it failed to run and would't boot), but i don't quite understand it soooo just to be on the safe side
|
|
||||||
exec('rm', '-f', '/.update')
|
|
||||||
# TODO: add similar handling for errors, and an option to not delete stuff on error/early exit
|
|
|
@ -1,10 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=Update system
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
ExecStart=/usr/bin/akshara update
|
|
||||||
User=root
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
55
akshara.hook
55
akshara.hook
|
@ -1,55 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
run_latehook() {
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Remove /new_root/.successful-update if exists
|
|
||||||
rm -f /new_root/.successful-update /new_root/.update
|
|
||||||
|
|
||||||
# Detect if update downloaded.
|
|
||||||
if [[ -d /new_root/.update_rootfs ]]; then
|
|
||||||
# Available, rename old /usr and move new /usr to /.
|
|
||||||
if [[ -d /new_root/.update_rootfs/usr ]]; then
|
|
||||||
mv /new_root/usr /new_root/.old.usr
|
|
||||||
mv /new_root/.update_rootfs/usr /new_root/usr
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Same for /etc.
|
|
||||||
if [[ -d /new_root/.update_rootfs/etc ]]; then
|
|
||||||
mv /new_root/.update_rootfs/etc /new_root/usr/etc
|
|
||||||
fi
|
|
||||||
if [[ -d /new_root/.new.etc ]]; then
|
|
||||||
mv /new_root/etc /new_root/.old.etc
|
|
||||||
mv /new_root/.new.etc /new_root/etc
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Same for /opt
|
|
||||||
if [[ -d /new_root/.update_rootfs/opt ]]; then
|
|
||||||
mv /new_root/opt /new_root/.old.opt
|
|
||||||
mv /new_root/.update_rootfs/opt /new_root/opt
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Same for /var.
|
|
||||||
if [[ -d /new_root/.new.var.lib ]]; then
|
|
||||||
mv /new_root/var/lib /new_root/.old.var.lib
|
|
||||||
mv /new_root/.new.var.lib /new_root/var/lib
|
|
||||||
fi
|
|
||||||
if [[ -d /new_root/.update_rootfs/var/cache/pacman ]]; then
|
|
||||||
mv /new_root/var/cache/pacman /new_root/.old.var.cache.pacman
|
|
||||||
mv /new_root/.update_rootfs/var/cache/pacman /new_root/var/cache/pacman
|
|
||||||
fi
|
|
||||||
|
|
||||||
mv /new_root/.update_rootfs /new_root/.old.update_rootfs
|
|
||||||
touch /new_root/.successful-update
|
|
||||||
fi
|
|
||||||
|
|
||||||
for i in usr varlibpacman usrlocal; do
|
|
||||||
rm -rf /new_root/.blend-overlays/$i.workdir
|
|
||||||
mkdir -p /new_root/.blend-overlays/$i
|
|
||||||
mkdir -p /new_root/.blend-overlays/$i.workdir
|
|
||||||
done
|
|
||||||
|
|
||||||
mount -t overlay overlay -o index=off -o metacopy=off -o ro,lowerdir=/new_root/usr,upperdir=/new_root/.blend-overlays/usr,workdir=/new_root/.blend-overlays/usr.workdir /new_root/usr
|
|
||||||
mount -t overlay overlay -o index=off -o metacopy=off -o ro,lowerdir=/new_root/var/lib/pacman,upperdir=/new_root/.blend-overlays/varlibpacman,workdir=/new_root/.blend-overlays/varlibpacman.workdir /new_root/var/lib/pacman
|
|
||||||
mount -t overlay overlay -o rw,lowerdir=/new_root/usr/local,upperdir=/new_root/.blend-overlays/usrlocal,workdir=/new_root/.blend-overlays/usrlocal.workdir /new_root/usr/local
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# SPDX-License-Identifier: GPL-3.0
|
|
||||||
|
|
||||||
build() {
|
|
||||||
add_module overlay
|
|
||||||
add_binary bash
|
|
||||||
add_binary xargs
|
|
||||||
add_binary find
|
|
||||||
add_binary grep
|
|
||||||
add_binary findmnt
|
|
||||||
add_binary uname
|
|
||||||
add_binary chroot
|
|
||||||
add_runscript
|
|
||||||
}
|
|
||||||
|
|
||||||
help() {
|
|
||||||
cat <<HELPEOF
|
|
||||||
This hook handles system updates and overlays. No
|
|
||||||
configuration is needed.
|
|
||||||
HELPEOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# vim: set ft=sh ts=4 sw=4 et:
|
|
|
@ -1,10 +0,0 @@
|
||||||
[Unit]
|
|
||||||
Description=Handle system operations
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=simple
|
|
||||||
ExecStart=/usr/bin/akshara daemon
|
|
||||||
User=root
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
|
@ -1,16 +0,0 @@
|
||||||
repo: 'https://pkg-repo.blendos.co/'
|
|
||||||
|
|
||||||
impl: 'https://github.com/blend-os/tracks/raw/main'
|
|
||||||
|
|
||||||
track: 'gnome'
|
|
||||||
|
|
||||||
packages:
|
|
||||||
- 'micro'
|
|
||||||
- 'caddy'
|
|
||||||
|
|
||||||
services:
|
|
||||||
- 'caddy'
|
|
||||||
|
|
||||||
package-repos:
|
|
||||||
- name: 'chaotic-aur'
|
|
||||||
repo-url: 'https://cdn-mirror.chaotic.cx/$repo/$arch'
|
|
Loading…
Reference in a new issue