-
Notifications
You must be signed in to change notification settings - Fork 5
feat: Schema Migration Service #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
eb363ea
docs: add schema migration feature design spec
William-W-Chen f046926
feat(config): add schema_source config for connections
William-W-Chen f71ba0a
feat: add in-memory plan store with 30min TTL
William-W-Chen dde2a97
feat: add git clone/pull helper for schema repos
William-W-Chen 1b7061d
feat: add pgschema CLI helper with plan JSON parser
William-W-Chen ebc8520
feat: add MigrationService proto definition
William-W-Chen e05f6e2
feat: implement MigrationService RPC handlers
William-W-Chen fe1ce96
feat: add migration RPC client and vite proxy
William-W-Chen b1295a2
feat: add React hooks for migration RPCs
William-W-Chen 79944e5
feat: add MigrationPanel component with diff view and apply
William-W-Chen 1db9f5a
feat: wire MigrationPanel into ContextPanel
William-W-Chen 6ab13e5
docs: add schema_source example to pgconsole.example.toml
William-W-Chen 36d554c
feat: add TARGETARCH support and unzip pgschema binaries in Dockerfile
William-W-Chen 6aaac74
feat: add MigrationPanel tab to RightPanel and update PanelTab type
William-W-Chen f7f8517
feat: enhance migration service with schema handling and logging inte…
William-W-Chen a1ca8e8
feat: improve error handling and logging in migration functions
William-W-Chen 79b3b66
refactor: refactor migration service and plan storage with improved t…
William-W-Chen 69e3a2e
feat: implement detailed panel state management and status messaging …
William-W-Chen 5ad1cc8
chore: remove outdated schema migration design document
William-W-Chen 16b182e
chore: update Dockerfile to correctly unzip and rename pgschema binar…
William-W-Chen 662cf60
feat: enhance migration service with schema validation and improved e…
William-W-Chen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| syntax = "proto3"; | ||
|
|
||
| package migration.v1; | ||
|
|
||
| service MigrationService { | ||
| rpc PlanMigration(PlanMigrationRequest) returns (PlanMigrationResponse); | ||
| rpc ApplyMigration(ApplyMigrationRequest) returns (stream ApplyMigrationResponse); | ||
| rpc GetSchemaSourceStatus(GetSchemaSourceStatusRequest) returns (GetSchemaSourceStatusResponse); | ||
| } | ||
|
|
||
| message PlanMigrationRequest { | ||
| string connection_id = 1; | ||
| } | ||
|
|
||
| message SchemaDiff { | ||
| string sql = 1; | ||
| string type = 2; | ||
| string operation = 3; | ||
| string path = 4; | ||
| bool can_run_in_transaction = 5; | ||
| } | ||
|
|
||
| message PlanMigrationResponse { | ||
| string plan_id = 1; | ||
| string branch = 2; | ||
| string commit_hash = 3; | ||
| string source_fingerprint = 4; | ||
| repeated SchemaDiff diffs = 5; | ||
| bool can_run_in_transaction = 6; | ||
| string summary = 7; | ||
| } | ||
|
|
||
| message ApplyMigrationRequest { | ||
| string connection_id = 1; | ||
| string plan_id = 2; | ||
| } | ||
|
|
||
| message ApplyMigrationResponse { | ||
| int32 step = 1; | ||
| int32 total_steps = 2; | ||
| string sql = 3; | ||
| string status = 4; | ||
| string error = 5; | ||
| } | ||
|
|
||
| message GetSchemaSourceStatusRequest { | ||
| string connection_id = 1; | ||
| } | ||
|
|
||
| message GetSchemaSourceStatusResponse { | ||
| bool configured = 1; | ||
| string repo = 2; | ||
| string branch = 3; | ||
| string path = 4; | ||
| string schema = 5; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| import { execFile } from 'child_process' | ||
| import { access, rm } from 'fs/promises' | ||
| import { join } from 'path' | ||
| import { tmpdir } from 'os' | ||
|
|
||
| function exec(cmd: string, args: string[], cwd?: string): Promise<string> { | ||
| return new Promise((resolve, reject) => { | ||
| execFile(cmd, args, { cwd, timeout: 60_000 }, (error, stdout, stderr) => { | ||
| if (error) { | ||
| reject(new Error(`git ${args[0]} failed: ${stderr || error.message}`)) | ||
| } else { | ||
| resolve(stdout.trim()) | ||
| } | ||
| }) | ||
| }) | ||
| } | ||
|
|
||
| // Cache the repo URL per directory so we can detect config changes | ||
| const repoDirUrls = new Map<string, string>() | ||
| // Per-connection lock to prevent concurrent clone/fetch races | ||
| const syncLocks = new Map<string, Promise<{ commitHash: string }>>() | ||
|
|
||
| export function getRepoDir(connectionId: string): string { | ||
| return join(tmpdir(), 'pgconsole-schema', connectionId) | ||
| } | ||
|
|
||
| export async function syncRepo(connectionId: string, repo: string, branch?: string): Promise<{ commitHash: string }> { | ||
| // Serialize concurrent sync requests for the same connection | ||
| const existing = syncLocks.get(connectionId) | ||
| if (existing) { | ||
| return existing | ||
| } | ||
|
|
||
| const promise = doSyncRepo(connectionId, repo, branch).finally(() => { | ||
| syncLocks.delete(connectionId) | ||
| }) | ||
| syncLocks.set(connectionId, promise) | ||
| return promise | ||
| } | ||
|
|
||
| async function doSyncRepo(connectionId: string, repo: string, branch?: string): Promise<{ commitHash: string }> { | ||
| const repoDir = getRepoDir(connectionId) | ||
|
|
||
| const exists = await access(join(repoDir, '.git')).then(() => true).catch(() => false) | ||
|
|
||
| // If the repo URL changed, wipe the old checkout | ||
| if (exists) { | ||
| const cachedUrl = repoDirUrls.get(repoDir) | ||
| if (cachedUrl && cachedUrl !== repo) { | ||
| await rm(repoDir, { recursive: true, force: true }) | ||
| } | ||
| } | ||
|
|
||
| const stillExists = await access(join(repoDir, '.git')).then(() => true).catch(() => false) | ||
|
|
||
| if (stillExists) { | ||
| await exec('git', ['fetch', 'origin', ...(branch ? [branch] : [])], repoDir) | ||
| await exec('git', ['reset', '--hard', branch ? `origin/${branch}` : 'FETCH_HEAD'], repoDir) | ||
| } else { | ||
| const cloneArgs = ['clone', '--depth', '1'] | ||
| if (branch) cloneArgs.push('--branch', branch) | ||
| cloneArgs.push(repo, repoDir) | ||
| await exec('git', cloneArgs) | ||
|
NFUChen marked this conversation as resolved.
|
||
| } | ||
|
|
||
| repoDirUrls.set(repoDir, repo) | ||
|
|
||
| const commitHash = await exec('git', ['rev-parse', 'HEAD'], repoDir) | ||
| return { commitHash } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.