diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index 2527303d92b5..52521ebe791a 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -289,9 +289,9 @@ export const layer = Layer.effect( const hasIgnore = yield* fs.existsSafe(gitignore) if (!hasIgnore) { yield* fs - .writeFileString( + .writeWithDirs( gitignore, - ["node_modules", "package.json", "package-lock.json", "bun.lock", ".gitignore"].join("\n"), + Buffer.from(["node_modules", "package.json", "package-lock.json", "bun.lock", ".gitignore"].join("\n")), ) .pipe( Effect.catchIf( diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index c016431bc781..624abfb1a5b5 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -919,6 +919,23 @@ it.effect("installs dependencies in writable OPENCODE_CONFIG_DIR", () => }).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(CrossSpawnSpawner.defaultLayer)), ) +it.effect("creates .gitignore in OPENCODE_CONFIG_DIR when directory does not yet exist", () => + Effect.gen(function* () { + const dir = yield* tmpdirScoped() + const configDir = path.join(dir, "nonexistent-config-dir") + + yield* withProcessEnv( + "OPENCODE_CONFIG_DIR", + configDir, + Config.Service.use((svc) => svc.get().pipe(Effect.andThen(svc.waitForDependencies()))).pipe( + provideInstanceEffect(dir), + ), + ) + + expect(yield* FSUtil.use.readFileString(path.join(configDir, ".gitignore"))).toContain("node_modules") + }).pipe(Effect.provide(testInstanceStoreLayer), Effect.provide(CrossSpawnSpawner.defaultLayer)), +) + // Note: deduplication and serialization of npm installs is now handled by the // core Npm.Service (via EffectFlock). Those behaviors are tested in the core // package's npm tests, not here.