import { Pipeline } from './../pipelines/pipeline.entity'; import { PipelineTask } from './../pipeline-tasks/pipeline-task.entity'; import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { F_OK } from 'constants'; import { access, mkdir } from 'fs/promises'; import { join } from 'path'; import { gitP } from 'simple-git'; import { Repository } from 'typeorm'; import { Project } from '../projects/project.entity'; import { ListBranchesArgs } from './dtos/list-branches.args'; import { ListLogsArgs } from './dtos/list-logs.args'; import { ConfigService } from '@nestjs/config'; const DEFAULT_REMOTE_NAME = 'origin'; const INFO_PATH = '@info'; @Injectable() export class ReposService { constructor( @InjectRepository(Project) private readonly projectRepository: Repository, private readonly configService: ConfigService, ) {} getWorkspaceRoot(project: Project): string { return join( this.configService.get('workspaces.root'), encodeURIComponent(project.name), INFO_PATH, ); } async getGit(project: Project, workspaceRoot?: string) { if (!workspaceRoot) { workspaceRoot = this.getWorkspaceRoot(project); } await access(workspaceRoot, F_OK).catch(async () => { await mkdir(workspaceRoot, { recursive: true }); }); const git = gitP(workspaceRoot); if (!(await git.checkIsRepo())) { await git.init(); await git.addRemote(DEFAULT_REMOTE_NAME, project.sshUrl); } await git.fetch(); return git; } async listLogs(dto: ListLogsArgs) { const project = await this.projectRepository.findOneOrFail({ id: dto.projectId, }); const git = await this.getGit(project); return await git.log( dto.branch ? ['--branches', dto.branch, '--'] : ['--all'], ); } async listBranches(dto: ListBranchesArgs) { const project = await this.projectRepository.findOneOrFail({ id: dto.projectId, }); const git = await this.getGit(project); return git.branch(); } async checkout(task: PipelineTask, workspaceRoot: string) { const git = await this.getGit(task.pipeline.project, workspaceRoot); try { await git.fetch(DEFAULT_REMOTE_NAME); } catch (err) { if (err.message.includes("couldn't find remote ref nonexistent")) { throw new NotFoundException(err.message); } throw err; } await git.checkout([task.commit]); } /** * get workspace root absolute path * * ! example: `/var/tmp/fennec-workspaces-root/project/pipeline_name-commit_hash` * @param task {PipelineTask} task (with pipeline and project info) */ getWorkspaceRootByTask(task: PipelineTask) { return join( this.configService.get('workspaces.root'), encodeURIComponent(task.pipeline.project.name), encodeURIComponent(`${task.pipeline.name}-${task.commit}`), ); } }