From 701eefc71eb3aa22535e51e06fb3d3e9e79721e7 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 3 Jan 2024 15:18:58 +0100 Subject: [PATCH] Switched the logic to create a repository using api/v1/orgs/OWNER/repos. Adapt the test case to check if there is an org too Signed-off-by: cmoulliard --- packages/integration/src/gitea/core.ts | 1 + .../src/actions/gitea.test.ts | 19 ++++- .../src/actions/gitea.ts | 75 ++++++++++++++----- 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/packages/integration/src/gitea/core.ts b/packages/integration/src/gitea/core.ts index f188613d2a..40f1c0fb6d 100644 --- a/packages/integration/src/gitea/core.ts +++ b/packages/integration/src/gitea/core.ts @@ -14,6 +14,7 @@ * limitations under the License. */ import { GiteaIntegrationConfig } from './config'; +import { Logger } from 'winston'; /** * Given a URL pointing to a file, returns a URL diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts index ee0457cac0..5486b5b69f 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -87,7 +87,20 @@ describe('publish:gitea', () => { it('should throw if there is no repositoryId returned', async () => { server.use( - rest.post('https://gitea.com/api/v1/user/repos', (req, res, ctx) => { + rest.get('https://gitea.com/api/v1/orgs/org1', (_req, res, ctx) => { + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json({ + id: 1, + name: 'org1', + visibility: 'public', + repo_admin_change_team_access: false, + username: 'org1', + }), + ); + }), + rest.post('https://gitea.com/api/v1/orgs/org1/repos', (req, res, ctx) => { // Basic auth must match the user and password defined part of the config expect(req.headers.get('Authorization')).toBe( 'Basic Z2l0ZWFfdXNlcjpnaXRlYV9wYXNzd29yZA==', @@ -108,13 +121,13 @@ describe('publish:gitea', () => { ...mockContext, input: { ...mockContext.input, - repoUrl: 'gitea.com?repo=repo', + repoUrl: 'gitea.com?repo=repo&owner=org1', }, }); expect(initRepoAndPush).toHaveBeenCalledWith({ dir: mockContext.workspacePath, - remoteUrl: 'https://gitea.com/api/v1/user/repo', + remoteUrl: 'https://gitea.com/org1/repo.git', defaultBranch: 'main', auth: { username: 'gitea_user', password: 'gitea_password' }, logger: mockContext.logger, diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index cc16359d75..b11d5bf765 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -31,18 +31,60 @@ import { examples } from './gitea.examples'; import fetch, { RequestInit, Response } from 'node-fetch'; import crypto from 'crypto'; +const checkGiteaOrg = async ( + config: GiteaIntegrationConfig, + options: { + owner: string; + }, +): Promise => { + const { owner } = options; + let response: Response; + // check first if the org = owner exists + const getOptions: RequestInit = { + method: 'GET', + headers: { + ...getGiteaRequestOptions(config).headers, + 'Content-Type': 'application/json', + }, + }; + try { + response = await fetch( + `${config.baseUrl}/api/v1/orgs/${owner}`, + getOptions, + ); + } catch (e) { + throw new Error(`Unable to get the Organization: ${owner}, ${e}`); + } + if (response.status !== 200) { + throw new Error( + `Organization ${owner} do not exist. Please create it first !`, + ); + } +}; + const createGiteaProject = async ( config: GiteaIntegrationConfig, options: { projectName: string; - // parent: string; owner?: string; description: string; }, ): Promise => { - const { projectName, description } = options; + const { projectName, description, owner } = options; - const fetchOptions: RequestInit = { + /* + Several options exist to create a repository using either the user or organisation + User: https://gitea.com/api/swagger#/user/createCurrentUserRepo + Api: URL/api/v1/user/repos + Remark: The user is the username defined part of the backstage integration config for the gitea URL ! + + Org: https://gitea.com/api/swagger#/organization/createOrgRepo + Api: URL/api/v1/orgs/${org_owner}/repos + This is the default scenario that we support currently + */ + let response: Response; + + const postOptions: RequestInit = { method: 'POST', body: JSON.stringify({ name: projectName, @@ -53,13 +95,11 @@ const createGiteaProject = async ( 'Content-Type': 'application/json', }, }; - - // TODO - // Create a repository in Gitea - // API request: https://gitea.com/api/swagger#/user/createCurrentUserRepo - let response: Response; try { - response = await fetch(`${config.baseUrl}/api/v1/user/repos`, fetchOptions); + response = await fetch( + `${config.baseUrl}/api/v1/orgs/${owner}/repos`, + postOptions, + ); } catch (e) { throw new Error(`Unable to create repository, ${e}`); } @@ -176,7 +216,7 @@ export function createPublishGiteaAction(options: { sourcePath, } = ctx.input; - const { repo, host } = parseRepoUrl(repoUrl, integrations); + const { repo, host, owner } = parseRepoUrl(repoUrl, integrations); const integrationConfig = integrations.gitea.byHost(host); if (!integrationConfig) { @@ -194,14 +234,15 @@ export function createPublishGiteaAction(options: { ); } - // TODO - // Check if the integration config includes a baseUrl + // check if the org exists within the gitea server + if (owner) { + await checkGiteaOrg(integrationConfig.config, { owner }); + } await createGiteaProject(integrationConfig.config, { description, - // owner: owner, + owner: owner, projectName: repo, - // parent: workspace, }); const auth = { @@ -216,8 +257,8 @@ export function createPublishGiteaAction(options: { ? gitAuthorEmail : config.getOptionalString('scaffolder.defaultAuthor.email'), }; - // TODO - const remoteUrl = `${integrationConfig.config.baseUrl}/api/v1/user/${repo}`; + // The owner to be used should be either the org name or user authenticated with the gitea server + const remoteUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}.git`; const commitResult = await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath), remoteUrl, @@ -228,7 +269,7 @@ export function createPublishGiteaAction(options: { gitAuthorInfo, }); - const repoContentsUrl = `${integrationConfig.config.baseUrl}/${repo}/+/refs/heads/${defaultBranch}`; + const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/+/refs/${defaultBranch}`; ctx.output('remoteUrl', remoteUrl); ctx.output('commitHash', commitResult?.commitHash); ctx.output('repoContentsUrl', repoContentsUrl);