Files
renovate/lib/modules/manager/git-submodules/extract.ts
T
Michael Kriese 312c380926 test(manager): cover the pre-commit warning switch and drop unreachable fallbacks (#46221)
* fix(util/http): make the legacy `headers` getter configurable

Every other property defined on `HttpError.prototype` in this file is
`configurable`, but `headers` was not. That makes the module fail with
`TypeError: Cannot redefine property: headers` whenever a test resets the
module registry and re-imports anything that pulls in `got.ts`.

Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>

* test(manager/pre-commit): cover the suppress-warning env var

`defaultConfig.prBodyNotes` drops the unsupported-manager warning when
`RENOVATE_X_SUPPRESS_PRE_COMMIT_WARNING` is set. Since the value is
computed at module load, the new spec resets the module registry and
re-imports the manager with the variable stubbed.

The `istanbul ignore next` on that arm is dropped because the branch is
now exercised.

Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>

* refactor(manager): remove unreachable fallbacks

Drop three defensive fallbacks that can never be taken:

- `docker-compose`: `config.services ?? config` always yields an object,
  so the `|| {}` guard behind it is dead
- `git-submodules`: `git.raw()` resolves to a string, so the `?? ''`
  never applies
- `maven`: the only site that builds a `MavenProp` always passes a
  `packageFile` string, so narrow the type and drop the `?? undefined`

Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>

---------

Co-authored-by: Claude Opus 5 <michael.kriese+claude-code@mend.io>
2026-09-16 06:43:02 +00:00

134 lines
3.6 KiB
TypeScript

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<string> {
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<string | null> {
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<GitModule[]> {
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\.(?<name>.+?)\.path\s(?<path>.+)/),
);
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<PackageFileContent | null> {
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 };
}