import type { PackageCacheNamespace } from '../../../util/cache/package/types.ts'; import { withCache } from '../../../util/cache/package/with-cache.ts'; import { ForgejoHttp } from '../../../util/http/forgejo.ts'; import { Datasource } from '../datasource.ts'; import { ForgejoTagsDatasource } from '../forgejo-tags/index.ts'; import { Commits, Tag } from '../forgejo-tags/schema.ts'; import type { DigestConfig, GetReleasesConfig, ReleaseResult, } from '../types.ts'; import { Releases } from './schema.ts'; export class ForgejoReleasesDatasource extends Datasource { static readonly id = 'forgejo-releases'; override http = new ForgejoHttp(ForgejoReleasesDatasource.id); static readonly defaultRegistryUrls = ['https://code.forgejo.org']; private static readonly cacheNamespace: PackageCacheNamespace = `datasource-${ForgejoReleasesDatasource.id}`; override readonly releaseTimestampSupport = true; override readonly releaseTimestampNote = 'The release timestamp is determined from the `published_at` field in the results.'; override readonly sourceUrlSupport = 'package'; override readonly sourceUrlNote = 'The source URL is determined by using the `packageName` and `registryUrl`.'; constructor() { super(ForgejoReleasesDatasource.id); } // getReleases fetches list of tags for the repository private async _getReleases({ registryUrl, packageName: repo, }: GetReleasesConfig): Promise { const url = `${ForgejoTagsDatasource.getApiUrl( registryUrl, )}repos/${repo}/releases?draft=false`; const tags = ( await this.http.getJson( url, { paginate: true, }, Releases, ) ).body; const dependency: ReleaseResult = { sourceUrl: ForgejoTagsDatasource.getSourceUrl(repo, registryUrl), registryUrl: ForgejoTagsDatasource.getRegistryURL(registryUrl), releases: tags.map(({ tag_name, published_at, prerelease }) => ({ version: tag_name, gitRef: tag_name, releaseTimestamp: published_at, isStable: !prerelease, })), }; return dependency; } getReleases(config: GetReleasesConfig): Promise { return withCache( { namespace: ForgejoReleasesDatasource.cacheNamespace, key: ForgejoTagsDatasource.getCacheKey( config.registryUrl, config.packageName, 'releases', ), fallback: true, }, () => this._getReleases(config), ); } // getTagCommit fetched the commit has for specified tag private async _getTagCommit( registryUrl: string | undefined, repo: string, tag: string, ): Promise { const url = `${ForgejoTagsDatasource.getApiUrl( registryUrl, )}repos/${repo}/tags/${tag}`; const { body } = await this.http.getJson(url, Tag); return body.commit.sha; } getTagCommit( registryUrl: string | undefined, repo: string, tag: string, ): Promise { return withCache( { namespace: ForgejoReleasesDatasource.cacheNamespace, key: ForgejoTagsDatasource.getCacheKey(registryUrl, repo, `tag-${tag}`), }, () => this._getTagCommit(registryUrl, repo, tag), ); } // getDigest fetched the latest commit for repository main branch // however, if newValue is provided, then getTagCommit is called private async _getDigest( { packageName: repo, registryUrl }: DigestConfig, newValue?: string, ): Promise { if (newValue?.length) { return this.getTagCommit(registryUrl, repo, newValue); } const url = `${ForgejoTagsDatasource.getApiUrl( registryUrl, )}repos/${repo}/commits?stat=false&verification=false&files=false&page=1&limit=1`; const { body } = await this.http.getJson(url, Commits); if (body.length === 0) { return null; } return body[0].sha; } override getDigest( config: DigestConfig, newValue?: string, ): Promise { return withCache( { namespace: ForgejoReleasesDatasource.cacheNamespace, key: ForgejoTagsDatasource.getCacheKey( config.registryUrl, config.packageName, 'digest', ), fallback: true, }, () => this._getDigest(config, newValue), ); } }