# All files / src/git project-clones.ts

25% Statements 3/12
100% Branches 0/0
50% Functions 1/2
25% Lines 3/12

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49

    20x

    5x
    5x

     /**
     * Project Clone Management
     *
     * Ensures project repositories are cloned into the repos/ directory.
     * These base clones are used as the source for git worktrees.
     */

    import { join } from 'node:path';
    import { access } from 'node:fs/promises';
    import { cloneProject } from './clone.js';
    import { createModuleLogger } from '../logger/index.js';

    const log = createModuleLogger('git');

    /**
     * Derive the canonical clone directory for a project (relative to workspace root).
     * Convention: repos/<sanitizedName>-<id>/
     */
    export function getProjectCloneDir(projectName: string, projectId: string): string {
      const sanitized = projectName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
      return join('repos', `${sanitized}-${projectId}`);
    }

    /**
     * Ensure a project's git repository is cloned to the workspace.
     * Uses the canonical path: <workspaceRoot>/repos/<sanitizedName>-<id>/
     *
     * @param project - Project with id, name, and url
     * @param workspaceRoot - Absolute path to the workspace root
     * @returns Absolute path to the clone directory
     */
    export async function ensureProjectClone(
      project: { id: string; name: string; url: string },
      workspaceRoot: string,
    ): Promise<string> {
      const relPath = getProjectCloneDir(project.name, project.id);
      const clonePath = join(workspaceRoot, relPath);

      try {
        await access(clonePath);
        log.debug({ project: project.name, clonePath }, 'project clone already exists');
        return clonePath;
      } catch {
        log.info({ project: project.name, url: project.url, clonePath }, 'cloning project for first time');
        await cloneProject(project.url, clonePath);
        return clonePath;
      }
    }