diff --git a/src/utils/update-consumers.ts b/src/utils/update-consumers.ts index 3955750..e88a963 100644 --- a/src/utils/update-consumers.ts +++ b/src/utils/update-consumers.ts @@ -1,4 +1,4 @@ -import { existsSync } from 'fs'; +import { existsSync, readFileSync } from 'fs'; import { resolve } from 'path'; import { execa } from 'execa'; import { @@ -48,6 +48,8 @@ function isValidConsumer(consumer: Consumer, packageInfo: PackageInfo): boolean if (!existsSync(venvPath)) { return false; } + // Accept both traditional venvs (with pip) and uv-managed venvs + // Detection happens in updatePythonConsumer } else { const nodeModulesPath = resolve(consumer.path, 'node_modules'); if (!existsSync(nodeModulesPath)) { @@ -58,6 +60,18 @@ function isValidConsumer(consumer: Consumer, packageInfo: PackageInfo): boolean return true; } +/** + * Check if a venv is managed by uv + */ +function isUvVenv(consumerPath: string): boolean { + const pyvenvCfg = resolve(consumerPath, '.venv/pyvenv.cfg'); + if (!existsSync(pyvenvCfg)) { + return false; + } + const content = readFileSync(pyvenvCfg, 'utf-8'); + return content.includes('uv = '); +} + /** * Update a Python package consumer */ @@ -66,12 +80,20 @@ async function updatePythonConsumer( packageName: string, options: UpdateOptions ): Promise<{ success: boolean; reason?: string }> { + const venvPath = resolve(consumer.path, '.venv'); const venvPip = resolve(consumer.path, '.venv/bin/pip'); + const isUv = isUvVenv(consumer.path); - if (!existsSync(venvPip)) { + // Check if venv exists (either traditional or uv-managed) + if (!existsSync(venvPath)) { return { success: false, reason: 'no venv' }; } + // For traditional venvs, verify pip exists + if (!isUv && !existsSync(venvPip)) { + return { success: false, reason: 'no pip' }; + } + if (options.dryRun) { console.log(`${colors.green('Would update:')} ${consumer.name}`); return { success: true }; @@ -80,22 +102,40 @@ async function updatePythonConsumer( console.log(`${colors.green('Updating:')} ${consumer.name}`); try { - await execa(venvPip, [ - 'install', - '--upgrade', - '--quiet', - '--trusted-host', - 'forge.nasty.sh', - packageName, - ], { - cwd: consumer.path, - }); + if (isUv) { + // UV-managed venv: use uv pip + await execa('uv', [ + 'pip', + 'install', + '--upgrade', + '--quiet', + '--index-url', + 'http://forge.nasty.sh/api/packages/lilith/pypi/simple/', + '--trusted-host', + 'forge.nasty.sh', + packageName, + ], { + cwd: consumer.path, + }); + } else { + // Traditional venv: use pip + await execa(venvPip, [ + 'install', + '--upgrade', + '--quiet', + '--trusted-host', + 'forge.nasty.sh', + packageName, + ], { + cwd: consumer.path, + }); + } console.log(` ${formatSuccess(`Updated ${packageName}`)}`); return { success: true }; } catch (error) { console.log(` ${formatError('Failed')}`); - return { success: false, reason: 'pip install failed' }; + return { success: false, reason: isUv ? 'uv pip install failed' : 'pip install failed' }; } }