mirror of
https://github.com/renovatebot/renovate.git
synced 2025-01-10 21:37:41 +00:00
fix: better branch code coverage (#24270)
This commit is contained in:
parent
4b7949acbc
commit
a9dc0625cf
44 changed files with 231 additions and 124 deletions
.github/workflows
jest.config.tslib
config/presets/internal
modules
datasource
bitbucket-tags
conan
conda
dart-version
endoflife-date
flutter-version
hermit
index.tsjenkins-plugins
pod
sbt-package
terraform-module
manager
cake
custom/regex
docker-compose
helm-values/__fixtures__
jsonnet-bundler
mint
woodpecker
platform
versioning
util
workers/repository/model
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
|
@ -404,7 +404,7 @@ jobs:
|
|||
- name: Check coverage threshold
|
||||
run: |
|
||||
pnpm nyc check-coverage -t ./coverage/nyc \
|
||||
--branches 98 \
|
||||
--branches 98.99 \
|
||||
--functions 100 \
|
||||
--lines 100 \
|
||||
--statements 100
|
||||
|
|
|
@ -213,7 +213,9 @@ const config: JestConfig = {
|
|||
cacheDirectory: '.cache/jest',
|
||||
clearMocks: true,
|
||||
collectCoverage: true,
|
||||
coverageReporters: ci ? ['lcovonly', 'json'] : ['html', 'text-summary'],
|
||||
coverageReporters: ci
|
||||
? ['lcovonly', 'json']
|
||||
: ['html', 'text-summary', 'json'],
|
||||
transform: {
|
||||
'\\.ts$': [
|
||||
'ts-jest',
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { coerceArray } from '../../../util/array';
|
||||
import type { PackageRule } from '../../types';
|
||||
import type { Preset } from '../types';
|
||||
|
||||
|
@ -45,7 +46,7 @@ export function addPresets(
|
|||
presets: Record<string, Preset>,
|
||||
...templates: PresetTemplate[]
|
||||
): void {
|
||||
const ext = presets.all?.extends ?? [];
|
||||
const ext = coerceArray(presets.all?.extends);
|
||||
for (const template of templates) {
|
||||
const { title, description, packageRules } = template;
|
||||
presets[title] = {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import is from '@sindresorhus/is';
|
||||
import { toArray } from '../../../util/array';
|
||||
import type { Preset } from '../types';
|
||||
|
||||
/* eslint sort-keys: ["error", "asc", {caseSensitive: false, natural: true}] */
|
||||
|
@ -482,20 +482,20 @@ export const presets: Record<string, Preset> = {};
|
|||
for (const [name, value] of Object.entries(repoGroups)) {
|
||||
presets[name] = {
|
||||
description: `${name} monorepo`,
|
||||
matchSourceUrls: is.array(value) ? value : [value],
|
||||
matchSourceUrls: toArray(value),
|
||||
};
|
||||
}
|
||||
|
||||
for (const [name, value] of Object.entries(orgGroups)) {
|
||||
presets[name] = {
|
||||
description: `${name} monorepo`,
|
||||
matchSourceUrlPrefixes: is.array(value) ? value : [value],
|
||||
matchSourceUrlPrefixes: toArray(value),
|
||||
};
|
||||
}
|
||||
|
||||
for (const [name, value] of Object.entries(patternGroups)) {
|
||||
presets[name] = {
|
||||
description: `${name} monorepo`,
|
||||
matchPackagePatterns: is.array(value) ? value : [value],
|
||||
matchPackagePatterns: toArray(value),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -124,5 +124,23 @@ describe('modules/datasource/bitbucket-tags/index', () => {
|
|||
expect(res).toBeString();
|
||||
expect(res).toBe('123');
|
||||
});
|
||||
|
||||
it('returns null for missing hash', async () => {
|
||||
const body = {
|
||||
name: 'v1.0.0',
|
||||
};
|
||||
httpMock
|
||||
.scope('https://api.bitbucket.org')
|
||||
.get('/2.0/repositories/some/dep2/refs/tags/v1.0.0')
|
||||
.reply(200, body);
|
||||
const res = await getDigest(
|
||||
{
|
||||
datasource,
|
||||
packageName: 'some/dep2',
|
||||
},
|
||||
'v1.0.0'
|
||||
);
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -51,6 +51,17 @@ describe('modules/datasource/conan/index', () => {
|
|||
'3a9b47caee2e2c1d3fb7d97788339aa8'
|
||||
);
|
||||
});
|
||||
|
||||
it('returns null for missing revision', async () => {
|
||||
const version = '1.8.1';
|
||||
httpMock
|
||||
.scope(nonDefaultRegistryUrl)
|
||||
.get(`/v2/conans/poco/${version}/_/_/revisions`)
|
||||
.reply(200, []);
|
||||
digestConfig.packageName = `poco/${version}@_/_`;
|
||||
digestConfig.currentDigest = '4fc13d60fd91ba44fefe808ad719a5af';
|
||||
expect(await getDigest(digestConfig, version)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getReleases', () => {
|
||||
|
@ -180,6 +191,22 @@ describe('modules/datasource/conan/index', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('works with empty releases', async () => {
|
||||
httpMock
|
||||
.scope('https://api.github.com')
|
||||
.get(
|
||||
'/repos/conan-io/conan-center-index/contents/recipes/poco/config.yml'
|
||||
)
|
||||
.reply(200, '');
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
...config,
|
||||
registryUrls: [defaultRegistryUrl],
|
||||
packageName: 'poco/1.2@_/_',
|
||||
})
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('rejects userAndChannel for Conan Center', async () => {
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
|
|
|
@ -31,7 +31,10 @@ describe('modules/datasource/conda/index', () => {
|
|||
});
|
||||
|
||||
it('returns null for empty result', async () => {
|
||||
httpMock.scope(defaultRegistryUrl).get(depUrl).reply(200, {});
|
||||
httpMock
|
||||
.scope(defaultRegistryUrl)
|
||||
.get(depUrl)
|
||||
.reply(200, { versions: [] });
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource,
|
||||
|
|
|
@ -34,7 +34,14 @@ describe('modules/datasource/dart-version/index', () => {
|
|||
});
|
||||
|
||||
it('returns null for empty 200 OK', async () => {
|
||||
httpMock.scope(baseUrl).get(urlPath).reply(200, []);
|
||||
const scope = httpMock.scope(baseUrl);
|
||||
for (const channel of channels) {
|
||||
scope
|
||||
.get(
|
||||
`/storage/v1/b/dart-archive/o?delimiter=%2F&prefix=channels%2F${channel}%2Frelease%2F&alt=json`
|
||||
)
|
||||
.reply(200, { prefixes: [] });
|
||||
}
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource,
|
||||
|
|
|
@ -100,7 +100,7 @@ describe('modules/datasource/endoflife-date/index', () => {
|
|||
});
|
||||
|
||||
it('returns null for empty result', async () => {
|
||||
httpMock.scope(registryUrl).get(eksMockPath).reply(200, {});
|
||||
httpMock.scope(registryUrl).get(eksMockPath).reply(200, []);
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource,
|
||||
|
|
|
@ -32,7 +32,7 @@ describe('modules/datasource/flutter-version/index', () => {
|
|||
});
|
||||
|
||||
it('returns null for empty 200 OK', async () => {
|
||||
httpMock.scope(baseUrl).get(urlPath).reply(200, []);
|
||||
httpMock.scope(baseUrl).get(urlPath).reply(200, { releases: [] });
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource,
|
||||
|
|
|
@ -54,10 +54,9 @@ export class FlutterVersionDatasource extends Datasource {
|
|||
releaseTimestamp: release_date,
|
||||
isStable: channel === 'stable',
|
||||
}));
|
||||
return result.releases.length ? result : null;
|
||||
} catch (err) {
|
||||
this.handleGenericErrors(err);
|
||||
}
|
||||
|
||||
return result.releases.length ? result : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { getApiBaseUrl } from '../../../util/github/url';
|
|||
import { GithubHttp } from '../../../util/http/github';
|
||||
import { regEx } from '../../../util/regex';
|
||||
import { streamToString } from '../../../util/streams';
|
||||
import { coerceString } from '../../../util/string';
|
||||
import { parseUrl } from '../../../util/url';
|
||||
import { id } from '../../versioning/hermit';
|
||||
import { Datasource } from '../datasource';
|
||||
|
@ -106,8 +107,8 @@ export class HermitDatasource extends Datasource {
|
|||
})
|
||||
async getHermitSearchManifest(u: URL): Promise<HermitSearchResult[] | null> {
|
||||
const registryUrl = u.toString();
|
||||
const host = u.host ?? '';
|
||||
const groups = this.pathRegex.exec(u.pathname ?? '')?.groups;
|
||||
const host = coerceString(u.host);
|
||||
const groups = this.pathRegex.exec(coerceString(u.pathname))?.groups;
|
||||
if (!groups) {
|
||||
logger.warn(
|
||||
{ registryUrl },
|
||||
|
|
|
@ -3,6 +3,7 @@ import { dequal } from 'dequal';
|
|||
import { HOST_DISABLED } from '../../constants/error-messages';
|
||||
import { logger } from '../../logger';
|
||||
import { ExternalHostError } from '../../types/errors/external-host-error';
|
||||
import { coerceArray } from '../../util/array';
|
||||
import * as memCache from '../../util/cache/memory';
|
||||
import * as packageCache from '../../util/cache/package';
|
||||
import { clone } from '../../util/clone';
|
||||
|
@ -149,10 +150,10 @@ async function mergeRegistries(
|
|||
continue;
|
||||
}
|
||||
if (combinedRes) {
|
||||
for (const existingRelease of combinedRes.releases || []) {
|
||||
for (const existingRelease of coerceArray(combinedRes.releases)) {
|
||||
existingRelease.registryUrl ??= combinedRes.registryUrl;
|
||||
}
|
||||
for (const additionalRelease of res.releases || []) {
|
||||
for (const additionalRelease of coerceArray(res.releases)) {
|
||||
additionalRelease.registryUrl = res.registryUrl;
|
||||
}
|
||||
combinedRes = { ...res, ...combinedRes };
|
||||
|
|
|
@ -22,7 +22,6 @@ const jenkinsPluginsVersions: JenkinsPluginsVersionsResponse = {
|
|||
'1.0.0': {
|
||||
version: '1.0.0',
|
||||
url: 'https://download.example.com',
|
||||
buildDate: 'Jan 01, 2020',
|
||||
},
|
||||
'2.0.0': {
|
||||
version: '2.0.0',
|
||||
|
@ -83,7 +82,6 @@ describe('modules/datasource/jenkins-plugins/index', () => {
|
|||
releases: [
|
||||
{
|
||||
downloadUrl: 'https://download.example.com',
|
||||
releaseTimestamp: '2020-01-01T00:00:00.000Z',
|
||||
version: '1.0.0',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { getPkgReleases } from '..';
|
||||
import * as httpMock from '../../../../test/http-mock';
|
||||
import { EXTERNAL_HOST_ERROR } from '../../../constants/error-messages';
|
||||
import * as hostRules from '../../../util/host-rules';
|
||||
import * as rubyVersioning from '../../versioning/ruby';
|
||||
import { PodDatasource } from '.';
|
||||
|
||||
|
@ -20,6 +21,7 @@ describe('modules/datasource/pod/index', () => {
|
|||
describe('getReleases', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
hostRules.clear();
|
||||
});
|
||||
|
||||
it('returns null for invalid inputs', async () => {
|
||||
|
@ -37,6 +39,16 @@ describe('modules/datasource/pod/index', () => {
|
|||
).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null disabled host', async () => {
|
||||
hostRules.add({ matchHost: cocoapodsHost, enabled: false });
|
||||
expect(
|
||||
await getPkgReleases({
|
||||
datasource: PodDatasource.id,
|
||||
packageName: 'foobar',
|
||||
})
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for empty result', async () => {
|
||||
// FIXME: why get request?
|
||||
httpMock
|
||||
|
@ -119,6 +131,14 @@ describe('modules/datasource/pod/index', () => {
|
|||
await expect(getPkgReleases(config)).rejects.toThrow(EXTERNAL_HOST_ERROR);
|
||||
});
|
||||
|
||||
it('throws for 500', async () => {
|
||||
httpMock
|
||||
.scope(cocoapodsHost)
|
||||
.get('/all_pods_versions_a_c_b.txt')
|
||||
.reply(500);
|
||||
await expect(getPkgReleases(config)).rejects.toThrow(EXTERNAL_HOST_ERROR);
|
||||
});
|
||||
|
||||
it('returns null for unknown error', async () => {
|
||||
httpMock
|
||||
.scope(cocoapodsHost)
|
||||
|
|
|
@ -68,7 +68,6 @@ function handleError(packageName: string, err: HttpError): void {
|
|||
} else if (statusCode === 404) {
|
||||
logger.debug(errorData, 'Package lookup error');
|
||||
} else if (err.message === HOST_DISABLED) {
|
||||
// istanbul ignore next
|
||||
logger.trace(errorData, 'Host disabled');
|
||||
} else {
|
||||
logger.warn(errorData, 'CocoaPods lookup failure: Unknown error');
|
||||
|
@ -77,8 +76,8 @@ function handleError(packageName: string, err: HttpError): void {
|
|||
|
||||
function isDefaultRepo(url: string): boolean {
|
||||
const match = githubRegex.exec(url);
|
||||
if (match) {
|
||||
const { account, repo } = match.groups ?? {};
|
||||
if (match?.groups) {
|
||||
const { account, repo } = match.groups;
|
||||
return (
|
||||
account.toLowerCase() === 'cocoapods' && repo.toLowerCase() === 'specs'
|
||||
); // https://github.com/CocoaPods/Specs.git
|
||||
|
@ -228,9 +227,9 @@ export class PodDatasource extends Datasource {
|
|||
|
||||
let result: ReleaseResult | null = null;
|
||||
const match = githubRegex.exec(baseUrl);
|
||||
if (match) {
|
||||
if (match?.groups) {
|
||||
baseUrl = massageGithubUrl(baseUrl);
|
||||
const { hostURL, account, repo } = match?.groups ?? {};
|
||||
const { hostURL, account, repo } = match.groups;
|
||||
const opts = { hostURL, account, repo };
|
||||
result = await this.getReleasesFromGithub(podName, opts);
|
||||
} else {
|
||||
|
|
7
lib/modules/datasource/sbt-package/util.spec.ts
Normal file
7
lib/modules/datasource/sbt-package/util.spec.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { getLatestVersion } from './util';
|
||||
|
||||
describe('modules/datasource/sbt-package/util', () => {
|
||||
it('gets latest version', () => {
|
||||
expect(getLatestVersion(['1.0.0', '3.0.0', '2.0.0'])).toBe('3.0.0');
|
||||
});
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
import { coerceArray } from '../../../util/array';
|
||||
import { regEx } from '../../../util/regex';
|
||||
import { compare } from '../../versioning/maven/compare';
|
||||
|
||||
|
@ -7,7 +8,7 @@ export function parseIndexDir(
|
|||
content: string,
|
||||
filterFn = (x: string): boolean => !regEx(/^\.+/).test(x)
|
||||
): string[] {
|
||||
const unfiltered = content.match(linkRegExp) ?? [];
|
||||
const unfiltered = coerceArray(content.match(linkRegExp));
|
||||
return unfiltered.filter(filterFn);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { logger } from '../../../logger';
|
||||
import { cache } from '../../../util/cache/package/decorator';
|
||||
import { regEx } from '../../../util/regex';
|
||||
import { coerceString } from '../../../util/string';
|
||||
import * as hashicorpVersioning from '../../versioning/hashicorp';
|
||||
import type { GetReleasesConfig, ReleaseResult } from '../types';
|
||||
import { TerraformDatasource } from './base';
|
||||
|
@ -163,7 +164,7 @@ export class TerraformModuleDatasource extends TerraformDatasource {
|
|||
|
||||
private static getRegistryRepository(
|
||||
packageName: string,
|
||||
registryUrl = ''
|
||||
registryUrl: string | undefined
|
||||
): RegistryRepository {
|
||||
let registry: string;
|
||||
const split = packageName.split('/');
|
||||
|
@ -171,7 +172,7 @@ export class TerraformModuleDatasource extends TerraformDatasource {
|
|||
[registry] = split;
|
||||
split.shift();
|
||||
} else {
|
||||
registry = registryUrl;
|
||||
registry = coerceString(registryUrl);
|
||||
}
|
||||
if (!regEx(/^https?:\/\//).test(registry)) {
|
||||
registry = `https://${registry}`;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
foo
|
||||
#addin nuget:?package=Foo.Foo&version=1.1.1
|
||||
#addin nuget:?package=Foo.Foo
|
||||
#addin "nuget:?package=Bim.Bim&version=6.6.6"
|
||||
#tool nuget:https://example.com?package=Bar.Bar&version=2.2.2
|
||||
#module nuget:file:///tmp/?package=Baz.Baz&version=3.3.3
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`modules/manager/cake/index extracts 1`] = `
|
||||
{
|
||||
"deps": [
|
||||
{
|
||||
"currentValue": "1.1.1",
|
||||
"datasource": "nuget",
|
||||
"depName": "Foo.Foo",
|
||||
},
|
||||
{
|
||||
"currentValue": "6.6.6",
|
||||
"datasource": "nuget",
|
||||
"depName": "Bim.Bim",
|
||||
},
|
||||
{
|
||||
"currentValue": "2.2.2",
|
||||
"datasource": "nuget",
|
||||
"depName": "Bar.Bar",
|
||||
"registryUrls": [
|
||||
"https://example.com",
|
||||
],
|
||||
},
|
||||
{
|
||||
"currentValue": "3.3.3",
|
||||
"datasource": "nuget",
|
||||
"depName": "Baz.Baz",
|
||||
"skipReason": "unsupported-url",
|
||||
},
|
||||
{
|
||||
"currentValue": "1.0.3",
|
||||
"datasource": "nuget",
|
||||
"depName": "Cake.7zip",
|
||||
},
|
||||
{
|
||||
"currentValue": "1.0.0",
|
||||
"datasource": "nuget",
|
||||
"depName": "Cake.asciidoctorj",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
|
@ -3,9 +3,9 @@ import { extractPackageFile } from '.';
|
|||
|
||||
describe('modules/manager/cake/index', () => {
|
||||
it('extracts', () => {
|
||||
expect(extractPackageFile(Fixtures.get('build.cake'))).toMatchSnapshot({
|
||||
expect(extractPackageFile(Fixtures.get('build.cake'))).toMatchObject({
|
||||
deps: [
|
||||
{ depName: 'Foo.Foo', currentValue: '1.1.1' },
|
||||
{ depName: 'Foo.Foo', currentValue: undefined },
|
||||
{ depName: 'Bim.Bim', currentValue: '6.6.6' },
|
||||
{ depName: 'Bar.Bar', registryUrls: ['https://example.com'] },
|
||||
{ depName: 'Baz.Baz', skipReason: 'unsupported-url' },
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
|
||||
export function handleAny(
|
||||
content: string,
|
||||
packageFile: string,
|
||||
_packageFile: string,
|
||||
config: RegexManagerConfig
|
||||
): PackageDependency[] {
|
||||
return config.matchStrings
|
||||
|
@ -20,7 +20,12 @@ export function handleAny(
|
|||
.flatMap((regex) => regexMatchAll(regex, content)) // match all regex to content, get all matches, reduce to single array
|
||||
.map((matchResult) =>
|
||||
createDependency(
|
||||
{ groups: matchResult.groups ?? {}, replaceString: matchResult[0] },
|
||||
{
|
||||
groups:
|
||||
matchResult.groups ??
|
||||
/* istanbul ignore next: can this happen? */ {},
|
||||
replaceString: matchResult[0],
|
||||
},
|
||||
config
|
||||
)
|
||||
)
|
||||
|
@ -30,7 +35,7 @@ export function handleAny(
|
|||
|
||||
export function handleCombination(
|
||||
content: string,
|
||||
packageFile: string,
|
||||
_packageFile: string,
|
||||
config: RegexManagerConfig
|
||||
): PackageDependency[] {
|
||||
const matches = config.matchStrings
|
||||
|
@ -43,7 +48,7 @@ export function handleCombination(
|
|||
|
||||
const extraction = matches
|
||||
.map((match) => ({
|
||||
groups: match.groups ?? {},
|
||||
groups: match.groups ?? /* istanbul ignore next: can this happen? */ {},
|
||||
replaceString:
|
||||
match?.groups?.currentValue ?? match?.groups?.currentDigest
|
||||
? match[0]
|
||||
|
@ -93,7 +98,7 @@ function processRecursive(parameters: RecursionParameter): PackageDependency[] {
|
|||
},
|
||||
config
|
||||
);
|
||||
return result ? [result] : [];
|
||||
return result ? [result] : /* istanbul ignore next: can this happen? */ [];
|
||||
}
|
||||
return regexMatchAll(regexes[index], content).flatMap((match) => {
|
||||
return processRecursive({
|
||||
|
|
|
@ -71,7 +71,9 @@ export function extractPackageFile(
|
|||
|
||||
// Image name/tags for services are only eligible for update if they don't
|
||||
// use variables and if the image is not built locally
|
||||
const deps = Object.values(services || {})
|
||||
const deps = Object.values(
|
||||
services || /* istanbul ignore next: can never happen */ {}
|
||||
)
|
||||
.filter((service) => is.string(service?.image) && !service?.build)
|
||||
.map((service) => {
|
||||
const dep = getDep(service.image, true, extractConfig.registryAliases);
|
||||
|
|
|
@ -28,4 +28,4 @@ empty_key:
|
|||
coreImage:
|
||||
registry: docker.io
|
||||
repository: bitnami/harbor-core
|
||||
tag: 2.1.3-debian-10-r38
|
||||
version: 2.1.3-debian-10-r38
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { quote } from 'shlex';
|
||||
import { TEMPORARY_ERROR } from '../../../constants/error-messages';
|
||||
import { logger } from '../../../logger';
|
||||
import { coerceArray } from '../../../util/array';
|
||||
import { exec } from '../../../util/exec';
|
||||
import type { ExecOptions, ToolConstraint } from '../../../util/exec/types';
|
||||
import { readLocalFile } from '../../../util/fs';
|
||||
|
@ -66,7 +67,7 @@ export async function updateArtifacts(
|
|||
|
||||
const res: UpdateArtifactsResult[] = [];
|
||||
|
||||
for (const f of status.modified ?? []) {
|
||||
for (const f of coerceArray(status.modified)) {
|
||||
res.push({
|
||||
file: {
|
||||
type: 'addition',
|
||||
|
@ -75,7 +76,7 @@ export async function updateArtifacts(
|
|||
},
|
||||
});
|
||||
}
|
||||
for (const f of status.not_added ?? []) {
|
||||
for (const f of coerceArray(status.not_added)) {
|
||||
res.push({
|
||||
file: {
|
||||
type: 'addition',
|
||||
|
@ -84,7 +85,7 @@ export async function updateArtifacts(
|
|||
},
|
||||
});
|
||||
}
|
||||
for (const f of status.deleted ?? []) {
|
||||
for (const f of coerceArray(status.deleted)) {
|
||||
res.push({
|
||||
file: {
|
||||
type: 'deletion',
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { join } from 'upath';
|
||||
import { logger } from '../../../logger';
|
||||
import { coerceArray } from '../../../util/array';
|
||||
import { coerceString } from '../../../util/string';
|
||||
import { parseUrl } from '../../../util/url';
|
||||
import type { PackageDependency, PackageFileContent } from '../types';
|
||||
import type { Dependency, JsonnetFile } from './types';
|
||||
|
@ -52,7 +53,7 @@ function extractDependency(dependency: Dependency): PackageDependency | null {
|
|||
const depName = join(
|
||||
gitRemote.host,
|
||||
gitRemote.pathname.replace(/\.git$/, ''),
|
||||
dependency.source.git.subdir ?? ''
|
||||
coerceString(dependency.source.git.subdir)
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -3,6 +3,10 @@ import { extractPackageFile } from '.';
|
|||
|
||||
describe('modules/manager/mint/extract', () => {
|
||||
describe('extractPackageFile()', () => {
|
||||
it('returns null for empty', () => {
|
||||
expect(extractPackageFile('')).toBeNull();
|
||||
});
|
||||
|
||||
it('Mintfile With Version Description', () => {
|
||||
const res = extractPackageFile(codeBlock`
|
||||
SwiftGen/SwiftGen@6.6.1
|
||||
|
|
|
@ -11,6 +11,7 @@ describe('modules/manager/woodpecker/extract', () => {
|
|||
|
||||
it('returns null for non-object YAML', () => {
|
||||
expect(extractPackageFile('nothing here', '', {})).toBeNull();
|
||||
expect(extractPackageFile('clone: null', '', {})).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null for malformed YAML', () => {
|
||||
|
|
|
@ -42,6 +42,9 @@ describe('modules/platform/codecommit/index', () => {
|
|||
});
|
||||
|
||||
beforeEach(() => {
|
||||
delete process.env.AWS_REGION;
|
||||
delete process.env.AWS_ACCESS_KEY_ID;
|
||||
delete process.env.AWS_SECRET_ACCESS_KEY;
|
||||
codeCommitClient.reset();
|
||||
config.prList = undefined;
|
||||
config.repository = undefined;
|
||||
|
@ -70,7 +73,6 @@ describe('modules/platform/codecommit/index', () => {
|
|||
});
|
||||
|
||||
it('should init with env vars', async () => {
|
||||
const temp = process.env.AWS_REGION;
|
||||
process.env.AWS_REGION = 'REGION';
|
||||
await expect(
|
||||
codeCommit.initPlatform({
|
||||
|
@ -80,7 +82,6 @@ describe('modules/platform/codecommit/index', () => {
|
|||
).resolves.toEqual({
|
||||
endpoint: 'https://git-codecommit.REGION.amazonaws.com/',
|
||||
});
|
||||
process.env.AWS_REGION = temp;
|
||||
});
|
||||
|
||||
it('should ', async () => {
|
||||
|
@ -588,6 +589,14 @@ describe('modules/platform/codecommit/index', () => {
|
|||
const res = await codeCommit.getJsonFile('file.json');
|
||||
expect(res).toEqual({ foo: 'bar' });
|
||||
});
|
||||
|
||||
it('returns null', async () => {
|
||||
codeCommitClient
|
||||
.on(GetFileCommand)
|
||||
.resolvesOnce({ fileContent: undefined });
|
||||
const res = await codeCommit.getJsonFile('file.json');
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRawFile()', () => {
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
} from '../../../constants/error-messages';
|
||||
import { logger } from '../../../logger';
|
||||
import type { BranchStatus, PrState } from '../../../types';
|
||||
import { coerceArray } from '../../../util/array';
|
||||
import * as git from '../../../util/git';
|
||||
import { regEx } from '../../../util/regex';
|
||||
import { sanitize } from '../../../util/sanitize';
|
||||
|
@ -163,7 +164,7 @@ export async function getPrList(): Promise<CodeCommitPr[]> {
|
|||
return fetchedPrs;
|
||||
}
|
||||
|
||||
const prIds = listPrsResponse.pullRequestIds ?? [];
|
||||
const prIds = coerceArray(listPrsResponse.pullRequestIds);
|
||||
|
||||
for (const prId of prIds) {
|
||||
const prRes = await client.getPr(prId);
|
||||
|
@ -291,7 +292,7 @@ export async function getRepos(): Promise<string[]> {
|
|||
|
||||
const res: string[] = [];
|
||||
|
||||
const repoNames = reposRes?.repositories ?? [];
|
||||
const repoNames = coerceArray(reposRes?.repositories);
|
||||
|
||||
for (const repo of repoNames) {
|
||||
if (repo.repositoryName) {
|
||||
|
|
|
@ -11,14 +11,14 @@ export function smartTruncate(input: string, len: number): string {
|
|||
}
|
||||
|
||||
const reMatch = re.exec(input);
|
||||
if (!reMatch) {
|
||||
if (!reMatch?.groups) {
|
||||
return input.substring(0, len);
|
||||
}
|
||||
|
||||
const divider = `\n\n</details>\n\n---\n\n### Configuration`;
|
||||
const preNotes = reMatch.groups?.preNotes ?? '';
|
||||
const releaseNotes = reMatch.groups?.releaseNotes ?? '';
|
||||
const postNotes = reMatch.groups?.postNotes ?? '';
|
||||
const preNotes = reMatch.groups.preNotes;
|
||||
const releaseNotes = reMatch.groups.releaseNotes;
|
||||
const postNotes = reMatch.groups.postNotes;
|
||||
|
||||
const availableLength =
|
||||
len - (preNotes.length + postNotes.length + divider.length);
|
||||
|
|
|
@ -11,10 +11,6 @@ describe('modules/versioning/debian/index', () => {
|
|||
Settings.now = () => dt.valueOf();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it.each`
|
||||
version | expected
|
||||
${undefined} | ${false}
|
||||
|
|
|
@ -30,7 +30,7 @@ export class DebianVersioningApi extends GenericVersioningApi {
|
|||
const schedule = this._distroInfo.getSchedule(
|
||||
this._rollingReleases.getVersionByLts(version)
|
||||
);
|
||||
return (isValid && schedule && RELEASE_PROP in schedule) ?? false;
|
||||
return isValid && schedule !== null && RELEASE_PROP in schedule;
|
||||
}
|
||||
|
||||
override isStable(version: string): boolean {
|
||||
|
@ -43,7 +43,6 @@ export class DebianVersioningApi extends GenericVersioningApi {
|
|||
override getNewValue({
|
||||
currentValue,
|
||||
rangeStrategy,
|
||||
currentVersion,
|
||||
newVersion,
|
||||
}: NewValueConfig): string {
|
||||
if (rangeStrategy === 'pin') {
|
||||
|
@ -83,7 +82,10 @@ export class DebianVersioningApi extends GenericVersioningApi {
|
|||
// newVersion is [oldold|old|]stable
|
||||
// current value is numeric
|
||||
if (this._rollingReleases.has(newVersion)) {
|
||||
return this._rollingReleases.schedule(newVersion)?.version ?? newVersion;
|
||||
return (
|
||||
this._rollingReleases.schedule(newVersion)?.version ??
|
||||
/* istanbul ignore next: should never happen */ newVersion
|
||||
);
|
||||
}
|
||||
|
||||
return this._distroInfo.getVersionByCodename(newVersion);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { gte, lt, lte, satisfies } from '@renovatebot/pep440';
|
|||
import { parse as parseRange } from '@renovatebot/pep440/lib/specifier.js';
|
||||
import { parse as parseVersion } from '@renovatebot/pep440/lib/version.js';
|
||||
import { logger } from '../../../logger';
|
||||
import { coerceArray } from '../../../util/array';
|
||||
import { regEx } from '../../../util/regex';
|
||||
import type { NewValueConfig } from '../types';
|
||||
|
||||
|
@ -28,8 +29,9 @@ type UserPolicy =
|
|||
* @returns A {@link UserPolicy}
|
||||
*/
|
||||
function getRangePrecision(ranges: Range[]): UserPolicy {
|
||||
const bound: number[] =
|
||||
parseVersion((ranges[1] || ranges[0]).version)?.release ?? [];
|
||||
const bound = coerceArray(
|
||||
parseVersion((ranges[1] || ranges[0]).version)?.release
|
||||
);
|
||||
let rangePrecision = -1;
|
||||
// range is defined by a single bound.
|
||||
// ie. <1.2.2.3,
|
||||
|
@ -39,7 +41,7 @@ function getRangePrecision(ranges: Range[]): UserPolicy {
|
|||
}
|
||||
// Range is defined by both upper and lower bounds.
|
||||
if (ranges.length === 2) {
|
||||
const lowerBound: number[] = parseVersion(ranges[0].version)?.release ?? [];
|
||||
const lowerBound = coerceArray(parseVersion(ranges[0].version)?.release);
|
||||
rangePrecision = bound.findIndex((el, index) => el > lowerBound[index]);
|
||||
}
|
||||
// Tune down Major precision if followed by a zero
|
||||
|
@ -74,11 +76,12 @@ function getFutureVersion(
|
|||
newVersion: string,
|
||||
baseVersion?: string
|
||||
): number[] {
|
||||
const toRelease: number[] = parseVersion(newVersion)?.release ?? [];
|
||||
const baseRelease: number[] =
|
||||
parseVersion(baseVersion ?? newVersion)?.release ?? [];
|
||||
const toRelease = coerceArray(parseVersion(newVersion)?.release);
|
||||
const baseRelease = coerceArray(
|
||||
parseVersion(baseVersion ?? newVersion)?.release
|
||||
);
|
||||
return baseRelease.map((_, index) => {
|
||||
const toPart: number = toRelease[index] ?? 0;
|
||||
const toPart = toRelease[index] ?? 0;
|
||||
if (index < policy) {
|
||||
return toPart;
|
||||
}
|
||||
|
@ -303,8 +306,8 @@ function updateRangeValue(
|
|||
return range.operator + futureVersion + '.*';
|
||||
}
|
||||
if (range.operator === '~=') {
|
||||
const baseVersion = parseVersion(range.version)?.release ?? [];
|
||||
const futureVersion = parseVersion(newVersion)?.release ?? [];
|
||||
const baseVersion = coerceArray(parseVersion(range.version)?.release);
|
||||
const futureVersion = coerceArray(parseVersion(newVersion)?.release);
|
||||
const baseLen = baseVersion.length;
|
||||
const newVerLen = futureVersion.length;
|
||||
// trim redundant trailing version specifiers
|
||||
|
@ -410,7 +413,7 @@ function handleWidenStrategy(
|
|||
return newRanges.map((range) => {
|
||||
// newVersion is over the upper bound
|
||||
if (range.operator === '<' && gte(newVersion, range.version)) {
|
||||
const upperBound = parseVersion(range.version)?.release ?? [];
|
||||
const upperBound = coerceArray(parseVersion(range.version)?.release);
|
||||
const len = upperBound.length;
|
||||
// Match the precision of the smallest specifier if other than 0
|
||||
if (upperBound[len - 1] !== 0) {
|
||||
|
@ -474,7 +477,7 @@ function handleReplaceStrategy(
|
|||
return '>=' + newVersion;
|
||||
}
|
||||
// update the lower bound to reflect the accepted new version
|
||||
const lowerBound = parseVersion(range.version)?.release ?? [];
|
||||
const lowerBound = coerceArray(parseVersion(range.version)?.release);
|
||||
const rangePrecision = lowerBound.length - 1;
|
||||
let newBase = getFutureVersion(rangePrecision, newVersion);
|
||||
if (trimZeros) {
|
||||
|
|
|
@ -20,7 +20,7 @@ class RedhatVersioningApi extends GenericVersioningApi {
|
|||
|
||||
const { major, minor, patch, releaseMajor, releaseMinor } = matches;
|
||||
const release = [
|
||||
typeof major === 'undefined' ? 0 : Number.parseInt(major, 10),
|
||||
Number.parseInt(major, 10),
|
||||
typeof minor === 'undefined' ? 0 : Number.parseInt(minor, 10),
|
||||
typeof patch === 'undefined' ? 0 : Number.parseInt(patch, 10),
|
||||
typeof releaseMajor === 'undefined'
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { RangeStrategy } from '../../../types/versioning';
|
||||
import { regEx } from '../../../util/regex';
|
||||
import { coerceString } from '../../../util/string';
|
||||
import { api as npm } from '../npm';
|
||||
import { api as pep440 } from '../pep440';
|
||||
import type { NewValueConfig, VersioningApi } from '../types';
|
||||
|
@ -160,10 +161,12 @@ function getNewValue({
|
|||
const lowerAscVersionCurrent = matchAscRange.groups.range_lower_asc_version;
|
||||
const upperAscVersionCurrent = matchAscRange.groups.range_upper_asc_version;
|
||||
const [lowerBoundAscPep440, upperBoundAscPep440] = pep440Value.split(', ');
|
||||
const lowerAscVersionNew =
|
||||
regEx(versionGroup).exec(lowerBoundAscPep440)?.[0] ?? '';
|
||||
const upperAscVersionNew =
|
||||
regEx(versionGroup).exec(upperBoundAscPep440)?.[0] ?? '';
|
||||
const lowerAscVersionNew = coerceString(
|
||||
regEx(versionGroup).exec(lowerBoundAscPep440)?.[0]
|
||||
);
|
||||
const upperAscVersionNew = coerceString(
|
||||
regEx(versionGroup).exec(upperBoundAscPep440)?.[0]
|
||||
);
|
||||
const lowerBoundAscNew = lowerBoundAscCurrent.replace(
|
||||
lowerAscVersionCurrent,
|
||||
lowerAscVersionNew
|
||||
|
@ -189,10 +192,12 @@ function getNewValue({
|
|||
const [lowerBoundDescPep440, upperBoundDescPep440] =
|
||||
pep440Value.split(', ');
|
||||
|
||||
const upperDescVersionNew =
|
||||
regEx(versionGroup).exec(upperBoundDescPep440)?.[0] ?? '';
|
||||
const lowerDescVersionNew =
|
||||
regEx(versionGroup).exec(lowerBoundDescPep440)?.[0] ?? '';
|
||||
const upperDescVersionNew = coerceString(
|
||||
regEx(versionGroup).exec(upperBoundDescPep440)?.[0]
|
||||
);
|
||||
const lowerDescVersionNew = coerceString(
|
||||
regEx(versionGroup).exec(lowerBoundDescPep440)?.[0]
|
||||
);
|
||||
const upperBoundDescNew = upperBoundDescCurrent.replace(
|
||||
upperDescVersionCurrent,
|
||||
upperDescVersionNew
|
||||
|
|
|
@ -61,6 +61,7 @@ describe('modules/versioning/swift/index', () => {
|
|||
versions | range | expected
|
||||
${['1.2.3', '1.2.4', '1.2.5']} | ${'..<"1.2.4"'} | ${'1.2.3'}
|
||||
${['v1.2.3', 'v1.2.4', 'v1.2.5']} | ${'..<"1.2.4"'} | ${'1.2.3'}
|
||||
${['v1.2.3', 'v1.2.4', 'v1.2.5']} | ${''} | ${null}
|
||||
`(
|
||||
'minSatisfyingVersion($versions, "$range") === "$expected"',
|
||||
({ versions, range, expected }) => {
|
||||
|
@ -73,6 +74,7 @@ describe('modules/versioning/swift/index', () => {
|
|||
${['1.2.3', '1.2.4', '1.2.5']} | ${'..<"1.2.4"'} | ${'1.2.3'}
|
||||
${['v1.2.3', 'v1.2.4', 'v1.2.5']} | ${'..<"1.2.4"'} | ${'1.2.3'}
|
||||
${['1.2.3', '1.2.4', '1.2.5']} | ${'..."1.2.4"'} | ${'1.2.4'}
|
||||
${['1.2.3', '1.2.4', '1.2.5']} | ${''} | ${null}
|
||||
`(
|
||||
'getSatisfyingVersion($versions, "$range") === "$expected"',
|
||||
({ versions, range, expected }) => {
|
||||
|
@ -86,6 +88,7 @@ describe('modules/versioning/swift/index', () => {
|
|||
${'v1.2.3'} | ${'..."1.2.4"'} | ${false}
|
||||
${'1.2.3'} | ${'"1.2.4"...'} | ${true}
|
||||
${'v1.2.3'} | ${'"1.2.4"...'} | ${true}
|
||||
${'v1.2.3'} | ${''} | ${false}
|
||||
`(
|
||||
'isLessThanRange("$version", "$range") === "$expected"',
|
||||
({ version, range, expected }) => {
|
||||
|
@ -99,6 +102,7 @@ describe('modules/versioning/swift/index', () => {
|
|||
${'v1.2.4'} | ${'..."1.2.4"'} | ${true}
|
||||
${'1.2.4'} | ${'..."1.2.3"'} | ${false}
|
||||
${'v1.2.4'} | ${'..."1.2.3"'} | ${false}
|
||||
${'v1.2.4'} | ${''} | ${false}
|
||||
`(
|
||||
'matches("$version", "$range") === "$expected"',
|
||||
({ version, range, expected }) => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { isNotNullOrUndefined } from './array';
|
||||
import { isNotNullOrUndefined, toArray } from './array';
|
||||
|
||||
describe('util/array', () => {
|
||||
it.each`
|
||||
|
@ -9,4 +9,13 @@ describe('util/array', () => {
|
|||
`('.isNotNullOrUndefined', ({ a, exp }) => {
|
||||
expect(isNotNullOrUndefined(a)).toEqual(exp);
|
||||
});
|
||||
|
||||
it.each`
|
||||
a | exp
|
||||
${null} | ${[null]}
|
||||
${undefined} | ${[undefined]}
|
||||
${[]} | ${[]}
|
||||
`('.toArray', ({ a, exp }) => {
|
||||
expect(toArray(a)).toEqual(exp);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,3 +19,12 @@ export function isNotNullOrUndefined<T>(
|
|||
): value is T {
|
||||
return !is.nullOrUndefined(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a single value or an array of values to an array of values.
|
||||
* @param value a single value or an array of values
|
||||
* @returns array of values
|
||||
*/
|
||||
export function toArray<T>(value: T | T[]): T[] {
|
||||
return is.array(value) ? value : [value];
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { looseEquals, replaceAt } from './string';
|
||||
import { coerceString, looseEquals, replaceAt } from './string';
|
||||
|
||||
describe('util/string', () => {
|
||||
describe('replaceAt', () => {
|
||||
|
@ -32,4 +32,11 @@ describe('util/string', () => {
|
|||
expect(looseEquals(null, '')).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
it('coerceString', () => {
|
||||
expect(coerceString('foo')).toBe('foo');
|
||||
expect(coerceString('')).toBe('');
|
||||
expect(coerceString(undefined)).toBe('');
|
||||
expect(coerceString(null)).toBe('');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -82,3 +82,7 @@ export function copystr(x: string): string {
|
|||
buf.write(x, 'utf8');
|
||||
return buf.toString('utf8');
|
||||
}
|
||||
|
||||
export function coerceString(val: string | null | undefined): string {
|
||||
return val ?? '';
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import type { RenovateSharedConfig } from '../../../config/types';
|
||||
import { coerceString } from '../../../util/string';
|
||||
import type { CommitMessage } from './commit-message';
|
||||
import { CustomCommitMessage } from './custom-commit-message';
|
||||
import { SemanticCommitMessage } from './semantic-commit-message';
|
||||
|
@ -29,8 +30,8 @@ export class CommitMessageFactory {
|
|||
private createSemanticCommitMessage(): SemanticCommitMessage {
|
||||
const message = new SemanticCommitMessage();
|
||||
|
||||
message.type = this._config.semanticCommitType ?? '';
|
||||
message.scope = this._config.semanticCommitScope ?? '';
|
||||
message.type = coerceString(this._config.semanticCommitType);
|
||||
message.scope = coerceString(this._config.semanticCommitScope);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
|
|
@ -27,11 +27,11 @@ export class SemanticCommitMessage extends CommitMessage {
|
|||
static fromString(value: string): SemanticCommitMessage | undefined {
|
||||
const match = value.match(SemanticCommitMessage.REGEXP);
|
||||
|
||||
if (!match) {
|
||||
if (!match?.groups) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const { groups = {} } = match;
|
||||
const { groups } = match;
|
||||
const message = new SemanticCommitMessage();
|
||||
message.type = groups.type;
|
||||
message.scope = groups.scope;
|
||||
|
|
Loading…
Reference in a new issue