import { withCache } from '../../../util/cache/package/with-cache.ts'; import { GitlabHttp } from '../../../util/http/gitlab.ts'; import { asTimestamp } from '../../../util/timestamp.ts'; import { joinUrlParts } from '../../../util/url.ts'; import { Datasource } from '../datasource.ts'; import type { GetReleasesConfig, ReleaseResult } from '../types.ts'; import { datasource } from './common.ts'; import type { GitlabPackage } from './types.ts'; // Gitlab Packages API: https://docs.gitlab.com/ee/api/packages.html export class GitlabPackagesDatasource extends Datasource { static readonly id = datasource; protected override http: GitlabHttp; override caching = true; override customRegistrySupport = true; override defaultRegistryUrls = ['https://gitlab.com']; override readonly releaseTimestampSupport = true; override readonly releaseTimestampNote = 'The release timestamp is determined from the `created_at` field in the results.'; constructor() { super(datasource); this.http = new GitlabHttp(datasource); } static getGitlabPackageApiUrl( registryUrl: string, projectName: string, packageName: string, ): string { const projectNameEncoded = encodeURIComponent(projectName); const packageNameEncoded = encodeURIComponent(packageName); return joinUrlParts( registryUrl, `api/v4/projects`, projectNameEncoded, `packages?package_name=${packageNameEncoded}&per_page=100`, ); } private async _getReleases({ registryUrl, packageName, }: GetReleasesConfig): Promise { /* v8 ignore next -- should never happen */ if (!registryUrl) { return null; } const [projectPart, packagePart] = packageName.split(':', 2); const apiUrl = GitlabPackagesDatasource.getGitlabPackageApiUrl( registryUrl, projectPart, packagePart, ); const result: ReleaseResult = { releases: [], }; let response: GitlabPackage[]; try { response = ( await this.http.getJsonUnchecked(apiUrl, { paginate: true, }) ).body; result.releases = response // Setting the package_name option when calling the GitLab API isn't enough to filter information about other packages // because this option is only implemented on GitLab > 12.9 and it only does a fuzzy search. .filter((r) => (r.conan_package_name ?? r.name) === packagePart) .map(({ version, created_at }) => ({ version, releaseTimestamp: asTimestamp(created_at), })); } catch (err) { this.handleGenericErrors(err); } return result.releases?.length ? result : null; } getReleases(config: GetReleasesConfig): Promise { return withCache( { namespace: `datasource-${datasource}`, // TODO: types (#22198) key: `${config.registryUrl}-${config.packageName}`, fallback: true, }, () => this._getReleases(config), ); } }