From 211ec252125b3e7f19f7d20d110f2881e932594d Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 14 Dec 2023 19:03:53 +0100 Subject: [PATCH 01/56] New plugin to publish a git repository on gitea Signed-off-by: cmoulliard --- .../new/factories/scaffolderModule.test.ts | 4 +- .../.eslintrc.js | 1 + .../CHANGELOG.md | 7 + .../scaffolder-backend-module-gitea/README.md | 5 + .../api-report.md | 20 ++ .../catalog-info.yaml | 9 + .../package.json | 41 ++++ .../src/actions/gitea.examples.ts | 158 ++++++++++++ .../src/actions/gitea.test.ts | 49 ++++ .../src/actions/gitea.ts | 232 ++++++++++++++++++ .../src/actions/index.ts | 16 ++ .../src/index.ts | 23 ++ yarn.lock | 16 ++ 13 files changed, 579 insertions(+), 2 deletions(-) create mode 100644 plugins/scaffolder-backend-module-gitea/.eslintrc.js create mode 100644 plugins/scaffolder-backend-module-gitea/CHANGELOG.md create mode 100644 plugins/scaffolder-backend-module-gitea/README.md create mode 100644 plugins/scaffolder-backend-module-gitea/api-report.md create mode 100644 plugins/scaffolder-backend-module-gitea/catalog-info.yaml create mode 100644 plugins/scaffolder-backend-module-gitea/package.json create mode 100644 plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts create mode 100644 plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts create mode 100644 plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts create mode 100644 plugins/scaffolder-backend-module-gitea/src/actions/index.ts create mode 100644 plugins/scaffolder-backend-module-gitea/src/index.ts diff --git a/packages/cli/src/lib/new/factories/scaffolderModule.test.ts b/packages/cli/src/lib/new/factories/scaffolderModule.test.ts index b43946228b..767ae0fc44 100644 --- a/packages/cli/src/lib/new/factories/scaffolderModule.test.ts +++ b/packages/cli/src/lib/new/factories/scaffolderModule.test.ts @@ -77,8 +77,8 @@ describe('scaffolderModule factory', () => { 'templating package.json.hbs', 'templating index.ts.hbs', 'copying index.ts', - 'copying example.test.ts', - 'copying example.ts', + 'copying gitea.test.ts', + 'copying gitea.ts', 'copying index.ts', 'Installing:', `moving plugins${sep}scaffolder-backend-module-test`, diff --git a/plugins/scaffolder-backend-module-gitea/.eslintrc.js b/plugins/scaffolder-backend-module-gitea/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md new file mode 100644 index 0000000000..a4f1f20179 --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md @@ -0,0 +1,7 @@ +# @backstage/plugin-scaffolder-backend-module-gitea + +## 0.1.0-next.0 + +### Minor Changes + +- TODO: Add Gitea Scaffolder Plugin diff --git a/plugins/scaffolder-backend-module-gitea/README.md b/plugins/scaffolder-backend-module-gitea/README.md new file mode 100644 index 0000000000..84b8ab6ea5 --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/README.md @@ -0,0 +1,5 @@ +# backstage-plugin-scaffolder-backend-module-gitea + +The gitea module for [@backstage/plugin-scaffolder-backend](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend). + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/scaffolder-backend-module-gitea/api-report.md b/plugins/scaffolder-backend-module-gitea/api-report.md new file mode 100644 index 0000000000..51e2f3bd17 --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/api-report.md @@ -0,0 +1,20 @@ +## API Report File for "backstage-plugin-scaffolder-backend-module-gitea" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { JsonObject } from '@backstage/types'; +import { TemplateAction } from '@backstage/plugin-scaffolder-node'; + +// @public +export function createAcmeExampleAction(): TemplateAction< + { + myParameter: string; + }, + JsonObject +>; + +// Warnings were encountered during analysis: +// +// src/index.d.ts:2:25 - (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" +``` diff --git a/plugins/scaffolder-backend-module-gitea/catalog-info.yaml b/plugins/scaffolder-backend-module-gitea/catalog-info.yaml new file mode 100644 index 0000000000..20511015cd --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-scaffolder-backend-module-gitea + title: 'backstage-plugin-scaffolder-backend-module-gitea' +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: maintainers diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json new file mode 100644 index 0000000000..9140c294f8 --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -0,0 +1,41 @@ +{ + "name": "backstage-plugin-scaffolder-backend-module-gitea", + "description": "The gitea module for @backstage/plugin-scaffolder-backend", + "version": "0.1.0-next.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/integration": "workspace:^", + "@backstage/plugin-scaffolder-backend": "workspace:^", + "@backstage/plugin-scaffolder-node": "workspace:^", + "node-fetch": "^2.6.7", + "yaml": "^2.0.0" + }, + "devDependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts new file mode 100644 index 0000000000..64d73d498f --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts @@ -0,0 +1,158 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: + 'Initializes a Gitea repository of contents in workspace and publish it to Gitea with default configuration.', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitea', + name: 'Publish to Gitea', + input: { + repoUrl: 'gitea.com?repo=repo&owner=owner', + }, + }, + ], + }), + }, + { + description: 'Initializes a Gitea repository with a description.', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitea', + name: 'Publish to Gitea', + input: { + repoUrl: 'gitea.com?repo=repo&owner=owner', + description: 'Initialize a gitea repository', + }, + }, + ], + }), + }, + { + description: + 'Initializes a Gitea repository with a default Branch, if not set defaults to master', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitea', + name: 'Publish to Gitea', + input: { + repoUrl: 'gitea.com?repo=repo&owner=owner', + defaultBranch: 'staging', + }, + }, + ], + }), + }, + { + description: + 'Initializes a Gitea repository with an initial commit message, if not set defaults to initial commit', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitea', + name: 'Publish to Gitea', + input: { + repoUrl: 'gitea.com?repo=repo&owner=owner', + gitCommitMessage: 'Initial Commit Message', + }, + }, + ], + }), + }, + { + description: + 'Initializes a Gitea repository with a repo Author Name, if not set defaults to Scaffolder', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitea', + name: 'Publish to Gitea', + input: { + repoUrl: 'gitea.com?repo=repo&owner=owner', + gitAuthorName: 'John Doe', + }, + }, + ], + }), + }, + { + description: 'Initializes a Gitea repository with a repo Author Email', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitea', + name: 'Publish to Gitea', + input: { + repoUrl: 'gitea.com?repo=repo&owner=owner', + gitAuthorEmail: 'johndoe@email.com', + }, + }, + ], + }), + }, + { + description: + 'Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitea', + name: 'Publish to Gitea', + input: { + repoUrl: 'gitea.com?repo=repo&owner=owner', + sourcePath: 'repository/', + }, + }, + ], + }), + }, + { + description: 'Initializes a Gitea repository with all properties being set', + example: yaml.stringify({ + steps: [ + { + id: 'publish', + action: 'publish:gitea', + name: 'Publish to Gitea', + input: { + repoUrl: 'gitea.com?repo=repo&owner=owner', + description: 'Initialize a gitea repository', + defaultBranch: 'staging', + gitCommitMessage: 'Initial Commit Message', + gitAuthorName: 'John Doe', + gitAuthorEmail: 'johndoe@email.com', + sourcePath: 'repository/', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts new file mode 100644 index 0000000000..bd40b68603 --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { PassThrough } from 'stream'; +import { createAcmeExampleAction } from './gitea'; +import { getVoidLogger } from '@backstage/backend-common'; + +describe('acme:example', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + it('should call action', async () => { + const action = createAcmeExampleAction(); + + const logger = getVoidLogger(); + jest.spyOn(logger, 'info'); + + await action.handler({ + input: { + myParameter: 'test', + }, + workspacePath: '/tmp', + logger, + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory() { + // Usage of mock-fs is recommended for testing of filesystem operations + throw new Error('Not implemented'); + }, + }); + + expect(logger.info).toHaveBeenCalledWith( + 'Running example template with parameters: test', + ); + }); +}); diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts new file mode 100644 index 0000000000..a740b4938e --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -0,0 +1,232 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { InputError } from '@backstage/errors'; +import { Config } from '@backstage/config'; +import { + getGiteaRequestOptions, + GiteaIntegrationConfig, + ScmIntegrationRegistry, +} from '@backstage/integration'; +import { + createTemplateAction, + getRepoSourceDirectory, + initRepoAndPush, + parseRepoUrl, +} from '@backstage/plugin-scaffolder-node'; +import { examples } from './gitea.examples'; +import fetch, { RequestInit, Response } from 'node-fetch'; +import crypto from 'crypto'; + +const createGiteaProject = async ( + config: GiteaIntegrationConfig, + options: { + projectName: string; + parent: string; + owner?: string; + description: string; + }, +): Promise => { + const { projectName, parent, owner, description } = options; + + const fetchOptions: RequestInit = { + method: 'PUT', + body: JSON.stringify({ + parent, + description, + owners: owner ? [owner] : [], + create_empty_commit: false, + }), + headers: { + ...getGiteaRequestOptions(config).headers, + 'Content-Type': 'application/json', + }, + }; + + // TODO + const response: Response = await fetch( + `${config.baseUrl}/a/projects/${encodeURIComponent(projectName)}`, + fetchOptions, + ); + if (response.status !== 201) { + throw new Error( + `Unable to create repository, ${response.status} ${ + response.statusText + }, ${await response.text()}`, + ); + } +}; + +const generateCommitMessage = ( + config: Config, + commitSubject?: string, +): string => { + const changeId = crypto.randomBytes(20).toString('hex'); + const msg = `${ + config.getOptionalString('scaffolder.defaultCommitMessage') || commitSubject + }\n\nChange-Id: I${changeId}`; + return msg; +}; + +/** + * Creates a new action that initializes a git repository using the content of the workspace. + * and publishes it to a Gitea instance. + * @public + */ +export function createPublishGiteaAction(options: { + integrations: ScmIntegrationRegistry; + config: Config; +}) { + const { integrations, config } = options; + + return createTemplateAction<{ + repoUrl: string; + description: string; + defaultBranch?: string; + gitCommitMessage?: string; + gitAuthorName?: string; + gitAuthorEmail?: string; + sourcePath?: string; + }>({ + id: 'publish:gitea', + description: + 'Initializes a git repository using the content of the workspace, and publishes it to Gitea.', + examples, + schema: { + input: { + type: 'object', + required: ['repoUrl'], + properties: { + repoUrl: { + title: 'Repository Location', + type: 'string', + }, + description: { + title: 'Repository Description', + type: 'string', + }, + defaultBranch: { + title: 'Default Branch', + type: 'string', + description: `Sets the default branch on the repository. The default value is 'master'`, + }, + gitCommitMessage: { + title: 'Git Commit Message', + type: 'string', + description: `Sets the commit message on the repository. The default value is 'initial commit'`, + }, + gitAuthorName: { + title: 'Default Author Name', + type: 'string', + description: `Sets the default author name for the commit. The default value is 'Scaffolder'`, + }, + gitAuthorEmail: { + title: 'Default Author Email', + type: 'string', + description: `Sets the default author email for the commit.`, + }, + sourcePath: { + title: 'Source Path', + type: 'string', + description: `Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.`, + }, + }, + }, + output: { + type: 'object', + properties: { + remoteUrl: { + title: 'A URL to the repository with the provider', + type: 'string', + }, + repoContentsUrl: { + title: 'A URL to the root of the repository', + type: 'string', + }, + commitHash: { + title: 'The git commit hash of the initial commit', + type: 'string', + }, + }, + }, + }, + async handler(ctx) { + const { + repoUrl, + description, + defaultBranch = 'main', + gitAuthorName, + gitAuthorEmail, + gitCommitMessage = 'initial commit', + sourcePath, + } = ctx.input; + + const { repo, host, owner, workspace } = parseRepoUrl( + repoUrl, + integrations, + ); + + const integrationConfig = integrations.gitea.byHost(host); + + if (!integrationConfig) { + throw new InputError( + `No matching integration configuration for host ${host}, please check your integrations config`, + ); + } + + if (!workspace) { + throw new InputError( + `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`, + ); + } + + await createGiteaProject(integrationConfig.config, { + description, + owner: owner, + projectName: repo, + parent: workspace, + }); + const auth = { + username: integrationConfig.config.username!, + password: integrationConfig.config.password!, + }; + const gitAuthorInfo = { + name: gitAuthorName + ? gitAuthorName + : config.getOptionalString('scaffolder.defaultAuthor.name'), + email: gitAuthorEmail + ? gitAuthorEmail + : config.getOptionalString('scaffolder.defaultAuthor.email'), + }; + // TODO + const remoteUrl = `${integrationConfig.config.baseUrl}/a/${repo}`; + const commitResult = await initRepoAndPush({ + dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath), + remoteUrl, + auth, + defaultBranch, + logger: ctx.logger, + commitMessage: generateCommitMessage(config, gitCommitMessage), + gitAuthorInfo, + }); + + const repoContentsUrl = `${integrationConfig.config.baseUrl}/${repo}/+/refs/heads/${defaultBranch}`; + ctx.output('remoteUrl', remoteUrl); + ctx.output('commitHash', commitResult?.commitHash); + ctx.output('repoContentsUrl', repoContentsUrl); + }, + }); +} diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/index.ts b/plugins/scaffolder-backend-module-gitea/src/actions/index.ts new file mode 100644 index 0000000000..ccf7f23fab --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/src/actions/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export * from './gitea'; diff --git a/plugins/scaffolder-backend-module-gitea/src/index.ts b/plugins/scaffolder-backend-module-gitea/src/index.ts new file mode 100644 index 0000000000..026da0735a --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/src/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A module for the scaffolder backend that lets you interact with gitea + * + * @packageDocumentation + */ + +export * from './actions'; diff --git a/yarn.lock b/yarn.lock index 3394871837..065b070197 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22051,6 +22051,22 @@ __metadata: languageName: node linkType: hard +"backstage-plugin-scaffolder-backend-module-gitea@workspace:plugins/scaffolder-backend-module-gitea": + version: 0.0.0-use.local + resolution: "backstage-plugin-scaffolder-backend-module-gitea@workspace:plugins/scaffolder-backend-module-gitea" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/integration": "workspace:^" + "@backstage/plugin-scaffolder-backend": "workspace:^" + "@backstage/plugin-scaffolder-node": "workspace:^" + node-fetch: ^2.6.7 + yaml: ^2.0.0 + languageName: unknown + linkType: soft + "badge-maker@npm:^3.3.0": version: 3.3.1 resolution: "badge-maker@npm:3.3.1" From f6426a32f90d505ca9836eacb1b1dd3b051986e5 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Fri, 15 Dec 2023 15:23:26 +0100 Subject: [PATCH 02/56] WIP. Create action to publish on gitea server Signed-off-by: cmoulliard --- .../src/actions/gitea.examples.ts | 6 +- .../src/actions/gitea.test.ts | 111 ++++++++++++++---- .../src/actions/gitea.ts | 38 +++--- plugins/scaffolder-node/src/actions/util.ts | 4 + 4 files changed, 117 insertions(+), 42 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts index 64d73d498f..748a869205 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.examples.ts @@ -20,7 +20,7 @@ import yaml from 'yaml'; export const examples: TemplateExample[] = [ { description: - 'Initializes a Gitea repository of contents in workspace and publish it to Gitea with default configuration.', + 'Initializes a Gitea repository using the content of the workspace and publish it to Gitea with default configuration.', example: yaml.stringify({ steps: [ { @@ -52,7 +52,7 @@ export const examples: TemplateExample[] = [ }, { description: - 'Initializes a Gitea repository with a default Branch, if not set defaults to master', + 'Initializes a Gitea repository with a default Branch, if not set defaults to main', example: yaml.stringify({ steps: [ { @@ -61,7 +61,7 @@ export const examples: TemplateExample[] = [ name: 'Publish to Gitea', input: { repoUrl: 'gitea.com?repo=repo&owner=owner', - defaultBranch: 'staging', + defaultBranch: 'main', }, }, ], 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 bd40b68603..52a29e2bbc 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -14,36 +14,103 @@ * limitations under the License. */ import { PassThrough } from 'stream'; -import { createAcmeExampleAction } from './gitea'; +/* import {rest} from 'msw';*/ +import { setupServer } from 'msw'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; import { getVoidLogger } from '@backstage/backend-common'; +import { createPublishGiteaAction } from './gitea'; +import { initRepoAndPush } from '@backstage/plugin-scaffolder-node'; -describe('acme:example', () => { - afterEach(() => { +jest.mock('@backstage/plugin-scaffolder-node', () => { + return { + ...jest.requireActual('@backstage/plugin-scaffolder-node'), + initRepoAndPush: jest.fn().mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }), + commitAndPushRepo: jest.fn().mockResolvedValue({ + commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', + }), + }; +}); + +describe('publish:gitea', () => { + const config = new ConfigReader({ + integrations: { + gitea: [ + { + host: 'gitea.com', + username: 'gitea_user', + password: 'gitea_password', + }, + ], + }, + }); + + const description = 'for the lols'; + const integrations = ScmIntegrations.fromConfig(config); + const action = createPublishGiteaAction({ integrations, config }); + const mockContext = { + input: { + repoUrl: 'gitea.com?repo=repo', + description, + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + const mockGitClient = { + createRepository: jest.fn(), + }; + + const server = setupServer(); + setupRequestMockHandlers(server); + + beforeEach(() => { jest.resetAllMocks(); }); - it('should call action', async () => { - const action = createAcmeExampleAction(); + it('should throw an error when the repoUrl is not well formed', async () => { + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'gitea.com?owner=o', description }, + }), + ).rejects.toThrow(/missing repo/); + }); - const logger = getVoidLogger(); - jest.spyOn(logger, 'info'); + it('should throw if there is no integration config provided for missing.com host', async () => { + await expect( + action.handler({ + ...mockContext, + input: { repoUrl: 'missing.com?repo=repo', description }, + }), + ).rejects.toThrow(/No matching integration configuration/); + }); - await action.handler({ - input: { - myParameter: 'test', - }, - workspacePath: '/tmp', - logger, - logStream: new PassThrough(), - output: jest.fn(), - createTemporaryDirectory() { - // Usage of mock-fs is recommended for testing of filesystem operations - throw new Error('Not implemented'); - }, + it('should throw if there is no repositoryId returned', async () => { + mockGitClient.createRepository.mockImplementation(() => ({ + remoteUrl: 'https://gitea.com', + id: null, + })); + + await action.handler(mockContext); + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + remoteUrl: 'https://gitea.com', + defaultBranch: 'main', + auth: { username: 'gitea_user', password: 'gitea_password' }, + logger: mockContext.logger, + commitMessage: 'initial commit', + gitAuthorInfo: {}, }); + }); - expect(logger.info).toHaveBeenCalledWith( - 'Running example template with parameters: test', - ); + afterEach(() => { + jest.resetAllMocks(); }); }); diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index a740b4938e..18e15fc191 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -35,20 +35,19 @@ const createGiteaProject = async ( config: GiteaIntegrationConfig, options: { projectName: string; - parent: string; + // parent: string; owner?: string; description: string; }, ): Promise => { - const { projectName, parent, owner, description } = options; + const { projectName, owner, description } = options; const fetchOptions: RequestInit = { - method: 'PUT', + method: 'POST', body: JSON.stringify({ - parent, + name: projectName, description, - owners: owner ? [owner] : [], - create_empty_commit: false, + owner, }), headers: { ...getGiteaRequestOptions(config).headers, @@ -57,8 +56,10 @@ const createGiteaProject = async ( }; // TODO + // Create a repository in Gitea + // API request: https://gitea.com/api/swagger#/user/createCurrentUserRepo const response: Response = await fetch( - `${config.baseUrl}/a/projects/${encodeURIComponent(projectName)}`, + `${config.baseUrl}/api/v1/user/repos`, fetchOptions, ); if (response.status !== 201) { @@ -174,31 +175,34 @@ export function createPublishGiteaAction(options: { sourcePath, } = ctx.input; - const { repo, host, owner, workspace } = parseRepoUrl( - repoUrl, - integrations, - ); + const { owner, repo, host } = parseRepoUrl(repoUrl, integrations); const integrationConfig = integrations.gitea.byHost(host); - if (!integrationConfig) { throw new InputError( `No matching integration configuration for host ${host}, please check your integrations config`, ); } - if (!workspace) { - throw new InputError( - `Invalid URL provider was included in the repo URL to create ${ctx.input.repoUrl}, missing workspace`, + if ( + !integrationConfig.config.username || + !integrationConfig.config.password + ) { + throw new Error( + 'Credentials for Gitea integration required for this action.', ); } + // TODO + // Check if the integration config includes a baseUrl + await createGiteaProject(integrationConfig.config, { description, owner: owner, projectName: repo, - parent: workspace, + // parent: workspace, }); + const auth = { username: integrationConfig.config.username!, password: integrationConfig.config.password!, @@ -212,7 +216,7 @@ export function createPublishGiteaAction(options: { : config.getOptionalString('scaffolder.defaultAuthor.email'), }; // TODO - const remoteUrl = `${integrationConfig.config.baseUrl}/a/${repo}`; + const remoteUrl = `${integrationConfig.config.baseUrl}/api/v1/user/${repo}`; const commitResult = await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath), remoteUrl, diff --git a/plugins/scaffolder-node/src/actions/util.ts b/plugins/scaffolder-node/src/actions/util.ts index 327cfa9c2f..7f7121ef16 100644 --- a/plugins/scaffolder-node/src/actions/util.ts +++ b/plugins/scaffolder-node/src/actions/util.ts @@ -92,6 +92,10 @@ export const parseRepoUrl = ( } break; } + case 'gitea': { + checkRequiredParams(parsed, 'repo'); + break; + } case 'gerrit': { checkRequiredParams(parsed, 'repo'); break; From 49d4e7617ffab6789ac65d88e8de05073275b1a6 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Fri, 15 Dec 2023 15:56:44 +0100 Subject: [PATCH 03/56] Import mising package to create the setupServer. Update yarn.locl and package.json Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/package.json | 4 +++- .../scaffolder-backend-module-gitea/src/actions/gitea.test.ts | 3 +-- yarn.lock | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 9140c294f8..239146d842 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -33,7 +33,9 @@ }, "devDependencies": { "@backstage/backend-common": "workspace:^", - "@backstage/cli": "workspace:^" + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "msw": "^1.0.0" }, "files": [ "dist" 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 52a29e2bbc..ae80f3bdb7 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -14,14 +14,13 @@ * limitations under the License. */ import { PassThrough } from 'stream'; -/* import {rest} from 'msw';*/ -import { setupServer } from 'msw'; import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; import { ScmIntegrations } from '@backstage/integration'; import { ConfigReader } from '@backstage/config'; import { getVoidLogger } from '@backstage/backend-common'; import { createPublishGiteaAction } from './gitea'; import { initRepoAndPush } from '@backstage/plugin-scaffolder-node'; +import { setupServer } from 'msw/node'; jest.mock('@backstage/plugin-scaffolder-node', () => { return { diff --git a/yarn.lock b/yarn.lock index 065b070197..3153025cca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22056,12 +22056,14 @@ __metadata: resolution: "backstage-plugin-scaffolder-backend-module-gitea@workspace:plugins/scaffolder-backend-module-gitea" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" + msw: ^1.0.0 node-fetch: ^2.6.7 yaml: ^2.0.0 languageName: unknown From bb0b6568afc6e4d5025a35857976eafd04aaa937 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Fri, 15 Dec 2023 16:48:30 +0100 Subject: [PATCH 04/56] Registering new publish gitea action within createBuiltinActions.ts Signed-off-by: cmoulliard --- .../catalog-info.yaml | 2 +- .../package.json | 2 +- .../actions/builtin/createBuiltinActions.ts | 2 + yarn.lock | 37 ++++++++++--------- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/catalog-info.yaml b/plugins/scaffolder-backend-module-gitea/catalog-info.yaml index 20511015cd..adfcc754fb 100644 --- a/plugins/scaffolder-backend-module-gitea/catalog-info.yaml +++ b/plugins/scaffolder-backend-module-gitea/catalog-info.yaml @@ -2,7 +2,7 @@ apiVersion: backstage.io/v1alpha1 kind: Component metadata: name: backstage-plugin-scaffolder-backend-module-gitea - title: 'backstage-plugin-scaffolder-backend-module-gitea' + title: '@backstage/plugin-scaffolder-backend-module-gitea' spec: lifecycle: experimental type: backstage-backend-plugin-module diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 239146d842..93dd86e92b 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -1,5 +1,5 @@ { - "name": "backstage-plugin-scaffolder-backend-module-gitea", + "name": "@backstage/plugin-scaffolder-backend-module-gitea", "description": "The gitea module for @backstage/plugin-scaffolder-backend", "version": "0.1.0-next.0", "main": "src/index.ts", diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index f9c15597a8..51b2a18a77 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -75,6 +75,8 @@ import { createPublishGerritReviewAction, } from '@backstage/plugin-scaffolder-backend-module-gerrit'; +import { createPublishGiteaAction } from '@backstage/plugin-scaffolder-backend-module-gitea'; + import { createPublishGitlabAction, createGitlabRepoPushAction, diff --git a/yarn.lock b/yarn.lock index 3153025cca..64bc6fc64e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8492,6 +8492,24 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-scaffolder-backend-module-gitea@workspace:^, @backstage/plugin-scaffolder-backend-module-gitea@workspace:plugins/scaffolder-backend-module-gitea": + version: 0.0.0-use.local + resolution: "@backstage/plugin-scaffolder-backend-module-gitea@workspace:plugins/scaffolder-backend-module-gitea" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/integration": "workspace:^" + "@backstage/plugin-scaffolder-backend": "workspace:^" + "@backstage/plugin-scaffolder-node": "workspace:^" + msw: ^1.0.0 + node-fetch: ^2.6.7 + yaml: ^2.0.0 + languageName: unknown + linkType: soft + "@backstage/plugin-scaffolder-backend-module-github@workspace:^, @backstage/plugin-scaffolder-backend-module-github@workspace:plugins/scaffolder-backend-module-github": version: 0.0.0-use.local resolution: "@backstage/plugin-scaffolder-backend-module-github@workspace:plugins/scaffolder-backend-module-github" @@ -8616,6 +8634,7 @@ __metadata: "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "workspace:^" "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "workspace:^" "@backstage/plugin-scaffolder-backend-module-gerrit": "workspace:^" + "@backstage/plugin-scaffolder-backend-module-gitea": "workspace:^" "@backstage/plugin-scaffolder-backend-module-github": "workspace:^" "@backstage/plugin-scaffolder-backend-module-gitlab": "workspace:^" "@backstage/plugin-scaffolder-common": "workspace:^" @@ -22051,24 +22070,6 @@ __metadata: languageName: node linkType: hard -"backstage-plugin-scaffolder-backend-module-gitea@workspace:plugins/scaffolder-backend-module-gitea": - version: 0.0.0-use.local - resolution: "backstage-plugin-scaffolder-backend-module-gitea@workspace:plugins/scaffolder-backend-module-gitea" - dependencies: - "@backstage/backend-common": "workspace:^" - "@backstage/backend-test-utils": "workspace:^" - "@backstage/cli": "workspace:^" - "@backstage/config": "workspace:^" - "@backstage/errors": "workspace:^" - "@backstage/integration": "workspace:^" - "@backstage/plugin-scaffolder-backend": "workspace:^" - "@backstage/plugin-scaffolder-node": "workspace:^" - msw: ^1.0.0 - node-fetch: ^2.6.7 - yaml: ^2.0.0 - languageName: unknown - linkType: soft - "badge-maker@npm:^3.3.0": version: 3.3.1 resolution: "badge-maker@npm:3.3.1" From b2953bd1085c85eb84240560cddd383a9c284a07 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Fri, 15 Dec 2023 17:02:14 +0100 Subject: [PATCH 05/56] Update package.json to add gitea module Signed-off-by: cmoulliard --- plugins/scaffolder-backend/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 9026761a7c..858591749c 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -64,6 +64,7 @@ "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "workspace:^", "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "workspace:^", "@backstage/plugin-scaffolder-backend-module-gerrit": "workspace:^", + "@backstage/plugin-scaffolder-backend-module-gitea": "workspace:^", "@backstage/plugin-scaffolder-backend-module-github": "workspace:^", "@backstage/plugin-scaffolder-backend-module-gitlab": "workspace:^", "@backstage/plugin-scaffolder-common": "workspace:^", From f1645f3e0a4649866775658f37306b102b9396c4 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Fri, 15 Dec 2023 18:19:39 +0100 Subject: [PATCH 06/56] Register the missing gitea action to publish Signed-off-by: cmoulliard --- .../scaffolder/actions/builtin/createBuiltinActions.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index 51b2a18a77..2ee0a7644f 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -75,14 +75,14 @@ import { createPublishGerritReviewAction, } from '@backstage/plugin-scaffolder-backend-module-gerrit'; -import { createPublishGiteaAction } from '@backstage/plugin-scaffolder-backend-module-gitea'; - import { createPublishGitlabAction, createGitlabRepoPushAction, createPublishGitlabMergeRequestAction, } from '@backstage/plugin-scaffolder-backend-module-gitlab'; +import { createPublishGiteaAction } from '@backstage/plugin-scaffolder-backend-module-gitea'; + /** * The options passed to {@link createBuiltinActions} * @public @@ -160,6 +160,10 @@ export const createBuiltinActions = ( integrations, config, }), + createPublishGiteaAction({ + integrations, + config, + }), createPublishGithubAction({ integrations, config, From f8afc5c059abf9ee3ce02e9ece15526c1fb09dff Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Tue, 19 Dec 2023 14:58:08 +0100 Subject: [PATCH 07/56] Align default branch name. master -> main Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 18e15fc191..011be04fc1 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -122,7 +122,7 @@ export function createPublishGiteaAction(options: { defaultBranch: { title: 'Default Branch', type: 'string', - description: `Sets the default branch on the repository. The default value is 'master'`, + description: `Sets the default branch on the repository. The default value is 'main'`, }, gitCommitMessage: { title: 'Git Commit Message', From a37b65c79ee3343282d3dd666141b28cb7bd0dfd Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Tue, 19 Dec 2023 15:00:47 +0100 Subject: [PATCH 08/56] Reverted name cgange from gitea.tst to example.ts, example.test.ts Signed-off-by: cmoulliard --- packages/cli/src/lib/new/factories/scaffolderModule.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/lib/new/factories/scaffolderModule.test.ts b/packages/cli/src/lib/new/factories/scaffolderModule.test.ts index 767ae0fc44..b43946228b 100644 --- a/packages/cli/src/lib/new/factories/scaffolderModule.test.ts +++ b/packages/cli/src/lib/new/factories/scaffolderModule.test.ts @@ -77,8 +77,8 @@ describe('scaffolderModule factory', () => { 'templating package.json.hbs', 'templating index.ts.hbs', 'copying index.ts', - 'copying gitea.test.ts', - 'copying gitea.ts', + 'copying example.test.ts', + 'copying example.ts', 'copying index.ts', 'Installing:', `moving plugins${sep}scaffolder-backend-module-test`, From 4d2c01bfbff613825ace8449559529a78e56fa1e Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Tue, 19 Dec 2023 15:02:25 +0100 Subject: [PATCH 09/56] Changing the version from 0.1.0-next.0 to 0.0.0 Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 93dd86e92b..c9e9a4e07d 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-gitea", "description": "The gitea module for @backstage/plugin-scaffolder-backend", - "version": "0.1.0-next.0", + "version": "0.0.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", From 827d88fce12dab230882a009f5104843b194ad7d Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Tue, 19 Dec 2023 15:03:42 +0100 Subject: [PATCH 10/56] Removing the CHANGELOG.md file as the releasing flow will create it Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/CHANGELOG.md | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 plugins/scaffolder-backend-module-gitea/CHANGELOG.md diff --git a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md deleted file mode 100644 index a4f1f20179..0000000000 --- a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ -# @backstage/plugin-scaffolder-backend-module-gitea - -## 0.1.0-next.0 - -### Minor Changes - -- TODO: Add Gitea Scaffolder Plugin From 11d697be7709e43d46417e7a8afe55c2bf3adc40 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Tue, 19 Dec 2023 15:12:41 +0100 Subject: [PATCH 11/56] Updating the api-report.md file Signed-off-by: cmoulliard --- .../api-report.md | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/api-report.md b/plugins/scaffolder-backend-module-gitea/api-report.md index 51e2f3bd17..f83ae73fb7 100644 --- a/plugins/scaffolder-backend-module-gitea/api-report.md +++ b/plugins/scaffolder-backend-module-gitea/api-report.md @@ -1,20 +1,27 @@ -## API Report File for "backstage-plugin-scaffolder-backend-module-gitea" +## API Report File for "@backstage/plugin-scaffolder-backend-module-gitea" > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; +import { ScmIntegrationRegistry } from '@backstage/integration'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; // @public -export function createAcmeExampleAction(): TemplateAction< +export function createPublishGiteaAction(options: { + integrations: ScmIntegrationRegistry; + config: Config; +}): TemplateAction< { - myParameter: string; + repoUrl: string; + description: string; + defaultBranch?: string | undefined; + gitCommitMessage?: string | undefined; + gitAuthorName?: string | undefined; + gitAuthorEmail?: string | undefined; + sourcePath?: string | undefined; }, JsonObject >; - -// Warnings were encountered during analysis: -// -// src/index.d.ts:2:25 - (tsdoc-characters-after-block-tag) The token "@backstage" looks like a TSDoc tag but contains an invalid character "/"; if it is not a tag, use a backslash to escape the "@" ``` From 90836c82e4f0cdecd3a210c02bfe91e8e9793380 Mon Sep 17 00:00:00 2001 From: Charles Moulliard Date: Tue, 19 Dec 2023 15:23:37 +0100 Subject: [PATCH 12/56] Update plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Charles Moulliard Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 011be04fc1..7b6547b30d 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -169,8 +169,8 @@ export function createPublishGiteaAction(options: { repoUrl, description, defaultBranch = 'main', - gitAuthorName, - gitAuthorEmail, + gitAuthorName = config.getOptionalString('scaffolder.defaultAuthor.name'), + gitAuthorEmail = config.getOptionalString('scaffolder.defaultAuthor.email'), gitCommitMessage = 'initial commit', sourcePath, } = ctx.input; From 3e20f53e49e2c26d10042d3f04ce6c3be3cea131 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 20 Dec 2023 09:36:09 +0100 Subject: [PATCH 13/56] Rename basic to Basic Signed-off-by: cmoulliard --- packages/integration/src/gitea/core.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integration/src/gitea/core.ts b/packages/integration/src/gitea/core.ts index 95caa24c06..6d11a4ffde 100644 --- a/packages/integration/src/gitea/core.ts +++ b/packages/integration/src/gitea/core.ts @@ -120,7 +120,7 @@ export function getGiteaRequestOptions(config: GiteaIntegrationConfig): { } if (username) { - headers.Authorization = `basic ${Buffer.from( + headers.Authorization = `Basic ${Buffer.from( `${username}:${password}`, ).toString('base64')}`; } else { From 07bd2fbeb43259acd8c8b64f2601b264b47d3164 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 20 Dec 2023 09:40:08 +0100 Subject: [PATCH 14/56] Rename token to Token Signed-off-by: cmoulliard --- packages/integration/src/gitea/core.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integration/src/gitea/core.ts b/packages/integration/src/gitea/core.ts index 6d11a4ffde..f188613d2a 100644 --- a/packages/integration/src/gitea/core.ts +++ b/packages/integration/src/gitea/core.ts @@ -124,7 +124,7 @@ export function getGiteaRequestOptions(config: GiteaIntegrationConfig): { `${username}:${password}`, ).toString('base64')}`; } else { - headers.Authorization = `token ${password}`; + headers.Authorization = `Token ${password}`; } return { From 51fec95f3340c20d772a73add9d28e748ef08a96 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 20 Dec 2023 10:52:28 +0100 Subject: [PATCH 15/56] Add test to valid the baseURL Signed-off-by: cmoulliard --- packages/integration/src/gitea/config.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/integration/src/gitea/config.ts b/packages/integration/src/gitea/config.ts index 28070d570d..ae1fe5ee87 100644 --- a/packages/integration/src/gitea/config.ts +++ b/packages/integration/src/gitea/config.ts @@ -16,7 +16,7 @@ import { Config } from '@backstage/config'; import { trimEnd } from 'lodash'; -import { isValidHost } from '../helpers'; +import { isValidHost, isValidUrl } from '../helpers'; /** * The configuration for a single Gitea integration. @@ -62,6 +62,10 @@ export function readGiteaConfig(config: Config): GiteaIntegrationConfig { throw new Error( `Invalid Gitea integration config, '${host}' is not a valid host`, ); + } else if (baseUrl && !isValidUrl(baseUrl)) { + throw new Error( + `Invalid Gitea integration config, '${baseUrl}' is not a valid baseUrl`, + ); } if (baseUrl) { From 1c91c52a096f95a0479eefbbbc98d0be46cca52d Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 20 Dec 2023 10:53:00 +0100 Subject: [PATCH 16/56] Updating the test case to create a git repo Signed-off-by: cmoulliard --- .../src/actions/gitea.test.ts | 46 +++++++++++++------ .../src/actions/gitea.ts | 1 - 2 files changed, 31 insertions(+), 16 deletions(-) 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 ae80f3bdb7..6461726dcf 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -14,12 +14,13 @@ * limitations under the License. */ import { PassThrough } from 'stream'; -import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; import { ScmIntegrations } from '@backstage/integration'; import { ConfigReader } from '@backstage/config'; import { getVoidLogger } from '@backstage/backend-common'; import { createPublishGiteaAction } from './gitea'; import { initRepoAndPush } from '@backstage/plugin-scaffolder-node'; +import { rest } from 'msw'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; import { setupServer } from 'msw/node'; jest.mock('@backstage/plugin-scaffolder-node', () => { @@ -28,9 +29,6 @@ jest.mock('@backstage/plugin-scaffolder-node', () => { initRepoAndPush: jest.fn().mockResolvedValue({ commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', }), - commitAndPushRepo: jest.fn().mockResolvedValue({ - commitHash: '220f19cc36b551763d157f1b5e4a4b446165dbd6', - }), }; }); @@ -40,6 +38,7 @@ describe('publish:gitea', () => { gitea: [ { host: 'gitea.com', + baseUrl: 'https://gitea.com', username: 'gitea_user', password: 'gitea_password', }, @@ -52,7 +51,7 @@ describe('publish:gitea', () => { const action = createPublishGiteaAction({ integrations, config }); const mockContext = { input: { - repoUrl: 'gitea.com?repo=repo', + repoUrl: 'gitea.com?repo=repo&owner=owner', description, }, workspacePath: 'lol', @@ -62,10 +61,6 @@ describe('publish:gitea', () => { createTemporaryDirectory: jest.fn(), }; - const mockGitClient = { - createRepository: jest.fn(), - }; - const server = setupServer(); setupRequestMockHandlers(server); @@ -92,15 +87,36 @@ describe('publish:gitea', () => { }); it('should throw if there is no repositoryId returned', async () => { - mockGitClient.createRepository.mockImplementation(() => ({ - remoteUrl: 'https://gitea.com', - id: null, - })); + server.use( + rest.put('https://gitea.com/api/v1/user/repo', (req, res, ctx) => { + expect(req.headers.get('Authorization')).toBe( + 'Basic Z2l0ZWFfdXNlcjpnaXRlYV9wYXNzd29yZA==', + ); + expect(req.body).toEqual({ + create_empty_commit: false, + owners: ['owner'], + description, + parent: 'workspace', + }); + return res( + ctx.status(201), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }), + ); + + await action.handler({ + ...mockContext, + input: { + ...mockContext.input, + repoUrl: 'gitea.com?owner=owner&repo=repo', + }, + }); - await action.handler(mockContext); expect(initRepoAndPush).toHaveBeenCalledWith({ dir: mockContext.workspacePath, - remoteUrl: 'https://gitea.com', + remoteUrl: 'https://gitea.com/api/v1/user/repo', 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 7b6547b30d..0c7a5113ee 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -47,7 +47,6 @@ const createGiteaProject = async ( body: JSON.stringify({ name: projectName, description, - owner, }), headers: { ...getGiteaRequestOptions(config).headers, From 331c4c242ac438cb0edfcd5d9c62b2b83447071e Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 20 Dec 2023 11:49:55 +0100 Subject: [PATCH 17/56] Try catch the method calling fetch Signed-off-by: cmoulliard --- .../src/actions/gitea.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 0c7a5113ee..03f3ce27e2 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -57,10 +57,12 @@ const createGiteaProject = async ( // TODO // Create a repository in Gitea // API request: https://gitea.com/api/swagger#/user/createCurrentUserRepo - const response: Response = await fetch( - `${config.baseUrl}/api/v1/user/repos`, - fetchOptions, - ); + let response: Response; + try { + response = await fetch(`${config.baseUrl}/api/v1/user/repos`, fetchOptions); + } catch (e) { + throw new Error(`Unable to create repository, ${e}`); + } if (response.status !== 201) { throw new Error( `Unable to create repository, ${response.status} ${ @@ -168,8 +170,12 @@ export function createPublishGiteaAction(options: { repoUrl, description, defaultBranch = 'main', - gitAuthorName = config.getOptionalString('scaffolder.defaultAuthor.name'), - gitAuthorEmail = config.getOptionalString('scaffolder.defaultAuthor.email'), + gitAuthorName = config.getOptionalString( + 'scaffolder.defaultAuthor.name', + ), + gitAuthorEmail = config.getOptionalString( + 'scaffolder.defaultAuthor.email', + ), gitCommitMessage = 'initial commit', sourcePath, } = ctx.input; From 38ebfd198044d433b27531084e181913f4371fd8 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 20 Dec 2023 12:00:48 +0100 Subject: [PATCH 18/56] Remove non needed declarations Signed-off-by: cmoulliard --- .../scaffolder-backend-module-gitea/src/actions/gitea.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 03f3ce27e2..a68f92592e 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -170,12 +170,8 @@ export function createPublishGiteaAction(options: { repoUrl, description, defaultBranch = 'main', - gitAuthorName = config.getOptionalString( - 'scaffolder.defaultAuthor.name', - ), - gitAuthorEmail = config.getOptionalString( - 'scaffolder.defaultAuthor.email', - ), + gitAuthorName, + gitAuthorEmail, gitCommitMessage = 'initial commit', sourcePath, } = ctx.input; From 31e0e51bb99fb05077a65c6692767670d7971761 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 20 Dec 2023 12:05:20 +0100 Subject: [PATCH 19/56] Fix url typo error: ../repo => ../repos Signed-off-by: cmoulliard --- .../scaffolder-backend-module-gitea/src/actions/gitea.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6461726dcf..6290b2b33d 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -88,7 +88,7 @@ describe('publish:gitea', () => { it('should throw if there is no repositoryId returned', async () => { server.use( - rest.put('https://gitea.com/api/v1/user/repo', (req, res, ctx) => { + rest.post('https://gitea.com/api/v1/user/repos', (req, res, ctx) => { expect(req.headers.get('Authorization')).toBe( 'Basic Z2l0ZWFfdXNlcjpnaXRlYV9wYXNzd29yZA==', ); From 3bff96fc389c8983a66603983a9a60e4462d87f2 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 20 Dec 2023 14:25:30 +0100 Subject: [PATCH 20/56] Fixing matching condition Signed-off-by: cmoulliard --- .../src/actions/gitea.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 6290b2b33d..4be82a0e12 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -38,7 +38,6 @@ describe('publish:gitea', () => { gitea: [ { host: 'gitea.com', - baseUrl: 'https://gitea.com', username: 'gitea_user', password: 'gitea_password', }, @@ -93,10 +92,8 @@ describe('publish:gitea', () => { 'Basic Z2l0ZWFfdXNlcjpnaXRlYV9wYXNzd29yZA==', ); expect(req.body).toEqual({ - create_empty_commit: false, - owners: ['owner'], + name: 'repo', description, - parent: 'workspace', }); return res( ctx.status(201), @@ -120,8 +117,11 @@ describe('publish:gitea', () => { defaultBranch: 'main', auth: { username: 'gitea_user', password: 'gitea_password' }, logger: mockContext.logger, - commitMessage: 'initial commit', - gitAuthorInfo: {}, + commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'), + gitAuthorInfo: { + email: undefined, + name: undefined, + }, }); }); From 3a40f3c5dfc2bbc746efbd4106c0481f184fb302 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 20 Dec 2023 16:59:50 +0100 Subject: [PATCH 21/56] Removing owner as non needed Signed-off-by: cmoulliard --- .../src/actions/gitea.test.ts | 3 ++- .../scaffolder-backend-module-gitea/src/actions/gitea.ts | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) 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 4be82a0e12..ee0457cac0 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -88,6 +88,7 @@ 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) => { + // Basic auth must match the user and password defined part of the config expect(req.headers.get('Authorization')).toBe( 'Basic Z2l0ZWFfdXNlcjpnaXRlYV9wYXNzd29yZA==', ); @@ -107,7 +108,7 @@ describe('publish:gitea', () => { ...mockContext, input: { ...mockContext.input, - repoUrl: 'gitea.com?owner=owner&repo=repo', + repoUrl: 'gitea.com?repo=repo', }, }); diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index a68f92592e..cc16359d75 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -40,7 +40,7 @@ const createGiteaProject = async ( description: string; }, ): Promise => { - const { projectName, owner, description } = options; + const { projectName, description } = options; const fetchOptions: RequestInit = { method: 'POST', @@ -176,7 +176,7 @@ export function createPublishGiteaAction(options: { sourcePath, } = ctx.input; - const { owner, repo, host } = parseRepoUrl(repoUrl, integrations); + const { repo, host } = parseRepoUrl(repoUrl, integrations); const integrationConfig = integrations.gitea.byHost(host); if (!integrationConfig) { @@ -199,7 +199,7 @@ export function createPublishGiteaAction(options: { await createGiteaProject(integrationConfig.config, { description, - owner: owner, + // owner: owner, projectName: repo, // parent: workspace, }); From 701eefc71eb3aa22535e51e06fb3d3e9e79721e7 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 3 Jan 2024 15:18:58 +0100 Subject: [PATCH 22/56] 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); From 17235fcaec32d047a64182c958aae5e4eab66bb3 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 3 Jan 2024 17:03:10 +0100 Subject: [PATCH 23/56] Removing the non needed import for logger Signed-off-by: cmoulliard --- packages/integration/src/gitea/core.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/integration/src/gitea/core.ts b/packages/integration/src/gitea/core.ts index 40f1c0fb6d..f188613d2a 100644 --- a/packages/integration/src/gitea/core.ts +++ b/packages/integration/src/gitea/core.ts @@ -14,7 +14,6 @@ * limitations under the License. */ import { GiteaIntegrationConfig } from './config'; -import { Logger } from 'winston'; /** * Given a URL pointing to a file, returns a URL From ca0ac99493605b1657f975b0a47103a38495097d Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 3 Jan 2024 17:04:08 +0100 Subject: [PATCH 24/56] Add a sleep of 2s to let the step to register the newly catalog created from the gitea server Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index b11d5bf765..e8c7328245 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -216,6 +216,8 @@ export function createPublishGiteaAction(options: { sourcePath, } = ctx.input; + const sleep = (ms: number | undefined) => + new Promise(r => setTimeout(r, ms)); const { repo, host, owner } = parseRepoUrl(repoUrl, integrations); const integrationConfig = integrations.gitea.byHost(host); @@ -269,6 +271,9 @@ export function createPublishGiteaAction(options: { gitAuthorInfo, }); + // TODO: As mentioned by Ben lambert, we should poll an endpoint to see if it's ready yet instead of hard coding a sleep + await sleep(2000); + const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/+/refs/${defaultBranch}`; ctx.output('remoteUrl', remoteUrl); ctx.output('commitHash', commitResult?.commitHash); From 8a88e91817f79258e6392acbd79338cb38856e7d Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 4 Jan 2024 09:54:00 +0100 Subject: [PATCH 25/56] Switched the sleep 3s to a while condition to check if we got from the repository created the metadata Signed-off-by: cmoulliard --- .../src/actions/gitea.ts | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index e8c7328245..50b215f7d8 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -31,6 +31,36 @@ import { examples } from './gitea.examples'; import fetch, { RequestInit, Response } from 'node-fetch'; import crypto from 'crypto'; +const checkGiteaOrgRepo = async ( + config: GiteaIntegrationConfig, + options: { + owner?: string; + repo: string; + defaultBranch?: string; + }, +): Promise => { + const { owner, repo, defaultBranch } = options; + let response: Response; + const getOptions: RequestInit = { + method: 'GET', + headers: { + ...getGiteaRequestOptions(config).headers, + 'Content-Type': 'application/json', + }, + }; + + try { + response = await fetch( + `${config.baseUrl}/api/v1/repos/${owner}/${repo}/contents?ref=${defaultBranch}`, + getOptions, + ); + } catch (e) { + throw new Error( + `Unable to get the repository: ${owner}/${repo} metadata , ${e}`, + ); + } + return response; +}; const checkGiteaOrg = async ( config: GiteaIntegrationConfig, options: { @@ -218,6 +248,7 @@ export function createPublishGiteaAction(options: { const sleep = (ms: number | undefined) => new Promise(r => setTimeout(r, ms)); + const { repo, host, owner } = parseRepoUrl(repoUrl, integrations); const integrationConfig = integrations.gitea.byHost(host); @@ -271,8 +302,21 @@ export function createPublishGiteaAction(options: { gitAuthorInfo, }); - // TODO: As mentioned by Ben lambert, we should poll an endpoint to see if it's ready yet instead of hard coding a sleep - await sleep(2000); + // Check if the repo is available + let response: Response; + response = await checkGiteaOrgRepo(integrationConfig.config, { + owner, + repo, + defaultBranch, + }); + while (response.status !== 200) { + await sleep(1000); + response = await checkGiteaOrgRepo(integrationConfig.config, { + owner, + repo, + defaultBranch, + }); + } const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/+/refs/${defaultBranch}`; ctx.output('remoteUrl', remoteUrl); From 458bf21b7f10bdad8ed2559c8f1c70d1d15773a7 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 4 Jan 2024 14:01:05 +0100 Subject: [PATCH 26/56] Added a new changeSet Signed-off-by: cmoulliard --- .changeset/metal-clocks-suffer.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/metal-clocks-suffer.md diff --git a/.changeset/metal-clocks-suffer.md b/.changeset/metal-clocks-suffer.md new file mode 100644 index 0000000000..f94deb380c --- /dev/null +++ b/.changeset/metal-clocks-suffer.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitea': minor +'@backstage/plugin-scaffolder-backend': minor +'@backstage/plugin-scaffolder-node': minor +'@backstage/integration': patch +--- + +Added support to create a git repository and publish a scaffolded project using a new action "publish:gitea" for gitea. The action currently supports to create a gitea org's repository - https://gitea.com/api/swagger#/organization/createOrgRepo From 39bc3d0d7d0913dc7ffc41a5fe103485f35ae5f1 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 4 Jan 2024 14:54:41 +0100 Subject: [PATCH 27/56] Updated the instructions to use the publish:gitea action Signed-off-by: cmoulliard --- .../scaffolder-backend-module-gitea/README.md | 162 +++++++++++++++++- 1 file changed, 159 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/README.md b/plugins/scaffolder-backend-module-gitea/README.md index 84b8ab6ea5..9d733e76ff 100644 --- a/plugins/scaffolder-backend-module-gitea/README.md +++ b/plugins/scaffolder-backend-module-gitea/README.md @@ -1,5 +1,161 @@ -# backstage-plugin-scaffolder-backend-module-gitea +# scaffolder-backend-module-gitea -The gitea module for [@backstage/plugin-scaffolder-backend](https://www.npmjs.com/package/@backstage/plugin-scaffolder-backend). +Welcome to the `publish:gitea` action of the `scaffolder-gitea-backend`. -_This plugin was created through the Backstage CLI_ +## Getting started + +To use this action, you will have to add the package using the following command to be executed at the root of your backstage project: + +```bash +yarn add --cwd packages/backend +@backstage/plugin-scaffolder-backend-module-gitea +``` + +Configure the action (if not yet done): +(you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options): + +```typescript +// plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts + +export const createBuiltinActions = ( +... + const actions = [ + ... + createPublishGiteaAction({ + integrations, + config, + }), + ... + ]; + return actions as TemplateAction[]; +}; +``` + +Before to create a template, include to your `app-config.yaml` file the +gitea host and credentials under the `integrations:` section + +```yaml +integrations: + gitea: + - host: gitea.com + username: '' + password: '' + - host: localhost:3333 + username: '' + password: '' +``` + +**NOTE**: As backstage will issue HTTPS/TLS requests to the gitea instance, it is needed to configure `gitea` with a valid certificate or at least with a +self-signed certificate `gitea cert --host localhost -ca`. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA pem file before to launch backstage ! + +When done, you can use the action in your template: + +```yaml +apiVersion: scaffolder.backstage.io/v1beta3 +kind: Template +metadata: + name: quarkus-web-template + title: Quarkus Hello world + description: Create a simple microservice using Quarkus + tags: + - java + - quarkus +spec: + owner: quarkus + type: service + parameters: + - title: Provide Information for Application + required: + - component_id + - owner + - java_package_name + properties: + component_id: + title: Name + type: string + description: Unique name of the component + default: my-quarkus-app + ui:field: EntityNamePicker + group_id: + title: Group Id + type: string + default: io.quarkus + description: Maven Group Id + artifact_id: + title: Artifact Id + type: string + default: quarkus-app + description: Maven Artifact Id + java_package_name: + title: Java Package Name + default: io.quarkus.demo + type: string + description: Name for the java package. eg (io.quarkus.blah) + owner: + title: Owner + type: string + description: IdP owner of the component + ui:field: OwnerPicker + ui:options: + allowedKinds: + - Group + + - title: Application git repository Information + required: + - repoUrl + properties: + repoUrl: + title: Repository Location + type: string + ui:field: RepoUrlPicker + ui:options: + allowedHosts: + - localhost:3333 + - gitea.: + + steps: + - id: template + name: Generating component + action: fetch:template + input: + url: ./skeleton + copyWithoutTemplating: + - .github/workflows/* + values: + component_id: ${{ parameters.component_id }} + namespace: ${{ parameters.component_id }}-dev + description: ${{ parameters.description }} + group_id: ${{ parameters.group_id }} + artifact_id: ${{ parameters.artifact_id }} + java_package_name: ${{ parameters.java_package_name }} + owner: ${{ parameters.owner }} + destination: ${{ (parameters.repoUrl | parseRepoUrl).owner }}/${{ (parameters.repoUrl | parseRepoUrl).repo }} + quay_destination: ${{ parameters.image_organization }}/${{ parameters.component_id }} + port: 8080 + + - id: publish + name: Publishing to a gitea git repository + action: publish:gitea + input: + description: This is ${{ parameters.component_id }} + repoUrl: ${{ parameters.repoUrl }} + defaultBranch: main + + - id: register + if: ${{ parameters.dryRun !== true }} + name: Register + action: catalog:register + input: + repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }} + catalogInfoPath: '/catalog-info.yaml' + + output: + links: + - title: Source Code Repository + url: ${{ steps.publish.output.remoteUrl }} + - title: Open Component in catalog + icon: catalog + entityRef: ${{ steps.register.output.entityRef }} +``` + +Enjoy ;-) From 1a49b1d137bdbe2ae46e98eb1320bf439e095211 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 4 Jan 2024 18:16:26 +0100 Subject: [PATCH 28/56] Added a TODO task as the code using while condition is failing like a mention to pass the branch name to the catalogInfoPath Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/README.md | 4 +++- .../src/actions/gitea.ts | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend-module-gitea/README.md b/plugins/scaffolder-backend-module-gitea/README.md index 9d733e76ff..6b42eca9d0 100644 --- a/plugins/scaffolder-backend-module-gitea/README.md +++ b/plugins/scaffolder-backend-module-gitea/README.md @@ -48,6 +48,8 @@ integrations: **NOTE**: As backstage will issue HTTPS/TLS requests to the gitea instance, it is needed to configure `gitea` with a valid certificate or at least with a self-signed certificate `gitea cert --host localhost -ca`. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA pem file before to launch backstage ! +**WARNING**: Please pass the branch name part of the `catalogInfoPath` for the action `register` till we will fix this issue (e.g `main/catalog-info.yaml`) ! + When done, you can use the action in your template: ```yaml @@ -147,7 +149,7 @@ spec: action: catalog:register input: repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }} - catalogInfoPath: '/catalog-info.yaml' + catalogInfoPath: 'main/catalog-info.yaml' output: links: diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 50b215f7d8..76ddb189a6 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -302,6 +302,16 @@ export function createPublishGiteaAction(options: { gitAuthorInfo, }); + /* + TODO: This code is commented till it will be fixed. + When we use the code hereafter, we got the following error: + "Unable to read url, Error: Unknown encoding: undefined\n at DefaultLocationService.processEntities" + as commented here: Still getting the error: https://github.com/backstage/backstage/pull/21890#issuecomment-1876733870 + + Such an issue do not exist using sleep 3s. + + WARNING: To allow to register within the catalog the new project, it is also needed to pass within the catalogInfoPath the branch name (e.g: main/catalog-info.yaml) + // Check if the repo is available let response: Response; response = await checkGiteaOrgRepo(integrationConfig.config, { @@ -317,6 +327,8 @@ export function createPublishGiteaAction(options: { defaultBranch, }); } + */ + await sleep(3000); const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/+/refs/${defaultBranch}`; ctx.output('remoteUrl', remoteUrl); From 82ae17b19c8764ee6691f55b13258d0f59d07803 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 4 Jan 2024 18:27:24 +0100 Subject: [PATCH 29/56] Use the proper repoContentsUrl to access gitea repo and enhance the test case Signed-off-by: cmoulliard --- .../src/actions/gitea.test.ts | 5 +++++ plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) 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 5486b5b69f..8936c34690 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -137,6 +137,11 @@ describe('publish:gitea', () => { name: undefined, }, }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'repoContentsUrl', + 'https://gitea.com/org1/repo/src/branch/main', + ); }); afterEach(() => { diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 76ddb189a6..b8ab703460 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -330,7 +330,7 @@ export function createPublishGiteaAction(options: { */ await sleep(3000); - const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/+/refs/${defaultBranch}`; + const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/src/branch/${defaultBranch}`; ctx.output('remoteUrl', remoteUrl); ctx.output('commitHash', commitResult?.commitHash); ctx.output('repoContentsUrl', repoContentsUrl); From a72521bae8a6991c4c2ddcd25aeba1bfff415512 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 4 Jan 2024 19:03:31 +0100 Subject: [PATCH 30/56] Adding a new test case covering the url we are looking for when we register a new entry to the catalog using the action. Removing the gerrit stuff Signed-off-by: cmoulliard --- packages/integration/src/gitea/core.test.ts | 36 +++++++++++---------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/packages/integration/src/gitea/core.test.ts b/packages/integration/src/gitea/core.test.ts index fe6381d52e..ba41fa4761 100644 --- a/packages/integration/src/gitea/core.test.ts +++ b/packages/integration/src/gitea/core.test.ts @@ -30,6 +30,22 @@ describe('gitea core', () => { const worker = setupServer(); setupRequestMockHandlers(worker); + describe('getGiteaFileCatalogInfoCntentsUrl', () => { + it('can create an url from arguments', () => { + const config: GiteaIntegrationConfig = { + host: 'gitea.com', + }; + expect( + getGiteaFileContentsUrl( + config, + 'https://gitea.com/org1/repo1/src/branch/main/catalog-info.yaml', + ), + ).toEqual( + 'https://gitea.com/api/v1/repos/org1/repo1/contents/catalog-info.yaml?ref=main', + ); + }); + }); + describe('getGiteaFileContentsUrl', () => { it('can create an url from arguments', () => { const config: GiteaIntegrationConfig = { @@ -94,29 +110,15 @@ describe('gitea core', () => { }); }); - describe('getGerritRequestOptions', () => { - it('adds token header when only a password is specified', () => { - const authRequest: GiteaIntegrationConfig = { - host: 'gerrit.com', - password: 'P', - }; - const anonymousRequest: GiteaIntegrationConfig = { - host: 'gerrit.com', - }; - expect( - (getGiteaRequestOptions(authRequest).headers as any).Authorization, - ).toEqual('token P'); - expect(getGiteaRequestOptions(anonymousRequest).headers).toBeUndefined(); - }); - + describe('getGiteaRequestOptions', () => { it('adds basic auth when username and password are specified', () => { const authRequest: GiteaIntegrationConfig = { - host: 'gerrit.com', + host: 'gitea.com', username: 'username', password: 'P', }; - const basicAuthentication = `basic ${Buffer.from( + const basicAuthentication = `Basic ${Buffer.from( `${authRequest.username}:${authRequest.password}`, ).toString('base64')}`; From 2b2fdccaa68bdb98826bce846d272574a35e0510 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 4 Jan 2024 19:15:53 +0100 Subject: [PATCH 31/56] Fixing the issue about the branch name not included to the url converted. Wrong: "https://localhost:3333/api/v1/repos/org1/repo1/contents/?ref=catalog-info.yaml" and good: "https://localhost:3333/api/v1/repos/org1/repo1/contents/catalog-info.yaml?ref=maincatalog-info.yaml" Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/README.md | 2 -- plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/README.md b/plugins/scaffolder-backend-module-gitea/README.md index 6b42eca9d0..4a710a400c 100644 --- a/plugins/scaffolder-backend-module-gitea/README.md +++ b/plugins/scaffolder-backend-module-gitea/README.md @@ -48,8 +48,6 @@ integrations: **NOTE**: As backstage will issue HTTPS/TLS requests to the gitea instance, it is needed to configure `gitea` with a valid certificate or at least with a self-signed certificate `gitea cert --host localhost -ca`. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA pem file before to launch backstage ! -**WARNING**: Please pass the branch name part of the `catalogInfoPath` for the action `register` till we will fix this issue (e.g `main/catalog-info.yaml`) ! - When done, you can use the action in your template: ```yaml diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index b8ab703460..7905f42e4a 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -31,6 +31,7 @@ import { examples } from './gitea.examples'; import fetch, { RequestInit, Response } from 'node-fetch'; import crypto from 'crypto'; +/* NOT USED. See TODO hereafter const checkGiteaOrgRepo = async ( config: GiteaIntegrationConfig, options: { @@ -61,6 +62,8 @@ const checkGiteaOrgRepo = async ( } return response; }; +*/ + const checkGiteaOrg = async ( config: GiteaIntegrationConfig, options: { @@ -330,7 +333,7 @@ export function createPublishGiteaAction(options: { */ await sleep(3000); - const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/src/branch/${defaultBranch}`; + const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/src/branch/${defaultBranch}/`; ctx.output('remoteUrl', remoteUrl); ctx.output('commitHash', commitResult?.commitHash); ctx.output('repoContentsUrl', repoContentsUrl); From 73295aa0df6c4b4f4ceef997b42ce502a1237cb6 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 4 Jan 2024 19:48:35 +0100 Subject: [PATCH 32/56] Change the request to test if the URL is reposning after the repo has been created. We dont get anymore Error: Unknown encoding: undefined during register action step Signed-off-by: cmoulliard --- .../src/actions/gitea.ts | 29 ++++--------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 7905f42e4a..412d437a7a 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -31,8 +31,8 @@ import { examples } from './gitea.examples'; import fetch, { RequestInit, Response } from 'node-fetch'; import crypto from 'crypto'; -/* NOT USED. See TODO hereafter -const checkGiteaOrgRepo = async ( +/* NOT USED. See TODO hereafter */ +const checkGiteaContentUrl = async ( config: GiteaIntegrationConfig, options: { owner?: string; @@ -44,15 +44,11 @@ const checkGiteaOrgRepo = async ( let response: Response; const getOptions: RequestInit = { method: 'GET', - headers: { - ...getGiteaRequestOptions(config).headers, - 'Content-Type': 'application/json', - }, }; try { response = await fetch( - `${config.baseUrl}/api/v1/repos/${owner}/${repo}/contents?ref=${defaultBranch}`, + `${config.baseUrl}/${owner}/${repo}/src/branch/${defaultBranch}`, getOptions, ); } catch (e) { @@ -62,7 +58,6 @@ const checkGiteaOrgRepo = async ( } return response; }; -*/ const checkGiteaOrg = async ( config: GiteaIntegrationConfig, @@ -305,33 +300,21 @@ export function createPublishGiteaAction(options: { gitAuthorInfo, }); - /* - TODO: This code is commented till it will be fixed. - When we use the code hereafter, we got the following error: - "Unable to read url, Error: Unknown encoding: undefined\n at DefaultLocationService.processEntities" - as commented here: Still getting the error: https://github.com/backstage/backstage/pull/21890#issuecomment-1876733870 - - Such an issue do not exist using sleep 3s. - - WARNING: To allow to register within the catalog the new project, it is also needed to pass within the catalogInfoPath the branch name (e.g: main/catalog-info.yaml) - - // Check if the repo is available + // Check if the gitea repo URL is available before to exit let response: Response; - response = await checkGiteaOrgRepo(integrationConfig.config, { + response = await checkGiteaContentUrl(integrationConfig.config, { owner, repo, defaultBranch, }); while (response.status !== 200) { await sleep(1000); - response = await checkGiteaOrgRepo(integrationConfig.config, { + response = await checkGiteaContentUrl(integrationConfig.config, { owner, repo, defaultBranch, }); } - */ - await sleep(3000); const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/src/branch/${defaultBranch}/`; ctx.output('remoteUrl', remoteUrl); From a6b552b40733bcbcec3bade18b57481620028568 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Fri, 5 Jan 2024 16:31:18 +0100 Subject: [PATCH 33/56] Adding a new rest.get to allow gitea to check if the URL of the repo exists Signed-off-by: cmoulliard --- .../src/actions/gitea.test.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) 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 8936c34690..0ab2fd507b 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -100,6 +100,16 @@ describe('publish:gitea', () => { }), ); }), + rest.get( + 'https://gitea.com/org1/repo/src/branch/main', + (_req, res, ctx) => { + return res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json({}), + ); + }, + ), 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( @@ -140,7 +150,7 @@ describe('publish:gitea', () => { expect(mockContext.output).toHaveBeenCalledWith( 'repoContentsUrl', - 'https://gitea.com/org1/repo/src/branch/main', + 'https://gitea.com/org1/repo/src/branch/main/', ); }); From 317d62675d3ee8c7cfb4c61bccb79a66021a6c66 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Fri, 5 Jan 2024 17:04:59 +0100 Subject: [PATCH 34/56] Fixing errors reported by documentation quality check github job Signed-off-by: cmoulliard --- .changeset/metal-clocks-suffer.md | 2 +- plugins/scaffolder-backend-module-gitea/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/metal-clocks-suffer.md b/.changeset/metal-clocks-suffer.md index f94deb380c..648276d4dd 100644 --- a/.changeset/metal-clocks-suffer.md +++ b/.changeset/metal-clocks-suffer.md @@ -5,4 +5,4 @@ '@backstage/integration': patch --- -Added support to create a git repository and publish a scaffolded project using a new action "publish:gitea" for gitea. The action currently supports to create a gitea org's repository - https://gitea.com/api/swagger#/organization/createOrgRepo +Added support to create a git repository and publish a scaffolded project using a new action "publish:gitea" for gitea. The action currently supports to create a gitea repository owned by an organization. See: https://gitea.com/api/swagger#/organization/createOrgRepo diff --git a/plugins/scaffolder-backend-module-gitea/README.md b/plugins/scaffolder-backend-module-gitea/README.md index 4a710a400c..8f9d39f4dc 100644 --- a/plugins/scaffolder-backend-module-gitea/README.md +++ b/plugins/scaffolder-backend-module-gitea/README.md @@ -46,7 +46,7 @@ integrations: ``` **NOTE**: As backstage will issue HTTPS/TLS requests to the gitea instance, it is needed to configure `gitea` with a valid certificate or at least with a -self-signed certificate `gitea cert --host localhost -ca`. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA pem file before to launch backstage ! +self-signed certificate `gitea cert --host localhost -ca`. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA file before to launch backstage ! When done, you can use the action in your template: From e300ddacd903fb35713c614c50f74cfced627298 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 10:28:04 +0100 Subject: [PATCH 35/56] Revert change Token to token as gitea checks token word only. "https://docs.gitea.com/next/development/api-usage\#authentication" Signed-off-by: cmoulliard --- packages/integration/src/gitea/core.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/integration/src/gitea/core.ts b/packages/integration/src/gitea/core.ts index f188613d2a..6d11a4ffde 100644 --- a/packages/integration/src/gitea/core.ts +++ b/packages/integration/src/gitea/core.ts @@ -124,7 +124,7 @@ export function getGiteaRequestOptions(config: GiteaIntegrationConfig): { `${username}:${password}`, ).toString('base64')}`; } else { - headers.Authorization = `Token ${password}`; + headers.Authorization = `token ${password}`; } return { From 0c211065dd715ca2fe0a1d0b3584e241bb3a8b04 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 10:40:39 +0100 Subject: [PATCH 36/56] Better documented the changes of this PR part of the changeset file Signed-off-by: cmoulliard --- .changeset/metal-clocks-suffer.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.changeset/metal-clocks-suffer.md b/.changeset/metal-clocks-suffer.md index 648276d4dd..4cba0ba35b 100644 --- a/.changeset/metal-clocks-suffer.md +++ b/.changeset/metal-clocks-suffer.md @@ -5,4 +5,10 @@ '@backstage/integration': patch --- -Added support to create a git repository and publish a scaffolded project using a new action "publish:gitea" for gitea. The action currently supports to create a gitea repository owned by an organization. See: https://gitea.com/api/swagger#/organization/createOrgRepo +Created a gitea module for the scaffolder. This module provides a new action "publish:gitea" able to create a gitea repository owned by an organization. See: https://gitea.com/api/swagger#/organization/createOrgRepo + +Fixed the gitea authorization headers (used by the integration module) to lower case the words: token and basic + +Added a new test case to the integration module to verify the url of the gitea repository created by the getGiteaFileContentsUrl function. + +Verifying if the basicURL processed by the readGiteaConfig function is valid From 71c68535ae3605cf4083ef264e7ce8440743cea4 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 10:41:38 +0100 Subject: [PATCH 37/56] Revert Basic to basic as the word checked by gitea api is basic Signed-off-by: cmoulliard --- packages/integration/src/gitea/core.test.ts | 2 +- packages/integration/src/gitea/core.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/integration/src/gitea/core.test.ts b/packages/integration/src/gitea/core.test.ts index ba41fa4761..ac29c469b6 100644 --- a/packages/integration/src/gitea/core.test.ts +++ b/packages/integration/src/gitea/core.test.ts @@ -118,7 +118,7 @@ describe('gitea core', () => { password: 'P', }; - const basicAuthentication = `Basic ${Buffer.from( + const basicAuthentication = `basic ${Buffer.from( `${authRequest.username}:${authRequest.password}`, ).toString('base64')}`; diff --git a/packages/integration/src/gitea/core.ts b/packages/integration/src/gitea/core.ts index 6d11a4ffde..95caa24c06 100644 --- a/packages/integration/src/gitea/core.ts +++ b/packages/integration/src/gitea/core.ts @@ -120,7 +120,7 @@ export function getGiteaRequestOptions(config: GiteaIntegrationConfig): { } if (username) { - headers.Authorization = `Basic ${Buffer.from( + headers.Authorization = `basic ${Buffer.from( `${username}:${password}`, ).toString('base64')}`; } else { From a755bf4bf758486236e0e7db1bc7ce984c431061 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 10:51:31 +0100 Subject: [PATCH 38/56] Remove the non needed code block as createBuiltinActions.ts is a new function exported from latest backstage distribution and gitea will be included when this PR will be merged Signed-off-by: cmoulliard --- .../scaffolder-backend-module-gitea/README.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/README.md b/plugins/scaffolder-backend-module-gitea/README.md index 8f9d39f4dc..c35138a34c 100644 --- a/plugins/scaffolder-backend-module-gitea/README.md +++ b/plugins/scaffolder-backend-module-gitea/README.md @@ -14,23 +14,6 @@ yarn add --cwd packages/backend Configure the action (if not yet done): (you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options): -```typescript -// plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts - -export const createBuiltinActions = ( -... - const actions = [ - ... - createPublishGiteaAction({ - integrations, - config, - }), - ... - ]; - return actions as TemplateAction[]; -}; -``` - Before to create a template, include to your `app-config.yaml` file the gitea host and credentials under the `integrations:` section From b39db7dc78060ab6f509858e6954cb68b6dc9ab1 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 10:52:37 +0100 Subject: [PATCH 39/56] Change to launch backstage with launching backstage Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend-module-gitea/README.md b/plugins/scaffolder-backend-module-gitea/README.md index c35138a34c..fe42b2fbaf 100644 --- a/plugins/scaffolder-backend-module-gitea/README.md +++ b/plugins/scaffolder-backend-module-gitea/README.md @@ -29,7 +29,7 @@ integrations: ``` **NOTE**: As backstage will issue HTTPS/TLS requests to the gitea instance, it is needed to configure `gitea` with a valid certificate or at least with a -self-signed certificate `gitea cert --host localhost -ca`. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA file before to launch backstage ! +self-signed certificate `gitea cert --host localhost -ca`. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA file before launching backstage ! When done, you can use the action in your template: From 6f145d5097cc02b80977632342327c17cd9a47a7 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 10:59:21 +0100 Subject: [PATCH 40/56] Removed non needed text from the template example and improved the changes todo in a template Signed-off-by: cmoulliard --- .../scaffolder-backend-module-gitea/README.md | 50 +++---------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/README.md b/plugins/scaffolder-backend-module-gitea/README.md index fe42b2fbaf..f13602f2d8 100644 --- a/plugins/scaffolder-backend-module-gitea/README.md +++ b/plugins/scaffolder-backend-module-gitea/README.md @@ -31,7 +31,10 @@ integrations: **NOTE**: As backstage will issue HTTPS/TLS requests to the gitea instance, it is needed to configure `gitea` with a valid certificate or at least with a self-signed certificate `gitea cert --host localhost -ca`. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA file before launching backstage ! -When done, you can use the action in your template: +When done, you can create a template which: + +- Declare the `RepoUrlPicker` within the `spec/parameters` section to select the gitea hosts +- Include a step able to publish by example the newly generated project using the action `action: publish:gitea` ```yaml apiVersion: scaffolder.backstage.io/v1beta3 @@ -47,43 +50,7 @@ spec: owner: quarkus type: service parameters: - - title: Provide Information for Application - required: - - component_id - - owner - - java_package_name - properties: - component_id: - title: Name - type: string - description: Unique name of the component - default: my-quarkus-app - ui:field: EntityNamePicker - group_id: - title: Group Id - type: string - default: io.quarkus - description: Maven Group Id - artifact_id: - title: Artifact Id - type: string - default: quarkus-app - description: Maven Artifact Id - java_package_name: - title: Java Package Name - default: io.quarkus.demo - type: string - description: Name for the java package. eg (io.quarkus.blah) - owner: - title: Owner - type: string - description: IdP owner of the component - ui:field: OwnerPicker - ui:options: - allowedKinds: - - Group - - - title: Application git repository Information + - title: Git repository Information required: - repoUrl properties: @@ -93,8 +60,8 @@ spec: ui:field: RepoUrlPicker ui:options: allowedHosts: - - localhost:3333 - gitea.: + - localhost: steps: - id: template @@ -112,8 +79,7 @@ spec: artifact_id: ${{ parameters.artifact_id }} java_package_name: ${{ parameters.java_package_name }} owner: ${{ parameters.owner }} - destination: ${{ (parameters.repoUrl | parseRepoUrl).owner }}/${{ (parameters.repoUrl | parseRepoUrl).repo }} - quay_destination: ${{ parameters.image_organization }}/${{ parameters.component_id }} + destination: ${{ (parameters.repoUrl | parseRepoUrl).owner }}/${{ (parameters.repoUrl | parseRepoUrl).repo } port: 8080 - id: publish @@ -141,4 +107,4 @@ spec: entityRef: ${{ steps.register.output.entityRef }} ``` -Enjoy ;-) +Access the newly gitea repository created using the `repoContentsUrl` ;-) From 25c3850edf06a1fb96139448d88ec3948bc95d40 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 11:09:16 +0100 Subject: [PATCH 41/56] Removing import of node-fetch to use the node built in fetch function directly Signed-off-by: cmoulliard --- .../scaffolder-backend-module-gitea/src/actions/gitea.test.ts | 2 +- plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) 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 0ab2fd507b..5e1e94b1b4 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.test.ts @@ -113,7 +113,7 @@ describe('publish:gitea', () => { 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==', + 'basic Z2l0ZWFfdXNlcjpnaXRlYV9wYXNzd29yZA==', ); expect(req.body).toEqual({ name: 'repo', diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 412d437a7a..c022bf4d08 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -28,10 +28,8 @@ import { parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; import { examples } from './gitea.examples'; -import fetch, { RequestInit, Response } from 'node-fetch'; import crypto from 'crypto'; -/* NOT USED. See TODO hereafter */ const checkGiteaContentUrl = async ( config: GiteaIntegrationConfig, options: { From 107050672c69def8fbfe4131041d9514f76da6e3 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 12:19:20 +0100 Subject: [PATCH 42/56] Add a function to test if while don't exceed 20s and test if signal is aborted Signed-off-by: cmoulliard --- .../src/actions/gitea.ts | 86 +++++++++++++++---- 1 file changed, 70 insertions(+), 16 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index c022bf4d08..d38802d76e 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -18,10 +18,13 @@ import { InputError } from '@backstage/errors'; import { Config } from '@backstage/config'; import { getGiteaRequestOptions, + GiteaIntegration, GiteaIntegrationConfig, ScmIntegrationRegistry, + ScmIntegrationsGroup, } from '@backstage/integration'; import { + ActionContext, createTemplateAction, getRepoSourceDirectory, initRepoAndPush, @@ -149,6 +152,54 @@ const generateCommitMessage = ( return msg; }; +/** + * Checks if the provided function can be executed within a specific period of time limit. + * @param fn + * @param timeLimit + */ +function checkDurationLimit(fn: () => void, timeLimit: number): boolean { + const startTime = process.hrtime(); + + // Call the function + fn(); + + const endTime = process.hrtime(startTime); + const durationInMs = endTime[0] * 1000 + endTime[1] / 1e6; + + // Check if the duration exceeds the time limit + return durationInMs <= timeLimit; +} + +export async function checkAvailabilityGiteaRepository( + integrationConfig: GiteaIntegrationConfig, + options: { + owner?: string; + repo: string; + defaultBranch: string; + ctx: ActionContext; + }, +) { + const { owner, repo, defaultBranch, ctx } = options; + const sleep = (ms: number | undefined) => new Promise(r => setTimeout(r, ms)); + let response: Response; + + response = await checkGiteaContentUrl(integrationConfig, { + owner, + repo, + defaultBranch, + }); + + while (response.status !== 200) { + if (ctx.signal?.aborted) return; + await sleep(1000); + response = await checkGiteaContentUrl(integrationConfig, { + owner, + repo, + defaultBranch, + }); + } +} + /** * Creates a new action that initializes a git repository using the content of the workspace. * and publishes it to a Gitea instance. @@ -242,9 +293,6 @@ export function createPublishGiteaAction(options: { sourcePath, } = ctx.input; - const sleep = (ms: number | undefined) => - new Promise(r => setTimeout(r, ms)); - const { repo, host, owner } = parseRepoUrl(repoUrl, integrations); const integrationConfig = integrations.gitea.byHost(host); @@ -299,19 +347,20 @@ export function createPublishGiteaAction(options: { }); // Check if the gitea repo URL is available before to exit - let response: Response; - response = await checkGiteaContentUrl(integrationConfig.config, { - owner, - repo, - defaultBranch, - }); - while (response.status !== 200) { - await sleep(1000); - response = await checkGiteaContentUrl(integrationConfig.config, { - owner, - repo, - defaultBranch, - }); + const operationTimeLimit = 20000; // 20 seconds + const checkDuration = checkDurationLimit( + () => + checkAvailabilityGiteaRepository(integrationConfig.config, { + owner, + repo, + defaultBranch, + ctx, + }), + operationTimeLimit, + ); + + if (!checkDuration) { + console.log('Operation exceeded the time limit.'); } const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/src/branch/${defaultBranch}/`; @@ -321,3 +370,8 @@ export function createPublishGiteaAction(options: { }, }); } + +async function dummySleep() { + const sleep = (ms: number | undefined) => new Promise(r => setTimeout(r, ms)); + await sleep(10000); +} From 48ecd88b134b3c79d70ee4ee6cad13b0a755cce2 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 12:22:24 +0100 Subject: [PATCH 43/56] Declaring const { username, password } earlier within the file Signed-off-by: cmoulliard --- .../src/actions/gitea.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index d38802d76e..35af81ce91 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -301,11 +301,9 @@ export function createPublishGiteaAction(options: { `No matching integration configuration for host ${host}, please check your integrations config`, ); } + const { username, password } = integrationConfig.config; - if ( - !integrationConfig.config.username || - !integrationConfig.config.password - ) { + if (!username || !password) { throw new Error( 'Credentials for Gitea integration required for this action.', ); @@ -323,8 +321,8 @@ export function createPublishGiteaAction(options: { }); const auth = { - username: integrationConfig.config.username!, - password: integrationConfig.config.password!, + username: username, + password: password, }; const gitAuthorInfo = { name: gitAuthorName @@ -370,8 +368,3 @@ export function createPublishGiteaAction(options: { }, }); } - -async function dummySleep() { - const sleep = (ms: number | undefined) => new Promise(r => setTimeout(r, ms)); - await sleep(10000); -} From 8c9b90bb3e7c38ca1be5b5288921800473355dfe Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 12:24:49 +0100 Subject: [PATCH 44/56] Declaring the variables username, password earlier and change the message to pass the host for the missing credentials Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 35af81ce91..17fbf7ab0b 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -304,9 +304,7 @@ export function createPublishGiteaAction(options: { const { username, password } = integrationConfig.config; if (!username || !password) { - throw new Error( - 'Credentials for Gitea integration required for this action.', - ); + throw new Error('Credentials for the gitea ${host} required.'); } // check if the org exists within the gitea server From 842b9c00b79cb6b28ea689e1aed83b4caec662e0 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 10 Jan 2024 12:38:13 +0100 Subject: [PATCH 45/56] Removing ScmIntegrationsGroup & GiteaIntegration from import as non used Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 17fbf7ab0b..a2845a74c6 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -18,10 +18,8 @@ import { InputError } from '@backstage/errors'; import { Config } from '@backstage/config'; import { getGiteaRequestOptions, - GiteaIntegration, GiteaIntegrationConfig, ScmIntegrationRegistry, - ScmIntegrationsGroup, } from '@backstage/integration'; import { ActionContext, From 4ed5ffebdfb0670afb61e4949e8f28b0ca290c2c Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Tue, 16 Jan 2024 14:27:14 +0100 Subject: [PATCH 46/56] Removing the export prefix for the function: checkAvailabilityGiteaRepository as it used internally Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index a2845a74c6..bd592cce4b 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -168,7 +168,7 @@ function checkDurationLimit(fn: () => void, timeLimit: number): boolean { return durationInMs <= timeLimit; } -export async function checkAvailabilityGiteaRepository( +async function checkAvailabilityGiteaRepository( integrationConfig: GiteaIntegrationConfig, options: { owner?: string; From 95b4910cf87076a5461a9ab64fd5f0d094e96b11 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 18 Jan 2024 16:12:30 +0100 Subject: [PATCH 47/56] Adding to tsc --skipLibCheck false --incremental false Signed-off-by: cmoulliard --- .github/workflows/deploy_packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index c91fec948e..40351cba78 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -161,7 +161,7 @@ jobs: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: build type declarations - run: yarn tsc:full + run: yarn tsc --skipLibCheck false --incremental false - name: build packages run: yarn backstage-cli repo build From d7b0c272c3f2535fccfbd00b89bd2bf2b83b2c66 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 18 Jan 2024 16:23:29 +0100 Subject: [PATCH 48/56] Revert "Adding to tsc --skipLibCheck false --incremental false" This reverts commit 11862cc739b2e2651a10d88169522e6fa16854fd. Signed-off-by: cmoulliard --- .github/workflows/deploy_packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index 40351cba78..c91fec948e 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -161,7 +161,7 @@ jobs: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: build type declarations - run: yarn tsc --skipLibCheck false --incremental false + run: yarn tsc:full - name: build packages run: yarn backstage-cli repo build From 9b0bf20dba51b75029916978de74e014416ce7af Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Tue, 30 Jan 2024 16:33:40 +0100 Subject: [PATCH 49/56] Removed non relevant information from changeset and splitting changes into 2 changeset files Signed-off-by: cmoulliard --- .changeset/metal-clocks-suffer.md | 8 -------- .changeset/thick-pillows-punch.md | 5 +++++ 2 files changed, 5 insertions(+), 8 deletions(-) create mode 100644 .changeset/thick-pillows-punch.md diff --git a/.changeset/metal-clocks-suffer.md b/.changeset/metal-clocks-suffer.md index 4cba0ba35b..c459be9fa5 100644 --- a/.changeset/metal-clocks-suffer.md +++ b/.changeset/metal-clocks-suffer.md @@ -1,14 +1,6 @@ --- '@backstage/plugin-scaffolder-backend-module-gitea': minor '@backstage/plugin-scaffolder-backend': minor -'@backstage/plugin-scaffolder-node': minor -'@backstage/integration': patch --- Created a gitea module for the scaffolder. This module provides a new action "publish:gitea" able to create a gitea repository owned by an organization. See: https://gitea.com/api/swagger#/organization/createOrgRepo - -Fixed the gitea authorization headers (used by the integration module) to lower case the words: token and basic - -Added a new test case to the integration module to verify the url of the gitea repository created by the getGiteaFileContentsUrl function. - -Verifying if the basicURL processed by the readGiteaConfig function is valid diff --git a/.changeset/thick-pillows-punch.md b/.changeset/thick-pillows-punch.md new file mode 100644 index 0000000000..def589d3f8 --- /dev/null +++ b/.changeset/thick-pillows-punch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-node': patch +--- + +Add gitea as new type to be used from integrations configuration From b4748b36eb875218ad7340ef19aa1f50bd83841b Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Tue, 30 Jan 2024 17:40:06 +0100 Subject: [PATCH 50/56] Refactoring the code to include the maxDuration within the function checking if gitea repository is available Signed-off-by: cmoulliard --- .../src/actions/gitea.ts | 60 +++++++------------ 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index bd592cce4b..e7f62f51da 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -150,25 +150,8 @@ const generateCommitMessage = ( return msg; }; -/** - * Checks if the provided function can be executed within a specific period of time limit. - * @param fn - * @param timeLimit - */ -function checkDurationLimit(fn: () => void, timeLimit: number): boolean { - const startTime = process.hrtime(); - - // Call the function - fn(); - - const endTime = process.hrtime(startTime); - const durationInMs = endTime[0] * 1000 + endTime[1] / 1e6; - - // Check if the duration exceeds the time limit - return durationInMs <= timeLimit; -} - async function checkAvailabilityGiteaRepository( + maxDuration: number, integrationConfig: GiteaIntegrationConfig, options: { owner?: string; @@ -177,24 +160,25 @@ async function checkAvailabilityGiteaRepository( ctx: ActionContext; }, ) { + const startTimestamp = Date.now(); + const { owner, repo, defaultBranch, ctx } = options; const sleep = (ms: number | undefined) => new Promise(r => setTimeout(r, ms)); let response: Response; - response = await checkGiteaContentUrl(integrationConfig, { - owner, - repo, - defaultBranch, - }); - - while (response.status !== 200) { + while (Date.now() - startTimestamp < maxDuration) { if (ctx.signal?.aborted) return; - await sleep(1000); + response = await checkGiteaContentUrl(integrationConfig, { owner, repo, defaultBranch, }); + + if (response.status !== 200) { + // Repository is not yet available/accessible ... + await sleep(1000); + } } } @@ -341,22 +325,18 @@ export function createPublishGiteaAction(options: { }); // Check if the gitea repo URL is available before to exit - const operationTimeLimit = 20000; // 20 seconds - const checkDuration = checkDurationLimit( - () => - checkAvailabilityGiteaRepository(integrationConfig.config, { - owner, - repo, - defaultBranch, - ctx, - }), - operationTimeLimit, + const maxDuration = 20000; // 20 seconds + await checkAvailabilityGiteaRepository( + maxDuration, + integrationConfig.config, + { + owner, + repo, + defaultBranch, + ctx, + }, ); - if (!checkDuration) { - console.log('Operation exceeded the time limit.'); - } - const repoContentsUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}/src/branch/${defaultBranch}/`; ctx.output('remoteUrl', remoteUrl); ctx.output('commitHash', commitResult?.commitHash); From 2d0e7cdc69982cc935778519da106d243bea693b Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 1 Feb 2024 11:40:47 +0100 Subject: [PATCH 51/56] Add new changeset file to document what changed for @backstage/integration Signed-off-by: cmoulliard --- .changeset/shiny-jeans-begin.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/shiny-jeans-begin.md diff --git a/.changeset/shiny-jeans-begin.md b/.changeset/shiny-jeans-begin.md new file mode 100644 index 0000000000..d542747b34 --- /dev/null +++ b/.changeset/shiny-jeans-begin.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration': patch +--- + +Validate if the `baseUrl` is a valid URL From 06c1fce211ffd767298209407062736a3bf352b5 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 14 Feb 2024 19:05:54 +0100 Subject: [PATCH 52/56] Migrate the module to new Backend System Signed-off-by: cmoulliard --- .../api-report-alpha.md | 13 +++++ .../package.json | 17 ++++++- .../src/alpha.ts | 16 +++++++ .../src/module.ts | 48 +++++++++++++++++++ yarn.lock | 2 +- 5 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 plugins/scaffolder-backend-module-gitea/api-report-alpha.md create mode 100644 plugins/scaffolder-backend-module-gitea/src/alpha.ts create mode 100644 plugins/scaffolder-backend-module-gitea/src/module.ts diff --git a/plugins/scaffolder-backend-module-gitea/api-report-alpha.md b/plugins/scaffolder-backend-module-gitea/api-report-alpha.md new file mode 100644 index 0000000000..8cd41a271c --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/api-report-alpha.md @@ -0,0 +1,13 @@ +## API Report File for "@backstage/plugin-scaffolder-backend-module-gitea" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; + +// @public +const giteaModule: () => BackendFeature; +export default giteaModule; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index c9e9a4e07d..13e57c9337 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -13,6 +13,21 @@ "backstage": { "role": "backend-plugin-module" }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, "scripts": { "start": "backstage-cli package start", "build": "backstage-cli package build", @@ -23,10 +38,10 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", - "@backstage/plugin-scaffolder-backend": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", "node-fetch": "^2.6.7", "yaml": "^2.0.0" diff --git a/plugins/scaffolder-backend-module-gitea/src/alpha.ts b/plugins/scaffolder-backend-module-gitea/src/alpha.ts new file mode 100644 index 0000000000..7c5ee49afa --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/src/alpha.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export { giteaModule as default } from './module'; diff --git a/plugins/scaffolder-backend-module-gitea/src/module.ts b/plugins/scaffolder-backend-module-gitea/src/module.ts new file mode 100644 index 0000000000..658f240471 --- /dev/null +++ b/plugins/scaffolder-backend-module-gitea/src/module.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + createBackendModule, + coreServices, +} from '@backstage/backend-plugin-api'; +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; +import { createPublishGiteaAction } from './actions'; +import { ScmIntegrations } from '@backstage/integration'; + +/** + * @public + * The Gitea Module for the Scaffolder Backend + */ +export const giteaModule = createBackendModule({ + pluginId: 'scaffolder', + moduleId: 'gitea', + register({ registerInit }) { + registerInit({ + deps: { + scaffolder: scaffolderActionsExtensionPoint, + config: coreServices.rootConfig, + }, + async init({ scaffolder, config }) { + const integrations = ScmIntegrations.fromConfig(config); + scaffolder.addActions( + createPublishGiteaAction({ + integrations, + config, + }), + ); + }, + }); + }, +}); diff --git a/yarn.lock b/yarn.lock index 64bc6fc64e..3291d13beb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8497,12 +8497,12 @@ __metadata: resolution: "@backstage/plugin-scaffolder-backend-module-gitea@workspace:plugins/scaffolder-backend-module-gitea" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" - "@backstage/plugin-scaffolder-backend": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" msw: ^1.0.0 node-fetch: ^2.6.7 From 446dba2323bcc09522521e655be887ed38d869b4 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 15 Feb 2024 16:36:07 +0100 Subject: [PATCH 53/56] Only export index.ts and not alpha.ts Signed-off-by: cmoulliard --- .../scaffolder-backend-module-gitea/package.json | 4 ---- .../scaffolder-backend-module-gitea/src/alpha.ts | 16 ---------------- .../scaffolder-backend-module-gitea/src/index.ts | 1 + 3 files changed, 1 insertion(+), 20 deletions(-) delete mode 100644 plugins/scaffolder-backend-module-gitea/src/alpha.ts diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 13e57c9337..f2623cc58e 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -15,14 +15,10 @@ }, "exports": { ".": "./src/index.ts", - "./alpha": "./src/alpha.ts", "./package.json": "./package.json" }, "typesVersions": { "*": { - "alpha": [ - "src/alpha.ts" - ], "package.json": [ "package.json" ] diff --git a/plugins/scaffolder-backend-module-gitea/src/alpha.ts b/plugins/scaffolder-backend-module-gitea/src/alpha.ts deleted file mode 100644 index 7c5ee49afa..0000000000 --- a/plugins/scaffolder-backend-module-gitea/src/alpha.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export { giteaModule as default } from './module'; diff --git a/plugins/scaffolder-backend-module-gitea/src/index.ts b/plugins/scaffolder-backend-module-gitea/src/index.ts index 026da0735a..0b68069178 100644 --- a/plugins/scaffolder-backend-module-gitea/src/index.ts +++ b/plugins/scaffolder-backend-module-gitea/src/index.ts @@ -21,3 +21,4 @@ */ export * from './actions'; +export { giteaModule as default } from './module'; From 905f4708312f292796378c69f212cd8b125a1096 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Thu, 15 Feb 2024 21:07:32 +0100 Subject: [PATCH 54/56] Remove files to be published Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index f2623cc58e..101c629243 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -6,9 +6,7 @@ "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" + "access": "public" }, "backstage": { "role": "backend-plugin-module" From 8db9fdaf29af2a059c9f70728bed531678d65840 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Fri, 16 Feb 2024 12:31:22 +0100 Subject: [PATCH 55/56] Add git repository to the package.json file of the module Signed-off-by: cmoulliard --- plugins/scaffolder-backend-module-gitea/package.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 101c629243..17f30b226b 100644 --- a/plugins/scaffolder-backend-module-gitea/package.json +++ b/plugins/scaffolder-backend-module-gitea/package.json @@ -8,6 +8,11 @@ "publishConfig": { "access": "public" }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/scaffolder-backend-module-gitea" + }, "backstage": { "role": "backend-plugin-module" }, From 9eb7cacef753cebd7db62786d8a41d89e83df3c7 Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Fri, 16 Feb 2024 13:49:40 +0100 Subject: [PATCH 56/56] Regenerated the API report Signed-off-by: cmoulliard --- .../api-report-alpha.md | 13 ------------- .../scaffolder-backend-module-gitea/api-report.md | 5 +++++ 2 files changed, 5 insertions(+), 13 deletions(-) delete mode 100644 plugins/scaffolder-backend-module-gitea/api-report-alpha.md diff --git a/plugins/scaffolder-backend-module-gitea/api-report-alpha.md b/plugins/scaffolder-backend-module-gitea/api-report-alpha.md deleted file mode 100644 index 8cd41a271c..0000000000 --- a/plugins/scaffolder-backend-module-gitea/api-report-alpha.md +++ /dev/null @@ -1,13 +0,0 @@ -## API Report File for "@backstage/plugin-scaffolder-backend-module-gitea" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; - -// @public -const giteaModule: () => BackendFeature; -export default giteaModule; - -// (No @packageDocumentation comment for this package) -``` diff --git a/plugins/scaffolder-backend-module-gitea/api-report.md b/plugins/scaffolder-backend-module-gitea/api-report.md index f83ae73fb7..373cc6258c 100644 --- a/plugins/scaffolder-backend-module-gitea/api-report.md +++ b/plugins/scaffolder-backend-module-gitea/api-report.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { ScmIntegrationRegistry } from '@backstage/integration'; @@ -24,4 +25,8 @@ export function createPublishGiteaAction(options: { }, JsonObject >; + +// @public +const giteaModule: () => BackendFeature; +export default giteaModule; ```