Compare commits

..

No commits in common. "076f654a2dc66cfbc07853e40bb330a61ddc540f" and "5776c490599e423df5b2d969fed73465b28e5330" have entirely different histories.

16 changed files with 104 additions and 161 deletions

3
.gitignore vendored
View file

@ -1,5 +1,4 @@
__pycache__ __pycache__
/tmp /tmp
/.venv /.idea
/.vscode
/dist /dist

View file

@ -1,39 +1,32 @@
[build-system] [build-system]
requires = ["hatchling"] requires = ['hatchling']
build-backend = "hatchling.build" build-backend = 'hatchling.build'
[project] [project]
authors = [ authors = [
{ name = "Marc Beninca", email = "git@marc.beninca.link" }, { name = 'Marc Beninca', email = 'git@marc.beninca.link' },
] ]
maintainers = [ maintainers = [
{ name = "Marc Beninca", email = "git@marc.beninca.link" }, { name = 'Marc Beninca', email = 'git@marc.beninca.link' },
] ]
classifiers = [ classifiers = [
"Programming Language :: Python :: 3", 'Programming Language :: Python :: 3',
"License :: OSI Approved :: GNU Affero General Public License v3", 'License :: OSI Approved :: GNU Affero General Public License v3',
"Operating System :: OS Independent", 'Operating System :: OS Independent',
] ]
dependencies = [] dependencies = []
description = "Read Write eXecute" description = 'Read Write eXecute'
dynamic = ["version"] dynamic = ['version']
keywords = [] keywords = []
license-files = { paths = ["license.md"] } license-files = { paths = ['license.md'] }
name = "rwx" name = 'rwx'
readme = "readme.md" readme = 'readme.md'
requires-python = ">= 3.11" requires-python = '>= 3.10'
[project.scripts] [project.scripts]
# command = "package.module:function" # command = 'package.module:function'
[project.urls] [project.urls]
[tool.hatch.version] [tool.hatch.version]
path = "rwx/__init__.py" path = 'rwx/__init__.py'
[tool.ruff]
line-length = 80
[tool.ruff.lint]
ignore = ["COM812", "D203", "D213", "ISC001"]
select = ["ALL"]

View file

@ -1,3 +1 @@
"""Read Write eXecute.""" __version__ = '0.0.1'
__version__ = "0.0.1"

View file

@ -1,19 +1,18 @@
#! /usr/bin/env python3 #! /usr/bin/env python3
"""Entry point.""" import os
from pathlib import Path
import fs import fs
if __name__ == "__main__":
file_path = Path(__file__).resolve() if __name__ == '__main__':
root_path = file_path.parent file_path = os.path.realpath(__file__)
directory_path = root_path / "tmp" root_path = os.path.dirname(file_path)
file_path = directory_path / "file" directory_path = os.path.join(root_path, 'tmp')
file_path = os.path.join(directory_path, 'file')
fs.wipe(directory_path) fs.wipe(directory_path)
fs.make_directory(directory_path) fs.make_directory(directory_path)
fs.write(file_path, "Martine écrit beaucoup.") fs.write(file_path, 'Martine écrit beaucoup.')
fs.empty_file(file_path) fs.empty_file(file_path)
fs.write(file_path, "Martine écrit moins.") fs.write(file_path, 'Martine écrit moins.')

View file

@ -1,9 +1,6 @@
"""Handle system arguments."""
import sys import sys
def split() -> tuple[str, list[str]]: def split() -> tuple[str, list[str]]:
"""Split command & actual arguments."""
command, *arguments = sys.argv command, *arguments = sys.argv
return command, arguments return command, arguments

View file

@ -1,17 +1,15 @@
"""Handle system commands & packages."""
commands: list[str] = [] commands: list[str] = []
packages: list[str] = [] packages: list[str] = []
def need(command: str) -> None: def need(command: str) -> None:
"""Assert package dependency for a command."""
match command: match command:
case "debootstrap": case 'debootstrap':
package = "debootstrap" package = 'debootstrap'
case "mksquashfs" | "unsquashfs": case 'mksquashfs' | 'unsquashfs':
package = "squashfs-tools" package = 'squashfs-tools'
case _: case _:
package = None package = None
if package and package not in packages: if package:
if package not in packages:
packages.append(package) packages.append(package)

View file

@ -1,19 +1,14 @@
import ps import ps
import rwx.cmd import rwx.cmd
rwx.cmd.need("mksquashfs") rwx.cmd.need('mksquashfs')
def mksquashfs(input_root: str, output_file: str): def mksquashfs(input_root: str, output_file: str):
ps.run( ps.run([
[ 'mksquashfs',
"mksquashfs",
input_root, input_root,
output_file, output_file,
"-comp", '-comp', 'zstd',
"zstd", '-Xcompression-level', str(18),
"-Xcompression-level", ])
str(18),
]
)

