wip(TRUEREF-0018): commit version-scoped indexing work

This commit is contained in:
Giancarmine Salucci
2026-03-25 19:03:22 +01:00
parent b9d52405fa
commit fef6f66930
21 changed files with 1208 additions and 19 deletions

View File

@@ -34,14 +34,22 @@ export class VersionService {
* Add a new version record for a repository.
* The version ID follows the convention: {repositoryId}/{tag}
*
* @param commitHash Optional commit hash. If not provided and repository is local,
* will attempt to resolve the tag to a commit hash automatically.
*
* @throws NotFoundError when the parent repository does not exist
* @throws AlreadyExistsError when the tag is already registered
*/
add(repositoryId: string, tag: string, title?: string): RepositoryVersion {
add(
repositoryId: string,
tag: string,
title?: string,
commitHash?: string
): RepositoryVersion {
// Verify parent repository exists.
const repo = this.db
.prepare(`SELECT id FROM repositories WHERE id = ?`)
.get(repositoryId) as { id: string } | undefined;
.prepare(`SELECT id, source, source_url FROM repositories WHERE id = ?`)
.get(repositoryId) as { id: string; source: string; source_url: string } | undefined;
if (!repo) {
throw new NotFoundError(`Repository ${repositoryId} not found`);
@@ -55,15 +63,29 @@ export class VersionService {
throw new AlreadyExistsError(`Version ${tag} already exists for repository ${repositoryId}`);
}
// For local repositories, attempt to resolve tag to commit hash if not provided
let resolvedCommitHash = commitHash;
if (!resolvedCommitHash && repo.source === 'local') {
try {
const { resolveTagToCommit } = require('$lib/server/utils/git.js');
resolvedCommitHash = resolveTagToCommit({ repoPath: repo.source_url, tag });
} catch (error) {
console.warn(
`[VersionService] Could not resolve tag '${tag}' to commit hash for ${repositoryId}: ${error instanceof Error ? error.message : String(error)}`
);
// Continue without commit hash — non-blocking
}
}
const now = Math.floor(Date.now() / 1000);
this.db
.prepare(
`INSERT INTO repository_versions
(id, repository_id, tag, title, state, total_snippets, indexed_at, created_at)
VALUES (?, ?, ?, ?, 'pending', 0, NULL, ?)`
(id, repository_id, tag, title, commit_hash, state, total_snippets, indexed_at, created_at)
VALUES (?, ?, ?, ?, ?, 'pending', 0, NULL, ?)`
)
.run(id, repositoryId, tag, title ?? null, now);
.run(id, repositoryId, tag, title ?? null, resolvedCommitHash ?? null, now);
const row = this.db
.prepare(`SELECT * FROM repository_versions WHERE id = ?`)
@@ -105,11 +127,14 @@ export class VersionService {
* Silently skips tags that are already registered (idempotent).
* All new records are created with state = 'pending'.
*
* Supports optional `commitHash` field to pin a version to a specific commit,
* overriding tag resolution (TRUEREF-0019).
*
* @throws NotFoundError when the parent repository does not exist
*/
registerFromConfig(
repositoryId: string,
previousVersions: { tag: string; title: string }[]
previousVersions: { tag: string; title: string; commitHash?: string }[]
): RepositoryVersion[] {
// Verify parent repository exists.
const repo = this.db
@@ -122,7 +147,7 @@ export class VersionService {
const registered: RepositoryVersion[] = [];
for (const { tag, title } of previousVersions) {
for (const { tag, title, commitHash } of previousVersions) {
const existing = this.getByTag(repositoryId, tag);
if (existing) {
// Already registered — skip silently.
@@ -130,10 +155,42 @@ export class VersionService {
continue;
}
const version = this.add(repositoryId, tag, title);
const version = this.add(repositoryId, tag, title, commitHash);
registered.push(version);
}
return registered;
}
/**
* Discover all version tags from a local repository and return them
* along with their resolved commit hashes.
*
* This is used for tag auto-discovery when adding a repository or
* refreshing available versions (TRUEREF-0019).
*
* @returns Array of { tag, commitHash } objects, newest first
* @throws Error when repository is not local or git operations fail
*/
discoverTags(repositoryId: string): Array<{ tag: string; commitHash: string }> {
const repo = this.db
.prepare(`SELECT id, source, source_url FROM repositories WHERE id = ?`)
.get(repositoryId) as { id: string; source: string; source_url: string } | undefined;
if (!repo) {
throw new NotFoundError(`Repository ${repositoryId} not found`);
}
if (repo.source !== 'local') {
throw new Error('Tag discovery is only supported for local repositories');
}
const { discoverVersionTags, resolveTagToCommit } = require('$lib/server/utils/git.js');
const tags = discoverVersionTags({ repoPath: repo.source_url });
return tags.map((tag: string) => {
const commitHash = resolveTagToCommit({ repoPath: repo.source_url, tag });
return { tag, commitHash };
});
}
}