From 49736330792b344f172943b858237b2ffe0fb7a0 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 16:37:04 +0200 Subject: [PATCH 01/13] added support for gitea for changelogs --- .../[owner]/[repo]/[...path].get.ts | 3 + .../releases/[provider]/[owner]/[repo].get.ts | 35 +++++++++- server/utils/changelog/baseFileUrl.ts | 12 +++- server/utils/changelog/detectChangelog.ts | 69 +++++++++++++++++++ server/utils/changelog/mdRepoInfo.ts | 22 ++++++ shared/schemas/changelog/release.ts | 5 ++ 6 files changed, 142 insertions(+), 4 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index a1582b1414..66bd991de7 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -3,6 +3,7 @@ import * as v from 'valibot' import { getBaseFileUrl } from '~~/server/utils/changelog/baseFileUrl' import { createForgejoRepoInfo, + createGiteaRepoInfo, createGithubRepoInfo, createGitLabRepoInfo, createTangledInfo, @@ -114,5 +115,7 @@ function getRepoInfo( return createGitLabRepoInfo(host ?? 'gitlab.com', owner, repo, path) case 'tangled': return createTangledInfo(owner, repo, path) + case 'gitea': + return createGiteaRepoInfo(host ?? 'gitea.com', owner, repo, path) } } diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts index 20e618072a..cac921157f 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts @@ -9,9 +9,14 @@ import { GithubReleaseCollectionSchama, ForgejoReleaseCollectionSchema, GitlabReleaseCollectionSchema, + GiteaReleaseCollectionSchema, } from '~~/shared/schemas/changelog/release' import { changelogRenderer } from '~~/server/utils/changelog/markdown' -import { createForgejoRepoInfo, createGithubRepoInfo } from '~~/server/utils/changelog/mdRepoInfo' +import { + createForgejoRepoInfo, + createGiteaRepoInfo, + createGithubRepoInfo, +} from '~~/server/utils/changelog/mdRepoInfo' import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost' export default defineCachedEventHandler( @@ -39,6 +44,8 @@ export default defineCachedEventHandler( return await getReleasesFromForgejo(owner, repo, host) case 'gitlab': return await getReleasesFromGitlab(owner, repo, host) + case 'gitea': + return await getReleasesFromGitea(owner, repo, host) default: throw createError({ @@ -158,3 +165,29 @@ async function getReleasesFromGitlab(owner: string, repo: string, host: string) } satisfies ReleaseData }) } + +async function getReleasesFromGitea(owner: string, repo: string, host: string) { + const data = await $fetch(`https://${host}/api/v1/repos/${owner}/${repo}/releases?draft=false`, { + headers: { + 'User-Agent': 'npmx.dev', + }, + }) + const releases = v.parse(GiteaReleaseCollectionSchema, data) + + const render = await changelogRenderer(createGiteaRepoInfo(host, owner, repo)) + + return releases.map(r => { + const { html, toc } = render(r.body, r.id) + return { + id: r.id, + html: html?.replace(/(?)\n/g, '
') ?? null, + title: r.name || r.tag_name, + prerelease: r.prerelease, + toc, + link: r.html_url, + publishedAt: r.published_at, + draft: r.draft, + tag: r.tag_name, + } satisfies ReleaseData + }) +} diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index 7cda63169b..9f79a7e226 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -16,10 +16,16 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { raw: `https://codeberg.org/${ref.owner}/${ref.repo}/raw/branch/HEAD`, } case 'forgejo': { - const host = ref.host return { - blob: `https://${host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, - raw: `https://${host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, + blob: `https://${ref.host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, + raw: `https://${ref.host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, + } + } + case 'gitea': { + // although similar to forgejo, we keep these seperate because they're seperate maintained projects + return { + blob: `https://${ref.host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, + raw: `https://${ref.host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, } } case 'gitlab': { diff --git a/server/utils/changelog/detectChangelog.ts b/server/utils/changelog/detectChangelog.ts index 6289a7aebd..1d04fd86df 100644 --- a/server/utils/changelog/detectChangelog.ts +++ b/server/utils/changelog/detectChangelog.ts @@ -8,6 +8,7 @@ import { GithubReleaseSchama, ForgejoReleaseSchama, GitlabReleaseSchame, + GiteaReleaseSchema, } from '~~/shared/schemas/changelog/release' import { resolveURL } from 'ufo' import * as v from 'valibot' @@ -66,6 +67,9 @@ async function checkReleases( case 'gitlab': { return checkLatestGitlabRelease(ref, directory) } + case 'gitea': { + return checkLatestGiteaRelease(ref, directory) + } } return [false, null] } @@ -340,3 +344,68 @@ async function checkLatestGitlabRelease( } return [false, null] } + +// gitea +async function checkLatestGiteaRelease( + ref: RepoRef, + directory?: string, +): Promise> { + try { + const host = ref.host ?? 'gitea.com' + + const response = await $fetch( + `https://${host}/api/v1/repos/${ref.owner}/${ref.repo}/releases/latest`, + { + headers: { + 'User-Agent': 'npmx.dev', + 'accept': 'application/json', + }, + }, + ) + + const release = v.parse(GiteaReleaseSchema, response) + + const matchedChangelog = release.body?.match(MD_REGEX)?.at(0) + + // /src/branch/ can be similar to /blob/ + if (!matchedChangelog || !matchedChangelog.includes('/src/branch/')) { + return [ + { + type: 'release', + link: `https://${host}/${ref.owner}/${ref.repo}/releases`, + provider: ref.provider, + repo: `${ref.owner}/${ref.repo}`, + host: ref.host, + }, + null, + ] + } + + const path = matchedChangelog.replace(/^.*\/src\/branch\/[^/]+\//i, '') + if ( + directory && + !( + path.startsWith(directory.endsWith('/') ? directory : `${directory}/`) || + ROOT_ONLY_REGEX.test(path) + ) + ) { + return [false, null] as const + } + return [ + { + provider: ref.provider, + type: 'md', + path, + repo: `${ref.owner}/${ref.repo}`, + link: matchedChangelog, + host: ref.host, + }, + null, + ] + } catch (e) { + if (e instanceof Error) { + return [null, e] + } + } + return [false, null] +} diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index fca0f1dd74..ece9875ccf 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -78,3 +78,25 @@ export function createTangledInfo(owner: string, repo: string, path?: string): M compareBaseUrl: `${hostBaseUrl}/${owner}/${repo}/compare`, } } + +// similar to forgejo, but they're seperate projects so keeping this also seperate +export function createGiteaRepoInfo( + host: string, + owner: string, + repo: string, + path?: string, +): MarkdownRepoInfo { + const hostBaseUrl = `https://${host}` + return { + hostBaseUrl, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/src/branch/HEAD`, + rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/branch/HEAD`, + path, + commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, + issueChar: '#', + issueBaseUrl: `${hostBaseUrl}/${owner}/${repo}/issues`, + prChar: '#', + prBaseUrl: `${hostBaseUrl}/${owner}/${repo}/pulls`, + compareBaseUrl: `${hostBaseUrl}/${owner}/${repo}/compare`, + } +} diff --git a/shared/schemas/changelog/release.ts b/shared/schemas/changelog/release.ts index 0ac03ddd5c..d02edd9742 100644 --- a/shared/schemas/changelog/release.ts +++ b/shared/schemas/changelog/release.ts @@ -46,3 +46,8 @@ export const GitlabReleaseSchame = v.object({ }) export const GitlabReleaseCollectionSchema = v.array(GitlabReleaseSchame) + +// these are currently the same with forgejo, but exporting this one seperetely in case either change their schema +export const GiteaReleaseSchema = ForgejoReleaseSchama + +export const GiteaReleaseCollectionSchema = v.array(GiteaReleaseSchema) From 36d2ac9e2ef51be2c3f4fe14bfcc159c38248230 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 16:45:51 +0200 Subject: [PATCH 02/13] ... --- shared/schemas/changelog/release.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/shared/schemas/changelog/release.ts b/shared/schemas/changelog/release.ts index d02edd9742..8734395b3c 100644 --- a/shared/schemas/changelog/release.ts +++ b/shared/schemas/changelog/release.ts @@ -47,7 +47,15 @@ export const GitlabReleaseSchame = v.object({ export const GitlabReleaseCollectionSchema = v.array(GitlabReleaseSchame) -// these are currently the same with forgejo, but exporting this one seperetely in case either change their schema -export const GiteaReleaseSchema = ForgejoReleaseSchama +export const GiteaReleaseSchema = v.object({ + id: v.number(), + tag_name: v.string(), + name: v.string(), + body: v.string(), + html_url: v.pipe(v.string(), v.url()), + draft: v.boolean(), + prerelease: v.boolean(), + published_at: v.pipe(v.string(), v.isoTimestamp()), +}) export const GiteaReleaseCollectionSchema = v.array(GiteaReleaseSchema) From 0756d225130180ceb9657d574b6a5f2a05d3232d Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 18:27:08 +0200 Subject: [PATCH 03/13] adding support for bitbucket to changelog --- .../[owner]/[repo]/[...path].get.ts | 3 +++ server/utils/changelog/baseFileUrl.ts | 8 ++++++- server/utils/changelog/markdown.ts | 21 +++++++++++-------- server/utils/changelog/mdRepoInfo.ts | 18 ++++++++++++++++ 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index 66bd991de7..56bb1f112e 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -2,6 +2,7 @@ import { resolveURL } from 'ufo' import * as v from 'valibot' import { getBaseFileUrl } from '~~/server/utils/changelog/baseFileUrl' import { + createBitbucketRepoInfo, createForgejoRepoInfo, createGiteaRepoInfo, createGithubRepoInfo, @@ -117,5 +118,7 @@ function getRepoInfo( return createTangledInfo(owner, repo, path) case 'gitea': return createGiteaRepoInfo(host ?? 'gitea.com', owner, repo, path) + case 'bitbucket': + return createBitbucketRepoInfo(owner, repo, path) } } diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index 9f79a7e226..aa0b771ca9 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -22,7 +22,7 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { } } case 'gitea': { - // although similar to forgejo, we keep these seperate because they're seperate maintained projects + // although similar to forgejo, we keep these seperate because they're seperately maintained projects return { blob: `https://${ref.host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, raw: `https://${ref.host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, @@ -41,6 +41,12 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { raw: `https://tangled.org/${ref.owner}/${ref.repo}/raw/HEAD`, } } + case 'bitbucket': { + return { + blob: `https://bitbucket.org/${ref.owner}/${ref.repo}/src/HEAD`, + raw: `https://bitbucket.org/${ref.owner}/${ref.repo}/raw/HEAD`, + } + } } return null } diff --git a/server/utils/changelog/markdown.ts b/server/utils/changelog/markdown.ts index e57d3f42e3..4edd42b82c 100644 --- a/server/utils/changelog/markdown.ts +++ b/server/utils/changelog/markdown.ts @@ -118,9 +118,9 @@ export interface MarkdownRepoInfo { /** the base url of repository commit */ commitBaseUrl: string /** base url for a repository issue */ - issueBaseUrl: string + issueBaseUrl?: string /** the text char that indicates an issue */ - issueChar: keyof typeof issuePrRegexes + issueChar?: keyof typeof issuePrRegexes /** base url for a repository pull/merge request */ prBaseUrl: string /** @@ -130,7 +130,7 @@ export interface MarkdownRepoInfo { x*/ prChar: keyof typeof issuePrRegexes /** base url for a repository compare */ - compareBaseUrl: string + compareBaseUrl?: string } function resolveUrl(url: string, repoInfo: MarkdownRepoInfo, toUserContentId: ToUserContentIdFn) { @@ -220,13 +220,13 @@ function resolveGitLinkText(href: string, label: string, repoInfo: MarkdownRepoI case href.startsWith(repoInfo.commitBaseUrl): { return lastSegment.slice(0, 7) // only show the first 6 letters/numbers of a commit } - case href.startsWith(repoInfo.issueBaseUrl): { + case !!repoInfo.issueBaseUrl && href.startsWith(repoInfo.issueBaseUrl): { return `${repoInfo.issueChar}${lastSegment}` } case href.startsWith(repoInfo.prBaseUrl): { return `${repoInfo.prChar}${lastSegment}` } - case href.startsWith(repoInfo.compareBaseUrl): { + case !!repoInfo.compareBaseUrl && href.startsWith(repoInfo.compareBaseUrl): { return lastSegment } // for account we don't resolve, this is something the git providers also don't do @@ -256,16 +256,19 @@ function createResolveGitTextToLinks(mdInfo: MarkdownRepoInfo): IOptions['textFi return `${match.slice(0, 7)}` }) - .replace(issuePrRegexes[mdInfo.issueChar], match => { - const id = match.replace(mdInfo.issueChar, '') - return `${match}` - }) // account .replace(accountRegex, match => { const acc = match.replace('@', '') return `${match}` }) + if (mdInfo.issueChar && mdInfo.issueBaseUrl) { + text = text.replace(issuePrRegexes[mdInfo.issueChar], match => { + const id = match.replace(mdInfo.issueChar!, '') + return `${match}` + }) + } + // pr/mr if (mdInfo.issueChar != mdInfo.prChar) { text = text.replace(issuePrRegexes[mdInfo.prChar], match => { diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index ece9875ccf..2681d9bdaf 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -100,3 +100,21 @@ export function createGiteaRepoInfo( compareBaseUrl: `${hostBaseUrl}/${owner}/${repo}/compare`, } } + +export function createBitbucketRepoInfo( + owner: string, + repo: string, + path?: string, +): MarkdownRepoInfo { + const hostBaseUrl = 'https://bitbucket.org' + return { + hostBaseUrl, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/src/HEAD`, + rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/HEAD`, + path, + commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commits`, + prChar: '#', + prBaseUrl: `${hostBaseUrl}/${owner}/${repo}/pull-requests`, + // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different + } +} From cc30bd03e710472dc5fffc31de18de264bb959ea Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 20:39:04 +0200 Subject: [PATCH 04/13] added an timeout & added fallback for if gitea host isn't defined --- .../changelog/md/[provider]/[owner]/[repo]/[...path].get.ts | 1 + .../api/changelog/releases/[provider]/[owner]/[repo].get.ts | 6 ++++++ .../releases/[provider]/[owner]/[repo]/raw/[tag].get.ts | 1 + server/utils/changelog/baseFileUrl.ts | 5 +++-- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index 56bb1f112e..1f7edb0e3c 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -58,6 +58,7 @@ export default defineCachedEventHandler( headers: { 'User-Agent': 'npmx.dev', }, + timeout: 15_000, }) const markdown = v.parse(v.string(), data) if (raw != undefined) { diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts index cac921157f..87b19ce128 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts @@ -19,6 +19,8 @@ import { } from '~~/server/utils/changelog/mdRepoInfo' import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost' +const TIMEOUT_TIME = 15_000 + export default defineCachedEventHandler( async event => { const provider = getRouterParam(event, 'provider') as ProviderId @@ -86,6 +88,7 @@ async function getReleasesFromGithub(owner: string, repo: string) { 'Accept': '*/*', 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const { releases } = v.parse(GithubReleaseCollectionSchama, data) @@ -114,6 +117,7 @@ async function getReleasesFromForgejo(owner: string, repo: string, host: string) headers: { 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const releases = v.parse(ForgejoReleaseCollectionSchema, data) @@ -144,6 +148,7 @@ async function getReleasesFromGitlab(owner: string, repo: string, host: string) headers: { 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const releases = v.parse(GitlabReleaseCollectionSchema, data) @@ -171,6 +176,7 @@ async function getReleasesFromGitea(owner: string, repo: string, host: string) { headers: { 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const releases = v.parse(GiteaReleaseCollectionSchema, data) diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts index a09079a1a8..6f3cf9f3b7 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts @@ -84,6 +84,7 @@ async function getMarkdownFromGithub(owner: string, repo: string, tag: string, e 'User-Agent': 'npmx.dev', }, ignoreResponseError: true, + timeout: 15_000, }, ) diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index aa0b771ca9..bd321ab81f 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -23,9 +23,10 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { } case 'gitea': { // although similar to forgejo, we keep these seperate because they're seperately maintained projects + const host = ref.host ?? 'gitea.com' return { - blob: `https://${ref.host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, - raw: `https://${ref.host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, + blob: `https://${host}/${ref.owner}/${ref.repo}/src/branch/HEAD`, + raw: `https://${host}/${ref.owner}/${ref.repo}/raw/branch/HEAD`, } } case 'gitlab': { From d04de2aa4f288e144f4c350cda0c85cf20bf57a1 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 20:54:33 +0200 Subject: [PATCH 05/13] adding more timeout when trying to retrieve markdown from releases --- .../releases/[provider]/[owner]/[repo]/raw/[tag].get.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts index 6f3cf9f3b7..94df96b8c7 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts @@ -8,6 +8,8 @@ import { } from '~~/shared/schemas/changelog/release' import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost' +const TIMEOUT_TIME = 15_000 + export default defineCachedEventHandler( async event => { const provider = getRouterParam(event, 'provider') as ProviderId @@ -84,7 +86,7 @@ async function getMarkdownFromGithub(owner: string, repo: string, tag: string, e 'User-Agent': 'npmx.dev', }, ignoreResponseError: true, - timeout: 15_000, + timeout: TIMEOUT_TIME, }, ) @@ -102,6 +104,7 @@ async function getMarkdownFromGithub(owner: string, repo: string, tag: string, e 'Accept': '*/*', 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const { releases } = v.parse(GithubReleaseCollectionSchama, responseUngh) @@ -130,6 +133,7 @@ async function getMarkdownFromForgejo( headers: { 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const release = v.parse(ForgejoReleaseSchama, data) @@ -151,6 +155,7 @@ async function getMarkdownFromGitlab( headers: { 'User-Agent': 'npmx.dev', }, + timeout: TIMEOUT_TIME, }) const release = v.parse(GitlabReleaseSchame, data) From 36c408ffd3e721841fef09ee2be26d141bd3971a Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 2 Sep 2026 21:38:58 +0200 Subject: [PATCH 06/13] handling timeout --- .../releases/[provider]/[owner]/[repo]/raw/[tag].get.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts index 94df96b8c7..98bb2a25a9 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts @@ -88,7 +88,7 @@ async function getMarkdownFromGithub(owner: string, repo: string, tag: string, e ignoreResponseError: true, timeout: TIMEOUT_TIME, }, - ) + ).catch(() => null) const parsed = v.safeParse(v.object({ body: v.string() }), responseGithub) From be0c0bacc162758866026286ecdca8e080045340 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 9 Sep 2026 14:49:08 +0200 Subject: [PATCH 07/13] added support for sourcehut --- .../[provider]/[owner]/[repo]/[...path].get.ts | 3 +++ server/utils/changelog/baseFileUrl.ts | 6 ++++++ server/utils/changelog/markdown.ts | 16 +++++++++------- server/utils/changelog/mdRepoInfo.ts | 16 ++++++++++++++++ 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index 1f7edb0e3c..caac1ec4c8 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -7,6 +7,7 @@ import { createGiteaRepoInfo, createGithubRepoInfo, createGitLabRepoInfo, + createSourcehutRepoInfo, createTangledInfo, } from '~~/server/utils/changelog/mdRepoInfo' import { validateHostWithValibot } from '~~/server/utils/changelog/validateHost' @@ -121,5 +122,7 @@ function getRepoInfo( return createGiteaRepoInfo(host ?? 'gitea.com', owner, repo, path) case 'bitbucket': return createBitbucketRepoInfo(owner, repo, path) + case 'sourcehut': + return createSourcehutRepoInfo(owner, repo, path) } } diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index bd321ab81f..540f44c9b5 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -48,6 +48,12 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { raw: `https://bitbucket.org/${ref.owner}/${ref.repo}/raw/HEAD`, } } + case 'sourcehut': { + return { + blob: `https://git.sr.ht/${ref.owner}/${ref.repo}/tree/HEAD/item`, + raw: `https://git.sr.ht/${ref.owner}/${ref.repo}/blob/HEAD`, + } + } } return null } diff --git a/server/utils/changelog/markdown.ts b/server/utils/changelog/markdown.ts index 4edd42b82c..f3e715384a 100644 --- a/server/utils/changelog/markdown.ts +++ b/server/utils/changelog/markdown.ts @@ -122,13 +122,13 @@ export interface MarkdownRepoInfo { /** the text char that indicates an issue */ issueChar?: keyof typeof issuePrRegexes /** base url for a repository pull/merge request */ - prBaseUrl: string + prBaseUrl?: string /** * the text char that indicates a pull/merge request * * if it's the same as issueChar, than links will be parsed as issues and repo host is reponsible to redirect to pull/merge request x*/ - prChar: keyof typeof issuePrRegexes + prChar?: keyof typeof issuePrRegexes /** base url for a repository compare */ compareBaseUrl?: string } @@ -220,10 +220,12 @@ function resolveGitLinkText(href: string, label: string, repoInfo: MarkdownRepoI case href.startsWith(repoInfo.commitBaseUrl): { return lastSegment.slice(0, 7) // only show the first 6 letters/numbers of a commit } - case !!repoInfo.issueBaseUrl && href.startsWith(repoInfo.issueBaseUrl): { + case !!repoInfo.issueChar && + !!repoInfo.issueBaseUrl && + href.startsWith(repoInfo.issueBaseUrl): { return `${repoInfo.issueChar}${lastSegment}` } - case href.startsWith(repoInfo.prBaseUrl): { + case !!repoInfo.prChar && !!repoInfo.prBaseUrl && href.startsWith(repoInfo.prBaseUrl): { return `${repoInfo.prChar}${lastSegment}` } case !!repoInfo.compareBaseUrl && href.startsWith(repoInfo.compareBaseUrl): { @@ -270,10 +272,10 @@ function createResolveGitTextToLinks(mdInfo: MarkdownRepoInfo): IOptions['textFi } // pr/mr - if (mdInfo.issueChar != mdInfo.prChar) { + if (mdInfo.issueChar != mdInfo.prChar && mdInfo.prChar && mdInfo.prBaseUrl) { text = text.replace(issuePrRegexes[mdInfo.prChar], match => { - const id = match.replace(mdInfo.prChar, '') - return `${match}` + const id = match.replace(mdInfo.prChar!, '') + return `${match}` }) } diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index 2681d9bdaf..ab3b71a9f1 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -118,3 +118,19 @@ export function createBitbucketRepoInfo( // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different } } + +export function createSourcehutRepoInfo( + owner: string, + repo: string, + path?: string, +): MarkdownRepoInfo { + const hostBaseUrl = 'https://git.sr.ht' + return { + hostBaseUrl, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/src/HEAD`, + rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/HEAD`, + path, + commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, + // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different + } +} From dcb22eb6cc113d6d1cf10e0ac71fb16e3a96779a Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 9 Sep 2026 15:00:00 +0200 Subject: [PATCH 08/13] fixed forgotten base urls for sourcehut --- server/utils/changelog/mdRepoInfo.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index ab3b71a9f1..0ea01977aa 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -127,8 +127,8 @@ export function createSourcehutRepoInfo( const hostBaseUrl = 'https://git.sr.ht' return { hostBaseUrl, - blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/src/HEAD`, - rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/HEAD`, + blobBaseUrl: `https://git.sr.ht/${owner}/${repo}/tree/HEAD/item`, + rawBaseUrl: `https://git.sr.ht/${owner}/${repo}/blob/HEAD`, path, commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different From f80b04110ffb149df287a2383a53a91d09bba9ba Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 9 Sep 2026 15:14:15 +0200 Subject: [PATCH 09/13] .. --- server/utils/changelog/mdRepoInfo.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index 0ea01977aa..539b2580c9 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -127,8 +127,8 @@ export function createSourcehutRepoInfo( const hostBaseUrl = 'https://git.sr.ht' return { hostBaseUrl, - blobBaseUrl: `https://git.sr.ht/${owner}/${repo}/tree/HEAD/item`, - rawBaseUrl: `https://git.sr.ht/${owner}/${repo}/blob/HEAD`, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/tree/HEAD/item`, + rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/blob/HEAD`, path, commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different From 3b3e2ce0bbd0a6f4967114fa73e345d0531cdceb Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Wed, 9 Sep 2026 15:18:34 +0200 Subject: [PATCH 10/13] .. --- server/utils/changelog/mdRepoInfo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index 539b2580c9..a1cd540a4f 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -131,6 +131,6 @@ export function createSourcehutRepoInfo( rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/blob/HEAD`, path, commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, - // bitbucket uses jira instead of normal issues which can't be resolved, further the compare page also seems to be different + // source hut doesn't have/support issues,pr & compare } } From 444487b370dc3fe12007df7a5887e0eadd4a418e Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Sat, 12 Sep 2026 19:46:41 +0200 Subject: [PATCH 11/13] added support for gitee --- .../[owner]/[repo]/[...path].get.ts | 3 + .../releases/[provider]/[owner]/[repo].get.ts | 33 ++++++++++ server/utils/changelog/baseFileUrl.ts | 6 ++ server/utils/changelog/detectChangelog.ts | 66 +++++++++++++++++++ server/utils/changelog/markdown.ts | 6 +- server/utils/changelog/mdRepoInfo.ts | 17 +++++ shared/schemas/changelog/release.ts | 11 ++++ .../server/utils/changelog/markdown.spec.ts | 43 +++++++++++- 8 files changed, 182 insertions(+), 3 deletions(-) diff --git a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts index caac1ec4c8..07174f2518 100644 --- a/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts +++ b/server/api/changelog/md/[provider]/[owner]/[repo]/[...path].get.ts @@ -5,6 +5,7 @@ import { createBitbucketRepoInfo, createForgejoRepoInfo, createGiteaRepoInfo, + createGiteeRepoInfo, createGithubRepoInfo, createGitLabRepoInfo, createSourcehutRepoInfo, @@ -124,5 +125,7 @@ function getRepoInfo( return createBitbucketRepoInfo(owner, repo, path) case 'sourcehut': return createSourcehutRepoInfo(owner, repo, path) + case 'gitee': + return createGiteeRepoInfo(owner, repo, path) } } diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts index 87b19ce128..9737803d41 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo].get.ts @@ -10,6 +10,7 @@ import { ForgejoReleaseCollectionSchema, GitlabReleaseCollectionSchema, GiteaReleaseCollectionSchema, + GiteeReleaseCollectionSchema, } from '~~/shared/schemas/changelog/release' import { changelogRenderer } from '~~/server/utils/changelog/markdown' import { @@ -48,6 +49,8 @@ export default defineCachedEventHandler( return await getReleasesFromGitlab(owner, repo, host) case 'gitea': return await getReleasesFromGitea(owner, repo, host) + case 'gitee': + return await getReleasesFromGitee(owner, repo) default: throw createError({ @@ -197,3 +200,33 @@ async function getReleasesFromGitea(owner: string, repo: string, host: string) { } satisfies ReleaseData }) } + +async function getReleasesFromGitee(owner: string, repo: string) { + const data = await $fetch( + `https://gitee.com/api/v5/repos/${owner}/${repo}/releases?per_page=30`, + { + headers: { + 'User-Agent': 'npmx.dev', + }, + timeout: TIMEOUT_TIME, + }, + ) + + const releases = v.parse(GiteeReleaseCollectionSchema, data) + + const render = await changelogRenderer(createGiteeRepoInfo(owner, repo)) + + return releases.map(r => { + const { html, toc } = render(r.body, r.id) + return { + id: r.id, + html: html?.replace(/(?)\n/g, '
') ?? null, + title: r.name || r.tag_name, + prerelease: r.prerelease, + toc, + link: `https://gitee.com/${owner}/${repo}/releases/tag/${r.tag_name}`, + publishedAt: r.created_at, + tag: r.tag_name, + } satisfies ReleaseData + }) +} diff --git a/server/utils/changelog/baseFileUrl.ts b/server/utils/changelog/baseFileUrl.ts index 540f44c9b5..8643dc3e3e 100644 --- a/server/utils/changelog/baseFileUrl.ts +++ b/server/utils/changelog/baseFileUrl.ts @@ -54,6 +54,12 @@ export function getBaseFileUrl(ref: RepoRef): RepoFileUrl | null { raw: `https://git.sr.ht/${ref.owner}/${ref.repo}/blob/HEAD`, } } + case 'gitee': { + return { + blob: `https://gitee.com/${ref.owner}/${ref.repo}/blob/main`, + raw: `https://gitee.com/${ref.owner}/${ref.repo}/raw/main`, + } + } } return null } diff --git a/server/utils/changelog/detectChangelog.ts b/server/utils/changelog/detectChangelog.ts index 1d04fd86df..f7e3be71b1 100644 --- a/server/utils/changelog/detectChangelog.ts +++ b/server/utils/changelog/detectChangelog.ts @@ -9,6 +9,7 @@ import { ForgejoReleaseSchama, GitlabReleaseSchame, GiteaReleaseSchema, + GiteeReleaseSchema, } from '~~/shared/schemas/changelog/release' import { resolveURL } from 'ufo' import * as v from 'valibot' @@ -70,6 +71,9 @@ async function checkReleases( case 'gitea': { return checkLatestGiteaRelease(ref, directory) } + case 'gitee': { + return checkLatestGiteeRelease(ref, directory) + } } return [false, null] } @@ -409,3 +413,65 @@ async function checkLatestGiteaRelease( } return [false, null] } + +async function checkLatestGiteeRelease( + ref: RepoRef, + directory?: string, +): Promise> { + try { + const response = await $fetch( + `https://gitee.com/api/v5/repos/${ref.owner}/${ref.repo}/releases/latest`, + { + headers: { + 'User-Agent': 'npmx.dev', + 'accept': 'application/json', + }, + }, + ) + + const release = v.parse(GiteeReleaseSchema, response) + + const matchedChangelog = release.body?.match(MD_REGEX)?.at(0) + + // if no changelog.md or the url doesn't contain /blob/ + if (!matchedChangelog || !matchedChangelog.includes('/blob/')) { + return [ + { + provider: ref.provider, + type: 'release', + repo: `${ref.owner}/${ref.repo}`, + link: `https://gitee.com/${ref.owner}/${ref.repo}/releases`, + }, + null, + ] + } + + const path = matchedChangelog.replace(/^.*\/blob\/[^/]+\//i, '') + + // makes sure that the correct directory is matched + if ( + directory && + !( + path.startsWith(directory.endsWith('/') ? directory : `${directory}/`) || + ROOT_ONLY_REGEX.test(path) + ) + ) { + return [false, null] + } + return [ + { + provider: ref.provider, + type: 'md', + path, + repo: `${ref.owner}/${ref.repo}`, + link: matchedChangelog, + }, + null, + ] + } catch (e) { + if (e instanceof Error) { + return [null, e] + } + } + return [false, null] +} diff --git a/server/utils/changelog/markdown.ts b/server/utils/changelog/markdown.ts index f3e715384a..43ca44b870 100644 --- a/server/utils/changelog/markdown.ts +++ b/server/utils/changelog/markdown.ts @@ -121,6 +121,8 @@ export interface MarkdownRepoInfo { issueBaseUrl?: string /** the text char that indicates an issue */ issueChar?: keyof typeof issuePrRegexes + /** custom regex in case the git provider's issues uses a different format */ + issueRegex?: RegExp /** base url for a repository pull/merge request */ prBaseUrl?: string /** @@ -218,7 +220,7 @@ function resolveGitLinkText(href: string, label: string, repoInfo: MarkdownRepoI switch (true) { case href.startsWith(repoInfo.commitBaseUrl): { - return lastSegment.slice(0, 7) // only show the first 6 letters/numbers of a commit + return lastSegment.slice(0, 7) // only show the first 7 letters/numbers of a commit } case !!repoInfo.issueChar && !!repoInfo.issueBaseUrl && @@ -265,7 +267,7 @@ function createResolveGitTextToLinks(mdInfo: MarkdownRepoInfo): IOptions['textFi }) if (mdInfo.issueChar && mdInfo.issueBaseUrl) { - text = text.replace(issuePrRegexes[mdInfo.issueChar], match => { + text = text.replace(mdInfo.issueRegex ?? issuePrRegexes[mdInfo.issueChar], match => { const id = match.replace(mdInfo.issueChar!, '') return `${match}` }) diff --git a/server/utils/changelog/mdRepoInfo.ts b/server/utils/changelog/mdRepoInfo.ts index a1cd540a4f..fec175da4f 100644 --- a/server/utils/changelog/mdRepoInfo.ts +++ b/server/utils/changelog/mdRepoInfo.ts @@ -134,3 +134,20 @@ export function createSourcehutRepoInfo( // source hut doesn't have/support issues,pr & compare } } + +export function createGiteeRepoInfo(owner: string, repo: string, path?: string): MarkdownRepoInfo { + const hostBaseUrl = `https://gitee.com` + return { + hostBaseUrl, + blobBaseUrl: `${hostBaseUrl}/${owner}/${repo}/blob/HREAD`, + rawBaseUrl: `${hostBaseUrl}/${owner}/${repo}/raw/HEAD`, + path, + commitBaseUrl: `${hostBaseUrl}/${owner}/${repo}/commit`, + issueChar: '#', + issueBaseUrl: `${hostBaseUrl}/${owner}/${repo}/issues`, + issueRegex: /\B#[\dA-Z]+\b/g, + prChar: '!', + prBaseUrl: `${hostBaseUrl}/${owner}/${repo}/pulls`, + compareBaseUrl: `${hostBaseUrl}/${owner}/${repo}/compare`, + } +} diff --git a/shared/schemas/changelog/release.ts b/shared/schemas/changelog/release.ts index 8734395b3c..f6915855ca 100644 --- a/shared/schemas/changelog/release.ts +++ b/shared/schemas/changelog/release.ts @@ -59,3 +59,14 @@ export const GiteaReleaseSchema = v.object({ }) export const GiteaReleaseCollectionSchema = v.array(GiteaReleaseSchema) + +export const GiteeReleaseSchema = v.object({ + id: v.number(), + tag_name: v.string(), + name: v.string(), + body: v.string(), + prerelease: v.boolean(), + created_at: v.pipe(v.string(), v.isoTimestamp()), +}) + +export const GiteeReleaseCollectionSchema = v.array(GiteeReleaseSchema) diff --git a/test/unit/server/utils/changelog/markdown.spec.ts b/test/unit/server/utils/changelog/markdown.spec.ts index 55f841b1b7..2b4aed5f07 100644 --- a/test/unit/server/utils/changelog/markdown.spec.ts +++ b/test/unit/server/utils/changelog/markdown.spec.ts @@ -1,6 +1,10 @@ import type { MarkdownRepoInfo } from '~~/server/utils/changelog/markdown' import { describe, expect, it, vi, beforeAll } from 'vitest' -import { createGithubRepoInfo, createGitLabRepoInfo } from '~~/server/utils/changelog/mdRepoInfo' +import { + createGithubRepoInfo, + createGitLabRepoInfo, + createGiteeRepoInfo, +} from '~~/server/utils/changelog/mdRepoInfo' // testing changelog specific needs, others things are tested at ../readme.spec.ts @@ -729,6 +733,43 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `, ) }) + + // gitee + describe('gitee issue formatting', () => { + it('should turn issue into formatted links', async () => { + const info = createGiteeRepoInfo('test-owner', 'test-repo') + const renderer = await changelogRenderer(info) + // text from date-fns v4.3.0 + const markdown = `- Fixed pt locale first day of week to be Sunday. See #IKF9K6 +- Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #I9T5LW +` + const result = renderer(markdown) + + expect(result.html).toBe(`
    +
  • Fixed pt locale first day of week to be Sunday. See #IKF9K6
  • +
  • Fixed zh-CN, zh-HK, and zh-TW locale month parsing for October, November, and December. See #I9T5LW
  • +
+`) + }) + + it('should turn issue/pr into links between ()', async () => { + const info = createGiteeRepoInfo('test-owner', 'test-repo') + const renderer = await changelogRenderer(info) + // text comes from npmx release 0.15.0 + const markdown = `- Minor ui improvements (#IKF9K6) +- deps: Update module-replacements (#I9T5LW) +- Release v0.15.0 (#IKEIK1)` + + const result = renderer(markdown) + + expect(result.html).toBe(`
    +
  • Minor ui improvements (#IKF9K6)
  • +
  • deps: Update module-replacements (#I9T5LW)
  • +
  • Release v0.15.0 (#IKEIK1)
  • +
+`) + }) + }) }) describe('format unformatted/auto links to git', () => { From 29e36c69cda30d49cd027c3d373c498b86d81bdf Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Sat, 12 Sep 2026 19:58:28 +0200 Subject: [PATCH 12/13] added support for getting markdown from gitea & gitee --- .../[owner]/[repo]/raw/[tag].get.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts index 98bb2a25a9..4b623d2124 100644 --- a/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts +++ b/server/api/changelog/releases/[provider]/[owner]/[repo]/raw/[tag].get.ts @@ -3,6 +3,8 @@ import * as v from 'valibot' import { ERROR_THROW_INCOMPLETE_PARAM } from '~~/shared/utils/constants' import { ForgejoReleaseSchama, + GiteaReleaseSchema, + GiteeReleaseSchema, GithubReleaseCollectionSchama, GitlabReleaseSchame, } from '~~/shared/schemas/changelog/release' @@ -42,6 +44,10 @@ export default defineCachedEventHandler( return await getMarkdownFromForgejo(owner, repo, encodedTag, host) case 'gitlab': return await getMarkdownFromGitlab(owner, repo, encodedTag, host) + case 'gitea': + return await getMarkdownFromGitea(owner, repo, encodedTag, host) + case 'gitee': + return await getMarkdownFromGitee(owner, repo, encodedTag) default: throw createError({ status: 404, @@ -162,3 +168,43 @@ async function getMarkdownFromGitlab( return release.description } + +async function getMarkdownFromGitea( + owner: string, + repo: string, + /** tag should be encoded */ + tag: string, + host: string = 'gitea.com', +) { + const data = await $fetch(`https://${host}/api/v1/repos/${owner}/${repo}/releases/tags/${tag}`, { + headers: { + 'User-Agent': 'npmx.dev', + }, + timeout: TIMEOUT_TIME, + }) + + const release = v.parse(GiteaReleaseSchema, data) + + return release.body +} + +async function getMarkdownFromGitee( + owner: string, + repo: string, + /** tag should be encoded */ + tag: string, +) { + const data = await $fetch( + `https://gitee.com/api/v5/repos/${owner}/${repo}/releases/tags/${tag}`, + { + headers: { + 'User-Agent': 'npmx.dev', + }, + timeout: TIMEOUT_TIME, + }, + ) + + const release = v.parse(GiteeReleaseSchema, data) + + return release.body +} From ca52fec74f975ce7475a0ea6ca9f5e9e01882123 Mon Sep 17 00:00:00 2001 From: WilcoSp Date: Sat, 12 Sep 2026 20:12:31 +0200 Subject: [PATCH 13/13] .. --- test/unit/server/utils/changelog/markdown.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/server/utils/changelog/markdown.spec.ts b/test/unit/server/utils/changelog/markdown.spec.ts index 2b4aed5f07..cc9e6ca1c4 100644 --- a/test/unit/server/utils/changelog/markdown.spec.ts +++ b/test/unit/server/utils/changelog/markdown.spec.ts @@ -752,7 +752,7 @@ describe('Turn plaintext #isssue/#pr, !pr, @account & commmit into links', () => `) }) - it('should turn issue/pr into links between ()', async () => { + it('should turn issue into links between ()', async () => { const info = createGiteeRepoInfo('test-owner', 'test-repo') const renderer = await changelogRenderer(info) // text comes from npmx release 0.15.0