Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: github-releases fill newDigest #10931

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 101 additions & 9 deletions lib/datasource/github-releases/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { logger } from '../../logger';
import * as packageCache from '../../util/cache/package';
import { GithubHttp } from '../../util/http/github';
import { ensureTrailingSlash } from '../../util/url';
import type { GetReleasesConfig, ReleaseResult } from '../types';
import type { GithubRelease } from './types';
import type {
GetPkgReleasesConfig,
GetReleasesConfig,
Release,
ReleaseResult,
} from '../types';
import type { GithubRelease, GithubReleaseAsset } from './types';

export const id = 'github-releases';
export const customRegistrySupport = true;
Expand All @@ -18,6 +24,70 @@ function getCacheKey(depHost: string, repo: string): string {
return `${depHost}:${repo}:${type}`;
}

type ChecksumAsset = {
assetName: string;
hashedFileName: string;
};

async function findChecksumAsset(
release: GithubRelease | null,
digest: string | null
): Promise<ChecksumAsset | null> {
if (!release || !digest) {
return null;
}
const smallAssets = release.assets.filter(
(a: GithubReleaseAsset) => a.size < 5 * 1024
);
for (const asset of smallAssets) {
const res = await http.get(asset.browser_download_url);
for (const line of res.body.split('\n')) {
const lineSplit = line.split(/\s+/, 2);
if (lineSplit[0] === digest) {
return {
assetName: asset.name,
hashedFileName: lineSplit[1],
};
}
}
}
return null;
}

async function findNewDigest(
currentVersion: string,
checksumAsset: ChecksumAsset | null,
release: GithubRelease
): Promise<string | null> {
if (!checksumAsset) {
return null;
}
const current = currentVersion.replace(/^v/, '');
const next = release.tag_name.replace(/^v/, '');
const releaseChecksumAssetName = checksumAsset.assetName.replaceAll(
current,
next
);
const releaseAsset = release.assets.find(
(a: GithubReleaseAsset) => a.name === releaseChecksumAssetName
);
if (!releaseAsset) {
return null;
}
const releaseFilename = checksumAsset.hashedFileName.replaceAll(
current,
next
);
const res = await http.get(releaseAsset.browser_download_url);
for (const line of res.body.split('\n')) {
const lineSplit = line.split(/\s+/, 2);
if (lineSplit[1] === releaseFilename) {
return lineSplit[0];
}
}
return null;
}

/**
* github.getReleases
*
Expand All @@ -31,7 +101,12 @@ function getCacheKey(depHost: string, repo: string): string {
export async function getReleases({
lookupName: repo,
registryUrl,
}: GetReleasesConfig): Promise<ReleaseResult | null> {
currentValue,
currentDigest,
}: GetReleasesConfig & GetPkgReleasesConfig): Promise<ReleaseResult | null> {
logger.debug(
`getReleases(${repo}, ${registryUrl}, ${currentValue}, ${currentDigest})`
);
const cachedResult = await packageCache.get<ReleaseResult>(
cacheNamespace,
getCacheKey(registryUrl, repo)
Expand All @@ -53,18 +128,35 @@ export async function getReleases({
paginate: true,
});
const githubReleases = res.body;
const currentRelease = githubReleases.find(
(r: GithubRelease) => r.tag_name === currentValue
);
const checksumAsset = await findChecksumAsset(currentRelease, currentDigest);
const dependency: ReleaseResult = {
sourceUrl: `${sourceUrlBase}${repo}`,
releases: null,
};
dependency.releases = githubReleases.map(
({ tag_name, published_at, prerelease }) => ({
version: tag_name,
gitRef: tag_name,
releaseTimestamp: published_at,
isStable: prerelease ? false : undefined,
dependency.releases = await Promise.all(
githubReleases.map(async (release) => {
const mapped: Release = {
version: release.tag_name,
gitRef: release.tag_name,
releaseTimestamp: release.published_at,
isStable: release.prerelease ? false : undefined,
};

const newDigest = await findNewDigest(
currentValue,
checksumAsset,
release
);
if (newDigest) {
mapped.newDigest = newDigest;
}
return mapped;
})
);

const cacheMinutes = 10;
await packageCache.set(
cacheNamespace,
Expand Down
7 changes: 7 additions & 0 deletions lib/datasource/github-releases/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,11 @@ export type GithubRelease = {
tag_name: string;
published_at: string;
prerelease: boolean;
assets: GithubReleaseAsset[];
};

export type GithubReleaseAsset = {
name: string;
browser_download_url: string;
size: number;
};
2 changes: 2 additions & 0 deletions lib/datasource/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export interface GetPkgReleasesConfig extends ReleasesConfigBase {
versioning?: string;
extractVersion?: string;
constraints?: Record<string, string>;
currentValue?: string;
currentDigest?: string;
}

export interface Release {
Expand Down