Skip to content

Instantly share code, notes, and snippets.

@InfiniteCoder01
Last active October 29, 2025 17:13
Show Gist options
  • Select an option

  • Save InfiniteCoder01/17de784f8ff9647dd623d8f0c25a2fa2 to your computer and use it in GitHub Desktop.

Select an option

Save InfiniteCoder01/17de784f8ff9647dd623d8f0c25a2fa2 to your computer and use it in GitHub Desktop.
Tiny clone of pacman in python

pacman.py

Tiny clone of pacman to fetch arch packages binary cache. Best used with something like LFN or LFS Note: mount and su executables are setuid, but you will get weird permission errors with them unless you chown :root ./root/usr/bin/su or similar.

import os
# Printing
def print_replace(text: str, end: str = '\n'):
print('\x1b[?7l', end='', flush=True) # Disable wrap
print('\x1b[2K', end='', flush=True) # Clear line
print(text, end=end, flush=True) # Print line
print('\x1b[?7h', end='', flush=True) # Enable wrap
def sizeof_fmt(size: int, suffix: str = 'B'):
# Source: https://stackoverflow.com/questions/1094841/get-a-human-readable-version-of-a-file-size
num = size
for unit in ('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi'):
if abs(num) < 1024.0:
return f'{num:3.1f}{unit}{suffix}'
num /= 1024.0
return f'{num:.1f}Yi{suffix}'
def progress_bar(value: int, max: int, label: str, filesize: bool = True):
WIDTH = 20
chars = int(value / max * WIDTH)
def fmt(val: int) -> str:
if filesize: return sizeof_fmt(val)
else: return str(val)
bar = f'[{'=' * chars}{' ' * (WIDTH - chars)}]'
print_replace(f'{fmt(value)} / {fmt(max)} {bar} {label}', end='\r')
# Download
from typing import IO
def download(url: str, file: IO[bytes]):
# Source: https://stackoverflow.com/questions/15644964/python-progress-bar-and-downloads (slightly modified)
import requests
response = requests.get(url, stream=True)
content_length = response.headers.get('content-length')
if content_length is None:
file.write(response.content)
else:
content_length = int(content_length)
downloaded = 0
for data in response.iter_content(chunk_size=4096):
file.write(data)
downloaded += len(data)
progress_bar(downloaded, content_length, f'Downloading {url}')
print_replace(f'Downloaded {url}')
def extract(file: IO[bytes], target_dir: str):
# Source: https://stackoverflow.com/questions/3667865/python-tarfile-progress-output
import tarfile
def track_progress(members: list[tarfile.TarInfo]):
for idx, member in enumerate(members):
progress_bar(idx + 1, len(members), f'Extracting {member.name}')
yield member
with tarfile.open(fileobj=file) as tarball:
tarball.extractall(target_dir, members=track_progress(tarball.getmembers()))
print_replace(f'Extracted {os.path.basename(target_dir)}')
def download_and_extract(url: str, target_dir: str, zst=False):
from io import BytesIO
with BytesIO() as pipe1:
download(url, pipe1)
pipe1.seek(0)
if zst:
with BytesIO() as pipe2:
import zstandard
unzstd = zstandard.ZstdDecompressor()
unzstd.copy_stream(pipe1, pipe2)
pipe2.seek(0)
extract(pipe2, target_dir)
else:
extract(pipe1, target_dir)
import platform
import os
import cache
ARCH = platform.uname().machine
MIRROR = 'https://mirrors.edge.kernel.org/archlinux'
PACKAGE_CACHE_PATH = './packages'
if not os.path.isdir(PACKAGE_CACHE_PATH):
for repo in ['core', 'extra']:
path = os.path.join(PACKAGE_CACHE_PATH, repo)
cache.download_and_extract(f'{MIRROR}/{repo}/os/{ARCH}/{repo}.db.tar.gz', path)
def split_version(package):
split = len(package)
split = min(split, package.index('<') if '<' in package else len(package))
split = min(split, package.index('>') if '>' in package else len(package))
split = min(split, package.index('=') if '=' in package else len(package))
package, version = package[:split], package[split:]
return package, version
package_info = {}
for repo in os.listdir(PACKAGE_CACHE_PATH):
for package in os.listdir(os.path.join(PACKAGE_CACHE_PATH, repo)):
with open(os.path.join(PACKAGE_CACHE_PATH, repo, package, 'desc'), 'r') as file:
info = {}
info['repo'] = repo
section = None
for line in file.readlines():
line = line.strip()
if line == '': continue
if line[0] == '%' and line[-1] == '%':
section = line[1:-1]
info[section] = []
else:
info[section].append(line)
for entry in info['NAME'] + (info['PROVIDES'] if 'PROVIDES' in info else []):
entry, _ = split_version(entry)
if entry not in package_info: package_info[entry] = []
package_info[entry].append(info)
pending_packages = set()
def install(name, target_dir, mirror=MIRROR, arch=ARCH):
if not name in package_info:
raise RuntimeError(f'package {name} not found')
packages = package_info[name]
for package in packages:
if package['NAME'][0] in pending_packages: return
if os.path.isfile(os.path.join(target_dir, 'installed', package['NAME'][0])): return
if len(packages) == 1:
package = packages[0]
else:
names = [package['NAME'][0] for package in packages]
while True:
print(f'Select a package for {name}:\n{'\n'.join([f' {idx + 1}) {name}' for idx, name in enumerate(names)])}')
choice = input('Input (default 1): ')
if choice.strip() == '':
package = packages[0]
break
try:
package = packages[int(choice) - 1]
break
except ValueError: pass
pending_packages.add(package['NAME'][0])
for dep in (package['DEPENDS'] if 'DEPENDS' in package else []):
dep, _ = split_version(dep)
try:
install(dep, target_dir, mirror, arch)
except RuntimeError as e:
e.add_note(f'while evaluating dependencies of {name}')
raise e
url = f'{mirror}/{package['repo']}/os/{arch}/{package['FILENAME'][0]}'
cache.download_and_extract(url, target_dir, True)
open(os.path.join(target_dir, 'installed', package['NAME'][0]), 'a').close()
pending_packages.remove(package['NAME'][0])
import argparse
parser = argparse.ArgumentParser(description='Arch linux from scratch :)')
parser.add_argument('target', type=str, help='target path')
parser.add_argument('packages', metavar='N', type=str, nargs='+', help='packages to install')
args = parser.parse_args()
os.makedirs(os.path.join(args.target, 'installed'), exist_ok=True)
for package in args.packages:
install(package, args.target)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment