sfdx-hardis
Version:
Swiss-army-knife Toolbox for Salesforce. Allows you to define a complete CD/CD Pipeline. Orchestrate base commands and assist users with interactive wizards
213 lines • 10.2 kB
JavaScript
import c from "chalk";
import { getCurrentGitBranch, isCI, uxLog } from "../utils/index.js";
import { AzureDevopsProvider } from "./azureDevops.js";
import { GithubProvider } from "./github.js";
import { GitlabProvider } from "./gitlab.js";
import { BitbucketProvider } from "./bitbucket.js";
import Debug from "debug";
import { CONSTANTS, getEnvVar } from "../../config/index.js";
import { prompts } from "../utils/prompts.js";
import { removeMermaidLinks } from "../utils/mermaidUtils.js";
const debug = Debug("sfdxhardis");
export class GitProvider {
static async getInstance(prompt = false) {
// Azure
if (process.env.SYSTEM_ACCESSTOKEN) {
const serverUrl = process.env.SYSTEM_COLLECTIONURI || null;
// a Personal Access Token must be defined
const token = process.env.CI_SFDX_HARDIS_AZURE_TOKEN || process.env.SYSTEM_ACCESSTOKEN || null;
if (serverUrl == null || token == null) {
uxLog(this, c.yellow(`To benefit from Azure Pipelines advanced integration, you need to define the following variables as ENV vars:
- SYSTEM_COLLECTIONURI
- SYSTEM_ACCESSTOKEN or CI_SFDX_HARDIS_AZURE_TOKEN`));
return null;
}
return new AzureDevopsProvider();
}
// Gitlab
else if (process.env.CI_JOB_TOKEN) {
const token = process.env.CI_SFDX_HARDIS_GITLAB_TOKEN || process.env.ACCESS_TOKEN || null;
if (token == null) {
uxLog(this, c.yellow(`To benefit from Gitlab advanced integration, you need to :
- Go to Settings -> Access tokens -> create a project token named "SFDX HARDIS BOT" with developer access and scope "api", then copy its value
- Go to Settings -> CI/CD -> Variables -> Create a masked variable named CI_SFDX_HARDIS_GITLAB_TOKEN, and paste the access token value`));
return null;
}
return new GitlabProvider();
}
// Github
else if (process.env.GITHUB_TOKEN) {
return new GithubProvider();
}
// Bitbucket
else if (process.env.BITBUCKET_WORKSPACE) {
const token = process.env.CI_SFDX_HARDIS_BITBUCKET_TOKEN || null;
if (token == null) {
uxLog(this, c.yellow(`To benefit from Bitbucket advanced integration, you need to :
- Go to Repository Settings -> Access Tokens -> Create a repository access token with the scopes pullrequest, pullrequest:write, repository, repository:write and copy its value
- Go to Repository Settings -> Repository Variables -> Create a variable named CI_SFDX_HARDIS_BITBUCKET_TOKEN and paste the access token value`));
return null;
}
return new BitbucketProvider();
}
// If prompt allowed and no vars found, request to user
else if (prompt && !isCI) {
await GitProvider.handleManualGitServerAuth();
return this.getInstance(false);
}
else if (isCI) {
uxLog(this, c.grey("To use sfdx-hardis GitProvider capabilities, SYSTEM_ACCESSTOKEN, CI_JOB_TOKEN, GITHUB_TOKEN or CI_SFDX_HARDIS_BITBUCKET_TOKEN must be accessible for Azure Pipelines, Gitlab, GitHub or Bitbucket"));
}
return null;
}
static async handleManualGitServerAuth() {
const promptRes = await prompts({
message: "Please select your Git Service Provider",
type: "select",
choices: [
{ title: "Azure DevOps", value: "azure" },
{ title: "GitHub", value: "github" },
{ title: "Gitlab", value: "gitlab" },
{ title: "Bitbucket", value: "bitbucket" },
]
});
if (promptRes.value === "azure") {
await AzureDevopsProvider.handleLocalIdentification();
}
else {
uxLog(this, c.yellow(`[GitProvider] Local authentication is not yet implemented for ${promptRes.value}`));
}
}
static async managePostPullRequestComment() {
const gitProvider = await GitProvider.getInstance();
if (gitProvider == null) {
uxLog(this, c.yellow("[Git Provider] WARNING: No git provider found to post pull request comment. Maybe you should configure it ?"));
uxLog(this, c.yellow(`[Git Provider] See documentation: ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integrations-home/#git-providers`));
return;
}
const prData = globalThis.pullRequestData;
const prCommentSent = globalThis.pullRequestCommentSent || false;
if (prData && gitProvider && prCommentSent === false) {
uxLog(this, c.yellow("[Git Provider] Try to post a pull request comment/note..."));
let markdownBody = "";
if (prData.deployErrorsMarkdownBody) {
markdownBody += prData.deployErrorsMarkdownBody;
}
if (prData.codeCoverageMarkdownBody) {
markdownBody += "\n\n" + prData.codeCoverageMarkdownBody;
}
if (prData.commitsSummary) {
markdownBody += "\n\n" + prData.commitsSummary;
}
if (prData?.flowDiffMarkdown?.markdownSummary) {
markdownBody += "\n\n" + prData.flowDiffMarkdown.markdownSummary;
}
markdownBody = removeMermaidLinks(markdownBody); // Remove "click" elements that are useless and ugly on some providers :)
const prMessageRequest = {
title: prData.title || '',
message: markdownBody,
status: prData.status || 'tovalidate',
messageKey: prData.messageKey || '',
};
// Post main message
const postResult = await gitProvider.tryPostPullRequestMessage(prMessageRequest);
if (postResult && postResult.posted === true) {
globalThis.pullRequestCommentSent = true;
}
// Post additional comments
for (const flowDiff of prData?.flowDiffMarkdown?.flowDiffMarkdownList || []) {
const flowDiffMessage = removeMermaidLinks(flowDiff.markdown); // Remove "click" elements that are useless and ugly on some providers :)
const prMessageRequestAdditional = {
title: `Differences for Flow ${flowDiff.name}`,
message: flowDiffMessage,
status: "valid",
messageKey: `sfdx-hardis-flow-diff-${flowDiff.name}`,
sourceFile: flowDiff.markdownFile,
};
await gitProvider.tryPostPullRequestMessage(prMessageRequestAdditional);
}
}
else {
uxLog(this, c.gray(`${JSON.stringify(prData || { noPrData: "" })} && ${gitProvider} && ${prCommentSent}`));
uxLog(this, c.yellow("[Git Provider] Skip post pull request comment"));
}
}
static async getDeploymentCheckId() {
const gitProvider = await GitProvider.getInstance();
if (gitProvider == null) {
return null;
}
try {
// Exotic way: get deployment check Id from current Pull Request: https://github.com/hardisgroupcom/sfdx-hardis/issues/637
if (this.isDeployBeforeMerge()) {
return gitProvider.getPullRequestDeploymentCheckId();
}
// Classic way: get deployment check Id from latest merged Pull Request
const currentGitBranch = await getCurrentGitBranch() || "";
return gitProvider.getBranchDeploymentCheckId(currentGitBranch);
}
catch (e) {
uxLog(this, c.yellow(`Error while trying to retrieve deployment check id:\n${e.message}`));
return null;
}
}
static async getCurrentBranchUrl() {
const gitProvider = await GitProvider.getInstance();
if (gitProvider == null) {
return null;
}
return gitProvider.getCurrentBranchUrl();
}
static async getJobUrl() {
const gitProvider = await GitProvider.getInstance();
if (gitProvider == null) {
return null;
}
return gitProvider.getCurrentJobUrl();
}
static async supportsMermaidInPrMarkdown() {
const gitProvider = await GitProvider.getInstance();
if (gitProvider == null) {
return false;
}
return gitProvider.supportsMermaidInPrMarkdown();
}
static async supportsSvgAttachments() {
const gitProvider = await GitProvider.getInstance();
if (gitProvider == null) {
return false;
}
return gitProvider.supportsSvgAttachments();
}
static prInfoCache = null;
static async getPullRequestInfo(options = { useCache: false }) {
// Return cached result if available and caching is enabled
if (options.useCache && GitProvider.prInfoCache !== null) {
debug("[PR Info] Returning cached pull request info");
return GitProvider.prInfoCache;
}
const gitProvider = await GitProvider.getInstance();
if (gitProvider == null) {
debug("[PR Info] No GitProvider instance found");
return null;
}
let prInfo = null;
try {
prInfo = await gitProvider.getPullRequestInfo();
debug("[GitProvider][PR Info] " + JSON.stringify(prInfo, null, 2));
GitProvider.prInfoCache = prInfo;
}
catch (e) {
uxLog(this, c.yellow("[GitProvider] Unable to get Pull Request info: " + e.message));
uxLog(this, c.yellow(`[GitProvider] Maybe you misconfigured your ${gitProvider.getLabel()} ?`));
uxLog(this, c.yellow(`[GitProvider] See ${CONSTANTS.DOC_URL_ROOT}/salesforce-ci-cd-setup-integrations-home/#git-providers`));
prInfo = null;
}
return prInfo;
}
static isDeployBeforeMerge() {
const deployBeforeMerge = getEnvVar("SFDX_HARDIS_DEPLOY_BEFORE_MERGE") || false;
return [true, "true"].includes(deployBeforeMerge);
}
}
//# sourceMappingURL=index.js.map