Skip to content

Commit

Permalink
refactor: more strict null checks (#15140)
Browse files Browse the repository at this point in the history
  • Loading branch information
viceice committed Apr 16, 2022
1 parent 15dd29c commit 5cf9123
Show file tree
Hide file tree
Showing 18 changed files with 105 additions and 77 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ module.exports = {

// TODO: fix lint
'@typescript-eslint/no-explicit-any': 0,
'@typescript-eslint/no-non-null-assertion': 2,
// TODO: https://github.com/renovatebot/renovate/issues/7154
'@typescript-eslint/no-non-null-assertion': 0,
'@typescript-eslint/no-unused-vars': [
2,
{
Expand Down
68 changes: 44 additions & 24 deletions lib/modules/datasource/docker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export async function getAuthHeaders(
{ registryHost, dockerRepository },
`Using ecr auth for Docker registry`
);
const [, region] = ecrRegex.exec(registryHost);
const [, region] = ecrRegex.exec(registryHost) ?? [];
const auth = await getECRAuthToken(region, opts);
if (auth) {
opts.headers = { authorization: `Basic ${auth}` };
Expand Down Expand Up @@ -132,7 +132,7 @@ export async function getAuthHeaders(
{ registryHost, dockerRepository, authenticateHeader },
`Invalid realm, testing direct auth`
);
return opts.headers;
return opts.headers ?? null;
}

const authUrl = `${authenticateHeader.params.realm}?service=${authenticateHeader.params.service}&scope=repository:${dockerRepository}:pull`;
Expand Down Expand Up @@ -200,7 +200,7 @@ export async function getAuthHeaders(
}

async function getECRAuthToken(
region: string,
region: string | undefined,
opts: HostRule
): Promise<string | null> {
const config: ECRClientConfig = { region };
Expand Down Expand Up @@ -244,7 +244,7 @@ export function getRegistryRepository(
}
let dockerRepository = packageName.replace(registryEndingWithSlash, '');
const fullUrl = `${registryHost}/${dockerRepository}`;
const { origin, pathname } = parseUrl(fullUrl);
const { origin, pathname } = parseUrl(fullUrl)!;
registryHost = origin;
dockerRepository = pathname.substring(1);
return {
Expand All @@ -253,7 +253,7 @@ export function getRegistryRepository(
};
}
}
let registryHost: string;
let registryHost: string | undefined;
const split = packageName.split('/');
if (split.length > 1 && (split[0].includes('.') || split[0].includes(':'))) {
[registryHost] = split;
Expand Down Expand Up @@ -299,11 +299,12 @@ export function extractDigestFromResponseBody(
}

export function isECRMaxResultsError(err: HttpError): boolean {
const resp = err.response as HttpResponse<any> | undefined;
return !!(
err.response?.statusCode === 405 &&
err.response?.headers?.['docker-distribution-api-version'] &&
resp?.statusCode === 405 &&
resp.headers?.['docker-distribution-api-version'] &&
// https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults
err.response.body?.['errors']?.[0]?.message?.includes(
resp.body?.['errors']?.[0]?.message?.includes(
'Member must have value less than or equal to 1000'
)
);
Expand Down Expand Up @@ -336,12 +337,12 @@ export const defaultConfig = {
},
};

function findLatestStable(tags: string[]): string {
function findLatestStable(tags: string[]): string | null {
const versions = tags
.filter((v) => dockerVersioning.isValid(v) && dockerVersioning.isStable(v))
.sort((a, b) => dockerVersioning.sortVersions(a, b));

return versions.pop() ?? tags.slice(-1).pop();
return versions.pop() ?? tags.slice(-1).pop() ?? null;
}

export class DockerDatasource extends Datasource {
Expand All @@ -361,7 +362,7 @@ export class DockerDatasource extends Datasource {
dockerRepository: string,
tag: string,
mode: 'head' | 'get' = 'get'
): Promise<HttpResponse> {
): Promise<HttpResponse | null> {
logger.debug(
`getManifestResponse(${registryHost}, ${dockerRepository}, ${tag})`
);
Expand Down Expand Up @@ -442,7 +443,7 @@ export class DockerDatasource extends Datasource {
registry: string,
dockerRepository: string,
tag: string
): Promise<string> {
): Promise<string | null> {
const manifestResponse = await this.getManifestResponse(
registry,
dockerRepository,
Expand Down Expand Up @@ -653,17 +654,25 @@ export class DockerDatasource extends Datasource {
`${registry}/api/v1/repository/${repository}/tag/?limit=${limit}&page=${page}&onlyActiveTags=true`;

let page = 1;
let url = pageUrl(page);
do {
const res = await this.http.getJson<{
tags: { name: string }[];
let url: string | null = pageUrl(page);
while (url && page <= 20) {
interface QuayRestDockerTags {
tags: {
name: string;
}[];
has_additional: boolean;
}>(url, {});
}

// typescript issue :-/
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const res = (await this.http.getJson<QuayRestDockerTags>(
url
)) as HttpResponse<QuayRestDockerTags>;
const pageTags = res.body.tags.map((tag) => tag.name);
tags = tags.concat(pageTags);
page += 1;
url = res.body.has_additional ? pageUrl(page) : null;
} while (url && page < 20);
}
return tags;
}

Expand All @@ -675,7 +684,9 @@ export class DockerDatasource extends Datasource {
// AWS ECR limits the maximum number of results to 1000
// See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults
const limit = ecrRegex.test(registryHost) ? 1000 : 10000;
let url = `${registryHost}/${dockerRepository}/tags/list?n=${limit}`;
let url:
| string
| null = `${registryHost}/${dockerRepository}/tags/list?n=${limit}`;
url = ensurePathPrefix(url, '/v2');
const headers = await getAuthHeaders(
this.http,
Expand Down Expand Up @@ -792,7 +803,8 @@ export class DockerDatasource extends Datasource {
const newTag = newValue || 'latest';
const { registryHost, dockerRepository } = getRegistryRepository(
packageName,
registryUrl
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
registryUrl!
);
return `${registryHost}:${dockerRepository}:${newTag}`;
},
Expand All @@ -803,13 +815,14 @@ export class DockerDatasource extends Datasource {
): Promise<string | null> {
const { registryHost, dockerRepository } = getRegistryRepository(
packageName,
registryUrl
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
registryUrl!
);
logger.debug(
`getDigest(${registryHost}, ${dockerRepository}, ${newValue})`
);
const newTag = newValue || 'latest';
let digest: string = null;
let digest: string | null = null;
try {
let manifestResponse = await this.getManifestResponse(
registryHost,
Expand All @@ -832,7 +845,8 @@ export class DockerDatasource extends Datasource {
dockerRepository,
newTag
);
digest = extractDigestFromResponseBody(manifestResponse);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
digest = extractDigestFromResponseBody(manifestResponse!);
}
logger.debug({ digest }, 'Got docker digest');
}
Expand Down Expand Up @@ -869,7 +883,8 @@ export class DockerDatasource extends Datasource {
}: GetReleasesConfig): Promise<ReleaseResult | null> {
const { registryHost, dockerRepository } = getRegistryRepository(
packageName,
registryUrl
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
registryUrl!
);
const tags = await this.getTags(registryHost, dockerRepository);
if (!tags) {
Expand All @@ -884,6 +899,11 @@ export class DockerDatasource extends Datasource {
const latestTag = tags.includes('latest')
? 'latest'
: findLatestStable(tags);

// istanbul ignore if: needs test
if (!latestTag) {
return ret;
}
const labels = await this.getLabels(
registryHost,
dockerRepository,
Expand Down
7 changes: 4 additions & 3 deletions lib/modules/datasource/galaxy-collection/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import is from '@sindresorhus/is';
import pMap from 'p-map';
import { logger } from '../../../logger';
import { cache } from '../../../util/cache/package/decorator';
Expand Down Expand Up @@ -71,9 +72,9 @@ export class GalaxyCollectionDatasource extends Datasource {
return release;
});

let newestVersionDetails: VersionsDetailResult;
let newestVersionDetails: VersionsDetailResult | undefined;
// asynchronously get release details
const enrichedReleases: Release[] = await pMap(
const enrichedReleases: (Release | null)[] = await pMap(
releases,
(basicRelease) =>
this.http
Expand Down Expand Up @@ -109,7 +110,7 @@ export class GalaxyCollectionDatasource extends Datasource {
{ concurrency: 5 } // allow 5 requests at maximum in parallel
);
// filter failed versions
const filteredReleases = enrichedReleases.filter(Boolean);
const filteredReleases = enrichedReleases.filter(is.truthy);
// extract base information which are only provided on the release from the newest release
const result: ReleaseResult = {
releases: filteredReleases,
Expand Down
2 changes: 1 addition & 1 deletion lib/modules/datasource/galaxy/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class GalaxyDatasource extends Datasource {
projectName;
const galaxyProjectUrl = registryUrl + userName + '/' + projectName;

let raw: HttpResponse<GalaxyResult> = null;
let raw: HttpResponse<GalaxyResult> | null = null;
try {
raw = await this.http.getJson<GalaxyResult>(galaxyAPIUrl);
} catch (err) {
Expand Down
7 changes: 4 additions & 3 deletions lib/modules/datasource/git-refs/base.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import is from '@sindresorhus/is';
import simpleGit from 'simple-git';
import { logger } from '../../../logger';
import { cache } from '../../../util/cache/package/decorator';
Expand Down Expand Up @@ -38,15 +39,15 @@ export class GitDatasource {
.map((line) => line.trim())
.map((line) => {
let match = refMatch.exec(line);
if (match) {
if (match?.groups) {
return {
type: match.groups.type,
value: match.groups.value,
hash: match.groups.hash,
};
}
match = headMatch.exec(line);
if (match) {
if (match?.groups) {
return {
type: '',
value: 'HEAD',
Expand All @@ -56,7 +57,7 @@ export class GitDatasource {
logger.trace(`malformed ref: ${line}`);
return null;
})
.filter(Boolean)
.filter(is.truthy)
.filter((ref) => ref.type !== 'pull' && !ref.value.endsWith('^{}'));

return refs;
Expand Down
18 changes: 14 additions & 4 deletions lib/modules/datasource/git-refs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ export class GitRefsDatasource extends Datasource {
override async getReleases({
packageName,
}: GetReleasesConfig): Promise<ReleaseResult | null> {
const rawRefs: RawRefs[] = await GitDatasource.getRawRefs(
const rawRefs: RawRefs[] | null = await GitDatasource.getRawRefs(
{ packageName },
this.id
);

if (!rawRefs) {
return null;
}

const refs = rawRefs
.filter((ref) => ref.type === 'tags' || ref.type === 'heads')
.map((ref) => ref.value);
Expand All @@ -44,7 +48,7 @@ export class GitRefsDatasource extends Datasource {
releases: uniqueRefs.map((ref) => ({
version: ref,
gitRef: ref,
newDigest: rawRefs.find((rawRef) => rawRef.value === ref).hash,
newDigest: rawRefs.find((rawRef) => rawRef.value === ref)?.hash,
})),
};

Expand All @@ -55,11 +59,17 @@ export class GitRefsDatasource extends Datasource {
{ packageName }: DigestConfig,
newValue?: string
): Promise<string | null> {
const rawRefs: RawRefs[] = await GitDatasource.getRawRefs(
const rawRefs: RawRefs[] | null = await GitDatasource.getRawRefs(
{ packageName },
this.id
);
let ref: RawRefs;

// istanbul ignore if
if (!rawRefs) {
return null;
}

let ref: RawRefs | undefined;
if (newValue) {
ref = rawRefs.find(
(rawRef) =>
Expand Down
2 changes: 1 addition & 1 deletion lib/modules/datasource/git-tags/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class GitTagsDatasource extends Datasource {
): Promise<string | null> {
const rawRefs = await GitDatasource.getRawRefs({ packageName }, this.id);
const findValue = newValue || 'HEAD';
const ref = rawRefs.find((rawRef) => rawRef.value === findValue);
const ref = rawRefs?.find((rawRef) => rawRef.value === findValue);
if (ref) {
return ref.hash;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/modules/datasource/github-releases/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { ensureTrailingSlash } from '../../../util/url';

const defaultSourceUrlBase = 'https://github.com/';

export function getSourceUrlBase(registryUrl: string): string {
export function getSourceUrlBase(registryUrl: string | undefined): string {
// default to GitHub.com if no GHE host is specified.
return ensureTrailingSlash(registryUrl ?? defaultSourceUrlBase);
}

export function getApiBaseUrl(registryUrl: string): string {
export function getApiBaseUrl(registryUrl: string | undefined): string {
const sourceUrlBase = getSourceUrlBase(registryUrl);
return sourceUrlBase === defaultSourceUrlBase
? `https://api.github.com/`
Expand Down
4 changes: 2 additions & 2 deletions lib/modules/datasource/github-releases/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class GithubReleasesDatasource extends Datasource {
async findDigestAsset(
release: GithubRelease,
digest: string
): Promise<DigestAsset> {
): Promise<DigestAsset | null> {
const digestFile = await this.findDigestFile(release, digest);
if (digestFile) {
return digestFile;
Expand Down Expand Up @@ -206,7 +206,7 @@ export class GithubReleasesDatasource extends Datasource {
currentRelease,
currentDigest
);
let newDigest: string;
let newDigest: string | null;
if (!digestAsset || newValue === currentValue) {
newDigest = currentDigest;
} else {
Expand Down
5 changes: 3 additions & 2 deletions lib/modules/datasource/github-releases/test/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as httpMock from '../../../../../test/http-mock';
import { partial } from '../../../../../test/util';
import type { GithubRelease } from '../types';

export class GitHubReleaseMocker {
Expand All @@ -15,12 +16,12 @@ export class GitHubReleaseMocker {
version: string,
assets: { [key: string]: string }
): GithubRelease {
const releaseData = {
const releaseData = partial<GithubRelease>({
tag_name: version,
published_at: '2020-03-09T11:00:00Z',
prerelease: false,
assets: [],
} as GithubRelease;
});
for (const assetFn of Object.keys(assets)) {
const assetPath = `/repos/${this.packageName}/releases/download/${version}/${assetFn}`;
const assetData = assets[assetFn];
Expand Down

0 comments on commit 5cf9123

Please sign in to comment.