Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/opencode/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,7 @@ export namespace Config {
})
.optional(),
plugin: z.string().array().optional(),
snapshot: z.boolean().optional(),
snapshot: z.union([z.boolean(), z.number().int().nonnegative()]).optional(),
share: z
.enum(["manual", "auto", "disabled"])
.optional()
Expand Down
48 changes: 26 additions & 22 deletions packages/opencode/src/snapshot/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { Scheduler } from "../scheduler"
export namespace Snapshot {
const log = Log.create({ service: "snapshot" })
const hour = 60 * 60 * 1000
const prune = "7.days"

export function init() {
Scheduler.register({
Expand All @@ -23,34 +22,39 @@ export namespace Snapshot {
}

export async function cleanup() {
if (Instance.project.vcs !== "git") return
const cfg = await Config.get()
if (cfg.snapshot === false) return
const git = gitdir()
const exists = await fs
.stat(git)
.then(() => true)
.catch(() => false)
if (!exists) return
const result = await $`git --git-dir ${git} --work-tree ${Instance.worktree} gc --prune=${prune}`
.quiet()
.cwd(Instance.directory)
.nothrow()
if (result.exitCode !== 0) {
log.warn("cleanup failed", {
exitCode: result.exitCode,
stderr: result.stderr.toString(),
stdout: result.stdout.toString(),
})
return
if (cfg.snapshot === false || cfg.snapshot === 0 || cfg.snapshot === undefined) return
const retentionDays: number = cfg.snapshot === true ? 7 : cfg.snapshot!
const snapshotDir = gitdir()
const parentDir = path.dirname(snapshotDir)
try {
const entries = await fs.readdir(parentDir, { withFileTypes: true })
let deletedCount = 0
for (const entry of entries) {
if (!entry.isDirectory()) continue
const projectDir = path.join(parentDir, entry.name)
const stats = await fs.stat(projectDir)
const ageMs = Date.now() - stats.mtimeMs
const ageDays = ageMs / (24 * 60 * 60 * 1000)
if (ageDays > retentionDays) {
await fs.rm(projectDir, { recursive: true, force: true })
deletedCount++
log.info("deleted old snapshot directory", {
project: entry.name,
ageDays: Math.floor(ageDays),
})
}
}
log.info("cleanup", { retentionDays, deletedCount })
} catch (error) {
log.warn("cleanup failed", { error: (error as Error).message })
}
log.info("cleanup", { prune })
}

export async function track() {
if (Instance.project.vcs !== "git") return
const cfg = await Config.get()
if (cfg.snapshot === false) return
if (cfg.snapshot === false || cfg.snapshot === 0) return
const git = gitdir()
if (await fs.mkdir(git, { recursive: true })) {
await $`git init`
Expand Down
14 changes: 14 additions & 0 deletions packages/opencode/test/snapshot/snapshot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { test, expect } from "bun:test"
import { $ } from "bun"
import { Snapshot } from "../../src/snapshot"
import { Instance } from "../../src/project/instance"
import { Config } from "../../src/config/config"
import { tmpdir } from "../fixture/fixture"

async function bootstrap() {
Expand Down Expand Up @@ -1038,3 +1039,16 @@ test("diffFull with whitespace changes", async () => {
},
})
})

test("snapshot config with boolean true uses default 7-day retention", async () => {
const cfg = { snapshot: true as true | number }
const retentionDays = cfg.snapshot === true ? 7 : cfg.snapshot
expect(retentionDays).toBe(7)
})

test("snapshot config with positive integer uses specified retention", async () => {
const cfg = { snapshot: 3 as true | number }
const retentionDays = cfg.snapshot === true ? 7 : cfg.snapshot
expect(retentionDays).toBe(3)
})

2 changes: 1 addition & 1 deletion packages/sdk/js/src/v2/gen/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1667,7 +1667,7 @@ export type Config = {
ignore?: Array<string>
}
plugin?: Array<string>
snapshot?: boolean
snapshot?: boolean | number
/**
* Control sharing behavior:'manual' allows manual sharing via commands, 'auto' enables automatic sharing, 'disabled' disables all sharing
*/
Expand Down