diff --git a/lib/modules/datasource/gitlab-packages/index.ts b/lib/modules/datasource/gitlab-packages/index.ts index e7fd124a9a3bb9e697e58628fa92113f34845a4a..edc1d8f023cf9005c89a62852df27f68ba3e40c4 100644 --- a/lib/modules/datasource/gitlab-packages/index.ts +++ b/lib/modules/datasource/gitlab-packages/index.ts @@ -49,6 +49,11 @@ export class GitlabPackagesDatasource extends Datasource { registryUrl, packageName, }: GetReleasesConfig): Promise<ReleaseResult | null> { + // istanbul ignore if + if (!registryUrl) { + return null; + } + const [projectPart, packagePart] = packageName.split(':', 2); const apiUrl = GitlabPackagesDatasource.getGitlabPackageApiUrl( @@ -58,7 +63,7 @@ export class GitlabPackagesDatasource extends Datasource { ); const result: ReleaseResult = { - releases: null, + releases: [], }; let response: GitlabPackage[]; diff --git a/lib/modules/datasource/gitlab-releases/index.ts b/lib/modules/datasource/gitlab-releases/index.ts index afe1843add646a21cacdec2ca3c681c2cf7b2fcb..556362c55b963d4f691b428c307ca5e92c51a85b 100644 --- a/lib/modules/datasource/gitlab-releases/index.ts +++ b/lib/modules/datasource/gitlab-releases/index.ts @@ -25,6 +25,11 @@ export class GitlabReleasesDatasource extends Datasource { registryUrl, packageName, }: GetReleasesConfig): Promise<ReleaseResult | null> { + // istanbul ignore if + if (!registryUrl) { + return null; + } + const urlEncodedRepo = encodeURIComponent(packageName); const apiUrl = `${registryUrl}/api/v4/projects/${urlEncodedRepo}/releases`; diff --git a/lib/modules/datasource/gitlab-tags/index.ts b/lib/modules/datasource/gitlab-tags/index.ts index d04583ab01a47e290f6012d84d2ca8ee8d7a029e..bb9185f7a5eb6132a179c033925b6c9684916679 100644 --- a/lib/modules/datasource/gitlab-tags/index.ts +++ b/lib/modules/datasource/gitlab-tags/index.ts @@ -48,7 +48,7 @@ export class GitlabTagsDatasource extends Datasource { const dependency: ReleaseResult = { sourceUrl: getSourceUrl(repo, registryUrl), - releases: null, + releases: [], }; dependency.releases = gitlabTags.map(({ name, commit }) => ({ version: name, @@ -75,8 +75,9 @@ export class GitlabTagsDatasource extends Datasource { ): Promise<string | null> { const depHost = getDepHost(registryUrl); - const urlEncodedRepo = encodeURIComponent(repo); - let digest: string; + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + const urlEncodedRepo = encodeURIComponent(repo!); + let digest: string | null = null; try { if (newValue) { diff --git a/lib/modules/datasource/golang-version/index.ts b/lib/modules/datasource/golang-version/index.ts index dac2bb89c900ce335143f3651f5c85e38072244a..ecaeb256281935f3f792d6cac0cf340a94cf54d4 100644 --- a/lib/modules/datasource/golang-version/index.ts +++ b/lib/modules/datasource/golang-version/index.ts @@ -57,10 +57,12 @@ export class GolangVersionDatasource extends Datasource { lines.splice(0, startOfReleases + 1); // Parse the release list - let release: Release = { version: undefined }; + let release: Omit<Release, 'version'> & { version?: string } = { + version: undefined, + }; let skipFutureRelease = false; while (lines.length !== 0) { - const line = lines.shift(); + const line = lines.shift()!; if (line === releaseBeginningChar) { if (release.version !== undefined) { throw new ExternalHostError( @@ -76,7 +78,7 @@ export class GolangVersionDatasource extends Datasource { new Error('Invalid file - release has empty version') ); } - res.releases.push(release); + res.releases.push(release as Release); } release = { version: undefined }; } else { @@ -85,7 +87,7 @@ export class GolangVersionDatasource extends Datasource { skipFutureRelease = true; } const releaseDateMatch = releaseDateRegex.exec(line); - if (releaseDateMatch) { + if (releaseDateMatch?.groups) { // Make a valid UTC timestamp const year = releaseDateMatch.groups.year.padStart(4, '0'); const month = releaseDateMatch.groups.month.padStart(2, '0'); @@ -93,7 +95,7 @@ export class GolangVersionDatasource extends Datasource { release.releaseTimestamp = `${year}-${month}-${day}T00:00:00.000Z`; } const releaseVersionMatch = releaseVersionRegex.exec(line); - if (releaseVersionMatch) { + if (releaseVersionMatch?.groups) { release.version = `${releaseVersionMatch.groups.versionMajor}.${releaseVersionMatch.groups.versionMinor}.${releaseVersionMatch.groups.patch}`; if (!isVersion(release.version)) { throw new ExternalHostError( diff --git a/lib/modules/datasource/gradle-version/index.ts b/lib/modules/datasource/gradle-version/index.ts index abf703e9437ea679c3e6b2771b1660721c1091ca..d2d8ddd67943bcd76c510ac7df594eb10577a452 100644 --- a/lib/modules/datasource/gradle-version/index.ts +++ b/lib/modules/datasource/gradle-version/index.ts @@ -26,11 +26,16 @@ export class GradleVersionDatasource extends Datasource { @cache({ namespace: `datasource-${GradleVersionDatasource.id}`, - key: ({ registryUrl }: GetReleasesConfig) => registryUrl, + key: ({ registryUrl }: GetReleasesConfig) => `${registryUrl}`, }) async getReleases({ registryUrl, - }: GetReleasesConfig): Promise<ReleaseResult> { + }: GetReleasesConfig): Promise<ReleaseResult | null> { + // istanbul ignore if + if (!registryUrl) { + return null; + } + let releases: Release[]; try { const response = await this.http.getJson<GradleRelease[]>(registryUrl); diff --git a/lib/modules/datasource/npm/get.ts b/lib/modules/datasource/npm/get.ts index 169b8330cd66cd740f7a9393859d0b6aaf208d49..d2b418dda5969113ce99ce9ef63a36288119ced9 100644 --- a/lib/modules/datasource/npm/get.ts +++ b/lib/modules/datasource/npm/get.ts @@ -87,7 +87,7 @@ export async function getDependency( return null; } - const latestVersion = res.versions[res['dist-tags']?.latest]; + const latestVersion = res.versions[res['dist-tags']?.latest ?? '']; res.repository = res.repository || latestVersion?.repository; res.homepage = res.homepage || latestVersion?.homepage; @@ -100,7 +100,7 @@ export async function getDependency( sourceUrl, sourceDirectory, versions: {}, - releases: null, + releases: [], 'dist-tags': res['dist-tags'], registryUrl, }; @@ -112,17 +112,17 @@ export async function getDependency( dep.releases = Object.keys(res.versions).map((version) => { const release: NpmRelease = { version, - gitRef: res.versions[version].gitHead, - dependencies: res.versions[version].dependencies, - devDependencies: res.versions[version].devDependencies, + gitRef: res.versions?.[version].gitHead, + dependencies: res.versions?.[version].dependencies, + devDependencies: res.versions?.[version].devDependencies, }; if (res.time?.[version]) { release.releaseTimestamp = res.time[version]; } - if (res.versions[version].deprecated) { + if (res.versions?.[version].deprecated) { release.isDeprecated = true; } - const source = getPackageSource(res.versions[version].repository); + const source = getPackageSource(res.versions?.[version].repository); if (source.sourceUrl && source.sourceUrl !== dep.sourceUrl) { release.sourceUrl = source.sourceUrl; } diff --git a/lib/modules/datasource/npm/index.ts b/lib/modules/datasource/npm/index.ts index cab5ed593207529ca5ddd8a2ad8ddd9dda2b6f90..e431bd24b9574394fad6b04a7d21f765c598cc28 100644 --- a/lib/modules/datasource/npm/index.ts +++ b/lib/modules/datasource/npm/index.ts @@ -24,6 +24,11 @@ export class NpmDatasource extends Datasource { packageName, registryUrl, }: GetReleasesConfig): Promise<ReleaseResult | null> { + // istanbul ignore if + if (!registryUrl) { + return null; + } + const res = await getDependency(this.http, registryUrl, packageName); if (res) { res.tags = res['dist-tags']; diff --git a/lib/modules/datasource/npm/npmrc.ts b/lib/modules/datasource/npm/npmrc.ts index b18cf8242d432a0dae606038af4f10cc52fb4464..7e8115a04c4d8bbca36d8162db36f4cf945eec6f 100644 --- a/lib/modules/datasource/npm/npmrc.ts +++ b/lib/modules/datasource/npm/npmrc.ts @@ -24,12 +24,12 @@ function envReplace(value: any, env = process.env): any { const ENV_EXPR = regEx(/(\\*)\$\{([^}]+)\}/g); - return value.replace(ENV_EXPR, (match, esc, envVarName) => { + return value.replace(ENV_EXPR, (match, _esc, envVarName) => { if (env[envVarName] === undefined) { logger.warn('Failed to replace env in config: ' + match); throw new Error('env-replace'); } - return env[envVarName]; + return env[envVarName]!; }); } @@ -82,7 +82,7 @@ export function convertNpmrcToRules(npmrc: Record<string, any>): NpmrcRules { if (matchHost) { hostRule.matchHost = matchHost; } - rules.hostRules.push(hostRule); + rules.hostRules?.push(hostRule); } // Generate packageRules const matchDataSources = ['npm']; @@ -91,7 +91,7 @@ export function convertNpmrcToRules(npmrc: Record<string, any>): NpmrcRules { if (is.nonEmptyString(registry)) { if (validateUrl(registry)) { // Default registry - rules.packageRules.push({ + rules.packageRules?.push({ matchDataSources, registryUrls: [registry], }); @@ -109,7 +109,7 @@ export function convertNpmrcToRules(npmrc: Record<string, any>): NpmrcRules { if (keyType === 'registry' && keyParts.length && is.nonEmptyString(value)) { const scope = keyParts.join(':'); if (validateUrl(value)) { - rules.packageRules.push({ + rules.packageRules?.push({ matchDataSources, matchPackagePrefixes: [scope + '/'], registryUrls: [value], @@ -153,7 +153,7 @@ export function setNpmrc(input?: string): void { } } const npmrcRules = convertNpmrcToRules(npmrc); - if (npmrcRules.hostRules.length) { + if (npmrcRules.hostRules?.length) { npmrcRules.hostRules.forEach((hostRule) => hostRules.add(hostRule)); } packageRules = npmrcRules.packageRules; diff --git a/lib/modules/datasource/npm/types.ts b/lib/modules/datasource/npm/types.ts index facc963e7ada25130e94d2c96206186a5571af28..33258c2008eff5ead3bcdd2271ea4760ad6acb69 100644 --- a/lib/modules/datasource/npm/types.ts +++ b/lib/modules/datasource/npm/types.ts @@ -3,33 +3,33 @@ import type { HostRule } from '../../../types'; import type { Release, ReleaseResult } from '../types'; export interface NpmrcRules { - hostRules?: HostRule[]; - packageRules?: PackageRule[]; + hostRules: HostRule[]; + packageRules: PackageRule[]; } +export type NpmResponseVersion = { + repository?: { + url: string; + directory: string; + }; + homepage?: string; + deprecated?: boolean; + gitHead?: string; + dependencies?: Record<string, string>; + devDependencies?: Record<string, string>; +}; + export interface NpmResponse { _id: string; - name?: string; - versions?: Record< - string, - { - repository?: { - url: string; - directory: string; - }; - homepage?: string; - deprecated?: boolean; - gitHead?: string; - dependencies?: Record<string, string>; - devDependencies?: Record<string, string>; - } - >; + name: string; + versions?: Record<string, NpmResponseVersion>; repository?: { url?: string; directory?: string; }; homepage?: string; time?: Record<string, string>; + 'dist-tags'?: Record<string, string>; } export interface NpmRelease extends Release { @@ -39,8 +39,8 @@ export interface NpmDependency extends ReleaseResult { releases: NpmRelease[]; deprecationSource?: string; name: string; - homepage: string; - sourceUrl: string; + homepage?: string; + sourceUrl?: string; versions: Record<string, any>; 'dist-tags'?: Record<string, string>; sourceDirectory?: string; diff --git a/lib/modules/datasource/nuget/common.ts b/lib/modules/datasource/nuget/common.ts index 4094084c8db65a126206dd6eb1cd328f63584384..570ed9f777def732521b467f0909c853ec6f984b 100644 --- a/lib/modules/datasource/nuget/common.ts +++ b/lib/modules/datasource/nuget/common.ts @@ -6,7 +6,7 @@ import type { ParsedRegistryUrl } from './types'; const buildMetaRe = regEx(/\+.+$/g); export function removeBuildMeta(version: string): string { - return version?.replace(buildMetaRe, ''); + return version.replace(buildMetaRe, ''); } const urlWhitespaceRe = regEx(/\s/g); @@ -16,7 +16,7 @@ export function massageUrl(url: string): string { // During `dotnet pack` certain URLs are being URL decoded which may introduce whitespaces // and causes Markdown link generation problems. - resultUrl = resultUrl?.replace(urlWhitespaceRe, '%20'); + resultUrl = resultUrl.replace(urlWhitespaceRe, '%20'); return resultUrl; } diff --git a/lib/modules/datasource/nuget/index.ts b/lib/modules/datasource/nuget/index.ts index 94ebe16134d809aadd2220d19ab6ae27c7cd33f4..53b7c78d7a96b3b1970fadfdff998aeda6d7d17b 100644 --- a/lib/modules/datasource/nuget/index.ts +++ b/lib/modules/datasource/nuget/index.ts @@ -25,8 +25,12 @@ export class NugetDatasource extends Datasource { async getReleases({ packageName, registryUrl, - }: GetReleasesConfig): Promise<ReleaseResult> { + }: GetReleasesConfig): Promise<ReleaseResult | null> { logger.trace(`nuget.getReleases(${packageName})`); + // istanbul ignore if + if (!registryUrl) { + return null; + } const { feedUrl, protocolVersion } = parseRegistryUrl(registryUrl); if (protocolVersion === 2) { return v2.getReleases(this.http, feedUrl, packageName); diff --git a/lib/modules/datasource/nuget/v2.ts b/lib/modules/datasource/nuget/v2.ts index 18e109f33ed47b4e58a5dc386c86938f49cf0c7b..7353f9514cdc296d64f471accb4f7fd72a8bba81 100644 --- a/lib/modules/datasource/nuget/v2.ts +++ b/lib/modules/datasource/nuget/v2.ts @@ -1,12 +1,13 @@ import { XmlDocument, XmlElement } from 'xmldoc'; import { logger } from '../../../logger'; import type { Http } from '../../../util/http'; +import type { HttpResponse } from '../../../util/http/types'; import { regEx } from '../../../util/regex'; import type { ReleaseResult } from '../types'; import { massageUrl, removeBuildMeta } from './common'; -function getPkgProp(pkgInfo: XmlElement, propName: string): string { - return pkgInfo.childNamed('m:properties').childNamed(`d:${propName}`)?.val; +function getPkgProp(pkgInfo: XmlElement, propName: string): string | undefined { + return pkgInfo.childNamed('m:properties')?.childNamed(`d:${propName}`)?.val; } export async function getReleases( @@ -17,12 +18,13 @@ export async function getReleases( const dep: ReleaseResult = { releases: [], }; - let pkgUrlList = `${feedUrl.replace( + let pkgUrlList: string | null = `${feedUrl.replace( regEx(/\/+$/), '' )}/FindPackagesById()?id=%27${pkgName}%27&$select=Version,IsLatestVersion,ProjectUrl,Published`; - do { - const pkgVersionsListRaw = await http.get(pkgUrlList); + while (pkgUrlList !== null) { + // typescript issue + const pkgVersionsListRaw: HttpResponse<string> = await http.get(pkgUrlList); const pkgVersionsListDoc = new XmlDocument(pkgVersionsListRaw.body); const pkgInfoList = pkgVersionsListDoc.childrenNamed('entry'); @@ -31,7 +33,7 @@ export async function getReleases( const version = getPkgProp(pkgInfo, 'Version'); const releaseTimestamp = getPkgProp(pkgInfo, 'Published'); dep.releases.push({ - version: removeBuildMeta(version), + version: removeBuildMeta(`${version}`), releaseTimestamp, }); try { @@ -55,7 +57,7 @@ export async function getReleases( .find((node) => node.attr.rel === 'next'); pkgUrlList = nextPkgUrlListLink ? nextPkgUrlListLink.attr.href : null; - } while (pkgUrlList !== null); + } // dep not found if no release, so we can try next registry if (dep.releases.length === 0) { diff --git a/lib/modules/datasource/nuget/v3.ts b/lib/modules/datasource/nuget/v3.ts index c7ce57d44ab384fecfc819e3f6a569c9ac49a021..b2c81d1e0a9e0f07c54a20134297bc1dfc6727c8 100644 --- a/lib/modules/datasource/nuget/v3.ts +++ b/lib/modules/datasource/nuget/v3.ts @@ -62,13 +62,16 @@ export async function getResourceUrl( .filter( ({ type, version }) => type === resourceType && semver.valid(version) ) - .sort((x, y) => semver.compare(x.version, y.version)); - const { serviceId, version } = services.pop(); + .sort((x, y) => + x.version && y.version ? semver.compare(x.version, y.version) : 0 + ); + const { serviceId, version } = services.pop()!; // istanbul ignore if if ( resourceType === 'RegistrationsBaseUrl' && - !version?.startsWith('3.0.0-') && + version && + !version.startsWith('3.0.0-') && !semver.satisfies(version, '^3.0.0') ) { logger.warn( @@ -122,8 +125,8 @@ export async function getReleases( await pAll(catalogPagesQueue, { concurrency: 5 }) ).flat(); - let homepage = null; - let latestStable: string = null; + let homepage: string | null = null; + let latestStable: string | null = null; const releases = catalogEntries.map( ({ version, published: releaseTimestamp, projectUrl, listed }) => { const release: Release = { version: removeBuildMeta(version) }; @@ -132,7 +135,7 @@ export async function getReleases( } if (semver.valid(version) && !semver.prerelease(version)) { latestStable = removeBuildMeta(version); - homepage = massageUrl(projectUrl || homepage); + homepage = projectUrl ? massageUrl(projectUrl) : homepage; } if (listed === false) { release.isDeprecated = true; @@ -146,10 +149,10 @@ export async function getReleases( } // istanbul ignore if: only happens when no stable version exists - if (latestStable === null) { - const last = catalogEntries.pop(); + if (latestStable === null && catalogPages.length) { + const last = catalogEntries.pop()!; latestStable = removeBuildMeta(last.version); - homepage ??= last.projectUrl; + homepage ??= last.projectUrl ?? null; } const dep: ReleaseResult = { diff --git a/lib/modules/datasource/types.ts b/lib/modules/datasource/types.ts index 318835e8ae6b09aa144a828997465dc46ca471a6..2d120fb771a1321a5a2056a51c335318460d5813 100644 --- a/lib/modules/datasource/types.ts +++ b/lib/modules/datasource/types.ts @@ -63,7 +63,7 @@ export interface ReleaseResult { changelogUrl?: string; dependencyUrl?: string; homepage?: string; - sourceUrl?: string; + sourceUrl?: string | null; sourceDirectory?: string; registryUrl?: string; replacementName?: string; diff --git a/tsconfig.strict.json b/tsconfig.strict.json index d5884d441e520ad79674d6bff4cb8855c6f81855..d508fdea282b0b99961dc9fe71a9ea88a485bf0f 100644 --- a/tsconfig.strict.json +++ b/tsconfig.strict.json @@ -41,26 +41,13 @@ "lib/modules/datasource/api.ts", "lib/modules/datasource/datasource.ts", "lib/modules/datasource/github-releases/test/index.ts", - "lib/modules/datasource/gitlab-packages/index.ts", - "lib/modules/datasource/gitlab-releases/index.ts", - "lib/modules/datasource/gitlab-tags/index.ts", "lib/modules/datasource/go/base.ts", "lib/modules/datasource/go/common.ts", "lib/modules/datasource/go/index.ts", "lib/modules/datasource/go/releases-direct.ts", "lib/modules/datasource/go/releases-goproxy.ts", "lib/modules/datasource/go/types.ts", - "lib/modules/datasource/golang-version/index.ts", - "lib/modules/datasource/gradle-version/index.ts", "lib/modules/datasource/index.ts", - "lib/modules/datasource/jenkins-plugins/index.ts", - "lib/modules/datasource/npm/get.ts", - "lib/modules/datasource/npm/index.ts", - "lib/modules/datasource/npm/npmrc.ts", - "lib/modules/datasource/npm/releases.ts", - "lib/modules/datasource/nuget/index.ts", - "lib/modules/datasource/nuget/v2.ts", - "lib/modules/datasource/nuget/v3.ts", "lib/modules/datasource/packagist/index.ts", "lib/modules/datasource/pypi/index.ts", "lib/modules/datasource/repology/index.ts",