Explorar o código

Revert "fix(auto-update): prevent writes in project .opencode directories"

This reverts commit 1ee08dd.
Alvin Real hai 3 meses
pai
achega
87ae1d6b76

+ 27 - 57
src/hooks/auto-update-checker/cache.test.ts

@@ -1,7 +1,5 @@
 import { describe, expect, mock, spyOn, test } from 'bun:test';
 import * as fs from 'node:fs';
-import * as os from 'node:os';
-import * as path from 'node:path';
 
 // Mock logger to avoid noise
 mock.module('../../utils/logger', () => ({
@@ -19,44 +17,27 @@ mock.module('../../cli/config-manager', () => ({
 // Cache buster for dynamic imports
 let importCounter = 0;
 
-const cacheDir =
-  process.platform === 'win32'
-    ? path.join(process.env.LOCALAPPDATA ?? os.homedir(), 'opencode')
-    : path.join(os.homedir(), '.cache', 'opencode');
-const packagesInstallDir = path.join(
-  cacheDir,
-  'packages',
-  'oh-my-opencode-slim@latest',
-);
-const packagesRuntimePath = path.join(
-  packagesInstallDir,
-  'node_modules',
-  'oh-my-opencode-slim',
-  'package.json',
-);
-const packagesWrapperPath = path.join(packagesInstallDir, 'package.json');
-const legacyPackageJsonPath = path.join(cacheDir, 'package.json');
-const legacyInstalledPath = path.join(
-  cacheDir,
-  'node_modules',
-  'oh-my-opencode-slim',
-);
-
 describe('auto-update-checker/cache', () => {
   describe('resolveInstallContext', () => {
     test('detects OpenCode packages install root from runtime package path', async () => {
       const existsSpy = spyOn(fs, 'existsSync').mockImplementation(
-        (p: string) => p === packagesWrapperPath,
+        (p: string) =>
+          p ===
+          '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest/package.json',
       );
       const { resolveInstallContext } = await import(
         `./cache?test=${importCounter++}`
       );
 
-      const context = resolveInstallContext(packagesRuntimePath);
+      const context = resolveInstallContext(
+        '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest/node_modules/oh-my-opencode-slim/package.json',
+      );
 
       expect(context).toEqual({
-        installDir: packagesInstallDir,
-        packageJsonPath: packagesWrapperPath,
+        installDir:
+          '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest',
+        packageJsonPath:
+          '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest/package.json',
       });
 
       existsSpy.mockRestore();
@@ -68,23 +49,8 @@ describe('auto-update-checker/cache', () => {
         `./cache?test=${importCounter++}`
       );
 
-      const context = resolveInstallContext(packagesRuntimePath);
-
-      expect(context).toBeNull();
-
-      existsSpy.mockRestore();
-    });
-
-    test('rejects project-local .opencode wrapper installs', async () => {
-      const existsSpy = spyOn(fs, 'existsSync').mockImplementation(
-        (p: string) => p === '/repo/.opencode/package.json',
-      );
-      const { resolveInstallContext } = await import(
-        `./cache?test=${importCounter++}`
-      );
-
       const context = resolveInstallContext(
-        '/repo/.opencode/node_modules/oh-my-opencode-slim/package.json',
+        '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest/node_modules/oh-my-opencode-slim/package.json',
       );
 
       expect(context).toBeNull();
@@ -109,16 +75,17 @@ describe('auto-update-checker/cache', () => {
     test('updates packages wrapper dependency and removes installed package', async () => {
       const existsSpy = spyOn(fs, 'existsSync').mockImplementation(
         (p: string) =>
-          p === packagesWrapperPath ||
-          p === path.join(
-            packagesInstallDir,
-            'node_modules',
-            'oh-my-opencode-slim',
-          ),
+          p ===
+            '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest/package.json' ||
+          p ===
+            '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest/node_modules/oh-my-opencode-slim',
       );
       const readSpy = spyOn(fs, 'readFileSync').mockImplementation(
         (p: string) => {
-          if (p === packagesWrapperPath) {
+          if (
+            p ===
+            '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest/package.json'
+          ) {
             return JSON.stringify({
               dependencies: {
                 'oh-my-opencode-slim': '0.9.1',
@@ -142,12 +109,14 @@ describe('auto-update-checker/cache', () => {
       const result = preparePackageUpdate(
         '0.9.11',
         'oh-my-opencode-slim',
-        packagesRuntimePath,
+        '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest/node_modules/oh-my-opencode-slim/package.json',
       );
 
-      expect(result).toBe(packagesInstallDir);
+      expect(result).toBe(
+        '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest',
+      );
       expect(rmSyncSpy).toHaveBeenCalledWith(
-        path.join(packagesInstallDir, 'node_modules', 'oh-my-opencode-slim'),
+        '/home/user/.cache/opencode/packages/oh-my-opencode-slim@latest/node_modules/oh-my-opencode-slim',
         { recursive: true, force: true },
       );
       expect(writtenData.length).toBeGreaterThan(0);
@@ -166,7 +135,8 @@ describe('auto-update-checker/cache', () => {
     test('keeps working when dependency is already on target version', async () => {
       const existsSpy = spyOn(fs, 'existsSync').mockImplementation(
         (p: string) =>
-          p === legacyPackageJsonPath || p === legacyInstalledPath,
+          p.endsWith('/.cache/opencode/package.json') ||
+          p.endsWith('/.cache/opencode/node_modules/oh-my-opencode-slim'),
       );
       const readSpy = spyOn(fs, 'readFileSync').mockReturnValue(
         JSON.stringify({
@@ -183,7 +153,7 @@ describe('auto-update-checker/cache', () => {
 
       const result = preparePackageUpdate('1.0.1', 'oh-my-opencode-slim', null);
 
-      expect(result).toBe(cacheDir);
+      expect(result?.endsWith('/.cache/opencode')).toBe(true);
       expect(writeSpy).not.toHaveBeenCalled();
       expect(rmSyncSpy).toHaveBeenCalled();
 

+ 0 - 30
src/hooks/auto-update-checker/cache.ts

@@ -19,29 +19,6 @@ interface AutoUpdateInstallContext {
   packageJsonPath: string;
 }
 
-function normalizePath(filePath: string): string {
-  return path.resolve(filePath);
-}
-
-function isWithinPath(childPath: string, parentPath: string): boolean {
-  const relativePath = path.relative(parentPath, childPath);
-  return (
-    relativePath === '' ||
-    (!relativePath.startsWith('..') && !path.isAbsolute(relativePath))
-  );
-}
-
-function isManagedInstallDir(installDir: string): boolean {
-  const normalizedInstallDir = normalizePath(installDir);
-  const normalizedCacheDir = normalizePath(CACHE_DIR);
-  const normalizedPackagesDir = path.join(normalizedCacheDir, 'packages');
-
-  return (
-    normalizedInstallDir === normalizedCacheDir ||
-    isWithinPath(normalizedInstallDir, normalizedPackagesDir)
-  );
-}
-
 /**
  * Removes a package from the bun.lock file if it's in JSON format.
  * Note: Newer Bun versions (1.1+) use a custom text format for bun.lock.
@@ -146,13 +123,6 @@ export function resolveInstallContext(
       path.basename(nodeModulesDir) === 'node_modules'
     ) {
       const installDir = path.dirname(nodeModulesDir);
-      if (!isManagedInstallDir(installDir)) {
-        log(
-          `[auto-update-checker] Skipping auto-update for unmanaged install root: ${installDir}`,
-        );
-        return null;
-      }
-
       const packageJsonPath = path.join(installDir, 'package.json');
       if (fs.existsSync(packageJsonPath)) {
         return { installDir, packageJsonPath };