fix(qmd): stop the legacy Python daemon before removing its directory
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f1d7894ba0
commit
0c203a3905
2 files changed
+35
No files matched your search
@@ -87,6 +87,17 @@ class Setup:
|
|||||||
if not node_version_ok(self.run(['node', '--version'])):
|
if not node_version_ok(self.run(['node', '--version'])):
|
||||||
raise ValueError('QMD local search needs Node.js 22 or newer.')
|
raise ValueError('QMD local search needs Node.js 22 or newer.')
|
||||||
|
|
||||||
|
def stop_legacy_daemon(self, legacy):
|
||||||
|
"""Ask the old Python daemon to exit so its files can be deleted (Windows keeps open files locked)."""
|
||||||
|
python = legacy / '.venv' / ('Scripts/python.exe' if WINDOWS else 'bin/python')
|
||||||
|
server = legacy / 'server.py'
|
||||||
|
if python.is_file() and server.is_file():
|
||||||
|
try:
|
||||||
|
self.run([str(python), str(server), 'stop', '--data-dir', str(legacy / 'data'),
|
||||||
|
'--model-cache', str(legacy / 'model-cache')], timeout=60, check=False)
|
||||||
|
except (ValueError, OSError, subprocess.SubprocessError):
|
||||||
|
pass
|
||||||
|
|
||||||
def migrate(self, state=None):
|
def migrate(self, state=None):
|
||||||
state = self.load() if state is None else state
|
state = self.load() if state is None else state
|
||||||
legacy_state = self.base / 'semantic-retrieval.json'
|
legacy_state = self.base / 'semantic-retrieval.json'
|
||||||
@@ -95,6 +106,7 @@ class Setup:
|
|||||||
if legacy.is_symlink():
|
if legacy.is_symlink():
|
||||||
raise ValueError('Legacy semantic retrieval directory must not be a symbolic link.')
|
raise ValueError('Legacy semantic retrieval directory must not be a symbolic link.')
|
||||||
if legacy.exists():
|
if legacy.exists():
|
||||||
|
self.stop_legacy_daemon(legacy)
|
||||||
shutil.rmtree(legacy)
|
shutil.rmtree(legacy)
|
||||||
legacy_state.unlink(missing_ok=True)
|
legacy_state.unlink(missing_ok=True)
|
||||||
self.say('Removed the previous Python semantic retrieval installation.')
|
self.say('Removed the previous Python semantic retrieval installation.')
|
||||||
|
|||||||
@@ -267,6 +267,29 @@ class SetupTest(unittest.TestCase):
|
|||||||
self.setup(FakeRunner(fail=('--detect',))).enable(['claude'])
|
self.setup(FakeRunner(fail=('--detect',))).enable(['claude'])
|
||||||
self.assertEqual(self.state()['device'], 'cpu')
|
self.assertEqual(self.state()['device'], 'cpu')
|
||||||
|
|
||||||
|
def legacy_install(self):
|
||||||
|
legacy = self.home / '.my-ai-configuration/semantic-retrieval'
|
||||||
|
python = legacy / '.venv' / ('Scripts/python.exe' if os.name == 'nt' else 'bin/python')
|
||||||
|
python.parent.mkdir(parents=True)
|
||||||
|
python.write_text('')
|
||||||
|
(legacy / 'server.py').write_text('')
|
||||||
|
return legacy, python
|
||||||
|
|
||||||
|
def test_migration_stops_the_legacy_daemon_before_removal(self):
|
||||||
|
legacy, python = self.legacy_install()
|
||||||
|
seen = []
|
||||||
|
runner = FakeRunner(installed=True, on_call=lambda a: seen.append(legacy.exists()) if 'server.py' in ' '.join(a) else None)
|
||||||
|
self.setup(runner).enable(['claude'])
|
||||||
|
self.assertIn([str(python), str(legacy / 'server.py'), 'stop', '--data-dir', str(legacy / 'data'),
|
||||||
|
'--model-cache', str(legacy / 'model-cache')], runner.calls)
|
||||||
|
self.assertEqual(seen, [True], 'stop runs while the legacy directory still exists')
|
||||||
|
self.assertFalse(legacy.exists())
|
||||||
|
|
||||||
|
def test_failing_legacy_stop_does_not_block_migration(self):
|
||||||
|
legacy, _ = self.legacy_install()
|
||||||
|
self.setup(FakeRunner(installed=True, fail=('server.py',))).enable(['claude'])
|
||||||
|
self.assertFalse(legacy.exists())
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
Reference in new issue
Block a user