import type { SimpleGit } from 'simple-git'; import upath from 'upath'; import { GlobalConfig } from '../../../config/global.ts'; import { logger } from '../../../logger/index.ts'; import { createSimpleGit } from '../../../util/git/index.ts'; import { getHttpUrl } from '../../../util/git/url.ts'; import { regEx } from '../../../util/regex.ts'; import { GitRefsDatasource } from '../../datasource/git-refs/index.ts'; import * as semVerVersioning from '../../versioning/semver/index.ts'; import type { ExtractConfig, PackageFileContent } from '../types.ts'; import type { GitModule } from './types.ts'; async function getUrl( git: SimpleGit, gitModulesPath: string, submoduleName: string, ): Promise { const path = ( await createSimpleGit().raw([ 'config', '--file', gitModulesPath, '--get', `submodule.${submoduleName}.url`, ]) )?.trim(); if (!path?.startsWith('../') && !path?.startsWith('./')) { return path; } const remoteUrl = ( await git.raw(['config', '--get', 'remote.origin.url']) ).trim(); return new URL(path, `${remoteUrl}/`).href; } async function getBranch( git: SimpleGit, gitModulesPath: string, submoduleName: string, ): Promise { const branchFromConfig = ( await createSimpleGit().raw([ 'config', '--file', gitModulesPath, '--get', `submodule.${submoduleName}.branch`, ]) ).trim(); return branchFromConfig === '.' ? (await git.branch(['--list'])).current.trim() : branchFromConfig || null; } async function getModules( git: SimpleGit, gitModulesPath: string, ): Promise { const res: GitModule[] = []; try { const modules = ( await git.raw([ 'config', '--file', gitModulesPath, '--get-regexp', '\\.path', ]) ) .trim() .split(regEx(/\n/)) .filter((s) => !!s); for (const line of modules) { const [, name, path] = line.split( regEx(/submodule\.(?.+?)\.path\s(?.+)/), ); res.push({ name, path }); } } catch (err) /* istanbul ignore next */ { logger.warn({ err }, 'Error getting git submodules during extract'); } return res; } export default async function extractPackageFile( _content: string, packageFile: string, _config: ExtractConfig, ): Promise { const localDir = GlobalConfig.get('localDir'); const git = createSimpleGit({ config: { baseDir: localDir } }); const gitModulesPath = upath.join(localDir, packageFile); const depNames = await getModules(git, gitModulesPath); if (!depNames.length) { return null; } const deps = []; for (const { name, path } of depNames) { try { const [currentDigest] = ( await git.subModule(['status', '--cached', path]) ) .trim() .replace(regEx(/^[-+]/), '') .split(regEx(/\s/)); const subModuleUrl = await getUrl(git, gitModulesPath, name); const httpSubModuleUrl = getHttpUrl(subModuleUrl); const branch = await getBranch(git, gitModulesPath, name); deps.push({ depName: path, packageName: httpSubModuleUrl, sourceUrl: httpSubModuleUrl.replace(regEx(/\.git$/), ''), currentValue: branch ?? undefined, currentDigest, ...(semVerVersioning.api.isVersion(branch) ? { versioning: semVerVersioning.id } : {}), }); } catch (err) /* istanbul ignore next */ { logger.warn( { err, packageFile }, 'Error mapping git submodules during extraction', ); } } return { deps, datasource: GitRefsDatasource.id }; }