Skip to content

Commit 2dae94e

Browse files
committed
core: show visual progress bar during database migration so users can see real-time status of projects, sessions, and messages being converted
1 parent c6adc19 commit 2dae94e

File tree

3 files changed

+381
-126
lines changed

3 files changed

+381
-126
lines changed

packages/opencode/src/index.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,31 @@ const cli = yargs(hideBin(process.argv))
8282
const marker = path.join(Global.Path.data, "opencode.db")
8383
if (!(await Bun.file(marker).exists())) {
8484
console.log("Performing one time database migration, may take a few minutes...")
85-
await JsonMigration.run(Database.Client().$client)
85+
const tty = process.stdout.isTTY
86+
const width = 36
87+
const orange = "\x1b[38;5;214m"
88+
const muted = "\x1b[0;2m"
89+
const reset = "\x1b[0m"
90+
let last = -1
91+
if (tty) process.stdout.write("\x1b[?25l")
92+
try {
93+
await JsonMigration.run(Database.Client().$client, {
94+
progress: (event) => {
95+
if (!tty) return
96+
const percent = Math.floor((event.current / event.total) * 100)
97+
if (percent === last && event.current !== event.total) return
98+
last = percent
99+
const fill = Math.round((percent / 100) * width)
100+
const bar = `${"■".repeat(fill)}${"・".repeat(width - fill)}`
101+
process.stdout.write(
102+
`\r${orange}${bar} ${percent.toString().padStart(3)}%${reset} ${muted}${event.label.padEnd(12)} ${event.current}/${event.total}${reset}`,
103+
)
104+
if (event.current === event.total) process.stdout.write("\n")
105+
},
106+
})
107+
} finally {
108+
if (tty) process.stdout.write("\x1b[?25h")
109+
}
86110
console.log("Database migration complete.")
87111
}
88112
})

packages/opencode/src/storage/json-migration.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,17 @@ import { existsSync } from "fs"
1111
export namespace JsonMigration {
1212
const log = Log.create({ service: "json-migration" })
1313

14-
export async function run(sqlite: Database) {
14+
export type Progress = {
15+
current: number
16+
total: number
17+
label: string
18+
}
19+
20+
type Options = {
21+
progress?: (event: Progress) => void
22+
}
23+
24+
export async function run(sqlite: Database, options?: Options) {
1525
const storageDir = path.join(Global.Path.data, "storage")
1626

1727
if (!existsSync(storageDir)) {
@@ -120,6 +130,25 @@ export namespace JsonMigration {
120130
shares: shareFiles.length,
121131
})
122132

133+
const total = Math.max(
134+
1,
135+
projectFiles.length +
136+
sessionFiles.length +
137+
messageFiles.length +
138+
partFiles.length +
139+
todoFiles.length +
140+
permFiles.length +
141+
shareFiles.length,
142+
)
143+
const progress = options?.progress
144+
let current = 0
145+
const step = (label: string, count: number) => {
146+
current = Math.min(total, current + count)
147+
progress?.({ current, total, label })
148+
}
149+
150+
progress?.({ current, total, label: "starting" })
151+
123152
sqlite.exec("BEGIN TRANSACTION")
124153

125154
// Migrate projects first (no FK deps)
@@ -151,6 +180,7 @@ export namespace JsonMigration {
151180
})
152181
}
153182
stats.projects += insert(projectValues, ProjectTable, "project")
183+
step("projects", end - i)
154184
}
155185
log.info("migrated projects", { count: stats.projects, duration: Math.round(performance.now() - start) })
156186

@@ -195,6 +225,7 @@ export namespace JsonMigration {
195225
})
196226
}
197227
stats.sessions += insert(sessionValues, SessionTable, "session")
228+
step("sessions", end - i)
198229
}
199230
log.info("migrated sessions", { count: stats.sessions })
200231
if (orphans.sessions > 0) {
@@ -241,6 +272,7 @@ export namespace JsonMigration {
241272
}
242273
values.length = count
243274
stats.messages += insert(values, MessageTable, "message")
275+
step("messages", end - i)
244276
}
245277
log.info("migrated messages", { count: stats.messages })
246278

@@ -281,6 +313,7 @@ export namespace JsonMigration {
281313
}
282314
values.length = count
283315
stats.parts += insert(values, PartTable, "part")
316+
step("parts", end - i)
284317
}
285318
log.info("migrated parts", { count: stats.parts })
286319

@@ -317,6 +350,7 @@ export namespace JsonMigration {
317350
}
318351
}
319352
stats.todos += insert(values, TodoTable, "todo")
353+
step("todos", end - i)
320354
}
321355
log.info("migrated todos", { count: stats.todos })
322356
if (orphans.todos > 0) {
@@ -341,6 +375,7 @@ export namespace JsonMigration {
341375
permValues.push({ project_id: projectID, data })
342376
}
343377
stats.permissions += insert(permValues, PermissionTable, "permission")
378+
step("permissions", end - i)
344379
}
345380
log.info("migrated permissions", { count: stats.permissions })
346381
if (orphans.permissions > 0) {
@@ -369,6 +404,7 @@ export namespace JsonMigration {
369404
shareValues.push({ session_id: sessionID, id: data.id, secret: data.secret, url: data.url })
370405
}
371406
stats.shares += insert(shareValues, SessionShareTable, "session_share")
407+
step("shares", end - i)
372408
}
373409
log.info("migrated session shares", { count: stats.shares })
374410
if (orphans.shares > 0) {
@@ -393,6 +429,8 @@ export namespace JsonMigration {
393429
log.warn("migration errors", { errors: stats.errors.slice(0, 20) })
394430
}
395431

432+
progress?.({ current: total, total, label: "complete" })
433+
396434
return stats
397435
}
398436
}

0 commit comments

Comments
 (0)