From 41b7368841fa94d2c1c66ddb302a0f71752e7355 Mon Sep 17 00:00:00 2001 From: Nigel George Date: Mon, 4 Dec 2023 07:02:30 +0000 Subject: [PATCH 1/9] add gitea service based on gitlab --- core/server/server.js | 2 + doc/server-secrets.md | 9 ++ services/gitea/gitea-base.js | 32 ++++ services/gitea/gitea-helper.js | 11 ++ .../gitea/gitea-languages-count.service.js | 80 ++++++++++ .../gitea/gitea-languages-count.tester.js | 34 +++++ services/gitea/gitea-release.service.js | 138 ++++++++++++++++++ 7 files changed, 306 insertions(+) create mode 100644 services/gitea/gitea-base.js create mode 100644 services/gitea/gitea-helper.js create mode 100644 services/gitea/gitea-languages-count.service.js create mode 100644 services/gitea/gitea-languages-count.tester.js create mode 100644 services/gitea/gitea-release.service.js diff --git a/core/server/server.js b/core/server/server.js index 8f92e4fd28fd9..fb3c5ef090d69 100644 --- a/core/server/server.js +++ b/core/server/server.js @@ -128,6 +128,7 @@ const publicConfigSchema = Joi.object({ }, restApiVersion: Joi.date().raw().required(), }, + gitea: defaultService, gitlab: defaultService, jira: defaultService, jenkins: Joi.object({ @@ -168,6 +169,7 @@ const privateConfigSchema = Joi.object({ gh_client_id: Joi.string(), gh_client_secret: Joi.string(), gh_token: Joi.string(), + gitea_token: Joi.string(), gitlab_token: Joi.string(), jenkins_user: Joi.string(), jenkins_pass: Joi.string(), diff --git a/doc/server-secrets.md b/doc/server-secrets.md index 1bacce44d6c06..13447e59a2242 100644 --- a/doc/server-secrets.md +++ b/doc/server-secrets.md @@ -167,6 +167,15 @@ These settings are used by shields.io for GitHub OAuth app authorization but will not be necessary for most self-hosted installations. See [production-hosting.md](./production-hosting.md). +### Gitea + +- `GITEA_ORIGINS` (yml: `public.services.gitea.authorizedOrigins`) +- `GITEA_TOKEN` (yml: `private.gitea_token`) + +A Gitea [Personal Access Token][gitea-pat] is required for accessing private content. If you need a Gitea token for your self-hosted Shields server then we recommend limiting the scopes to the minimal set necessary for the badges you are using. + +[gitea-pat]: https://docs.gitea.com/development/api-usage#generating-and-listing-api-tokens + ### GitLab - `GITLAB_ORIGINS` (yml: `public.services.gitlab.authorizedOrigins`) diff --git a/services/gitea/gitea-base.js b/services/gitea/gitea-base.js new file mode 100644 index 0000000000000..071a08e66ac35 --- /dev/null +++ b/services/gitea/gitea-base.js @@ -0,0 +1,32 @@ +import { BaseJsonService } from '../index.js' + +export default class GiteaBase extends BaseJsonService { + static auth = { + passKey: 'gitea_token', + serviceKey: 'gitea', + } + + async fetch({ url, options, schema, httpErrors }) { + return this._requestJson( + this.authHelper.withBearerAuthHeader({ + schema, + url, + options, + httpErrors, + }), + ) + } + + async fetchPage({ page, requestParams, schema }) { + const { res, buffer } = await this._request( + this.authHelper.withBearerAuthHeader({ + ...requestParams, + ...{ options: { searchParams: { page } } }, + }), + ) + + const json = this._parseJson(buffer) + const data = this.constructor._validate(json, schema) + return { res, data } + } +} diff --git a/services/gitea/gitea-helper.js b/services/gitea/gitea-helper.js new file mode 100644 index 0000000000000..e7f5461f766e6 --- /dev/null +++ b/services/gitea/gitea-helper.js @@ -0,0 +1,11 @@ +const documentation = ` +Note that the gitea_url parameter is required because there is no gitea hosted service by Gitea themselves.` + +function httpErrorsFor(notFoundMessage = 'project not found') { + return { + 401: notFoundMessage, + 404: notFoundMessage, + } +} + +export { documentation, httpErrorsFor } diff --git a/services/gitea/gitea-languages-count.service.js b/services/gitea/gitea-languages-count.service.js new file mode 100644 index 0000000000000..5a686b6aa6cbf --- /dev/null +++ b/services/gitea/gitea-languages-count.service.js @@ -0,0 +1,80 @@ +import Joi from 'joi' +import { nonNegativeInteger, optionalUrl } from '../validators.js' +import { metric } from '../text-formatters.js' +import { pathParam, queryParam } from '../index.js' +import { documentation, httpErrorsFor } from './gitea-helper.js' +import GiteaBase from './gitea-base.js' + +/* +We're expecting a response like { "Python": 39624, "Shell": 104 } +The keys could be anything and {} is a valid response (e.g: for an empty repo) +*/ +const schema = Joi.object().pattern(/./, nonNegativeInteger) + +const queryParamSchema = Joi.object({ + gitea_url: optionalUrl, +}).required() + +export default class GiteaLanguageCount extends GiteaBase { + static category = 'analysis' + + static route = { + base: 'gitea/languages/count', + pattern: ':user/:repo', + queryParamSchema, + } + + static openApi = { + '/gitea/languages/count/{user}/{repo}': { + get: { + summary: 'Gitea language count', + description: documentation, + parameters: [ + pathParam({ + name: 'user', + example: 'go-gitea', + }), + pathParam({ + name: 'repo', + example: 'gitea', + }), + queryParam({ + name: 'gitea_url', + example: 'https://try.gitea.io', + required: true, + }), + ], + }, + }, + } + + static defaultBadgeData = { label: 'languages' } + + static render({ languagesCount }) { + return { + message: metric(languagesCount), + color: 'blue', + } + } + + async fetch({ user, repo, baseUrl }) { + // https://try.gitea.io/api/swagger#/repository/repoGetLanguages + return super.fetch({ + schema, + url: `${baseUrl}/api/v1/repos/${user}/${repo}/languages`, + httpErrors: httpErrorsFor('repo not found'), + }) + } + + async handle( + { user, repo }, + { gitea_url: baseUrl = 'https://try.gitea.io' }, + ) { + const data = await this.fetch({ + user, + repo, + baseUrl, + }) + return this.constructor.render({ languagesCount: Object.keys(data).length }) + } +} diff --git a/services/gitea/gitea-languages-count.tester.js b/services/gitea/gitea-languages-count.tester.js new file mode 100644 index 0000000000000..a9506ba5ab6b3 --- /dev/null +++ b/services/gitea/gitea-languages-count.tester.js @@ -0,0 +1,34 @@ +import Joi from 'joi' +import { createServiceTester } from '../tester.js' + +export const t = await createServiceTester() + +t.create('language count (self-managed)') + .get('/hansen6878/test.json?gitea_url=https://try.gitea.io') + .expectBadge({ + label: 'languages', + message: '0', + }) + +t.create('language count (empty repo)') + .get('/hansen6878/test.json') + .expectBadge({ + label: 'languages', + message: '0', + }) + +t.create('language count (repo not found)') + .get('/open/do-not-exist.json') + .expectBadge({ + label: 'languages', + message: 'repo not found', + }) + +t.create('language count').get('/Bastrabun/bogus-mirror.json').expectBadge({ + label: 'languages', + message: Joi.number().integer().positive(), +}) + +t.create('language count (repo not found)') + .get('/badges/helmets.json') + .expectBadge({ label: 'languages', message: 'repo not found' }) diff --git a/services/gitea/gitea-release.service.js b/services/gitea/gitea-release.service.js new file mode 100644 index 0000000000000..28bae7b703e5f --- /dev/null +++ b/services/gitea/gitea-release.service.js @@ -0,0 +1,138 @@ +import Joi from 'joi' +import { optionalUrl } from '../validators.js' +import { latest, renderVersionBadge } from '../version.js' +import { NotFound, pathParam, queryParam } from '../index.js' +import { documentation, httpErrorsFor } from './gitea-helper.js' +import GiteaBase from './gitea-base.js' + +const schema = Joi.array().items( + Joi.object({ + name: Joi.string().required(), + tag_name: Joi.string().required(), + }), +) + +const sortEnum = ['date', 'semver'] +const displayNameEnum = ['tag', 'release'] +const dateOrderByEnum = ['created_at', 'released_at'] + +const queryParamSchema = Joi.object({ + gitea_url: optionalUrl, + include_prereleases: Joi.equal(''), + sort: Joi.string() + .valid(...sortEnum) + .default('date'), + display_name: Joi.string() + .valid(...displayNameEnum) + .default('tag'), + date_order_by: Joi.string() + .valid(...dateOrderByEnum) + .default('created_at'), +}).required() + +export default class GiteaRelease extends GiteaBase { + static category = 'version' + + static route = { + base: 'gitea/v/release', + pattern: ':user/:repo', + queryParamSchema, + } + + static openApi = { + '/gitea/v/release/{user}/{repo}': { + get: { + summary: 'Gitea Release', + description: documentation, + parameters: [ + pathParam({ + name: 'user', + example: 'go-gitea', + }), + pathParam({ + name: 'repo', + example: 'gitea', + }), + queryParam({ + name: 'gitea_url', + example: 'https://try.gitea.io', + required: true, + }), + queryParam({ + name: 'include_prereleases', + schema: { type: 'boolean' }, + example: null, + }), + queryParam({ + name: 'sort', + schema: { type: 'string', enum: sortEnum }, + example: 'semver', + }), + queryParam({ + name: 'display_name', + schema: { type: 'string', enum: displayNameEnum }, + example: 'release', + }), + queryParam({ + name: 'date_order_by', + schema: { type: 'string', enum: dateOrderByEnum }, + example: 'created_at', + }), + ], + }, + }, + } + + static defaultBadgeData = { label: 'release' } + + async fetch({ user, repo, baseUrl }) { + // https://try.gitea.io/api/swagger#/repository/repoGetRelease + return super.fetch({ + schema, + url: `${baseUrl}/api/v1/repos/${user}/${repo}/releases`, + httpErrors: httpErrorsFor('repo not found'), + }) + } + + static transform({ releases, isSemver, includePrereleases, displayName }) { + if (releases.length === 0) { + throw new NotFound({ prettyMessage: 'no releases found' }) + } + + const displayKey = displayName === 'tag' ? 'tag_name' : 'name' + + if (!isSemver) { + return releases[0][displayKey] + } + + return latest( + releases.map(t => t[displayKey]), + { pre: includePrereleases }, + ) + } + + async handle( + { user, repo }, + { + gitea_url: baseUrl = 'https://try.gitea.io', + include_prereleases: pre, + sort, + display_name: displayName, + date_order_by: orderBy, + }, + ) { + const isSemver = sort === 'semver' + const releases = await this.fetch({ + user, + repo, + baseUrl, + }) + const version = this.constructor.transform({ + releases, + isSemver, + includePrereleases: pre !== undefined, + displayName, + }) + return renderVersionBadge({ version }) + } +} From a931179f7e458c18b5ed50550405e319218bed93 Mon Sep 17 00:00:00 2001 From: Nigel George Date: Mon, 4 Dec 2023 12:10:05 +0000 Subject: [PATCH 2/9] update gitea to use mocks --- services/gitea/gitea-helper.js | 2 +- .../gitea/gitea-languages-count.service.js | 2 +- .../gitea/gitea-languages-count.tester.js | 44 ++++++++++++------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/services/gitea/gitea-helper.js b/services/gitea/gitea-helper.js index e7f5461f766e6..66536e3555bd5 100644 --- a/services/gitea/gitea-helper.js +++ b/services/gitea/gitea-helper.js @@ -1,7 +1,7 @@ const documentation = ` Note that the gitea_url parameter is required because there is no gitea hosted service by Gitea themselves.` -function httpErrorsFor(notFoundMessage = 'project not found') { +function httpErrorsFor(notFoundMessage = 'user or repo not found') { return { 401: notFoundMessage, 404: notFoundMessage, diff --git a/services/gitea/gitea-languages-count.service.js b/services/gitea/gitea-languages-count.service.js index 5a686b6aa6cbf..61e5ab10b611c 100644 --- a/services/gitea/gitea-languages-count.service.js +++ b/services/gitea/gitea-languages-count.service.js @@ -62,7 +62,7 @@ export default class GiteaLanguageCount extends GiteaBase { return super.fetch({ schema, url: `${baseUrl}/api/v1/repos/${user}/${repo}/languages`, - httpErrors: httpErrorsFor('repo not found'), + httpErrors: httpErrorsFor('user or repo not found'), }) } diff --git a/services/gitea/gitea-languages-count.tester.js b/services/gitea/gitea-languages-count.tester.js index a9506ba5ab6b3..f74695a7cbd4d 100644 --- a/services/gitea/gitea-languages-count.tester.js +++ b/services/gitea/gitea-languages-count.tester.js @@ -3,32 +3,42 @@ import { createServiceTester } from '../tester.js' export const t = await createServiceTester() -t.create('language count (self-managed)') - .get('/hansen6878/test.json?gitea_url=https://try.gitea.io') +t.create('language count (empty repo)') + .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com') + .intercept(nock => + nock('https://gitea.example.com/') + .get('/api/v1/repos/go-gitea/gitea/languages') + .reply(200, {}), + ) .expectBadge({ label: 'languages', message: '0', }) -t.create('language count (empty repo)') - .get('/hansen6878/test.json') +t.create('language count (self-managed)') + .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com') + .intercept(nock => + nock('https://gitea.example.com/') + .get('/api/v1/repos/go-gitea/gitea/languages') + .reply(200, { CPP: 500, SQL: 25 }), + ) .expectBadge({ label: 'languages', - message: '0', + message: Joi.number().integer().positive(), }) -t.create('language count (repo not found)') - .get('/open/do-not-exist.json') +t.create('language count (user or repo not found)') + .get('/open/do-not-exist.json?gitea_url=https://gitea.example.com') + .intercept(nock => + nock('https://gitea.example.com/') + .get('/api/v1/repos/open/do-not-exist/languages') + .reply(404, { + errors: ['user redirect does not exist [name: open]'], + message: 'GetUserByName', + url: 'https://gitea.example.com/api/swagger', + }), + ) .expectBadge({ label: 'languages', - message: 'repo not found', + message: 'user or repo not found', }) - -t.create('language count').get('/Bastrabun/bogus-mirror.json').expectBadge({ - label: 'languages', - message: Joi.number().integer().positive(), -}) - -t.create('language count (repo not found)') - .get('/badges/helmets.json') - .expectBadge({ label: 'languages', message: 'repo not found' }) From 2e2815496a004c6128935b85b913513d2b4477d4 Mon Sep 17 00:00:00 2001 From: Nigel George Date: Mon, 4 Dec 2023 13:21:44 +0000 Subject: [PATCH 3/9] add gitea release test --- services/gitea/gitea-release.service.js | 4 +- services/gitea/gitea-release.tester.js | 104 ++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 services/gitea/gitea-release.tester.js diff --git a/services/gitea/gitea-release.service.js b/services/gitea/gitea-release.service.js index 28bae7b703e5f..6184b4ff968dd 100644 --- a/services/gitea/gitea-release.service.js +++ b/services/gitea/gitea-release.service.js @@ -14,7 +14,7 @@ const schema = Joi.array().items( const sortEnum = ['date', 'semver'] const displayNameEnum = ['tag', 'release'] -const dateOrderByEnum = ['created_at', 'released_at'] +const dateOrderByEnum = ['created_at', 'published_at'] const queryParamSchema = Joi.object({ gitea_url: optionalUrl, @@ -90,7 +90,7 @@ export default class GiteaRelease extends GiteaBase { return super.fetch({ schema, url: `${baseUrl}/api/v1/repos/${user}/${repo}/releases`, - httpErrors: httpErrorsFor('repo not found'), + httpErrors: httpErrorsFor('user or repo not found'), }) } diff --git a/services/gitea/gitea-release.tester.js b/services/gitea/gitea-release.tester.js new file mode 100644 index 0000000000000..0eeaefe3b61bb --- /dev/null +++ b/services/gitea/gitea-release.tester.js @@ -0,0 +1,104 @@ +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() +const responseBody = [ + { + id: 3318489, + tag_name: 'v2.0.0', + name: 'v2.0.0', + draft: false, + prerelease: false, + created_at: '2021-09-15T01:06:43Z', + published_at: '2021-09-15T01:06:43Z', + }, + { + id: 3318488, + tag_name: 'v4.0.0', + name: 'v4.0.0', + draft: false, + prerelease: false, + created_at: '2021-09-15T01:05:43Z', + published_at: '2021-09-15T01:05:43Z', + }, + { + id: 3318487, + tag_name: 'v5.0.0-beta.1', + name: 'v5.0.0-beta.1', + draft: false, + prerelease: false, + created_at: '2021-09-11T23:53:07Z', + published_at: '2021-09-11T23:53:07Z', + }, +] + +t.create('Release (latest by date)') + .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com') + .intercept(nock => + nock('https://gitea.example.com/') + .get('/api/v1/repos/go-gitea/gitea/releases') + .reply(200, responseBody), + ) + .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' }) + +t.create('Release (latest by date, order by created_at)') + .get( + '/go-gitea/gitea.json?gitea_url=https://gitea.example.com&date_order_by=created_at', + ) + .intercept(nock => + nock('https://gitea.example.com/') + .get('/api/v1/repos/go-gitea/gitea/releases') + .reply(200, responseBody), + ) + .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' }) + +t.create('Release (latest by date, order by published_at)') + .get( + '/go-gitea/gitea.json?gitea_url=https://gitea.example.com&date_order_by=published_at', + ) + .intercept(nock => + nock('https://gitea.example.com/') + .get('/api/v1/repos/go-gitea/gitea/releases') + .reply(200, responseBody), + ) + .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' }) + +t.create('Release (latest by semver)') + .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com&sort=semver') + .intercept(nock => + nock('https://gitea.example.com/') + .get('/api/v1/repos/go-gitea/gitea/releases') + .reply(200, responseBody), + ) + .expectBadge({ label: 'release', message: 'v4.0.0', color: 'blue' }) + +t.create('Release (latest by semver pre-release)') + .get( + '/go-gitea/gitea.json?gitea_url=https://gitea.example.com&sort=semver&include_prereleases', + ) + .intercept(nock => + nock('https://gitea.example.com/') + .get('/api/v1/repos/go-gitea/gitea/releases') + .reply(200, responseBody), + ) + .expectBadge({ label: 'release', message: 'v5.0.0-beta.1', color: 'orange' }) + +t.create('Release (project not found)') + .get('/fdroid/nonexistant.json?gitea_url=https://gitea.example.com') + .intercept(nock => + nock('https://gitea.example.com/') + .get('/api/v1/repos/fdroid/nonexistant/releases') + .reply(404, { + errors: ['user redirect does not exist [name: fdroid]'], + message: 'GetUserByName', + url: 'https://gitea.example.com/api/swagger', + }), + ) + .expectBadge({ label: 'release', message: 'user or repo not found' }) + +t.create('Release (no tags)') + .get('/fdroid/fdroiddata.json?gitea_url=https://gitea.example.com') + .intercept(nock => + nock('https://gitea.example.com/') + .get('/api/v1/repos/fdroid/fdroiddata/releases') + .reply(200, []), + ) + .expectBadge({ label: 'release', message: 'no releases found' }) From e122619e3bdc7640532015d788dfadab9ee28674 Mon Sep 17 00:00:00 2001 From: Nigel George Date: Fri, 8 Dec 2023 08:48:41 +0000 Subject: [PATCH 4/9] move tests to use public repo on codeberg and fixes --- services/gitea/gitea-helper.js | 4 +- .../gitea/gitea-languages-count.service.js | 7 +- .../gitea/gitea-languages-count.tester.js | 25 ++-- services/gitea/gitea-release.service.js | 27 ++-- services/gitea/gitea-release.tester.js | 129 +++++++----------- 5 files changed, 78 insertions(+), 114 deletions(-) diff --git a/services/gitea/gitea-helper.js b/services/gitea/gitea-helper.js index 66536e3555bd5..60e5ed1b99ea0 100644 --- a/services/gitea/gitea-helper.js +++ b/services/gitea/gitea-helper.js @@ -1,5 +1,7 @@ const documentation = ` -Note that the gitea_url parameter is required because there is no gitea hosted service by Gitea themselves.` +Note that only internet-accessible Gitea compatible instances are supported, for example +[https://codeberg.org](https://codeberg.org). +` function httpErrorsFor(notFoundMessage = 'user or repo not found') { return { diff --git a/services/gitea/gitea-languages-count.service.js b/services/gitea/gitea-languages-count.service.js index 61e5ab10b611c..887144d3ba6bf 100644 --- a/services/gitea/gitea-languages-count.service.js +++ b/services/gitea/gitea-languages-count.service.js @@ -40,8 +40,7 @@ export default class GiteaLanguageCount extends GiteaBase { }), queryParam({ name: 'gitea_url', - example: 'https://try.gitea.io', - required: true, + example: 'https://codeberg.org', }), ], }, @@ -58,7 +57,7 @@ export default class GiteaLanguageCount extends GiteaBase { } async fetch({ user, repo, baseUrl }) { - // https://try.gitea.io/api/swagger#/repository/repoGetLanguages + // https://codeberg.org/api/swagger#/repository/repoGetLanguages return super.fetch({ schema, url: `${baseUrl}/api/v1/repos/${user}/${repo}/languages`, @@ -68,7 +67,7 @@ export default class GiteaLanguageCount extends GiteaBase { async handle( { user, repo }, - { gitea_url: baseUrl = 'https://try.gitea.io' }, + { gitea_url: baseUrl = 'https://codeberg.org' }, ) { const data = await this.fetch({ user, diff --git a/services/gitea/gitea-languages-count.tester.js b/services/gitea/gitea-languages-count.tester.js index f74695a7cbd4d..27cdf5cbdff00 100644 --- a/services/gitea/gitea-languages-count.tester.js +++ b/services/gitea/gitea-languages-count.tester.js @@ -4,17 +4,19 @@ import { createServiceTester } from '../tester.js' export const t = await createServiceTester() t.create('language count (empty repo)') - .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com') - .intercept(nock => - nock('https://gitea.example.com/') - .get('/api/v1/repos/go-gitea/gitea/languages') - .reply(200, {}), - ) + .get('/CanisHelix/shields-badge-test-empty.json') .expectBadge({ label: 'languages', message: '0', }) +t.create('language count') + .get('/CanisHelix/shields-badge-test.json') + .expectBadge({ + label: 'languages', + message: Joi.number().integer().positive(), + }) + t.create('language count (self-managed)') .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com') .intercept(nock => @@ -28,16 +30,7 @@ t.create('language count (self-managed)') }) t.create('language count (user or repo not found)') - .get('/open/do-not-exist.json?gitea_url=https://gitea.example.com') - .intercept(nock => - nock('https://gitea.example.com/') - .get('/api/v1/repos/open/do-not-exist/languages') - .reply(404, { - errors: ['user redirect does not exist [name: open]'], - message: 'GetUserByName', - url: 'https://gitea.example.com/api/swagger', - }), - ) + .get('/CanisHelix/does-not-exist.json') .expectBadge({ label: 'languages', message: 'user or repo not found', diff --git a/services/gitea/gitea-release.service.js b/services/gitea/gitea-release.service.js index 6184b4ff968dd..a8f91bdc9d9d8 100644 --- a/services/gitea/gitea-release.service.js +++ b/services/gitea/gitea-release.service.js @@ -9,6 +9,7 @@ const schema = Joi.array().items( Joi.object({ name: Joi.string().required(), tag_name: Joi.string().required(), + prerelease: Joi.boolean().required(), }), ) @@ -55,8 +56,7 @@ export default class GiteaRelease extends GiteaBase { }), queryParam({ name: 'gitea_url', - example: 'https://try.gitea.io', - required: true, + example: 'https://codeberg.org', }), queryParam({ name: 'include_prereleases', @@ -86,7 +86,7 @@ export default class GiteaRelease extends GiteaBase { static defaultBadgeData = { label: 'release' } async fetch({ user, repo, baseUrl }) { - // https://try.gitea.io/api/swagger#/repository/repoGetRelease + // https://codeberg.org/api/swagger#/repository/repoGetRelease return super.fetch({ schema, url: `${baseUrl}/api/v1/repos/${user}/${repo}/releases`, @@ -101,20 +101,27 @@ export default class GiteaRelease extends GiteaBase { const displayKey = displayName === 'tag' ? 'tag_name' : 'name' - if (!isSemver) { - return releases[0][displayKey] + if (isSemver) { + return latest( + releases.map(t => t[displayKey]), + { pre: includePrereleases }, + ) } - return latest( - releases.map(t => t[displayKey]), - { pre: includePrereleases }, - ) + if (!includePrereleases) { + const stableReleases = releases.filter(release => !release.prerelease) + if (stableReleases.length > 0) { + return stableReleases[0][displayKey] + } + } + + return releases[0][displayKey] } async handle( { user, repo }, { - gitea_url: baseUrl = 'https://try.gitea.io', + gitea_url: baseUrl = 'https://codeberg.org', include_prereleases: pre, sort, display_name: displayName, diff --git a/services/gitea/gitea-release.tester.js b/services/gitea/gitea-release.tester.js index 0eeaefe3b61bb..7adab19228341 100644 --- a/services/gitea/gitea-release.tester.js +++ b/services/gitea/gitea-release.tester.js @@ -1,104 +1,67 @@ import { createServiceTester } from '../tester.js' export const t = await createServiceTester() -const responseBody = [ - { - id: 3318489, - tag_name: 'v2.0.0', - name: 'v2.0.0', - draft: false, - prerelease: false, - created_at: '2021-09-15T01:06:43Z', - published_at: '2021-09-15T01:06:43Z', - }, - { - id: 3318488, - tag_name: 'v4.0.0', - name: 'v4.0.0', - draft: false, - prerelease: false, - created_at: '2021-09-15T01:05:43Z', - published_at: '2021-09-15T01:05:43Z', - }, - { - id: 3318487, - tag_name: 'v5.0.0-beta.1', - name: 'v5.0.0-beta.1', - draft: false, - prerelease: false, - created_at: '2021-09-11T23:53:07Z', - published_at: '2021-09-11T23:53:07Z', - }, -] t.create('Release (latest by date)') - .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com') - .intercept(nock => - nock('https://gitea.example.com/') - .get('/api/v1/repos/go-gitea/gitea/releases') - .reply(200, responseBody), - ) - .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' }) + .get('/CanisHelix/shields-badge-test.json') + .expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' }) t.create('Release (latest by date, order by created_at)') - .get( - '/go-gitea/gitea.json?gitea_url=https://gitea.example.com&date_order_by=created_at', - ) - .intercept(nock => - nock('https://gitea.example.com/') - .get('/api/v1/repos/go-gitea/gitea/releases') - .reply(200, responseBody), - ) - .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' }) + .get('/CanisHelix/shields-badge-test.json?date_order_by=created_at') + .expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' }) t.create('Release (latest by date, order by published_at)') - .get( - '/go-gitea/gitea.json?gitea_url=https://gitea.example.com&date_order_by=published_at', - ) - .intercept(nock => - nock('https://gitea.example.com/') - .get('/api/v1/repos/go-gitea/gitea/releases') - .reply(200, responseBody), - ) - .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' }) + .get('/CanisHelix/shields-badge-test.json?date_order_by=published_at') + .expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' }) t.create('Release (latest by semver)') - .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com&sort=semver') - .intercept(nock => - nock('https://gitea.example.com/') - .get('/api/v1/repos/go-gitea/gitea/releases') - .reply(200, responseBody), - ) + .get('/CanisHelix/shields-badge-test.json?sort=semver') .expectBadge({ label: 'release', message: 'v4.0.0', color: 'blue' }) t.create('Release (latest by semver pre-release)') - .get( - '/go-gitea/gitea.json?gitea_url=https://gitea.example.com&sort=semver&include_prereleases', - ) - .intercept(nock => - nock('https://gitea.example.com/') - .get('/api/v1/repos/go-gitea/gitea/releases') - .reply(200, responseBody), - ) - .expectBadge({ label: 'release', message: 'v5.0.0-beta.1', color: 'orange' }) + .get('/CanisHelix/shields-badge-test.json?sort=semver&include_prereleases') + .expectBadge({ label: 'release', message: 'v5.0.0-rc1', color: 'orange' }) t.create('Release (project not found)') - .get('/fdroid/nonexistant.json?gitea_url=https://gitea.example.com') - .intercept(nock => - nock('https://gitea.example.com/') - .get('/api/v1/repos/fdroid/nonexistant/releases') - .reply(404, { - errors: ['user redirect does not exist [name: fdroid]'], - message: 'GetUserByName', - url: 'https://gitea.example.com/api/swagger', - }), - ) + .get('/CanisHelix/does-not-exist.json') .expectBadge({ label: 'release', message: 'user or repo not found' }) t.create('Release (no tags)') - .get('/fdroid/fdroiddata.json?gitea_url=https://gitea.example.com') + .get('/CanisHelix/shields-badge-test-empty.json') + .expectBadge({ label: 'release', message: 'no releases found' }) + +t.create('Release (latest by date) (self-managed)') + .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com') .intercept(nock => nock('https://gitea.example.com/') - .get('/api/v1/repos/fdroid/fdroiddata/releases') - .reply(200, []), + .get('/api/v1/repos/go-gitea/gitea/releases') + .reply(200, [ + { + id: 3318489, + tag_name: 'v2.0.0', + name: 'v2.0.0', + draft: false, + prerelease: false, + created_at: '2021-09-15T01:06:43Z', + published_at: '2021-09-15T01:06:43Z', + }, + { + id: 3318488, + tag_name: 'v4.0.0', + name: 'v4.0.0', + draft: false, + prerelease: false, + created_at: '2021-09-15T01:05:43Z', + published_at: '2021-09-15T01:05:43Z', + }, + { + id: 3318487, + tag_name: 'v5.0.0-beta.1', + name: 'v5.0.0-beta.1', + draft: false, + prerelease: false, + created_at: '2021-09-11T23:53:07Z', + published_at: '2021-09-11T23:53:07Z', + }, + ]), ) - .expectBadge({ label: 'release', message: 'no releases found' }) + .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' }) From 1af458516741f9435c968862abb779b2e6a4b4f0 Mon Sep 17 00:00:00 2001 From: Nigel George Date: Thu, 14 Dec 2023 13:08:07 +0900 Subject: [PATCH 5/9] add pagination, update tests to live, set gitea_url as required --- services/gitea/gitea-base.js | 60 ++++++++++++++++++- services/gitea/gitea-helper.js | 6 +- .../gitea/gitea-languages-count.service.js | 1 + .../gitea/gitea-languages-count.tester.js | 20 ++----- services/gitea/gitea-release.service.js | 9 ++- services/gitea/gitea-release.tester.js | 59 +++++------------- 6 files changed, 90 insertions(+), 65 deletions(-) diff --git a/services/gitea/gitea-base.js b/services/gitea/gitea-base.js index 071a08e66ac35..517efc0afe245 100644 --- a/services/gitea/gitea-base.js +++ b/services/gitea/gitea-base.js @@ -18,15 +18,73 @@ export default class GiteaBase extends BaseJsonService { } async fetchPage({ page, requestParams, schema }) { + const { + options: { searchParams: existingQuery, ...restOptions } = {}, + ...rest + } = requestParams + + requestParams = { + options: { + searchParams: { + ...existingQuery, + ...{ page }, + }, + ...restOptions, + }, + ...rest, + } + const { res, buffer } = await this._request( this.authHelper.withBearerAuthHeader({ ...requestParams, - ...{ options: { searchParams: { page } } }, }), ) const json = this._parseJson(buffer) const data = this.constructor._validate(json, schema) + return { res, data } } + + async fetchPaginatedArrayData({ + url, + options, + schema, + httpErrors, + firstPageOnly = false, + }) { + const requestLimit = 100 + const requestParams = { + url, + options: { + headers: { Accept: 'application/json' }, + searchParams: { limit: requestLimit }, + ...options, + }, + httpErrors, + } + + const { + res: { headers }, + data, + } = await this.fetchPage({ page: 1, requestParams, schema }) + const numberOfItems = headers['x-total-count'] + + let numberOfPages = 1 + if (numberOfItems > 0) { + numberOfPages = Math.ceil(numberOfItems / requestLimit) + } + + if (numberOfPages === 1 || firstPageOnly) { + return data + } + + const pageData = await Promise.all( + [...Array(numberOfPages - 1).keys()].map((_, i) => + this.fetchPage({ page: ++i + 1, requestParams, schema }), + ), + ) + + return [...data].concat(...pageData.map(p => p.data)) + } } diff --git a/services/gitea/gitea-helper.js b/services/gitea/gitea-helper.js index 60e5ed1b99ea0..fc03e6b651959 100644 --- a/services/gitea/gitea-helper.js +++ b/services/gitea/gitea-helper.js @@ -3,10 +3,10 @@ Note that only internet-accessible Gitea compatible instances are supported, for [https://codeberg.org](https://codeberg.org). ` -function httpErrorsFor(notFoundMessage = 'user or repo not found') { +function httpErrorsFor() { return { - 401: notFoundMessage, - 404: notFoundMessage, + 403: 'private repo', + 404: 'user or repo not found', } } diff --git a/services/gitea/gitea-languages-count.service.js b/services/gitea/gitea-languages-count.service.js index 887144d3ba6bf..8458a020bcd3f 100644 --- a/services/gitea/gitea-languages-count.service.js +++ b/services/gitea/gitea-languages-count.service.js @@ -41,6 +41,7 @@ export default class GiteaLanguageCount extends GiteaBase { queryParam({ name: 'gitea_url', example: 'https://codeberg.org', + required: true, }), ], }, diff --git a/services/gitea/gitea-languages-count.tester.js b/services/gitea/gitea-languages-count.tester.js index 27cdf5cbdff00..9d4f6a965e327 100644 --- a/services/gitea/gitea-languages-count.tester.js +++ b/services/gitea/gitea-languages-count.tester.js @@ -4,33 +4,23 @@ import { createServiceTester } from '../tester.js' export const t = await createServiceTester() t.create('language count (empty repo)') - .get('/CanisHelix/shields-badge-test-empty.json') + .get( + '/CanisHelix/shields-badge-test-empty.json?gitea_url=https://codeberg.org', + ) .expectBadge({ label: 'languages', message: '0', }) t.create('language count') - .get('/CanisHelix/shields-badge-test.json') - .expectBadge({ - label: 'languages', - message: Joi.number().integer().positive(), - }) - -t.create('language count (self-managed)') - .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com') - .intercept(nock => - nock('https://gitea.example.com/') - .get('/api/v1/repos/go-gitea/gitea/languages') - .reply(200, { CPP: 500, SQL: 25 }), - ) + .get('/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org') .expectBadge({ label: 'languages', message: Joi.number().integer().positive(), }) t.create('language count (user or repo not found)') - .get('/CanisHelix/does-not-exist.json') + .get('/CanisHelix/does-not-exist.json?gitea_url=https://codeberg.org') .expectBadge({ label: 'languages', message: 'user or repo not found', diff --git a/services/gitea/gitea-release.service.js b/services/gitea/gitea-release.service.js index a8f91bdc9d9d8..72372f887d687 100644 --- a/services/gitea/gitea-release.service.js +++ b/services/gitea/gitea-release.service.js @@ -57,6 +57,7 @@ export default class GiteaRelease extends GiteaBase { queryParam({ name: 'gitea_url', example: 'https://codeberg.org', + required: true, }), queryParam({ name: 'include_prereleases', @@ -85,12 +86,13 @@ export default class GiteaRelease extends GiteaBase { static defaultBadgeData = { label: 'release' } - async fetch({ user, repo, baseUrl }) { + async fetch({ user, repo, baseUrl, isSemver }) { // https://codeberg.org/api/swagger#/repository/repoGetRelease - return super.fetch({ + return this.fetchPaginatedArrayData({ schema, url: `${baseUrl}/api/v1/repos/${user}/${repo}/releases`, - httpErrors: httpErrorsFor('user or repo not found'), + httpErrors: httpErrorsFor(), + firstPageOnly: !isSemver, }) } @@ -133,6 +135,7 @@ export default class GiteaRelease extends GiteaBase { user, repo, baseUrl, + isSemver, }) const version = this.constructor.transform({ releases, diff --git a/services/gitea/gitea-release.tester.js b/services/gitea/gitea-release.tester.js index 7adab19228341..ba3eab0d7fc4f 100644 --- a/services/gitea/gitea-release.tester.js +++ b/services/gitea/gitea-release.tester.js @@ -2,23 +2,31 @@ import { createServiceTester } from '../tester.js' export const t = await createServiceTester() t.create('Release (latest by date)') - .get('/CanisHelix/shields-badge-test.json') + .get('/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org') .expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' }) t.create('Release (latest by date, order by created_at)') - .get('/CanisHelix/shields-badge-test.json?date_order_by=created_at') + .get( + '/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&date_order_by=created_at', + ) .expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' }) t.create('Release (latest by date, order by published_at)') - .get('/CanisHelix/shields-badge-test.json?date_order_by=published_at') + .get( + '/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&date_order_by=published_at', + ) .expectBadge({ label: 'release', message: 'v3.0.0', color: 'blue' }) t.create('Release (latest by semver)') - .get('/CanisHelix/shields-badge-test.json?sort=semver') + .get( + '/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&sort=semver', + ) .expectBadge({ label: 'release', message: 'v4.0.0', color: 'blue' }) t.create('Release (latest by semver pre-release)') - .get('/CanisHelix/shields-badge-test.json?sort=semver&include_prereleases') + .get( + '/CanisHelix/shields-badge-test.json?gitea_url=https://codeberg.org&sort=semver&include_prereleases', + ) .expectBadge({ label: 'release', message: 'v5.0.0-rc1', color: 'orange' }) t.create('Release (project not found)') @@ -26,42 +34,7 @@ t.create('Release (project not found)') .expectBadge({ label: 'release', message: 'user or repo not found' }) t.create('Release (no tags)') - .get('/CanisHelix/shields-badge-test-empty.json') - .expectBadge({ label: 'release', message: 'no releases found' }) - -t.create('Release (latest by date) (self-managed)') - .get('/go-gitea/gitea.json?gitea_url=https://gitea.example.com') - .intercept(nock => - nock('https://gitea.example.com/') - .get('/api/v1/repos/go-gitea/gitea/releases') - .reply(200, [ - { - id: 3318489, - tag_name: 'v2.0.0', - name: 'v2.0.0', - draft: false, - prerelease: false, - created_at: '2021-09-15T01:06:43Z', - published_at: '2021-09-15T01:06:43Z', - }, - { - id: 3318488, - tag_name: 'v4.0.0', - name: 'v4.0.0', - draft: false, - prerelease: false, - created_at: '2021-09-15T01:05:43Z', - published_at: '2021-09-15T01:05:43Z', - }, - { - id: 3318487, - tag_name: 'v5.0.0-beta.1', - name: 'v5.0.0-beta.1', - draft: false, - prerelease: false, - created_at: '2021-09-11T23:53:07Z', - published_at: '2021-09-11T23:53:07Z', - }, - ]), + .get( + '/CanisHelix/shields-badge-test-empty.json?gitea_url=https://codeberg.org', ) - .expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' }) + .expectBadge({ label: 'release', message: 'no releases found' }) From 1a71bde2d08507d4de81f29b1af1fa168fb154b0 Mon Sep 17 00:00:00 2001 From: Nigel George Date: Thu, 14 Dec 2023 14:16:40 +0900 Subject: [PATCH 6/9] add auth test (wip) --- services/gitea/gitea-base.spec.js | 38 +++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 services/gitea/gitea-base.spec.js diff --git a/services/gitea/gitea-base.spec.js b/services/gitea/gitea-base.spec.js new file mode 100644 index 0000000000000..6ca96ba9b5d09 --- /dev/null +++ b/services/gitea/gitea-base.spec.js @@ -0,0 +1,38 @@ +import Joi from 'joi' +import { expect } from 'chai' +import nock from 'nock' +import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js' +import GiteaBase from './gitea-base.js' + +class DummyGiteaService extends GiteaBase { + static route = { base: 'fake-base' } + + async handle() { + const data = await this.fetch({ + schema: Joi.any(), + url: 'https://codeberg.com/api/v1/repos/CanisHelix/shields-badge-test/releases', + }) + return { message: data.message } + } +} + +describe('GiteaBase', function () { + describe('auth', function () { + cleanUpNockAfterEach() + + const config = { private: { gitea_token: 'fake-key' } } + + it('sends the auth information as configured', async function () { + const scope = nock('https://codeberg.com') + .get('/api/v1/repos/CanisHelix/shields-badge-test/releases') + .query({ key: 'fake-key' }) + .reply(200, { message: 'fake message' }) + + expect( + await DummyGiteaService.invoke(defaultContext, config, {}), + ).to.deep.equal({ message: 'fake message' }) + + scope.done() + }) + }) +}) From 9494df1ce0b557fd59b8078104cfe3a744c15771 Mon Sep 17 00:00:00 2001 From: Nigel George Date: Thu, 14 Dec 2023 18:22:23 +0900 Subject: [PATCH 7/9] fix base auth test --- services/gitea/gitea-base.spec.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/services/gitea/gitea-base.spec.js b/services/gitea/gitea-base.spec.js index 6ca96ba9b5d09..868e964920080 100644 --- a/services/gitea/gitea-base.spec.js +++ b/services/gitea/gitea-base.spec.js @@ -10,7 +10,7 @@ class DummyGiteaService extends GiteaBase { async handle() { const data = await this.fetch({ schema: Joi.any(), - url: 'https://codeberg.com/api/v1/repos/CanisHelix/shields-badge-test/releases', + url: 'https://codeberg.org/api/v1/repos/CanisHelix/shields-badge-test/releases', }) return { message: data.message } } @@ -20,17 +20,27 @@ describe('GiteaBase', function () { describe('auth', function () { cleanUpNockAfterEach() - const config = { private: { gitea_token: 'fake-key' } } + const config = { + public: { + services: { + gitea: { + authorizedOrigins: ['https://codeberg.org'], + }, + }, + }, + private: { + gitea_token: 'fake-key', + }, + } it('sends the auth information as configured', async function () { - const scope = nock('https://codeberg.com') + const scope = nock('https://codeberg.org') .get('/api/v1/repos/CanisHelix/shields-badge-test/releases') - .query({ key: 'fake-key' }) + .matchHeader('Authorization', 'Bearer fake-key') .reply(200, { message: 'fake message' }) - expect( await DummyGiteaService.invoke(defaultContext, config, {}), - ).to.deep.equal({ message: 'fake message' }) + ).to.not.have.property('isError') scope.done() }) From dc113e0a999b26566bfd62f2eeee3f8a079ce461 Mon Sep 17 00:00:00 2001 From: Nigel George Date: Mon, 18 Dec 2023 13:23:08 +0900 Subject: [PATCH 8/9] fix required optionalUrl, remove default, assume semver from firstpage --- services/gitea/gitea-base.js | 71 ------------------- services/gitea/gitea-helper.js | 3 +- .../gitea/gitea-languages-count.service.js | 9 +-- services/gitea/gitea-release.service.js | 11 ++- services/gitea/gitea-release.tester.js | 2 +- 5 files changed, 10 insertions(+), 86 deletions(-) diff --git a/services/gitea/gitea-base.js b/services/gitea/gitea-base.js index 517efc0afe245..2a14ba846ef01 100644 --- a/services/gitea/gitea-base.js +++ b/services/gitea/gitea-base.js @@ -16,75 +16,4 @@ export default class GiteaBase extends BaseJsonService { }), ) } - - async fetchPage({ page, requestParams, schema }) { - const { - options: { searchParams: existingQuery, ...restOptions } = {}, - ...rest - } = requestParams - - requestParams = { - options: { - searchParams: { - ...existingQuery, - ...{ page }, - }, - ...restOptions, - }, - ...rest, - } - - const { res, buffer } = await this._request( - this.authHelper.withBearerAuthHeader({ - ...requestParams, - }), - ) - - const json = this._parseJson(buffer) - const data = this.constructor._validate(json, schema) - - return { res, data } - } - - async fetchPaginatedArrayData({ - url, - options, - schema, - httpErrors, - firstPageOnly = false, - }) { - const requestLimit = 100 - const requestParams = { - url, - options: { - headers: { Accept: 'application/json' }, - searchParams: { limit: requestLimit }, - ...options, - }, - httpErrors, - } - - const { - res: { headers }, - data, - } = await this.fetchPage({ page: 1, requestParams, schema }) - const numberOfItems = headers['x-total-count'] - - let numberOfPages = 1 - if (numberOfItems > 0) { - numberOfPages = Math.ceil(numberOfItems / requestLimit) - } - - if (numberOfPages === 1 || firstPageOnly) { - return data - } - - const pageData = await Promise.all( - [...Array(numberOfPages - 1).keys()].map((_, i) => - this.fetchPage({ page: ++i + 1, requestParams, schema }), - ), - ) - - return [...data].concat(...pageData.map(p => p.data)) - } } diff --git a/services/gitea/gitea-helper.js b/services/gitea/gitea-helper.js index fc03e6b651959..51242d819d508 100644 --- a/services/gitea/gitea-helper.js +++ b/services/gitea/gitea-helper.js @@ -1,6 +1,5 @@ const documentation = ` -Note that only internet-accessible Gitea compatible instances are supported, for example -[https://codeberg.org](https://codeberg.org). +Note that the gitea_url parameter is required because there is canonical hosted gitea service provided by Gitea. ` function httpErrorsFor() { diff --git a/services/gitea/gitea-languages-count.service.js b/services/gitea/gitea-languages-count.service.js index 8458a020bcd3f..1e8df38c25df1 100644 --- a/services/gitea/gitea-languages-count.service.js +++ b/services/gitea/gitea-languages-count.service.js @@ -12,7 +12,7 @@ The keys could be anything and {} is a valid response (e.g: for an empty repo) const schema = Joi.object().pattern(/./, nonNegativeInteger) const queryParamSchema = Joi.object({ - gitea_url: optionalUrl, + gitea_url: optionalUrl.required(), }).required() export default class GiteaLanguageCount extends GiteaBase { @@ -58,7 +58,7 @@ export default class GiteaLanguageCount extends GiteaBase { } async fetch({ user, repo, baseUrl }) { - // https://codeberg.org/api/swagger#/repository/repoGetLanguages + // https://try.gitea.io/api/swagger#/repository/repoGetLanguages return super.fetch({ schema, url: `${baseUrl}/api/v1/repos/${user}/${repo}/languages`, @@ -66,10 +66,7 @@ export default class GiteaLanguageCount extends GiteaBase { }) } - async handle( - { user, repo }, - { gitea_url: baseUrl = 'https://codeberg.org' }, - ) { + async handle({ user, repo }, { gitea_url: baseUrl }) { const data = await this.fetch({ user, repo, diff --git a/services/gitea/gitea-release.service.js b/services/gitea/gitea-release.service.js index 72372f887d687..2d364ccca4a21 100644 --- a/services/gitea/gitea-release.service.js +++ b/services/gitea/gitea-release.service.js @@ -18,7 +18,7 @@ const displayNameEnum = ['tag', 'release'] const dateOrderByEnum = ['created_at', 'published_at'] const queryParamSchema = Joi.object({ - gitea_url: optionalUrl, + gitea_url: optionalUrl.required(), include_prereleases: Joi.equal(''), sort: Joi.string() .valid(...sortEnum) @@ -86,13 +86,12 @@ export default class GiteaRelease extends GiteaBase { static defaultBadgeData = { label: 'release' } - async fetch({ user, repo, baseUrl, isSemver }) { - // https://codeberg.org/api/swagger#/repository/repoGetRelease - return this.fetchPaginatedArrayData({ + async fetch({ user, repo, baseUrl }) { + // https://try.gitea.io/api/swagger#/repository/repoGetRelease + return super.fetch({ schema, url: `${baseUrl}/api/v1/repos/${user}/${repo}/releases`, httpErrors: httpErrorsFor(), - firstPageOnly: !isSemver, }) } @@ -123,7 +122,7 @@ export default class GiteaRelease extends GiteaBase { async handle( { user, repo }, { - gitea_url: baseUrl = 'https://codeberg.org', + gitea_url: baseUrl, include_prereleases: pre, sort, display_name: displayName, diff --git a/services/gitea/gitea-release.tester.js b/services/gitea/gitea-release.tester.js index ba3eab0d7fc4f..1f6894e6215cf 100644 --- a/services/gitea/gitea-release.tester.js +++ b/services/gitea/gitea-release.tester.js @@ -30,7 +30,7 @@ t.create('Release (latest by semver pre-release)') .expectBadge({ label: 'release', message: 'v5.0.0-rc1', color: 'orange' }) t.create('Release (project not found)') - .get('/CanisHelix/does-not-exist.json') + .get('/CanisHelix/does-not-exist.json?gitea_url=https://codeberg.org') .expectBadge({ label: 'release', message: 'user or repo not found' }) t.create('Release (no tags)') From c39884bb9170c7d72c8134a845d3b8c16517e447 Mon Sep 17 00:00:00 2001 From: Nigel George Date: Mon, 18 Dec 2023 21:18:08 +0900 Subject: [PATCH 9/9] update example to use stable repository --- services/gitea/gitea-languages-count.service.js | 4 ++-- services/gitea/gitea-release.service.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/services/gitea/gitea-languages-count.service.js b/services/gitea/gitea-languages-count.service.js index 1e8df38c25df1..819198521b647 100644 --- a/services/gitea/gitea-languages-count.service.js +++ b/services/gitea/gitea-languages-count.service.js @@ -32,11 +32,11 @@ export default class GiteaLanguageCount extends GiteaBase { parameters: [ pathParam({ name: 'user', - example: 'go-gitea', + example: 'forgejo', }), pathParam({ name: 'repo', - example: 'gitea', + example: 'forgejo', }), queryParam({ name: 'gitea_url', diff --git a/services/gitea/gitea-release.service.js b/services/gitea/gitea-release.service.js index 2d364ccca4a21..2188f0e135fcf 100644 --- a/services/gitea/gitea-release.service.js +++ b/services/gitea/gitea-release.service.js @@ -48,11 +48,11 @@ export default class GiteaRelease extends GiteaBase { parameters: [ pathParam({ name: 'user', - example: 'go-gitea', + example: 'forgejo', }), pathParam({ name: 'repo', - example: 'gitea', + example: 'forgejo', }), queryParam({ name: 'gitea_url',