Document bundle installation and normalize release directory permissions

This commit is contained in:
Samuel Onoja committed 2026-10-02 10:20:52 +01:00
1 parent bfec60848d
commit ac184576f6
2 files changed
+34 -9

No files matched your search

+24 -9
View File
@@ -50,8 +50,23 @@ def sync_dir(path):
os.close(fd)
def ensure_directory(path, mode=0o755):
missing = []
cursor = path
while not cursor.exists():
if cursor.is_symlink():
fail(f'Refusing dangling directory symlink: {cursor}')
missing.append(cursor)
cursor = cursor.parent
for directory in reversed(missing):
directory.mkdir(mode=mode)
directory.chmod(mode)
sync_dir(directory.parent)
def atomic_bytes(path, data, mode=0o644):
path.parent.mkdir(parents=True, exist_ok=True)
ensure_directory(path.parent)
fd, temporary = tempfile.mkstemp(prefix='.ferese-', dir=path.parent)
try:
with os.fdopen(fd, 'wb') as stream:
@@ -68,7 +83,7 @@ def atomic_bytes(path, data, mode=0o644):
def atomic_copy(path, source, mode):
path.parent.mkdir(parents=True, exist_ok=True)
ensure_directory(path.parent)
fd, temporary = tempfile.mkstemp(prefix='.ferese-', dir=path.parent)
try:
with os.fdopen(fd, 'wb') as output:
@@ -150,10 +165,10 @@ def bundle(args):
if output.exists() or output.is_symlink():
fail(f'Bundle already exists: {output}')
output.parent.mkdir(parents=True, exist_ok=True)
ensure_directory(output.parent)
with tempfile.TemporaryDirectory(prefix='.bundle-', dir=output.parent) as temporary:
staging = Path(temporary) / 'release'
staging.mkdir()
ensure_directory(staging)
files = []
for rule in INVENTORY['files']:
sources = sorted(source.glob(rule['source']))
@@ -210,7 +225,7 @@ class Installation:
@contextlib.contextmanager
def lock(self):
self.base.mkdir(parents=True, exist_ok=True)
ensure_directory(self.base)
lock_path = self.path(PREFIX + '/.install.lock')
fd = os.open(lock_path, os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600)
try:
@@ -339,7 +354,7 @@ class Installation:
def write_state(self, absolute, state, source=None):
path = self.path(absolute)
path.parent.mkdir(parents=True, exist_ok=True)
ensure_directory(path.parent)
if state['kind'] == 'absent':
path.unlink(missing_ok=True)
sync_dir(path.parent)
@@ -359,7 +374,7 @@ class Installation:
def archive(self, journal):
history = self.path(PREFIX + '/transactions')
history.mkdir(mode=0o700, exist_ok=True)
ensure_directory(history, 0o700)
os.rename(self.transaction, history / journal['id'])
sync_dir(history)
sync_dir(self.base)
@@ -433,7 +448,7 @@ class Installation:
if releases.is_symlink():
fail(f'Refusing symlinked release directory: {releases}')
releases.mkdir(parents=True, exist_ok=True)
ensure_directory(releases)
destination = releases / manifest['release']
if os.path.lexists(destination):
if self.manifest(manifest['release']) != manifest:
@@ -443,7 +458,7 @@ class Installation:
with tempfile.TemporaryDirectory(prefix='.stage-', dir=releases) as temporary:
staging = Path(temporary) / 'release'
staging.mkdir()
ensure_directory(staging)
for item in manifest['files']:
atomic_copy(staging / item['target'], origin / item['target'], int(item['mode'], 8))
+10
View File
@@ -224,6 +224,16 @@ m.main()
self.assertIn('in progress', result.stderr)
self.assertFalse((self.base / 'current').exists())
def test_restrictive_umask_does_not_make_installed_release_private(self):
result = subprocess.run([sys.executable, '-c',
f"import os, runpy; os.umask(0o077); runpy.run_path({str(HELPER)!r}, run_name='__main__')",
'install', str(self.bundles[0]), '--root', str(self.root)],
capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stderr)
for relative in ('usr', 'usr/local', PREFIX, PREFIX + '/releases', PREFIX + '/releases/first',
PREFIX + '/releases/first/wallpapers', 'usr/share/wayland-sessions'):
self.assertEqual((self.root / relative).stat().st_mode & 0o777, 0o755, relative)
def test_dry_run_does_not_write(self):
before = self.snapshot()
self.run_command('install', self.bundles[0], '--dry-run')