View file

@ -1,20 +1,20 @@
import cmd import cmd
import ps import ps
cmd.need("debootstrap") cmd.need('debootstrap')
BOOTSTRAP_ARCHITECTURE = "amd64" BOOTSTRAP_ARCHITECTURE = 'amd64'
BOOTSTRAP_VARIANT = "minbase" BOOTSTRAP_VARIANT = 'minbase'
def bootstrap(root_path: str, suite: str, mirror_location: str): def bootstrap(root_path: str, suite: str, mirror_location: str):
command = [ command = [
("debootstrap",), ('debootstrap',),
("--arch", BOOTSTRAP_ARCHITECTURE), ('--arch', BOOTSTRAP_ARCHITECTURE),
("--variant", BOOTSTRAP_VARIANT), ('--variant', BOOTSTRAP_VARIANT),
(suite,), (suite,),
(root_path,), (root_path,),
(mirror_location,), (mirror_location,),
] ]
return ps.run(command) completed_process = ps.run(command)
return completed_process

View file

@ -1,2 +0,0 @@
class Exception(Exception):
pass

View file

@ -1,41 +1,36 @@
"""Operations involving FileSystems."""
import os import os
import shutil import shutil
from rwx import ps from rwx import ps
CHARSET = "UTF-8" CHARSET = 'UTF-8'
def create_image(file_path: str, size_bytes: int) -> None: def create_image(file_path: str, size_bytes: int):
"""Create a virtual device image file."""
ps.run( ps.run(
("qemu-img", "create"), ('qemu-img', 'create'),
("-f", "qcow2"), ('-f', 'qcow2'),
(file_path, size_bytes), (file_path, size_bytes),
) )
def empty_file(path: str) -> None: def empty_file(path: str):
"""Empty the file at provided path.""" write(path, str())
write(path, "")
def get_mount_uuid(path: str) -> str: def get_mount_uuid(path: str):
"""Return the UUID of provided mountpoint path."""
return ps.run_line( return ps.run_line(
("findmnt",), ('findmnt',),
("--noheadings",), ('--noheadings',),
("--output", "UUID"), ('--output', 'UUID'),
(path,), (path,),
) )
def get_path_mount(path: str): def get_path_mount(path: str):
return ps.run_line( return ps.run_line(
("stat",), ('stat',),
("--format", "%m"), ('--format', '%m'),
(path,), (path,),
) )
@ -49,7 +44,7 @@ def make_directory(path: str):
def read_file(file_path: str): def read_file(file_path: str):
with open(file_path, "br") as file_object: with open(file_path, 'br') as file_object:
return file_object.read() return file_object.read()
@ -71,5 +66,5 @@ def wipe(path: str):
def write(file_path: str, text: str, charset=CHARSET): def write(file_path: str, text: str, charset=CHARSET):
with open(file_path, "bw") as file_object: with open(file_path, 'bw') as file_object:
file_object.write(text.encode(charset)) file_object.write(text.encode(charset))

View file

