Files
renovate/lib/modules/manager/gomod/artifacts-extra.ts
Michael Kriese 55a7341be2 test(manager): close long-tail branch coverage gaps (#45954)
* test(manager): close azure-pipelines, bundler and cocoapods gaps

Adds a pipeline whose step task is not versioned, so nothing is
extracted, and a gem lock line carrying no version, which is skipped.

Marks four guards that cannot be false: `extractContainer()` always
returns a dep, `getLockFilePath()` always returns a path, and the
cocoapods dep name, spec name and git account all derive from values
their enclosing check already proved present.

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

* test(manager): close cargo, composer and cpanfile gaps

Adds a composer repository map that disables something other than
packagist, which is dropped rather than recorded.

Marks five guards: the cargo warning only fires for a crate dep and its
retry only runs when a dep was already current, a gitlab rule without a
token never passes the auth check, and both cpanfile handlers run only
once their match produced a value.

Two implicit elses stay counted, in the cargo version resolver and the
composer repository mapper, since a coverage-ignore hint does not attach
to an `else if`.

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

* test(manager): close deps-edn, docker-compose and dockerfile gaps

Adds three extractions: a deps.edn alias whose value is not a map, a
compose extension carrying no image, and a dockerfile escape directive
naming a character other than a backtick, which leaves the default in
place.

Marks the deps-edn repository name check, since object keys are always
strings, and notes the repository spec chain, where a coverage-ignore
hint does not attach to an `else if`.

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

* test(manager/flux): close the flux coverage gaps

Adds two GitRepository resources whose url is ssh rather than http, one
pinned to a tag and one to a commit. Neither yields a source url, since
only http urls can be turned into one.

Marks the Kustomization image guard: the schema rejects an image without
a name, so `extractImage()` cannot return null there.

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

* test(manager): mark github-actions, gitlabci and glasskube guards

None of these six needs a test. A parsed action ref is either a docker
or a repository ref, `getDep()` always returns a dep for a job container
and a service, and the remaining three need a listed file that cannot be
read, a listed file that yields no deps, or a manifest whose
repositories match no name.

The glasskube resource kind chain stays counted, since a coverage-ignore
hint does not attach to an `else if`.

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

* test(manager): mark gomod and gradle-wrapper guards

Four guards that need setups no test provides: a removed dep that is not
added back, a go.mod the replace massaging leaves unchanged, and a
daemon-jvm file carrying no toolchain version. A parsed go directive
always yields a version.

The additional-dependency count chain stays counted, since a
coverage-ignore hint does not attach to an `else if`.

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-15 07:10:10 +00:00

142 lines
3.9 KiB
TypeScript

import { diffLines } from 'diff';
import { markdownTable } from 'markdown-table';
import type { UpdateArtifactsConfig } from '../types.ts';
import { parseLine } from './line-parser.ts';
import type { ExtraDep } from './types.ts';
export function getExtraDeps(
goModBefore: string,
goModAfter: string,
excludeDeps: string[],
): ExtraDep[] {
const result: ExtraDep[] = [];
const diff = diffLines(goModBefore, goModAfter, {
newlineIsToken: true,
});
const addDeps: Record<string, string> = {};
const rmDeps: Record<string, string> = {};
for (const { added, removed, value } of diff) {
if (!added && !removed) {
continue;
}
const res = parseLine(value);
if (!res) {
continue;
}
const { depName, depType, currentValue } = res;
if (!depName || !currentValue) {
continue;
}
let expandedDepName = depName;
// NOTE: Right now the only special depType we care about is 'toolchain' because the table
// rendering prior to this change was ambiguous with regards to go version vs toolchain
// version updates.
if (depType === 'toolchain') {
expandedDepName = `${depName} (${depType})`;
}
if (added) {
addDeps[expandedDepName] = currentValue;
} else {
rmDeps[expandedDepName] = currentValue;
}
}
for (const [depName, currentValue] of Object.entries(rmDeps)) {
if (excludeDeps.includes(depName)) {
continue;
}
const newValue = addDeps[depName];
// v8 ignore else -- needs a removed dep that is not added back
if (newValue) {
result.push({
depName,
currentValue,
newValue,
});
}
}
return result;
}
export function extraDepsTable(extraDeps: ExtraDep[]): string {
const tableLines: string[][] = [];
tableLines.push(['**Package**', '**Change**']);
for (const { depName, currentValue, newValue } of extraDeps) {
const depNameQuoted = `\`${depName}\``;
const versionChangeQuoted = `\`${currentValue}\` -> \`${newValue}\``;
tableLines.push([depNameQuoted, versionChangeQuoted]);
}
return markdownTable(tableLines, {
align: ['l', 'l'],
});
}
export function getExtraDepsNotice(
goModBefore: string | null,
goModAfter: string | null,
excludeDeps: string[],
{ minimumReleaseAge }: Pick<UpdateArtifactsConfig, 'minimumReleaseAge'>,
): string | null {
if (!goModBefore || !goModAfter) {
return null;
}
const extraDeps = getExtraDeps(goModBefore, goModAfter, excludeDeps);
if (extraDeps.length === 0) {
return null;
}
const noticeLines: string[] = [
'In order to perform the update(s) described in the table above, Renovate ran the `go get` command, which resulted in the following additional change(s):',
'\n',
];
const goUpdated = extraDeps.some(({ depName }) => depName === 'go');
const toolchainUpdated = extraDeps.some(
({ depName }) => depName === 'go (toolchain)',
);
const otherDepsCount =
extraDeps.length - (goUpdated ? 1 : 0) - (toolchainUpdated ? 1 : 0);
if (otherDepsCount === 1) {
noticeLines.push(`- ${otherDepsCount} additional dependency was updated`);
// NOTE: a zero count adds no line. A coverage-ignore hint cannot suppress
// the implicit else on an `else if`.
} else if (otherDepsCount > 1) {
noticeLines.push(
`- ${otherDepsCount} additional dependencies were updated`,
);
}
if (goUpdated) {
noticeLines.push(
'- The `go` directive was updated for compatibility reasons',
);
}
if (minimumReleaseAge) {
noticeLines.push('\n');
noticeLines.push(
`Due to Go's usage of [Minimal Version Selection (MVS)](https://go.dev/ref/mod#minimal-version-selection), these packages have been updated to the minimum version available, so will still abide by \`minimumReleaseAge=${minimumReleaseAge}\``,
);
}
noticeLines.push('\n');
noticeLines.push('Details:');
noticeLines.push('\n');
noticeLines.push(extraDepsTable(extraDeps));
return noticeLines.join('\n');
}