Skip to content

Instantly share code, notes, and snippets.

@gbzarelli
Last active October 12, 2022 12:37
Show Gist options
  • Save gbzarelli/fe1a6a5d7a1195bd721112c83e3786cb to your computer and use it in GitHub Desktop.
Save gbzarelli/fe1a6a5d7a1195bd721112c83e3786cb to your computer and use it in GitHub Desktop.
Backstage Action - Gitlab Pipeline: Start a new pipeline.
import { Gitlab } from '@gitbeaker/node';
import { createTemplateAction } from '@backstage/plugin-scaffolder-backend';
import { ScmIntegrationRegistry } from '@backstage/integration';
import { InputError } from '@backstage/errors';
import { parseRepoUrl } from './util';
export const createGitlabPipelineAction = (integrations: ScmIntegrationRegistry) =>
createTemplateAction<{
repoUrl: string;
branchName: string;
}>({
id: 'pipeline:gitlab:create',
schema: {
input: {
required: ['repoUrl', 'branchName'],
type: 'object',
properties: {
repoUrl: {
type: 'string',
title: 'Repository Location',
description: `Accepts the format 'gitlab.com/group_name/project_name' where 'project_name' is the repository name and 'group_name' is a group or username`,
},
branchName: {
type: 'string',
title: 'Destination Branch name',
description: 'The description of the merge request',
},
},
},
},
async handler(ctx) {
const {
repoUrl,
branchName,
} = ctx.input;
const { host, owner, repo } = parseRepoUrl(repoUrl, integrations);
const projectPath = `${owner}/${repo}`;
const integrationConfig = integrations.gitlab.byHost(host);
if (!integrationConfig) {
throw new InputError(
`No matching integration configuration for host ${host}, please check your integrations config`,
);
}
const token = integrationConfig.config.token!;
const tokenType = 'token';
const api = new Gitlab({
host: integrationConfig.config.baseUrl,
[tokenType]: token,
});
try {
await api.Pipelines.create(projectPath,branchName)
} catch (e) {
throw new InputError(`The pipeline creation failed ${e}`);
}
},
});
/*
* Copyright 2021 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.
*
* From: https://github.com/backstage/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/util.ts
*
*/
import { InputError } from '@backstage/errors';
import { isChildPath } from '@backstage/backend-common';
import { join as joinPath, normalize as normalizePath } from 'path';
import { ScmIntegrationRegistry } from '@backstage/integration';
export const getRepoSourceDirectory = (
workspacePath: string,
sourcePath: string | undefined,
) => {
if (sourcePath) {
const safeSuffix = normalizePath(sourcePath).replace(
/^(\.\.(\/|\\|$))+/,
'',
);
const path = joinPath(workspacePath, safeSuffix);
if (!isChildPath(workspacePath, path)) {
throw new Error('Invalid source path');
}
return path;
}
return workspacePath;
};
export type RepoSpec = {
repo: string;
host: string;
owner?: string;
organization?: string;
workspace?: string;
project?: string;
};
export const parseRepoUrl = (
repoUrl: string,
integrations: ScmIntegrationRegistry,
): RepoSpec => {
let parsed;
try {
parsed = new URL(`https://${repoUrl}`);
} catch (error) {
throw new InputError(
`Invalid repo URL passed to publisher, got ${repoUrl}, ${error}`,
);
}
const host = parsed.host;
const owner = parsed.searchParams.get('owner') ?? undefined;
const organization = parsed.searchParams.get('organization') ?? undefined;
const workspace = parsed.searchParams.get('workspace') ?? undefined;
const project = parsed.searchParams.get('project') ?? undefined;
const type = integrations.byHost(host)?.type;
if (!type) {
throw new InputError(
`No matching integration configuration for host ${host}, please check your integrations config`,
);
}
if (type === 'bitbucket') {
if (host === 'bitbucket.org') {
if (!workspace) {
throw new InputError(
`Invalid repo URL passed to publisher: ${repoUrl}, missing workspace`,
);
}
}
if (!project) {
throw new InputError(
`Invalid repo URL passed to publisher: ${repoUrl}, missing project`,
);
}
} else {
if (!owner && type !== 'gerrit') {
throw new InputError(
`Invalid repo URL passed to publisher: ${repoUrl}, missing owner`,
);
}
}
const repo = parsed.searchParams.get('repo');
if (!repo) {
throw new InputError(
`Invalid repo URL passed to publisher: ${repoUrl}, missing repo`,
);
}
return { host, owner, repo, organization, workspace, project };
};
export const isExecutable = (fileMode: number) => {
const executeBitMask = 0o000111;
const res = fileMode & executeBitMask;
return res > 0;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment