Merge pull request #24383 from ch007m/add-new-parameters-gitea

Add new parameter: repoVisibility for the action: publish:gitea
This commit is contained in:
Ben Lambert
2024-04-23 11:23:46 +02:00
committed by GitHub
6 changed files with 158 additions and 42 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-gitea': patch
---
Allow defining `repoVisibility` field for the action `publish:gitea`
@@ -10,6 +10,13 @@ To use this action, you will have to add the package using the following command
yarn --cwd packages/backend add @backstage/plugin-scaffolder-backend-module-gitea
```
Alternatively, if you use the new backend system, then register it like this:
```typescript
// packages/backend/src/index.ts
backend.add(import('@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):
@@ -27,29 +34,29 @@ integrations:
password: '<GITEA_LOCALHOST_PASSWORD>'
```
**Important**: 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` trusted by a CA authority. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA file before launching backstage !
**Important**: 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` trusted by a CA authority. Don't forget to set the env var `NODE_EXTRA_CA_CERTS` to point to the CA file before launching backstage or you can set temporarily `NODE_TLS_REJECT_UNAUTHORIZED=0` but this is not recommended for production!
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`
- Declare the `RepoUrlPicker` within the `spec/parameters` section to select the gitea host and to provide the name of the repository
- Add an `enum` list allowing the user to define the visibility about the repository to be created: `public` or `private`. If this field is omitted, `public` is then used by the action.
- Include in a step the action: `publish:gitea`
**Warning**: The list of the `allowedOwners` of the `repoUrlPicker` must match the list of the `organizations` which are available on the gitea host !
```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
name: simple-gitea-project
title: Create a gitea repository
description: Create a gitea repository
spec:
owner: quarkus
owner: guests
type: service
parameters:
- title: Git repository Information
- title: Choose a location
required:
- repoUrl
properties:
@@ -58,42 +65,33 @@ spec:
type: string
ui:field: RepoUrlPicker
ui:options:
allowedOwners:
- qteam
- qshift
allowedHosts:
- gitea.<YOUR_DOMAIN>:<PORT>
- localhost:<PORT>
- gitea.localtest.me:3333
repoVisibility:
title: Visibility of the repository
type: string
default: 'public'
enum:
- 'public'
- 'private'
enumNames:
- 'public'
- 'private'
steps:
- id: template
name: Generating component
action: fetch:template
input:
url: ./skeleton
values:
name: ${{ parameters.name }}
...
- id: publish
name: Publishing to a gitea git repository
action: publish:gitea
input:
description: This is ${{ parameters.component_id }}
description: This is ${{ parameters.repoUrl | parseRepoUrl | pick('repo') }}
repoVisibility: ${{ parameters.repoVisibility }}
repoUrl: ${{ parameters.repoUrl }}
defaultBranch: main
- id: register
if: ${{ parameters.dryRun !== true }}
name: Register
action: catalog:register
input:
repoContentsUrl: ${{ steps['publish'].output.repoContentsUrl }}
catalogInfoPath: 'main/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 }}
```
Access the newly gitea repository created using the `repoContentsUrl` ;-)
@@ -18,6 +18,7 @@ export function createPublishGiteaAction(options: {
repoUrl: string;
description: string;
defaultBranch?: string | undefined;
repoVisibility?: 'private' | 'public' | undefined;
gitCommitMessage?: string | undefined;
gitAuthorName?: string | undefined;
gitAuthorEmail?: string | undefined;
@@ -50,6 +50,23 @@ export const examples: TemplateExample[] = [
],
}),
},
{
description: 'Initializes a private Gitea repository ',
example: yaml.stringify({
steps: [
{
id: 'publish',
action: 'publish:gitea',
name: 'Publish to Gitea',
input: {
repoUrl: 'gitea.com?repo=repo&owner=owner',
defaultBranch: 'main',
repoVisibility: 'private',
},
},
],
}),
},
{
description:
'Initializes a Gitea repository with a default Branch, if not set defaults to main',
@@ -150,6 +167,7 @@ export const examples: TemplateExample[] = [
gitAuthorName: 'John Doe',
gitAuthorEmail: 'johndoe@email.com',
sourcePath: 'repository/',
repoVisibility: 'public',
},
},
],
@@ -53,6 +53,13 @@ describe('publish:gitea', () => {
description,
},
});
const mockContextWithPublicRepoVisibility = createMockActionContext({
input: {
repoUrl: 'gitea.com?repo=repo&owner=owner',
description,
private: false,
},
});
const server = setupServer();
setupRequestMockHandlers(server);
@@ -111,6 +118,7 @@ describe('publish:gitea', () => {
);
expect(req.body).toEqual({
name: 'repo',
private: false,
description,
});
return res(
@@ -148,6 +156,71 @@ describe('publish:gitea', () => {
);
});
it('should create a Gitea repository where visibility is public', async () => {
server.use(
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.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(
'basic Z2l0ZWFfdXNlcjpnaXRlYV9wYXNzd29yZA==',
);
expect(req.body).toEqual({
name: 'repo',
private: false,
description,
});
return res(
ctx.status(201),
ctx.set('Content-Type', 'application/json'),
ctx.json({}),
);
}),
);
await action.handler({
...mockContextWithPublicRepoVisibility,
input: {
...mockContextWithPublicRepoVisibility.input,
repoUrl: 'gitea.com?repo=repo&owner=org1',
},
});
expect(initRepoAndPush).toHaveBeenCalledWith({
dir: mockContextWithPublicRepoVisibility.workspacePath,
remoteUrl: 'https://gitea.com/org1/repo.git',
defaultBranch: 'main',
auth: { username: 'gitea_user', password: 'gitea_password' },
logger: mockContextWithPublicRepoVisibility.logger,
commitMessage: expect.stringContaining('initial commit\n\nChange-Id:'),
gitAuthorInfo: {
email: undefined,
name: undefined,
},
});
});
afterEach(() => {
jest.resetAllMocks();
});
@@ -81,7 +81,7 @@ const checkGiteaOrg = async (
);
} catch (e) {
throw new Error(
`Unable to get the Organization: ${owner}; Error cause: ${e.cause.message}`,
`Unable to get the Organization: ${owner}; Error cause: ${e.message}, code: ${e.cause.code}`,
);
}
if (response.status !== 200) {
@@ -96,10 +96,11 @@ const createGiteaProject = async (
options: {
projectName: string;
owner?: string;
repoVisibility?: string;
description: string;
},
): Promise<void> => {
const { projectName, description, owner } = options;
const { projectName, description, owner, repoVisibility } = options;
/*
Several options exist to create a repository using either the user or organisation
@@ -112,12 +113,23 @@ const createGiteaProject = async (
This is the default scenario that we support currently
*/
let response: Response;
let isPrivate: boolean;
if (repoVisibility === 'private') {
isPrivate = true;
} else if (repoVisibility === 'public') {
isPrivate = false;
} else {
// Provide a default value if repoVisibility is neither "private" nor "public"
isPrivate = false;
}
const postOptions: RequestInit = {
method: 'POST',
body: JSON.stringify({
name: projectName,
description,
private: isPrivate,
}),
headers: {
...getGiteaRequestOptions(config).headers,
@@ -202,6 +214,7 @@ export function createPublishGiteaAction(options: {
repoUrl: string;
description: string;
defaultBranch?: string;
repoVisibility?: 'private' | 'public';
gitCommitMessage?: string;
gitAuthorName?: string;
gitAuthorEmail?: string;
@@ -229,6 +242,12 @@ export function createPublishGiteaAction(options: {
type: 'string',
description: `Sets the default branch on the repository. The default value is 'main'`,
},
repoVisibility: {
title: 'Repository Visibility',
description: `Sets the visibility of the repository. The default value is 'public'.`,
type: 'string',
enum: ['private', 'public'],
},
gitCommitMessage: {
title: 'Git Commit Message',
type: 'string',
@@ -274,6 +293,7 @@ export function createPublishGiteaAction(options: {
repoUrl,
description,
defaultBranch = 'main',
repoVisibility = 'public',
gitAuthorName,
gitAuthorEmail,
gitCommitMessage = 'initial commit',
@@ -301,6 +321,7 @@ export function createPublishGiteaAction(options: {
await createGiteaProject(integrationConfig.config, {
description,
repoVisibility,
owner: owner,
projectName: repo,
});