Fix wallpaper upgrade compatibility and Settings icon clipping

Allow installed releases to omit the two newer appearance wallpapers while keeping new bundle verification strict. Fit the Settings header symbol within its square slot without clipping.

Verified 30 installer tests, the real upgrade dry-run, and release rendering previews on GPU and software backends.
This commit is contained in:
Samuel Onoja committed 2026-10-04 11:18:08 +01:00
1 parent 3533fac521
commit 4dd425f1f1
3 files changed
+41 -3

No files matched your search

+10 -3
View File
@@ -125,7 +125,7 @@ def record(rule, name=None):
for key, value in rule.items() if key != 'source'} for key, value in rule.items() if key != 'source'}
def validate_bundle(bundle): def validate_bundle(bundle, *, installed=False):
if bundle.is_symlink() or not bundle.is_dir(): if bundle.is_symlink() or not bundle.is_dir():
fail(f'Bundle must be a directory, not a symlink: {bundle}') fail(f'Bundle must be a directory, not a symlink: {bundle}')
@@ -162,7 +162,14 @@ def validate_bundle(bundle):
seen.add(target) seen.add(target)
actual = {str(path.relative_to(bundle)) for path in bundle.rglob('*') if path.is_file()} actual = {str(path.relative_to(bundle)) for path in bundle.rglob('*') if path.is_file()}
if not expected.keys() <= seen or actual != seen | {'manifest.json'}: required = expected.keys()
if installed:
# Installed releases may predate the appearance-specific wallpapers.
# New bundles must include them; upgrades and rollback verify the older
# release's recorded files without requiring these later additions.
required = required - {'wallpapers/ferese-wallpaper-dark.jpg', 'wallpapers/ferese-wallpaper-light.png'}
if not required <= seen or actual != seen | {'manifest.json'}:
fail('Bundle is incomplete or contains unlisted files') fail('Bundle is incomplete or contains unlisted files')
return manifest return manifest
@@ -277,7 +284,7 @@ class Installation:
if not (directory / 'manifest.json').is_file(): if not (directory / 'manifest.json').is_file():
fail(f'Release {release} predates verified bundles; select a bundled release for rollback') fail(f'Release {release} predates verified bundles; select a bundled release for rollback')
return validate_bundle(directory) return validate_bundle(directory, installed=True)
def current_publication(self, current): def current_publication(self, current):
if not current: if not current:
+30
View File
@@ -112,6 +112,36 @@ m.main()
for name in ('ferese-session', 'ferese-session-shell', 'ferese-screenshot'): for name in ('ferese-session', 'ferese-session-shell', 'ferese-screenshot'):
self.assertEqual((self.bundles[0] / name).stat().st_mode & 0o777, 0o755) self.assertEqual((self.bundles[0] / name).stat().st_mode & 0o777, 0o755)
def without_appearance_wallpapers(self, bundle):
manifest_path = bundle / 'manifest.json'
manifest = json.loads(manifest_path.read_text())
removed = {'wallpapers/ferese-wallpaper-dark.jpg', 'wallpapers/ferese-wallpaper-light.png'}
for target in removed:
(bundle / target).unlink()
manifest['files'] = [item for item in manifest['files'] if item['target'] not in removed]
manifest_path.write_text(json.dumps(manifest))
def test_upgrade_and_rollback_accept_installed_release_before_appearance_wallpapers(self):
self.install()
legacy = self.base / 'releases/first'
self.without_appearance_wallpapers(legacy)
self.install(1)
self.run_command('rollback')
self.assertEqual(os.readlink(self.base / 'current'), 'releases/first')
self.assertEqual(installer.Installation(self.root).manifest('first')['release'], 'first')
(legacy / 'ferese').write_bytes(b'corrupt')
with self.assertRaisesRegex(ValueError, 'Checksum mismatch'):
installer.Installation(self.root).manifest('first')
def test_new_bundle_requires_appearance_wallpapers(self):
incomplete = Path(self.temporary.name) / 'incomplete'
shutil.copytree(self.bundles[0], incomplete)
self.without_appearance_wallpapers(incomplete)
result = self.run_command('install', incomplete, success=False)
self.assertIn('Bundle is incomplete', result.stderr)
self.assertFalse((self.base / 'current').exists())
def test_fresh_upgrade_and_rollback_restore_all_integration(self): def test_fresh_upgrade_and_rollback_restore_all_integration(self):
self.install() self.install()
first = [(self.root / path).read_bytes() for path in (PORTAL, UNIT, DESKTOP)] first = [(self.root / path).read_bytes() for path in (PORTAL, UNIT, DESKTOP)]
+1
View File
@@ -34,6 +34,7 @@ pub fn brand_icon() -> svg_icon::Icon {
.symbolic(false) .symbolic(false)
.icon() .icon()
.size(32) .size(32)
.content_fit(cosmic::iced::ContentFit::Contain)
} }
pub fn action_icon(path: &str, tint: Color) -> svg_icon::Icon { pub fn action_icon(path: &str, tint: Color) -> svg_icon::Icon {