@antoniobergas/scaffolder-backend-module-azure-pipelines
Version:
A collection of Backstage scaffolder backend modules for Azure pipelines.
472 lines (463 loc) • 16.2 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var errors = require('@backstage/errors');
var integration = require('@backstage/integration');
var pluginScaffolderNode = require('@backstage/plugin-scaffolder-node');
var fetch = require('node-fetch');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch);
const createAzurePipelineAction = (options) => {
const { integrations } = options;
return pluginScaffolderNode.createTemplateAction({
id: "azure:pipeline:create",
schema: {
input: {
required: [
"organization",
"project",
"folder",
"name",
"repositoryId",
"repositoryName"
],
type: "object",
properties: {
createApiVersion: {
type: "string",
title: "Create API version",
description: "The Azure Create Pipeline API version to use. Defaults to 6.1-preview.1"
},
server: {
type: "string",
title: "Server hostname",
description: "The hostname of the Azure DevOps service. Defaults to dev.azure.com"
},
organization: {
type: "string",
title: "Organization",
description: "The name of the Azure DevOps organization."
},
project: {
type: "string",
title: "Project",
description: "The name of the Azure project."
},
folder: {
type: "string",
title: "Folder",
description: "The name of the folder of the pipeline."
},
name: {
type: "string",
title: "Name",
description: "The name of the pipeline."
},
repositoryId: {
type: "string",
title: "Repository ID",
description: "The ID of the repository."
},
repositoryName: {
type: "string",
title: "Repository Name",
description: "The name of the repository."
},
yamlPath: {
type: "string",
title: "Azure DevOps Pipelines Definition",
description: "The location of the Azure DevOps Pipeline definition file. Defaults to /azure-pipelines.yaml"
},
token: {
title: "Authentication Token",
type: "string",
description: "The token to use for authorization."
}
}
}
},
async handler(ctx) {
var _a, _b;
const {
createApiVersion,
server,
organization,
project,
folder,
name,
repositoryId,
yamlPath,
repositoryName
} = ctx.input;
const host = server != null ? server : "dev.azure.com";
const apiVersion = createApiVersion != null ? createApiVersion : "6.1-preview.1";
const type = (_a = integrations.byHost(host)) == null ? void 0 : _a.type;
if (!type) {
throw new errors.InputError(
`No matching integration configuration for host ${host}, please check your integrations config`
);
}
const url = `https://${host}/${organization}`;
const credentialProvider = integration.DefaultAzureDevOpsCredentialsProvider.fromIntegrations(integrations);
const credentials = await credentialProvider.getCredentials({ url });
if (credentials === void 0 && ctx.input.token === void 0) {
throw new errors.InputError(
`No credentials provided ${url}, please check your integrations config`
);
}
const token = (_b = ctx.input.token) != null ? _b : credentials.token;
ctx.logger.info(
`Creating an Azure pipeline for the repository ${repositoryName} with the ID ${repositoryId}.`
);
await fetch__default["default"](
`https://${host}/${organization}/${project}/_apis/pipelines?api-version=${apiVersion}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Basic ${Buffer.from(`PAT:${token}`).toString(
"base64"
)}`,
"X-TFS-FedAuthRedirect": "Suppress"
},
body: JSON.stringify({
folder,
name,
configuration: {
type: "yaml",
path: yamlPath || "/azure-pipelines.yaml",
repository: {
id: repositoryId,
name: repositoryName,
type: "azureReposGit"
}
}
})
}
).then((response) => {
if (response.ok) {
ctx.logger.info(
`Successfully created ${name} Azure pipeline in ${folder}.`
);
} else {
ctx.logger.error(
`Failed to create Azure pipeline. Status code ${response.status}.`
);
}
return response.json();
}).then((data) => {
ctx.logger.info(`The Azure pipeline ID is ${data.id}.`);
ctx.output("pipelineId", data.id.toString());
ctx.output("pipelineUrl", data._links.web.href);
});
}
});
};
const permitAzurePipelineAction = (options) => {
const { integrations } = options;
return pluginScaffolderNode.createTemplateAction({
id: "azure:pipeline:permit",
schema: {
input: {
required: [
"organization",
"project",
"resourceId",
"resourceType",
"authorized",
"pipelineId"
],
type: "object",
properties: {
permitsApiVersion: {
type: "string",
title: "Permits API version",
description: "The Azure Permits Pipeline API version to use. Defaults to 7.1-preview.1"
},
server: {
type: "string",
title: "Server hostname",
description: "The hostname of the Azure DevOps service. Defaults to dev.azure.com"
},
organization: {
type: "string",
title: "Organization",
description: "The name of the Azure DevOps organization."
},
project: {
type: "string",
title: "Project",
description: "The name of the Azure project."
},
resourceId: {
type: "string",
title: "Resource ID",
description: "The resource ID."
},
resourceType: {
type: "string",
title: "Resource Type",
description: "The type of the resource (e.g. endpoint)."
},
authorized: {
type: "boolean",
title: "Authorized",
description: "A true or false authorization indicator."
},
pipelineId: {
type: "string",
title: "Pipeline ID",
description: "The pipeline ID."
},
token: {
title: "Authenticatino Token",
type: "string",
description: "The token to use for authorization."
}
}
}
},
async handler(ctx) {
var _a, _b;
const {
permitsApiVersion,
server,
organization,
project,
resourceId,
resourceType,
authorized,
pipelineId
} = ctx.input;
const host = server != null ? server : "dev.azure.com";
const apiVersion = permitsApiVersion != null ? permitsApiVersion : "7.1-preview.1";
const type = (_a = integrations.byHost(host)) == null ? void 0 : _a.type;
if (!type) {
throw new errors.InputError(
`No matching integration configuration for host ${host}, please check your integrations config`
);
}
const url = `https://${host}/${organization}`;
const credentialProvider = integration.DefaultAzureDevOpsCredentialsProvider.fromIntegrations(integrations);
const credentials = await credentialProvider.getCredentials({ url });
if (credentials === void 0 && ctx.input.token === void 0) {
throw new errors.InputError(
`No credentials provided ${url}, please check your integrations config`
);
}
const token = (_b = ctx.input.token) != null ? _b : credentials.token;
if (ctx.input.authorized === true) {
ctx.logger.info(
`Authorizing Azure pipeline with ID ${pipelineId} for ${resourceType} with ID ${resourceId}.`
);
} else {
ctx.logger.info(
`Unauthorizing Azure pipeline with ID ${pipelineId} for ${resourceType} with ID ${resourceId}.`
);
}
await fetch__default["default"](
`https://${host}/${organization}/${project}/_apis/pipelines/pipelinepermissions/${resourceType}/${resourceId}?api-version=${apiVersion}`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Basic ${Buffer.from(`PAT:${token}`).toString(
"base64"
)}`,
"X-TFS-FedAuthRedirect": "Suppress"
},
body: JSON.stringify({
pipelines: [
{
authorized,
id: parseInt(pipelineId, 10)
}
]
})
}
).then((response) => {
if (response.ok) {
ctx.logger.info(
`Successfully changed the Azure pipeline permissions.`
);
} else {
ctx.logger.error(
`Failed to change the Azure pipeline permissions. Status code ${response.status}.`
);
}
});
}
});
};
const runAzurePipelineAction = (options) => {
const { integrations } = options;
async function checkPipelineStatus(host, organization, project, runId, token, buildApiVersion) {
const response = await fetch__default["default"](
`https://${host}/${organization}/${project}/_apis/build/builds/${runId}?api-version=${buildApiVersion}`,
{
headers: {
Authorization: `Basic ${Buffer.from(`PAT:${token}`).toString("base64")}`,
"X-TFS-FedAuthRedirect": "Suppress"
}
}
);
if (!response.ok) {
throw new Error(`Failed to retrieve pipeline run status. Status code ${response.status}.`);
}
const json = await response.json();
const status = json.status;
if (status === "completed") {
return json.result === "succeeded";
} else if (status === "inProgress" || status === "notStarted") {
await new Promise((resolve) => setTimeout(resolve, 1e4));
return checkPipelineStatus(host, organization, project, runId, token, buildApiVersion);
} else {
throw new Error(`Azure pipeline failed with status: ${status}.`);
}
}
return pluginScaffolderNode.createTemplateAction({
id: "azure:pipeline:run",
schema: {
input: {
required: [
"organization",
"pipelineId",
"project"
],
type: "object",
properties: {
runApiVersion: {
type: "string",
title: "Run API version",
description: "The Azure Run Pipeline API version to use. Defaults to 7.0"
},
buildApiVersion: {
type: "string",
title: "Build API version",
description: "The Builds API version to use. Defaults to 6.1-preview.6"
},
server: {
type: "string",
title: "Server hostname",
description: "The hostname of the Azure DevOps service. Defaults to dev.azure.com"
},
organization: {
type: "string",
title: "Organization",
description: "The name of the Azure DevOps organization."
},
pipelineId: {
type: "string",
title: "Pipeline ID",
description: "The pipeline ID."
},
project: {
type: "string",
title: "Project",
description: "The name of the Azure project."
},
branch: {
title: "Repository Branch",
type: "string",
description: "The branch of the pipeline's repository."
},
token: {
title: "Authentication Token",
type: "string",
description: "The token to use for authorization."
},
pipelineParameters: {
title: "Pipeline Parameters",
type: "object",
description: "The values you need as parameters on the request to start a build."
}
}
}
},
async handler(ctx) {
var _a, _b;
const {
runApiVersion,
buildApiVersion,
server,
organization,
pipelineId,
project,
branch,
pipelineParameters
} = ctx.input;
const host = server != null ? server : "dev.azure.com";
const apiVersions = {
runApiVersion: runApiVersion != null ? runApiVersion : "7.0",
buildApiVersion: buildApiVersion != null ? buildApiVersion : "6.1-preview.6"
};
const type = (_a = integrations.byHost(host)) == null ? void 0 : _a.type;
if (!type) {
throw new errors.InputError(
`No matching integration configuration for host ${host}, please check your integrations config`
);
}
const url = `https://${host}/${organization}`;
const credentialProvider = integration.DefaultAzureDevOpsCredentialsProvider.fromIntegrations(integrations);
const credentials = await credentialProvider.getCredentials({ url });
if (credentials === void 0 && ctx.input.token === void 0) {
throw new errors.InputError(
`No credentials provided ${url}, please check your integrations config`
);
}
const token = (_b = ctx.input.token) != null ? _b : credentials.token;
ctx.logger.info(`Running Azure pipeline with the ID ${pipelineId}.`);
const request = {
resources: {
repositories: {
self: {
refName: `refs/heads/${branch != null ? branch : "main"}`
}
}
},
templateParameters: pipelineParameters,
yamlOverrides: ""
};
const body = JSON.stringify(request);
await fetch__default["default"](
`https://${host}/${organization}/${project}/_apis/pipelines/${pipelineId}/runs?api-version=${apiVersions.runApiVersion}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
Authorization: `Basic ${Buffer.from(`PAT:${token}`).toString(
"base64"
)}`,
"X-TFS-FedAuthRedirect": "Suppress"
},
body
}
).then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error(`Failed to run Azure pipeline. Status code ${response.status}.`);
}
}).then((json) => {
const pipelineUrl = json._links.web.href;
ctx.logger.info(`Successfully started Azure pipeline run: ${pipelineUrl}`);
const pipelineRunId = json.id;
return checkPipelineStatus(host, organization, project, pipelineRunId, token, apiVersions.buildApiVersion);
}).then((success) => {
if (success) {
ctx.logger.info(`Azure pipeline completed successfully.`);
} else {
ctx.logger.error(`Azure pipeline failed.`);
}
}).catch((error) => {
ctx.logger.error(error.message);
});
}
});
};
exports.createAzurePipelineAction = createAzurePipelineAction;
exports.permitAzurePipelineAction = permitAzurePipelineAction;
exports.runAzurePipelineAction = runAzurePipelineAction;
//# sourceMappingURL=index.cjs.js.map