From fef351fe2f327efb3e82537a04e2a3c617e33123 Mon Sep 17 00:00:00 2001 From: servicememeai Date: Sat, 24 Oct 2026 20:24:04 +0900 Subject: [PATCH 1/8] fix(storage): skip full inventory on fresh V2 and ordinary restarts; preflight only CE transition --- scripts/scripts_cf14.0/library_storage.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/scripts/scripts_cf14.0/library_storage.py b/scripts/scripts_cf14.0/library_storage.py index e0a8e782..fa2f36e1 100644 --- a/scripts/scripts_cf14.0/library_storage.py +++ b/scripts/scripts_cf14.0/library_storage.py @@ -110,7 +110,7 @@ def _render_auto(original, generated_body): return ''.join(out).rstrip() + '\n\n' + BEGIN + '\n' + body + END + '\n' -def configure_auto_storage(conf, generated_body, installdir, environment=None): +def configure_auto_storage(conf, generated_body, installdir, environment=None, *, fresh=False): """Preflight old routing, register supported libraries, then replace config. Neither a failed preflight nor a failed apply changes seafile.conf. Apply @@ -129,6 +129,20 @@ def configure_auto_storage(conf, generated_body, installdir, environment=None): final = _render_auto(original, generated_body) check = ConfigParser(interpolation=None) check.read_string(final) + + # A fresh installation has no old repositories to register. Existing V2 + # already has persistent native RepoStorageId routes; do not rescan and + # rewrite every library on each restart. Preflight is only for an explicit + # transition from an existing non-V2 Seafile installation. + if fresh or current_root == LIBRARY_ROOT: + if current_root == LIBRARY_ROOT and ( + current.getboolean('storage', 'enable_storage_classes', fallback=False) or + any(current.get(section, 'name', fallback='filesystem') != 'filesystem' + for section in ('commit_object_backend', 'fs_object_backend', 'block_backend'))): + raise ValueError('Existing V2 storage configuration is incompatible') + _atomic_write(conf, final) + return + if not current.has_section('cloudfile_v2'): current.add_section('cloudfile_v2') current.set('cloudfile_v2', 'auto_local_root', LIBRARY_ROOT) From d90c80472ec789c732d87395f6daaf2b105a00d4 Mon Sep 17 00:00:00 2001 From: servicememeai Date: Sat, 24 Oct 2026 20:24:35 +0900 Subject: [PATCH 2/8] fix(storage): only explicit Seafile CE transitions run V2 inventory --- scripts/scripts_cf14.0/cloudfile_bootstrap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/scripts_cf14.0/cloudfile_bootstrap.py b/scripts/scripts_cf14.0/cloudfile_bootstrap.py index 56607695..2864f0d2 100644 --- a/scripts/scripts_cf14.0/cloudfile_bootstrap.py +++ b/scripts/scripts_cf14.0/cloudfile_bootstrap.py @@ -855,7 +855,7 @@ def write_cloudfile_seafile_conf(): conf_path = join(topdir, 'conf', 'seafile.conf') fresh = fresh_install() if auto_storage_enabled(conf_path, os.environ, fresh=fresh): - configure_auto_storage(conf_path, ''.join(lines), installdir) + configure_auto_storage(conf_path, ''.join(lines), installdir, fresh=fresh) return # Keep the S3 configuration in the same restart-safe generated block as From 4bd2fbd12d453adb8986c7ac7cc6bb12a721da31 Mon Sep 17 00:00:00 2001 From: servicememeai Date: Sat, 24 Oct 2026 20:27:53 +0900 Subject: [PATCH 3/8] test(storage): fresh UUID V2 and routine restart never run registration inventory --- tests/test_library_storage_config.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_library_storage_config.py b/tests/test_library_storage_config.py index 19db4016..1e665e06 100644 --- a/tests/test_library_storage_config.py +++ b/tests/test_library_storage_config.py @@ -188,6 +188,25 @@ def test_new_auto_uses_filesystem_and_reconciles_before_write(self): self.assertTrue(storage.auto_storage_enabled(self.conf, {})) self.assertEqual([], list(self.conf.parent.glob('.storage-*'))) + def test_fresh_uuid_v2_install_skips_native_inventory(self): + with patch.object(storage.subprocess, 'run') as run: + storage.configure_auto_storage(self.conf, '[cloudfile]\\nexample = true\\n', + self.installdir, self.env, fresh=True) + run.assert_not_called() + config = ConfigParser(interpolation=None) + config.read(self.conf) + self.assertEqual(str(self.root), config['cloudfile_v2']['auto_local_root']) + self.assertEqual('false', config['storage']['enable_storage_classes']) + + def test_existing_v2_restart_never_rewrites_route_registry(self): + self.configure() # explicit first transition from an existing CE config + calls_before = len(self.calls) + with patch.object(storage.subprocess, 'run') as run: + storage.configure_auto_storage(self.conf, '[cloudfile]\\nexample = true\\n', + self.installdir, self.env) + run.assert_not_called() + self.assertEqual(calls_before, len(self.calls)) + def test_native_layout_symlinks_are_resolved_but_managed_root_is_not(self): shared = self.top / 'shared-seafile' shared.mkdir() From 2cc10fa5e30ecdc3ea6102491852d1dd7fa6f45d Mon Sep 17 00:00:00 2001 From: servicememeai Date: Sat, 24 Oct 2026 20:29:45 +0900 Subject: [PATCH 4/8] test(storage): use literal newline escapes in bootstrap fixture --- tests/test_library_storage_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_library_storage_config.py b/tests/test_library_storage_config.py index 1e665e06..c3916e04 100644 --- a/tests/test_library_storage_config.py +++ b/tests/test_library_storage_config.py @@ -190,7 +190,7 @@ def test_new_auto_uses_filesystem_and_reconciles_before_write(self): def test_fresh_uuid_v2_install_skips_native_inventory(self): with patch.object(storage.subprocess, 'run') as run: - storage.configure_auto_storage(self.conf, '[cloudfile]\\nexample = true\\n', + storage.configure_auto_storage(self.conf, '[cloudfile]\nexample = true\n', self.installdir, self.env, fresh=True) run.assert_not_called() config = ConfigParser(interpolation=None) @@ -202,7 +202,7 @@ def test_existing_v2_restart_never_rewrites_route_registry(self): self.configure() # explicit first transition from an existing CE config calls_before = len(self.calls) with patch.object(storage.subprocess, 'run') as run: - storage.configure_auto_storage(self.conf, '[cloudfile]\\nexample = true\\n', + storage.configure_auto_storage(self.conf, '[cloudfile]\nexample = true\n', self.installdir, self.env) run.assert_not_called() self.assertEqual(calls_before, len(self.calls)) From 56c42451a169fbd4d23698ce6d79a9ec29cdb3ae Mon Sep 17 00:00:00 2001 From: servicememeai Date: Sat, 24 Oct 2026 20:35:46 +0900 Subject: [PATCH 5/8] test(storage): one-off lightweight UUID V2 compatibility evidence --- .github/workflows/dev.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 53a4d90c..4131f346 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -51,3 +51,14 @@ jobs: run: | python3 -m unittest discover -s tests -p test_build_receipt.py -q python3 -m unittest discover -s tests -p test_current_build_policy.py -q + + uuid-v2-startup-contract: + if: github.event_name == 'pull_request' && github.event.pull_request.head.ref == 'fix/v2-uuid-bootstrap-20261024' + runs-on: ubuntu-24.04 + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + - name: Run V2 bootstrap configuration tests without any service + run: | + python3 -m unittest discover -s tests -p test_library_storage_config.py -q + python3 -m unittest discover -s tests -p test_v06_default_library_storage.py -q From 280ec027eea5cb441c66c924869586f064e8378a Mon Sep 17 00:00:00 2001 From: servicememeai Date: Sat, 24 Oct 2026 20:37:50 +0900 Subject: [PATCH 6/8] test(storage): reset CE fixture between distinct explicit transition cases --- tests/test_library_storage_config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_library_storage_config.py b/tests/test_library_storage_config.py index c3916e04..ae750746 100644 --- a/tests/test_library_storage_config.py +++ b/tests/test_library_storage_config.py @@ -233,6 +233,9 @@ def test_explicit_transition_inventories_original_classes(self): def test_schema_missing_and_state_drift_allow_explicit_apply(self): for status in ('SCHEMA_MISSING', 'STATE_DRIFT'): with self.subTest(status=status): + # Every case models a distinct explicit CE transition. + # A completed V2 restart intentionally skips reconcile. + self.conf.write_text(self.original) modes = [] def cli(command, **kwargs): modes.append(command[-1]) From 9af5f1bf9c41b68359a191e3b259360e6190155f Mon Sep 17 00:00:00 2001 From: servicememeai Date: Sat, 24 Oct 2026 20:41:36 +0900 Subject: [PATCH 7/8] test(storage): align expectations with native route authority and Compose normalization --- tests/test_v06_default_library_storage.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_v06_default_library_storage.py b/tests/test_v06_default_library_storage.py index 96ce64c2..303beb8b 100644 --- a/tests/test_v06_default_library_storage.py +++ b/tests/test_v06_default_library_storage.py @@ -44,7 +44,11 @@ def test_merged_new_install_compose_contract(self): mounts = [v for v in process["volumes"] if v["target"] == parent] self.assertEqual(1, len(mounts), "one parent mount per process") self.assertEqual("rw", mounts[0].get("mode", "rw")) - self.assertFalse(mounts[0]["bind"]["create_host_path"]) + # Compose JSON may omit explicit false from the normalized form. + # The deployment overlay must still declare it for safe mounts. + self.assertFalse(mounts[0].get("bind", {}).get("create_host_path", False)) + self.assertEqual(2, (ROOT / "docker-compose.auto-library-storage.yml") + .read_text().count("create_host_path: false")) self.assertEqual(False, str(server["environment"]["CF_ENABLE_CHECKOUT"]).lower() == "true") From 39f5f276bfc3910af8d545fb1bcd64bb26510ec9 Mon Sep 17 00:00:00 2001 From: servicememeai Date: Sat, 24 Oct 2026 20:45:51 +0900 Subject: [PATCH 8/8] ci(storage): remove one-off targeted acceptance jobs after success --- .github/workflows/dev.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 4131f346..53a4d90c 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -51,14 +51,3 @@ jobs: run: | python3 -m unittest discover -s tests -p test_build_receipt.py -q python3 -m unittest discover -s tests -p test_current_build_policy.py -q - - uuid-v2-startup-contract: - if: github.event_name == 'pull_request' && github.event.pull_request.head.ref == 'fix/v2-uuid-bootstrap-20261024' - runs-on: ubuntu-24.04 - timeout-minutes: 5 - steps: - - uses: actions/checkout@v4 - - name: Run V2 bootstrap configuration tests without any service - run: | - python3 -m unittest discover -s tests -p test_library_storage_config.py -q - python3 -m unittest discover -s tests -p test_v06_default_library_storage.py -q