diff --git a/lib/datasource/helm/__fixtures__/index.yaml b/lib/datasource/helm/__fixtures__/index.yaml index b4240618e0099e..870b7b419e4b23 100644 --- a/lib/datasource/helm/__fixtures__/index.yaml +++ b/lib/datasource/helm/__fixtures__/index.yaml @@ -1851,7 +1851,6 @@ entries: version: 1.1.0 - apiVersion: v1 appVersion: 0.50.1 - created: 2019-02-13T00:56:01.476895808Z description: A Helm chart for Datawire Ambassador digest: aa09c62be843190cc85736ba59d6411579d83ba30e9305e6b2420ea013bb5979 engine: gotpl diff --git a/lib/datasource/helm/__fixtures__/sample.yaml b/lib/datasource/helm/__fixtures__/sample.yaml new file mode 100644 index 00000000000000..9e3c6d97bb1041 --- /dev/null +++ b/lib/datasource/helm/__fixtures__/sample.yaml @@ -0,0 +1,95 @@ +apiVersion: v1 +entries: + airflow: + - annotations: + category: WorkFlow + apiVersion: v2 + appVersion: 2.1.3 + created: "2021-09-15T23:14:58.888273096Z" + dependencies: + - name: common + repository: https://charts.bitnami.com/bitnami + tags: + - bitnami-common + version: 1.x.x + - condition: postgresql.enabled + name: postgresql + repository: https://charts.bitnami.com/bitnami + version: 10.x.x + - condition: redis.enabled + name: redis + repository: https://charts.bitnami.com/bitnami + version: 15.x.x + description: Apache Airflow is a platform to programmatically author, schedule + and monitor workflows. + digest: a651914c82b5aa5d005b24591c2474245942e6bede1a434ea74227d1a58a269c + home: https://github.com/bitnami/charts/tree/master/bitnami/airflow + icon: https://bitnami.com/assets/stacks/airflow/img/airflow-stack-220x234.png + keywords: + - apache + - airflow + - workflow + - dag + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: airflow + sources: + - https://github.com/bitnami/bitnami-docker-airflow + - https://airflow.apache.org/ + urls: + - https://charts.bitnami.com/bitnami/airflow-11.0.1.tgz + version: 11.0.1 + coredns: + - annotations: + artifacthub.io/changes: | + - Initial helm chart changelog + apiVersion: v2 + appVersion: 1.8.4 + created: "2021-07-24T17:35:31.367313224Z" + description: CoreDNS is a DNS server that chains plugins and provides Kubernetes + DNS Services + digest: d7015cc7970f7304d8b023d12a854f1684f5b79b120e3eea22b78ac590d05b35 + home: https://coredns.io + icon: https://coredns.io/images/CoreDNS_Colour_Horizontal.png + keywords: + - coredns + - dns + - kubedns + maintainers: + - name: mrueg + - name: haad + name: coredns + sources: + - https://github.com/coredns/coredns + type: application + urls: + - https://github.com/coredns/helm/releases/download/coredns-1.16.3/coredns-1.16.3.tgz + version: 1.16.3 + pgadmin4: + - apiVersion: v1 + appVersion: "5.5" + created: "2021-08-23T07:25:42.700577873Z" + description: pgAdmin4 is a web based administration tool for PostgreSQL database + digest: b15e5de91fcbe8c2cc896af8448e41ad94a9bfae9592fc13d3e5805d4cb707b0 + home: https://www.pgadmin.org/ + icon: https://wiki.postgresql.org/images/3/30/PostgreSQL_logo.3colors.120x120.png + keywords: + - pgadmin + - postgres + - database + - sql + maintainers: + - email: rowanruseler@gmail.com + name: rowanruseler + name: pgadmin4 + sources: + - https://some.test + - https://github.com/rowanruseler/helm-charts + urls: + - pgadmin4-1.7.2.tgz + version: 1.7.2 + dummy: + - home: + urls: + - some.tgz diff --git a/lib/datasource/helm/__snapshots__/index.spec.ts.snap b/lib/datasource/helm/__snapshots__/index.spec.ts.snap index 9c3f47752b6e9b..c8e5c05df9affd 100644 --- a/lib/datasource/helm/__snapshots__/index.spec.ts.snap +++ b/lib/datasource/helm/__snapshots__/index.spec.ts.snap @@ -20,7 +20,6 @@ Object { "registryUrl": "https://example-repository.com", "releases": Array [ Object { - "releaseTimestamp": "2019-02-13T00:56:01.476Z", "version": "1.0.0", }, Object { diff --git a/lib/datasource/helm/common.spec.ts b/lib/datasource/helm/common.spec.ts new file mode 100644 index 00000000000000..a9b3058a08b4cb --- /dev/null +++ b/lib/datasource/helm/common.spec.ts @@ -0,0 +1,26 @@ +import { load } from 'js-yaml'; +import { loadFixture } from '../../../test/util'; +import { findSourceUrl } from './common'; +import type { HelmRepository } from './types'; + +// Truncated index.yaml file +const repo = load(loadFixture('sample.yaml'), { + json: true, +}) as HelmRepository; + +describe('datasource/helm/common', () => { + describe('findSourceUrl', () => { + test.each` + input | output + ${'airflow'} | ${'https://github.com/bitnami/charts'} + ${'coredns'} | ${'https://github.com/coredns/helm'} + ${'pgadmin4'} | ${'https://github.com/rowanruseler/helm-charts'} + ${'dummy'} | ${undefined} + `( + '$input -> $output', + ({ input, output }: { input: string; output: string }) => { + expect(findSourceUrl(repo.entries[input][0])).toEqual(output); + } + ); + }); +}); diff --git a/lib/datasource/helm/common.ts b/lib/datasource/helm/common.ts new file mode 100644 index 00000000000000..d6327bffb727f3 --- /dev/null +++ b/lib/datasource/helm/common.ts @@ -0,0 +1,33 @@ +import type { HelmRelease } from './types'; + +const chartRepo = /charts?|helm|helm-charts/i; +const githubUrl = + /^(?https:\/\/github\.com\/[^/]+\/(?[^/]+))(?:\/|$)/; +const githubRelease = /^(https:\/\/github\.com\/[^/]+\/[^/]+)\/releases\//; + +export function findSourceUrl(release: HelmRelease): string { + // it's a github release :) + let match = githubRelease.exec(release.urls[0]); + if (match) { + return match[1]; + } + + match = githubUrl.exec(release.home); + if (chartRepo.test(match?.groups.repo)) { + return match.groups.url; + } + + if (!release.sources?.length) { + return undefined; + } + + for (const url of release.sources) { + match = githubUrl.exec(url); + if (chartRepo.test(match?.groups.repo)) { + return match.groups.url; + } + } + + // fallback + return release.sources[0]; +} diff --git a/lib/datasource/helm/index.ts b/lib/datasource/helm/index.ts index 64e8ebabbc24a9..c23b04e75e99b5 100644 --- a/lib/datasource/helm/index.ts +++ b/lib/datasource/helm/index.ts @@ -5,6 +5,7 @@ import { cache } from '../../util/cache/package/decorator'; import { ensureTrailingSlash } from '../../util/url'; import { Datasource } from '../datasource'; import type { GetReleasesConfig, ReleaseResult } from '../types'; +import { findSourceUrl } from './common'; import type { HelmRepository, RepositoryData } from './types'; export class HelmDatasource extends Datasource { @@ -52,10 +53,10 @@ export class HelmDatasource extends Datasource { for (const [name, releases] of Object.entries(doc.entries)) { result[name] = { homepage: releases[0].home, - sourceUrl: releases[0].sources ? releases[0].sources[0] : undefined, + sourceUrl: findSourceUrl(releases[0]), releases: releases.map((release) => ({ version: release.version, - releaseTimestamp: release.created ? release.created : null, + releaseTimestamp: release.created ?? null, })), }; } diff --git a/lib/datasource/helm/types.ts b/lib/datasource/helm/types.ts index 2f3877706bb473..fe482f27dbf185 100644 --- a/lib/datasource/helm/types.ts +++ b/lib/datasource/helm/types.ts @@ -1,15 +1,15 @@ import type { ReleaseResult } from '../types'; +export interface HelmRelease { + home?: string; + sources?: string[]; + version: string; + created: string; + urls: string[]; +} + export interface HelmRepository { - entries: Record< - string, - { - home?: string; - sources?: string[]; - version: string; - created: string; - }[] - >; + entries: Record; } export type RepositoryData = Record;