@ -1,39 +1,33 @@
import cmd import cmd
import ps import ps
cmd.need("grub-mkimage") cmd.need('grub-mkimage')
COMPRESSION = "xz" COMPRESSION = 'xz'
ENV_BYTES = 1024 ENV_BYTES = 1024
ENV_COMMENT = "#" ENV_COMMENT = '#'
ENV_HEADER = f"""{ENV_COMMENT} GRUB Environment Block ENV_HEADER = f'''{ENV_COMMENT} GRUB Environment Block
""" '''
MODULES = { MODULES = {
"i386-pc": [ 'i386-pc': [
("biosdisk",), ('biosdisk',),
("ntldr",), ('ntldr',),
], ]
} }
def make_image( def make_image(image_format: str, image_path: str, modules: list[str],
image_format: str, memdisk_path: str, pubkey_path: str = None) -> None:
image_path: str,
modules: list[str],
memdisk_path: str,
pubkey_path: str | None = None,
) -> None:
args = [ args = [
("grub-mkimage",), ('grub-mkimage',),
("--compress", COMPRESSION), ('--compress', COMPRESSION),
("--format", image_format), ('--format', image_format),
("--output", image_path), ('--output', image_path),
("--memdisk", memdisk_path), ('--memdisk', memdisk_path),
] ]
if pubkey_path: if pubkey_path:
args.append(("--pubkey", pubkey_path)) args.append(('--pubkey', pubkey_path))
args.extend(modules) args.extend(modules)
if modules := MODULES.get(image_format): if modules := MODULES.get(image_format, None):
args.extend(modules) args.extend(modules)
ps.run(*args) ps.run(*args)

View file

@ -2,31 +2,21 @@ import logging
import sys import sys
def get_file_logger(name: str) -> logging.Logger: def get_logger(name: str) -> logging.Logger:
formatter = logging.Formatter( formatter = logging.Formatter(
"%(name)s: %(asctime)s | %(levelname)s | %(filename)s:%(lineno)s | %(process)d >>> %(message)s", "%(name)s: %(asctime)s | %(levelname)s | %(filename)s:%(lineno)s | %(process)d >>> %(message)s"
) )
#
# file_handler = logging.FileHandler('log.txt')
# file_handler.setFormatter(formatter)
# file_handler.setLevel(logging.INFO)
out_handler = logging.StreamHandler(stream=sys.stdout) out_handler = logging.StreamHandler(stream=sys.stdout)
out_handler.setFormatter(formatter) out_handler.setFormatter(formatter)
out_handler.setLevel(logging.INFO) out_handler.setLevel(logging.INFO)
#
logger = logging.getLogger(name) logger = logging.getLogger(name)
# logger.addHandler(file_handler)
logger.addHandler(out_handler) logger.addHandler(out_handler)
logger.setLevel(logging.INFO) logger.setLevel(logging.INFO)
#
return logger return logger
def get_stream_logger(level: int) -> logging.Logger:
out_handler = logging.StreamHandler(stream=sys.stdout)
out_handler.setLevel(level)
#
logger = logging.getLogger()
logger.addHandler(out_handler)
logger.setLevel(level)
#
return logger
stream = get_stream_logger(logging.INFO)

View file

@ -2,7 +2,7 @@ from os import path
class Project: class Project:
def __init__(self, file_path: str) -> None: def __init__(self, file_path: str):
self.file: str = path.realpath(file_path) self.file: str = path.realpath(file_path)
self.root: str = path.dirname(self.file) self.root: str = path.dirname(self.file)
self.name: str = path.basename(self.root) self.name: str = path.basename(self.root)

View file

@ -1,7 +1,4 @@
"""Project consisting only of a Sphinx documentation."""
from os import path from os import path
from sphinx.cmd.build import build_main from sphinx.cmd.build import build_main
from rwx.fs import wipe from rwx.fs import wipe
@ -9,28 +6,22 @@ from rwx.prj import Project
class SphinxProject(Project): class SphinxProject(Project):
def __init__(self, file_path: str) -> None: def __init__(self, file_path: str):
super().__init__(file_path) super().__init__(file_path)
def build(self) -> None: def build(self):
output_root: str = path.join(self.root, "out") output_root: str = path.join(self.root, 'out')
wipe(output_root) wipe(output_root)
arguments: list[str] = [ arguments: list[str] = [
"-E", "-E",
"-j", "-j", "2",
"2", "-b", "html",
"-b", "-D", f"project={self.name}",
"html", "-D", "master_doc={}".format("index"),
"-D", "-D", "html_theme={}".format("sphinx_rtd_theme"),
f"project={self.name}", "-c", self.root,
"-D",
"master_doc={}".format("index"),
"-D",
"html_theme={}".format("sphinx_rtd_theme"),
"-c",
self.root,
# "-C", # "-C",
path.join(self.root, self.name), path.join(self.root, self.name),
path.join(output_root, "web"), path.join(output_root, self.name),
] ]
build_main(arguments) build_main(arguments)

View file

@ -14,9 +14,7 @@ def get_tuples_args(tuples) -> list[str]:
def run(*tuples) -> subprocess.CompletedProcess: def run(*tuples) -> subprocess.CompletedProcess:
return subprocess.run( return subprocess.run(get_tuples_args(tuples), capture_output=False)
get_tuples_args(tuples), capture_output=False, check=True
)
def run_line(*tuples, charset: str = txt.CHARSET) -> str: def run_line(*tuples, charset: str = txt.CHARSET) -> str:
@ -25,8 +23,6 @@ def run_line(*tuples, charset: str = txt.CHARSET) -> str:
def run_lines(*tuples, charset: str = txt.CHARSET) -> list[str]: def run_lines(*tuples, charset: str = txt.CHARSET) -> list[str]:
process = subprocess.run( process = subprocess.run(get_tuples_args(tuples), capture_output=True)
get_tuples_args(tuples), capture_output=True, check=True
)
string = process.stdout.decode(charset) string = process.stdout.decode(charset)
return string.rstrip().splitlines() return string.rstrip().splitlines()

View file

@ -1 +1 @@
CHARSET = "UTF-8" CHARSET = 'UTF-8'