fix(migrations,projects,clone): normalize legacy schema before writes and harden conflict detection

Why

- Legacy installs can have a sessions table shape that predates provider/custom_name columns. Running migrateLegacySessionNames first caused its INSERT OR REPLACE INTO sessions (...) to target columns that may not exist and fail during startup migration.

- Some upgraded databases had projects.project_id as plain TEXT instead of a real PRIMARY KEY. That breaks assumptions used by id-based lookups and can allow invalid/duplicate identity semantics over time.

- projectsDb.createProjectPath inferred outcomes from 
ow.isArchived, but the upsert path always returns the post-update row with isArchived=0, so archived-reactivation and fresh-create could be misclassified.

- git clone accepted user-controlled URLs directly in argv position, so inputs beginning with - could be interpreted as options instead of a repository argument.

What

- Added 
ebuildProjectsTableWithPrimaryKeySchema in migrations: detect table shape via getTableInfo('projects'), verify project_id has pk=1, and rebuild when missing.

- Rebuild flow now creates a canonical projects__new table (project_id TEXT PRIMARY KEY), copies rows with transformation, backfills empty ids via SQLITE_UUID_SQL, deduplicates conflicting ids/paths, then swaps tables inside a transaction.

- Replaced the prior ddColumnToTableIfNotExists(...) + UPDATE project_id sequence with PK-aware detection/rebuild logic so legacy DBs converge to the required schema.

- Reordered migration sequence to run 
ebuildSessionsTableWithProjectSchema before migrateLegacySessionNames, ensuring sessions is normalized before legacy session_names merge writes execute.

- Updated projectsDb.createProjectPath to generate an ttemptedId before insert, pass it into the prepared statement, and classify outcomes by comparing returned 
ow.project_id to ttemptedId (created vs 
eactivated_archived), with no-row remaining ctive_conflict.

- Hardened clone execution by inserting -- before clone URL in git argv and rejecting normalized GitHub URLs that start with - in startCloneProject.

Tests

- Added integration coverage for projectsDb.createProjectPath branches: fresh insert, archived reactivation, and active conflict.

- Added clone service test for option-prefixed githubUrl rejection (INVALID_GITHUB_URL).
This commit is contained in:
Haileyesus
2026-04-28 17:15:13 +03:00
parent 8570bd7bab
commit 805e283fb6
5 changed files with 235 additions and 15 deletions

View File

@@ -125,7 +125,7 @@ const defaultDependencies: CloneProjectDependencies = {
return tokenRow;
},
spawnGitClone: (cloneUrl: string, clonePath: string): GitCloneProcess =>
spawn('git', ['clone', '--progress', cloneUrl, clonePath], {
spawn('git', ['clone', '--progress', '--', cloneUrl, clonePath], {
stdio: ['ignore', 'pipe', 'pipe'],
env: {
...process.env,
@@ -167,6 +167,13 @@ export async function startCloneProject(
});
}
if (normalizedGithubUrl.startsWith('-')) {
throw new AppError('Invalid githubUrl', {
code: 'INVALID_GITHUB_URL',
statusCode: 400,
});
}
const pathValidation = await dependencies.validatePath(normalizedWorkspacePath);
if (!pathValidation.valid || !pathValidation.resolvedPath) {
throw new AppError(pathValidation.error || 'Invalid workspace path', {