diff --git a/.github/workflows/test-action.yml b/.github/workflows/test-action.yml index f5aad64..b829e9f 100644 --- a/.github/workflows/test-action.yml +++ b/.github/workflows/test-action.yml @@ -24,8 +24,8 @@ jobs: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} ATLAN_INSTANCE_URL: ${{secrets.ATLAN_INSTANCE_URL}} ATLAN_API_TOKEN: ${{secrets.ATLAN_API_TOKEN}} - # DBT_ENVIRONMENT_BRANCH_MAP: | - # main: DBT-DEMO-PROD - # beta: Wide World Importers PE1 - # test-action: Wide World Importers PE1 + DBT_ENVIRONMENT_BRANCH_MAP: | + main: DBT-DEMO-PROD + beta: Wide World Importers PE1 + test-action: Wide World Importers PE1 IGNORE_MODEL_ALIAS_MATCHING: true diff --git a/src/api/create-resource.js b/adapters/api/create-resource.js similarity index 69% rename from src/api/create-resource.js rename to adapters/api/create-resource.js index d3f51de..95947e9 100644 --- a/src/api/create-resource.js +++ b/adapters/api/create-resource.js @@ -1,12 +1,12 @@ import { v4 as uuidv4 } from "uuid"; import fetch from "node-fetch"; import stringify from "json-stringify-safe"; -import { getAPIToken, getInstanceUrl } from "../utils/index.js"; +import { + ATLAN_INSTANCE_URL, + ATLAN_API_TOKEN, +} from "../utils/get-environment-variables.js"; -const ATLAN_INSTANCE_URL = getInstanceUrl(); -const ATLAN_API_TOKEN = getAPIToken(); - -export default async function createResource( //Done +export default async function createResource( guid, name, link, @@ -49,14 +49,15 @@ export default async function createResource( //Done .then((e) => e.json()) .catch((err) => { console.log(err); - sendSegmentEventOfIntegration("dbt_ci_action_failure", { - reason: "failed_to_create_resource", - asset_name: name, - msg: err, + sendSegmentEventOfIntegration({ + action: "dbt_ci_action_failure", + properties: { + reason: "failed_to_create_resource", + asset_name: name, // This should change + msg: err, + }, }); }); - console.log("Created Resource:", response); - return response; } diff --git a/src/api/get-asset.js b/adapters/api/get-asset.js similarity index 62% rename from src/api/get-asset.js rename to adapters/api/get-asset.js index 97b0030..488a94e 100644 --- a/src/api/get-asset.js +++ b/adapters/api/get-asset.js @@ -1,17 +1,15 @@ import fetch from "node-fetch"; -import core from "@actions/core"; -import dotenv from "dotenv"; import stringify from "json-stringify-safe"; - -dotenv.config(); - -const ATLAN_INSTANCE_URL = - core.getInput("ATLAN_INSTANCE_URL") || process.env.ATLAN_INSTANCE_URL; -const ATLAN_API_TOKEN = - core.getInput("ATLAN_API_TOKEN") || process.env.ATLAN_API_TOKEN; +import { + getErrorModelNotFound, + getErrorDoesNotMaterialize, +} from "../templates/atlan.js"; +import { + ATLAN_INSTANCE_URL, + ATLAN_API_TOKEN, +} from "../utils/get-environment-variables.js"; export default async function getAsset({ - //Done name, sendSegmentEventOfIntegration, environment, @@ -21,7 +19,7 @@ export default async function getAsset({ Authorization: `Bearer ${ATLAN_API_TOKEN}`, "Content-Type": "application/json", }; - console.log("At line 24 inside getAsset function"); + var raw = stringify({ dsl: { from: 0, @@ -88,8 +86,7 @@ export default async function getAsset({ headers: myHeaders, body: raw, }; - console.log("Before SendSegmentEventOfIntegration"); - console.log("At line 92 inside getAsset"); + var response = await fetch( `${ATLAN_INSTANCE_URL}/api/meta/search/indexsearch#findAssetByExactName`, requestOptions @@ -105,16 +102,47 @@ export default async function getAsset({ }, }); }); - console.log("<><><><><><><><><><><><><>"); - console.log(response); - if (!response?.entities?.length) + + if (!response?.entities?.length) { return { - error: `❌ Model with name **${name}** could not be found or is deleted

`, + error: getErrorModelNotFound(name), }; + } + + if (Array.isArray(response.entities)) { + response.entities.sort((entityA, entityB) => { + const hasDbtModelSqlAssetsA = + entityA.attributes.dbtModelSqlAssets && + entityA.attributes.dbtModelSqlAssets.length > 0; + const hasDbtModelSqlAssetsB = + entityB.attributes.dbtModelSqlAssets && + entityB.attributes.dbtModelSqlAssets.length > 0; + + if (hasDbtModelSqlAssetsA && !hasDbtModelSqlAssetsB) { + return -1; // entityA comes before entityB + } else if (!hasDbtModelSqlAssetsA && hasDbtModelSqlAssetsB) { + return 1; // entityB comes before entityA + } + + // Primary sorting criterion: Latest createTime comes first + if (entityA.createTime > entityB.createTime) { + return -1; + } else if (entityA.createTime < entityB.createTime) { + return 1; + } + + return 0; // No difference in sorting for these two entities + }); + } if (!response?.entities[0]?.attributes?.dbtModelSqlAssets?.length > 0) return { - error: `❌ Model with name [${name}](${ATLAN_INSTANCE_URL}/assets/${response.entities[0].guid}/overview?utm_source=dbt_${integration}_action) does not materialise any asset

`, + error: getErrorDoesNotMaterialize( + name, + ATLAN_INSTANCE_URL, + response, + integration + ), }; return response.entities[0]; diff --git a/src/api/get-classifications.js b/adapters/api/get-classifications.js similarity index 60% rename from src/api/get-classifications.js rename to adapters/api/get-classifications.js index c912590..cc954f2 100644 --- a/src/api/get-classifications.js +++ b/adapters/api/get-classifications.js @@ -1,10 +1,8 @@ import fetch from "node-fetch"; -import { sendSegmentEvent } from "./index.js"; -import stringify from "json-stringify-safe"; -import { getAPIToken, getInstanceUrl } from "../utils/index.js"; - -const ATLAN_INSTANCE_URL = getInstanceUrl(); -const ATLAN_API_TOKEN = getAPIToken(); +import { + ATLAN_INSTANCE_URL, + ATLAN_API_TOKEN, +} from "../utils/get-environment-variables.js"; export default async function getClassifications({ sendSegmentEventOfIntegration, @@ -26,9 +24,12 @@ export default async function getClassifications({ ) .then((e) => e.json()) .catch((err) => { - sendSegmentEventOfIntegration("dbt_ci_action_failure", { - reason: "failed_to_get_classifications", - msg: err, + sendSegmentEventOfIntegration({ + action: "dbt_ci_action_failure", + properties: { + reason: "failed_to_get_classifications", + msg: err, + }, }); }); diff --git a/src/api/get-downstream-assets.js b/adapters/api/get-downstream-assets.js similarity index 77% rename from src/api/get-downstream-assets.js rename to adapters/api/get-downstream-assets.js index 910db08..8dec807 100644 --- a/src/api/get-downstream-assets.js +++ b/adapters/api/get-downstream-assets.js @@ -1,23 +1,18 @@ import fetch from "node-fetch"; -import core from "@actions/core"; -import dotenv from "dotenv"; import { getConnectorImage, getCertificationImage, getImageURL, } from "../utils/index.js"; import stringify from "json-stringify-safe"; - -dotenv.config(); - -const ATLAN_INSTANCE_URL = - core.getInput("ATLAN_INSTANCE_URL") || process.env.ATLAN_INSTANCE_URL; -const ATLAN_API_TOKEN = - core.getInput("ATLAN_API_TOKEN") || process.env.ATLAN_API_TOKEN; +import { + ATLAN_INSTANCE_URL, + ATLAN_API_TOKEN, +} from "../utils/get-environment-variables.js"; const ASSETS_LIMIT = 100; -export default async function getDownstreamAssets( //Done +export default async function getDownstreamAssets( asset, guid, totalModifiedFiles, @@ -28,7 +23,7 @@ export default async function getDownstreamAssets( //Done authorization: `Bearer ${ATLAN_API_TOKEN}`, "content-type": "application/json", }; - console.log("At line 31 inside getDownstreamAssets function"); + var raw = stringify({ guid: guid, size: Math.max(Math.ceil(ASSETS_LIMIT / totalModifiedFiles), 1), @@ -96,12 +91,15 @@ ${getImageURL( asset.guid }/lineage/overview?utm_source=dbt_${integration}_action)`; - sendSegmentEventOfIntegration("dbt_ci_action_failure", { - reason: "failed_to_fetch_lineage", - asset_guid: asset.guid, - asset_name: asset.name, - asset_typeName: asset.typeName, - msg: err, + sendSegmentEventOfIntegration({ + action: "dbt_ci_action_failure", + properties: { + reason: "failed_to_fetch_lineage", + asset_guid: asset.guid, + asset_name: asset.name, + asset_typeName: asset.typeName, + msg: err, + }, }); return comment; @@ -123,7 +121,6 @@ ${getImageURL( error: handleError(err), }; }); - console.log("At line 126 inside getDownstreamAssets function", response); if (response.error) return response; return response; diff --git a/src/api/index.js b/adapters/api/index.js similarity index 100% rename from src/api/index.js rename to adapters/api/index.js diff --git a/src/api/segment.js b/adapters/api/segment.js similarity index 63% rename from src/api/segment.js rename to adapters/api/segment.js index bd01636..0a0e6f2 100644 --- a/src/api/segment.js +++ b/adapters/api/segment.js @@ -1,21 +1,16 @@ import fetch from "node-fetch"; -import core from "@actions/core"; -import dotenv from "dotenv"; - -dotenv.config(); - -const { IS_DEV } = process.env; -const ATLAN_INSTANCE_URL = - core.getInput("ATLAN_INSTANCE_URL") || process.env.ATLAN_INSTANCE_URL; -const ATLAN_API_TOKEN = - core.getInput("ATLAN_API_TOKEN") || process.env.ATLAN_API_TOKEN; +import { + IS_DEV, + ATLAN_INSTANCE_URL, + ATLAN_API_TOKEN, +} from "../utils/get-environment-variables.js"; export async function sendSegmentEvent(action, body) { const myHeaders = { authorization: `Bearer ${ATLAN_API_TOKEN}`, "content-type": "application/json", }; - console.log("At line 18 inide sendSegmentEvent"); + const requestOptions = { method: "POST", headers: myHeaders, @@ -37,6 +32,6 @@ export async function sendSegmentEvent(action, body) { } else { console.log("send segment event", action, body); } - console.log("At line 40 inside sendSegmentEvent"); + return response; } diff --git a/src/assets/atlan-logo.png b/adapters/assets/atlan-logo.png similarity index 100% rename from src/assets/atlan-logo.png rename to adapters/assets/atlan-logo.png diff --git a/src/assets/certification_deprecated.png b/adapters/assets/certification_deprecated.png similarity index 100% rename from src/assets/certification_deprecated.png rename to adapters/assets/certification_deprecated.png diff --git a/src/assets/certification_drafted.png b/adapters/assets/certification_drafted.png similarity index 100% rename from src/assets/certification_drafted.png rename to adapters/assets/certification_drafted.png diff --git a/src/assets/certification_verified.png b/adapters/assets/certification_verified.png similarity index 100% rename from src/assets/certification_verified.png rename to adapters/assets/certification_verified.png diff --git a/src/assets/connector_airflow.png b/adapters/assets/connector_airflow.png similarity index 100% rename from src/assets/connector_airflow.png rename to adapters/assets/connector_airflow.png diff --git a/src/assets/connector_athena.png b/adapters/assets/connector_athena.png similarity index 100% rename from src/assets/connector_athena.png rename to adapters/assets/connector_athena.png diff --git a/src/assets/connector_aws-s3.png b/adapters/assets/connector_aws-s3.png similarity index 100% rename from src/assets/connector_aws-s3.png rename to adapters/assets/connector_aws-s3.png diff --git a/src/assets/connector_azure-datalake.png b/adapters/assets/connector_azure-datalake.png similarity index 100% rename from src/assets/connector_azure-datalake.png rename to adapters/assets/connector_azure-datalake.png diff --git a/src/assets/connector_bigquery.png b/adapters/assets/connector_bigquery.png similarity index 100% rename from src/assets/connector_bigquery.png rename to adapters/assets/connector_bigquery.png diff --git a/src/assets/connector_databricks.png b/adapters/assets/connector_databricks.png similarity index 100% rename from src/assets/connector_databricks.png rename to adapters/assets/connector_databricks.png diff --git a/src/assets/connector_dbt.png b/adapters/assets/connector_dbt.png similarity index 100% rename from src/assets/connector_dbt.png rename to adapters/assets/connector_dbt.png diff --git a/src/assets/connector_gcp.png b/adapters/assets/connector_gcp.png similarity index 100% rename from src/assets/connector_gcp.png rename to adapters/assets/connector_gcp.png diff --git a/src/assets/connector_glue.png b/adapters/assets/connector_glue.png similarity index 100% rename from src/assets/connector_glue.png rename to adapters/assets/connector_glue.png diff --git a/src/assets/connector_grafana.png b/adapters/assets/connector_grafana.png similarity index 100% rename from src/assets/connector_grafana.png rename to adapters/assets/connector_grafana.png diff --git a/src/assets/connector_looker.png b/adapters/assets/connector_looker.png similarity index 100% rename from src/assets/connector_looker.png rename to adapters/assets/connector_looker.png diff --git a/src/assets/connector_mocks.png b/adapters/assets/connector_mocks.png similarity index 100% rename from src/assets/connector_mocks.png rename to adapters/assets/connector_mocks.png diff --git a/src/assets/connector_mysql.png b/adapters/assets/connector_mysql.png similarity index 100% rename from src/assets/connector_mysql.png rename to adapters/assets/connector_mysql.png diff --git a/src/assets/connector_oracle.png b/adapters/assets/connector_oracle.png similarity index 100% rename from src/assets/connector_oracle.png rename to adapters/assets/connector_oracle.png diff --git a/src/assets/connector_postgres.png b/adapters/assets/connector_postgres.png similarity index 100% rename from src/assets/connector_postgres.png rename to adapters/assets/connector_postgres.png diff --git a/src/assets/connector_powerbi.png b/adapters/assets/connector_powerbi.png similarity index 100% rename from src/assets/connector_powerbi.png rename to adapters/assets/connector_powerbi.png diff --git a/src/assets/connector_presto.png b/adapters/assets/connector_presto.png similarity index 100% rename from src/assets/connector_presto.png rename to adapters/assets/connector_presto.png diff --git a/src/assets/connector_python.png b/adapters/assets/connector_python.png similarity index 100% rename from src/assets/connector_python.png rename to adapters/assets/connector_python.png diff --git a/src/assets/connector_r.png b/adapters/assets/connector_r.png similarity index 100% rename from src/assets/connector_r.png rename to adapters/assets/connector_r.png diff --git a/src/assets/connector_redash.png b/adapters/assets/connector_redash.png similarity index 100% rename from src/assets/connector_redash.png rename to adapters/assets/connector_redash.png diff --git a/src/assets/connector_redshift.png b/adapters/assets/connector_redshift.png similarity index 100% rename from src/assets/connector_redshift.png rename to adapters/assets/connector_redshift.png diff --git a/src/assets/connector_sisense.png b/adapters/assets/connector_sisense.png similarity index 100% rename from src/assets/connector_sisense.png rename to adapters/assets/connector_sisense.png diff --git a/src/assets/connector_snowflake.png b/adapters/assets/connector_snowflake.png similarity index 100% rename from src/assets/connector_snowflake.png rename to adapters/assets/connector_snowflake.png diff --git a/src/assets/connector_tableau.png b/adapters/assets/connector_tableau.png similarity index 100% rename from src/assets/connector_tableau.png rename to adapters/assets/connector_tableau.png diff --git a/adapters/gateway.js b/adapters/gateway.js index df652c5..6b4402e 100644 --- a/adapters/gateway.js +++ b/adapters/gateway.js @@ -1,9 +1,10 @@ // Common Gateway for all integrations +import logger from "./logger/logger.js"; export async function runAction(token, integrationModule) { if (token === undefined) { - console.log("Token not provided."); + logger.logInfo("Token not provided.", "runAction"); return; } const integration = new integrationModule(token); - await integration.run(); // Add error part + await integration.run(); } diff --git a/adapters/index.js b/adapters/index.js index 8b34844..16d2e29 100644 --- a/adapters/index.js +++ b/adapters/index.js @@ -1,18 +1,14 @@ // main.js -import dotenv from "dotenv"; import { runAction } from "./gateway.js"; import GitHubIntegration from "./integrations/github-integration.js"; import GitLabIntegration from "./integrations/gitlab-integration.js"; -import core from "@actions/core"; - -dotenv.config(); - -const GITHUB_TOKEN = core.getInput("GITHUB_TOKEN") || process.env.GITHUB_TOKEN; -const GITLAB_TOKEN = process.env.GITLAB_TOKEN; +import { + GITLAB_TOKEN, + GITHUB_TOKEN, +} from "./utils/get-environment-variables.js"; async function run() { //Add new integrations over here - console.log("oii"); await runAction(GITHUB_TOKEN, GitHubIntegration); await runAction(GITLAB_TOKEN, GitLabIntegration); } diff --git a/adapters/integrations/github-integration.js b/adapters/integrations/github-integration.js index 0988609..ff51448 100644 --- a/adapters/integrations/github-integration.js +++ b/adapters/integrations/github-integration.js @@ -1,490 +1,847 @@ // githubIntegration.js -import dotenv from "dotenv"; // Check do we actually need it or not import IntegrationInterface from "./contract/contract.js"; import github from "@actions/github"; -import { isIgnoreModelAliasMatching } from "../../src/utils/get-environment-variables.js"; +import stringify from "json-stringify-safe"; import { getCertificationImage, getConnectorImage, -} from "../../src/utils/get-image-url.js"; -import { getEnvironments } from "../../src/utils/get-environment-variables.js"; -import stringify from "json-stringify-safe"; + getEnvironments, + auth, + truncate, +} from "../utils/index.js"; import { getAsset, getDownstreamAssets, sendSegmentEvent, createResource, getClassifications, -} from "../../src/api/index.js"; -import { getImageURL, auth } from "../../src/utils/index.js"; -import { isDev } from "../../src/utils/get-environment-variables.js"; -import { truncate } from "../../src/utils/create-comment.js"; -import { getInstanceUrl } from "../../src/utils/get-environment-variables.js"; - -const IS_DEV = isDev(); -const ATLAN_INSTANCE_URL = getInstanceUrl(); - -dotenv.config(); - +} from "../api/index.js"; +import { + getSetResourceOnAssetComment, + getErrorResponseStatus401, + getErrorResponseStatusUndefined, + getAssetInfo, + getDownstreamTable, + getViewAssetButton, + getMDCommentForModel, + getMDCommentForMaterialisedView, + getTableMD, +} from "../templates/github-integration.js"; +import { getNewModelAddedComment, getBaseComment } from "../templates/atlan.js"; +import { + IS_DEV, + ATLAN_INSTANCE_URL, + IGNORE_MODEL_ALIAS_MATCHING, +} from "../utils/get-environment-variables.js"; +import logger from "../logger/logger.js"; +var headSHA; +const integrationName = "github"; export default class GitHubIntegration extends IntegrationInterface { constructor(token) { super(token); } async run() { - //Done - //Complete - console.log("Run Github"); - console.log(IS_DEV); - const timeStart = Date.now(); - const { context } = github; - console.log("Context:", context); - const octokit = github.getOctokit(this.token); - const { pull_request } = context?.payload; - console.log(pull_request, "hii"); - - console.log("Interesting"); - const { state, merged } = pull_request; - console.log("state", state); - console.log("merged", merged); - if (!(await this.authIntegration({ octokit, context }))) { - //DONE - //Complete - throw { message: "Wrong API Token" }; - } + try { + const timeStart = Date.now(); + const { context } = github; + + const octokit = github.getOctokit(this.token); + const { pull_request } = context?.payload; + const { state, merged } = pull_request; + headSHA = pull_request?.head?.sha; + + logger.withInfo( + "GitHub Integration is running...", + integrationName, + headSHA, + "run" + ); - let total_assets = 0; - console.log("After auth Integration"); - if (state === "open") { - total_assets = await this.printDownstreamAssets({ octokit, context }); - } else if (state === "closed" && merged) { - console.log("Hmm"); - total_assets = await this.setResourceOnAsset({ octokit, context }); - } + if (!(await this.authIntegration({ octokit, context }))) { + logger.withError( + "Authentication failed. Wrong API Token.", + integrationName, + headSHA, + "run" + ); + throw { message: "Wrong API Token" }; + } - if (total_assets !== 0) { - this.sendSegmentEventOfIntegration("dbt_ci_action_run", { - //Complete - asset_count: total_assets, - total_time: Date.now() - timeStart, - }); + let total_assets = 0; + + if (state === "open") { + total_assets = await this.printDownstreamAssets({ octokit, context }); + } else if (state === "closed" && merged) { + total_assets = await this.setResourceOnAsset({ octokit, context }); + } + + if (total_assets !== 0) { + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_run", + properties: { + asset_count: total_assets, + total_time: Date.now() - timeStart, + }, + }); + } + + logger.withInfo( + "Successfully Completed DBT_CI_ACTION", + integrationName, + headSHA, + "run" + ); + } catch (error) { + logger.withError( + `Error in run(): ${error.message}`, + integrationName, + headSHA, + "run" + ); + throw error; } } async printDownstreamAssets({ octokit, context }) { - //Done - console.log("Brother"); - // const changedFiles = await this.getChangedFiles({ octokit, context }); //Complete - var changedFiles = [ - { - fileName: "instacart_beverages_order_customer", - filePath: "instacart_beverages_order_customer.sql", - status: "modified", - }, - ]; - let comments = ``; - let totalChangedFiles = 0; - - for (const { fileName, filePath, status } of changedFiles) { - const aliasName = await this.getAssetName({ - //Complete - octokit, - context, - fileName, - filePath, - }); - const assetName = isIgnoreModelAliasMatching() ? fileName : aliasName; //Complete - console.log("acha2"); - const environments = getEnvironments(); - - let environment = null; - for (const [baseBranchName, environmentName] of environments) { - if (baseBranchName === context.payload.pull_request.base.ref) { - environment = environmentName; - break; + logger.withInfo( + "Printing downstream assets...", + integrationName, + headSHA, + "printDownstreamAssets" + ); + + try { + const changedFiles = await this.getChangedFiles({ octokit, context }); + let comments = ``; + let totalChangedFiles = 0; + + for (const { fileName, filePath, status } of changedFiles) { + logger.withInfo( + `Processing file: ${fileName}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + const aliasName = await this.getAssetName({ + octokit, + context, + fileName, + filePath, + }); + const assetName = IGNORE_MODEL_ALIAS_MATCHING ? fileName : aliasName; + + const environments = getEnvironments(); + let environment = null; + for (const [baseBranchName, environmentName] of environments) { + if (baseBranchName === context.payload.pull_request.base.ref) { + environment = environmentName; + break; + } } - } - console.log("Before getAsset"); - const asset = await getAsset({ - //Done - name: assetName, - sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, - environment: environment, - integration: "github", - }); - console.log("After getAsset"); - if (totalChangedFiles !== 0) comments += "\n\n---\n\n"; - console.log("Status: ", status); - if (status === "added") { - comments += `### ${getConnectorImage("dbt")} ${fileName} 🆕 -Its a new model and not present in Atlan yet, you'll see the downstream impact for it after its present in Atlan.`; - totalChangedFiles++; - continue; - } - console.log("Before filtering"); - console.log("Asset", asset); - if (asset.error) { - comments += asset.error; - totalChangedFiles++; - continue; - } - const materialisedAsset = asset.attributes.dbtModelSqlAssets[0]; - const timeStart = Date.now(); - const totalModifiedFiles = changedFiles.filter( - (i) => i.status === "modified" - ).length; - console.log("Before getDownstreamAssets"); - const downstreamAssets = await getDownstreamAssets( - //Complete - asset, - materialisedAsset.guid, - totalModifiedFiles - ); - console.log("After getDownstreamAssets"); - if (downstreamAssets.error) { - comments += downstreamAssets.error; - totalChangedFiles++; - continue; - } - console.log("At line 139 after getDownstreamAssets in printDownstream"); - this.sendSegmentEventOfIntegration("dbt_ci_action_downstream_unfurl", { - //Complete - asset_guid: asset.guid, - asset_type: asset.typeName, - downstream_count: downstreamAssets.entities.length, - total_fetch_time: Date.now() - timeStart, - }); - console.log("At line 147 after getDownstreamAssets in printDownstream"); - const classifications = await getClassifications({ - //Complete - sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, - }); + logger.withInfo( + `Processing asset: ${assetName} in environment: ${environment}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + const asset = await getAsset({ + name: assetName, + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + environment: environment, + integration: "github", + }); + + if (totalChangedFiles !== 0) comments += "\n\n---\n\n"; + + if (status === "added") { + logger.withInfo( + `New model added: ${fileName}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + comments += getNewModelAddedComment(fileName); + totalChangedFiles++; + continue; + } - const comment = await this.renderDownstreamAssetsComment({ - //Done - //Complete - octokit, - context, - asset, - materialisedAsset, - downstreamAssets, - classifications, - }); + if (asset.error) { + logger.withError( + `Asset error for ${assetName}: ${asset.error}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + comments += asset.error; + totalChangedFiles++; + continue; + } - comments += comment; + const materialisedAsset = asset?.attributes?.dbtModelSqlAssets?.[0]; + const timeStart = Date.now(); - totalChangedFiles++; - } + const totalModifiedFiles = changedFiles.filter( + (i) => i.status === "modified" + ).length; - comments = `### ${getImageURL("atlan-logo", 15, 15)} Atlan impact analysis -Here is your downstream impact analysis for **${totalChangedFiles} ${ - totalChangedFiles > 1 ? "models" : "model" - }** you have edited. + const downstreamAssets = await getDownstreamAssets( + asset, + materialisedAsset.guid, + totalModifiedFiles, + this.sendSegmentEventOfIntegration, + "github" + ); -${comments}`; + if (downstreamAssets.error) { + logger.withError( + `Downstream assets error for ${assetName}: ${downstreamAssets.error}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + comments += downstreamAssets.error; + totalChangedFiles++; + continue; + } - const existingComment = await this.checkCommentExists({ octokit, context }); //Complete + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_downstream_unfurl", + properties: { + asset_guid: asset.guid, + asset_type: asset.typeName, + downstream_count: downstreamAssets.entities.length, + total_fetch_time: Date.now() - timeStart, + }, + }); + + const classifications = await getClassifications({ + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + }); + + const comment = await this.renderDownstreamAssetsComment({ + octokit, + context, + asset, + materialisedAsset, + downstreamAssets, + classifications, + }); - if (totalChangedFiles > 0) - await this.createIssueComment({ - //Complete - octokit, - context, - content: comments, - comment_id: existingComment?.id, - }); + comments += comment; - if (totalChangedFiles === 0 && existingComment) - await this.deleteComment({ - //Complete + totalChangedFiles++; + } + + comments = getBaseComment(totalChangedFiles, comments); + + const existingComment = await this.checkCommentExists({ octokit, context, - comment_id: existingComment.id, }); - return totalChangedFiles; + logger.withInfo( + `Existing Comment: ${existingComment?.id}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + + if (totalChangedFiles > 0) + await this.createIssueComment({ + octokit, + context, + content: comments, + comment_id: existingComment?.id, + }); + + if (totalChangedFiles === 0 && existingComment) + await this.deleteComment({ + octokit, + context, + comment_id: existingComment?.id, + }); + + logger.withInfo( + "Successfully printed Downstream Assets", + integrationName, + headSHA, + "printDownstreamAssets" + ); + + return totalChangedFiles; + } catch (error) { + logger.withError( + `Error in printDownstreamAssets: ${error.message}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + throw error; + } } async setResourceOnAsset({ octokit, context }) { - //Done - //Complete - const changedFiles = await this.getChangedFiles({ octokit, context }); //Complete - const { pull_request } = context.payload; - var totalChangedFiles = 0; - - if (changedFiles.length === 0) return; + logger.withInfo( + "Setting resources on assets...", + integrationName, + headSHA, + "setResourceOnAsset" + ); - for (const { fileName, filePath } of changedFiles) { - const assetName = await this.getAssetName({ - //Complete - octokit, - context, - fileName, - filePath, - }); + try { + const changedFiles = await this.getChangedFiles({ octokit, context }); + const { pull_request } = context.payload; + var totalChangedFiles = 0; + let tableMd = ``; + let setResourceFailed = false; + + if (changedFiles.length === 0) { + logger.withInfo( + "No changed files found. Skipping resource setup.", + integrationName, + headSHA, + "setResourceOnAsset" + ); + return totalChangedFiles; + } - const environments = getEnvironments(); + for (const { fileName, filePath } of changedFiles) { + logger.withInfo( + `Processing file: ${fileName}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + const aliasName = await this.getAssetName({ + octokit, + context, + fileName, + filePath, + }); + + const assetName = IGNORE_MODEL_ALIAS_MATCHING ? fileName : aliasName; + + logger.withInfo( + `Resolved asset name: ${assetName}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); - let environment = null; - for (const [baseBranchName, environmentName] of environments) { - if (baseBranchName === context.payload.pull_request.base.ref) { - environment = environmentName; - break; + const environments = getEnvironments(); + let environment = null; + for (const [baseBranchName, environmentName] of environments) { + if (baseBranchName === context.payload.pull_request.base.ref) { + environment = environmentName; + break; + } } - } - const asset = await getAsset({ - //Done - name: assetName, - sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, - environment: environment, - integration: "github", - }); + logger.withInfo( + `Processing asset: ${assetName} in environment: ${environment}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); - if (asset.error) continue; + const asset = await getAsset({ + name: assetName, + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + environment: environment, + integration: "github", + }); + + if (asset.error) { + logger.withError( + `Failed to retrieve asset: ${assetName}, Error: ${asset.error}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + continue; + } - const { guid: modelGuid } = asset; - const { guid: tableAssetGuid } = - asset?.attributes?.dbtModelSqlAssets?.[0]; + const materialisedAsset = asset?.attributes?.dbtModelSqlAssets?.[0]; + const timeStart = Date.now(); - if (modelGuid) - await createResource( - //Complete - modelGuid, - "Pull Request on GitHub", - pull_request.html_url, - this.sendSegmentEventOfIntegration - ); + const totalModifiedFiles = changedFiles.filter( + (i) => i.status === "modified" + ).length; - if (tableAssetGuid) - await createResource( - //Complete - tableAssetGuid, - "Pull Request on GitHub", - pull_request.html_url, - this.sendSegmentEventOfIntegration + const downstreamAssets = await getDownstreamAssets( + asset, + materialisedAsset.guid, + totalModifiedFiles, + this.sendSegmentEventOfIntegration, + "github" ); - totalChangedFiles++; - } - - const comment = await this.createIssueComment({ - //Complete - octokit, - context, - content: `🎊 Congrats on the merge! + if (downstreamAssets.error) { + logger.withError( + `Failed to retrieve downstream assets for: ${assetName}, Error: ${downstreamAssets.error}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + continue; + } -This pull request has been added as a resource to all the assets modified. ✅ -`, - comment_id: null, - forceNewComment: true, - }); + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_downstream_unfurl", + properties: { + asset_guid: asset.guid, + asset_type: asset.typeName, + downstream_count: downstreamAssets.entities.length, + total_fetch_time: Date.now() - timeStart, + }, + }); + + const model = asset; + const materialisedView = asset?.attributes?.dbtModelSqlAssets?.[0]; + + let PR_TITLE = pull_request.title; + + if (downstreamAssets.entityCount != 0) { + if (model) { + const { guid: modelGuid } = model; + const resp = await createResource( + modelGuid, + PR_TITLE, + pull_request.html_url, + this.sendSegmentEventOfIntegration + ); + const md = getMDCommentForModel(ATLAN_INSTANCE_URL, model); + tableMd += getTableMD(md, resp); + if (!resp) { + setResourceFailed = true; + logger.withError( + `Setting resource failed for model: ${modelGuid}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + } + } - return totalChangedFiles; - } + if (materialisedView) { + const { guid: tableAssetGuid } = materialisedView; + const resp = await createResource( + tableAssetGuid, + PR_TITLE, + pull_request.html_url, + this.sendSegmentEventOfIntegration + ); + const md = getMDCommentForMaterialisedView( + ATLAN_INSTANCE_URL, + materialisedView + ); + tableMd += getTableMD(md, resp); + if (!resp) { + setResourceFailed = true; + logger.withError( + `Setting resource failed for materialized view: ${tableAssetGuid}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + } + } + } + totalChangedFiles++; + } - async authIntegration({ octokit, context }) { - //DONE - //COMPLETE - console.log("Here is Context:", context); - const response = await auth(); - console.log("Inside authIntegration befor comment exists"); - const existingComment = await this.checkCommentExists({ octokit, context }); - - console.log("Existing Comment", existingComment); - - if (response?.status === 401) { - console.log("Inside authIntegration befor createIssueComment"); - await this.createIssueComment({ + const comment = await this.createIssueComment({ octokit, context, - content: `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Bearer Token as \`ATLAN_API_TOKEN\` as this repository's action secret. + content: getSetResourceOnAssetComment(tableMd, setResourceFailed), + comment_id: null, + forceNewComment: true, + }); -Atlan Instance URL: ${ATLAN_INSTANCE_URL} + logger.withInfo( + "Successfully set the resource on the asset", + integrationName, + headSHA, + "setResourceOnAsset" + ); -Set your repository action secrets [here](https://github.com/${context.payload.repository.full_name}/settings/secrets/actions). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).`, - comment_id: existingComment?.id, - }); - return false; + return totalChangedFiles; + } catch (error) { + logger.withError( + `Error in setResourceOnAsset: ${error}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + throw error; } + } + + async authIntegration({ octokit, context }) { + logger.withInfo( + "Authenticating with Atlan", + integrationName, + headSHA, + "authIntegration" + ); - if (response === undefined) { - await this.createIssueComment({ + try { + const response = await auth(); + + const existingComment = await this.checkCommentExists({ octokit, context, - content: `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Instance URL as \`ATLAN_INSTANCE_URL\` as this repository's action secret. + }); -Atlan Instance URL: ${ATLAN_INSTANCE_URL} + logger.withInfo( + `Existing Comment: ${existingComment?.id}`, + integrationName, + headSHA, + "authIntegration" + ); -Make sure your Atlan Instance URL is set in the following format. -\`https://tenant.atlan.com\` + if (response?.status === 401) { + logger.withError( + "Authentication failed: Status 401", + integrationName, + headSHA, + "authIntegration" + ); + await this.createIssueComment({ + octokit, + context, + content: getErrorResponseStatus401(ATLAN_INSTANCE_URL, context), + comment_id: existingComment?.id, + }); + return false; + } -Set your repository action secrets [here](https://github.com/${context.payload.repository.full_name}/settings/secrets/actions). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).`, - comment_id: existingComment?.id, - }); - return false; + if (response === undefined) { + logger.withError( + "Authentication failed: Undefined response", + integrationName, + headSHA, + "authIntegration" + ); + await this.createIssueComment({ + octokit, + context, + content: getErrorResponseStatusUndefined(ATLAN_INSTANCE_URL, context), + comment_id: existingComment?.id, + }); + return false; + } + logger.withInfo( + "Successfully Authenticated with Atlan", + integrationName, + headSHA, + "authIntegration" + ); + return true; + } catch (error) { + logger.withError( + `Error in authIntegration: ${error.message}`, + integrationName, + headSHA, + "authIntegration" + ); + throw error; } - - return true; } async sendSegmentEventOfIntegration({ action, properties }) { - //Done - //FullyComplete - // IMPORT ATLAN_INSTANCE_URL. - const domain = new URL(ATLAN_INSTANCE_URL).hostname; - - const raw = stringify({ - category: "integration", - object: "github", - action, - userId: "atlan-annonymous-github", - properties: { - ...properties, - //get context for this - // github_action_id: `https://github.com/${context.payload.repository.full_name}/actions/runs/${context.runId}`, - domain, - }, - }); + try { + const domain = new URL(ATLAN_INSTANCE_URL).hostname; + const { context } = github; //confirm this + logger.withInfo( + `Sending Segment event for action: ${action}`, + integrationName, + headSHA, + "sendSegmentEventOfIntegration" + ); - return sendSegmentEvent(action, raw); + const raw = stringify({ + category: "integration", + object: "github", + action, + userId: "atlan-annonymous-github", + properties: { + ...properties, + github_action_id: `https://github.com/${context?.payload?.repository?.full_name}/actions/runs/${context?.runId}`, + domain, + }, + }); + + return sendSegmentEvent(action, raw); + } catch (error) { + logger.withError( + `Error sending Segment event for action: ${action} - ${error.message}`, + integrationName, + headSHA, + "sendSegmentEventOfIntegration" + ); + throw error; + } } async getChangedFiles({ octokit, context }) { - //Done - //FullyComplete - const { repository, pull_request } = context.payload, - owner = repository.owner.login, - repo = repository.name, - pull_number = pull_request.number; - - const res = await octokit.request( - `GET /repos/${owner}/${repo}/pulls/${pull_number}/files`, - { - owner, - repo, - pull_number, - } - ); + try { + logger.withInfo( + "Fetching changed files...", + integrationName, + headSHA, + "getChangedFiles" + ); - var changedFiles = res.data - .map(({ filename, status }) => { - try { - const [modelName] = filename - .match(/.*models\/(.*)\.sql/)[1] - .split("/") - .reverse()[0] - .split("."); - - if (modelName) { - return { - fileName: modelName, - filePath: filename, - status, - }; - } - } catch (e) {} - }) - .filter((i) => i !== undefined); + const { repository, pull_request } = context.payload, + owner = repository.owner.login, + repo = repository.name, + pull_number = pull_request.number; - changedFiles = changedFiles.filter((item, index) => { - return ( - changedFiles.findIndex((obj) => obj.fileName === item.fileName) === - index + const res = await octokit.request( + `GET /repos/${owner}/${repo}/pulls/${pull_number}/files`, + { + owner, + repo, + pull_number, + } ); - }); - console.log("Changed Files: ", changedFiles); + var changedFiles = res.data + .map(({ filename, status }) => { + try { + const [modelName] = filename + .match(/.*models\/(.*)\.sql/)[1] + .split("/") + .reverse()[0] + .split("."); + + if (modelName) { + return { + fileName: modelName, + filePath: filename, + status, + }; + } + } catch (e) { + logger.withError( + `Error processing file: ${filename} - ${e.message}`, + integrationName, + headSHA, + "getChangedFiles" + ); + } + }) + .filter((i) => i !== undefined); + + changedFiles = changedFiles.filter((item, index) => { + return ( + changedFiles.findIndex((obj) => obj.fileName === item.fileName) === + index + ); + }); - return changedFiles; + logger.withInfo( + "Successfully fetched changed files", + integrationName, + headSHA, + "getChangedFiles" + ); + + return changedFiles; + } catch (error) { + logger.withError( + `Error fetching changed files - ${error.message}`, + integrationName, + headSHA, + "getChangedFiles" + ); + throw error; + } } async getAssetName({ octokit, context, fileName, filePath }) { - //Done - // FullyComplete - var regExp = - /{{\s*config\s*\(\s*(?:[^,]*,)*\s*alias\s*=\s*['"]([^'"]+)['"](?:\s*,[^,]*)*\s*\)\s*}}/im; - var fileContents = await this.getFileContents({ - octokit, - context, - filePath, - }); + try { + logger.withInfo( + "Getting asset name...", + integrationName, + headSHA, + "getAssetName" + ); - if (fileContents) { - var matches = regExp.exec(fileContents); + var regExp = + /{{\s*config\s*\(\s*(?:[^,]*,)*\s*alias\s*=\s*['"]([^'"]+)['"](?:\s*,[^,]*)*\s*\)\s*}}/im; + var fileContents = await this.getFileContents({ + octokit, + context, + filePath, + }); - if (matches) { - return matches[1].trim(); + if (fileContents) { + var matches = regExp.exec(fileContents); + if (matches) { + logger.withInfo( + `Found a match: ${matches[1].trim()}`, + integrationName, + headSHA, + "getAssetName" + ); + return matches[1].trim(); + } } + logger.withInfo( + `Using filename as asset name: ${fileName}`, + integrationName, + headSHA, + "getAssetName" + ); + return fileName; + } catch (error) { + logger.withError( + `Error getting asset name - ${error.message}`, + integrationName, + headSHA, + "getAssetName" + ); + throw error; } - - return fileName; } async getFileContents({ octokit, context, filePath }) { - //Done - // FullyComplete - const { repository, pull_request } = context.payload, - owner = repository.owner.login, - repo = repository.name, - head_sha = pull_request.head.sha; - - const res = await octokit - .request( - `GET /repos/${owner}/${repo}/contents/${filePath}?ref=${head_sha}`, - { - owner, - repo, - path: filePath, - } - ) - .catch((e) => { - console.log("Error fetching file contents: ", e); - return null; - }); - - if (!res) return null; + try { + logger.withInfo( + "Fetching file contents...", + integrationName, + headSHA, + "getFileContents" + ); - const buff = Buffer.from(res.data.content, "base64"); + const { repository, pull_request } = context.payload, + owner = repository.owner.login, + repo = repository.name, + head_sha = pull_request.head.sha; + + const res = await octokit + .request( + `GET /repos/${owner}/${repo}/contents/${filePath}?ref=${head_sha}`, + { + owner, + repo, + path: filePath, + } + ) + .catch((e) => { + logger.withError( + `Error fetching file contents: ${e.message}`, + integrationName, + headSHA, + "getFileContents" + ); + return null; + }); + + if (!res) return null; + + const buff = Buffer.from(res.data.content, "base64"); + + logger.withInfo( + "Successfully fetched file contents", + integrationName, + headSHA, + "getFileContents" + ); - return buff.toString("utf8"); + return buff.toString("utf8"); + } catch (error) { + logger.withError( + `Error in getFileContents: ${error.message}`, + integrationName, + headSHA, + "getFileContents" + ); + throw error; + } } async checkCommentExists({ octokit, context }) { - //Done - //FullyComplete - if (IS_DEV) return null; + logger.withInfo( + "Checking for existing comments...", + integrationName, + headSHA, + "checkCommentExists" + ); + + if (IS_DEV) { + logger.withInfo( + "Development mode enabled. Skipping comment check.", + integrationName, + headSHA, + "checkCommentExists" + ); + return null; + } const { pull_request } = context.payload; - const comments = await octokit.rest.issues.listComments({ - ...context.repo, - issue_number: pull_request.number, - }); + try { + const comments = await octokit.rest.issues.listComments({ + ...context.repo, + issue_number: pull_request.number, + }); - return comments.data.find( - (comment) => - comment.user.login === "github-actions[bot]" && - comment.body.includes( - "" - ) - ); + const existingComment = comments.data.find( + (comment) => + comment.user.login === "github-actions[bot]" && + comment.body.includes( + "" + ) + ); + if (existingComment) { + logger.withInfo( + "Found existing comment: " + existingComment?.id, + integrationName, + headSHA, + "checkCommentExists" + ); + } else { + logger.withInfo( + "No existing comment found", + integrationName, + headSHA, + "checkCommentExists" + ); + } + + return existingComment; + } catch (error) { + logger.withError( + "Error checking for existing comments: " + error.message, + integrationName, + headSHA, + "checkCommentExists" + ); + throw error; + } } async createIssueComment({ - //Done - // FullyComplete octokit, context, content, comment_id = null, forceNewComment = false, }) { - console.log("Inside CreateIssue:", context); - console.log("Inside CreateIssue Comment"); + logger.withInfo( + "Creating an issue comment...", + integrationName, + headSHA, + "createIssueComment" + ); + const { pull_request } = context?.payload || {}; - console.log("Inside CreateIssue Comment"); + content = ` ${content}`; @@ -494,10 +851,15 @@ ${content}`; body: content, }; - console.log(content, content.length); - console.log("Inside CreateIssue Comment Complete"); - - if (IS_DEV) return content; + if (IS_DEV) { + logger.withInfo( + "Development mode enabled. Skipping comment creation.", + integrationName, + headSHA, + "createIssueComment" + ); + return content; + } if (comment_id && !forceNewComment) return octokit.rest.issues.updateComment({ ...commentObj, comment_id }); @@ -505,8 +867,13 @@ ${content}`; } async deleteComment({ octokit, context, comment_id }) { - //Done - //FullyComplete + logger.withInfo( + `Deleted comment with ID ${comment_id}`, + integrationName, + headSHA, + "deleteComment" + ); + const { pull_request } = context.payload; return octokit.rest.issues.deleteComment({ @@ -517,8 +884,6 @@ ${content}`; } async renderDownstreamAssetsComment({ - //Done - //FullyComplete octokit, context, asset, @@ -526,163 +891,148 @@ ${content}`; downstreamAssets, classifications, }) { - let impactedData = downstreamAssets.entities.map( - ({ - displayText, - guid, - typeName, - attributes, - meanings, - classificationNames, - }) => { - // Modifying the typeName and getting the readableTypeName - let readableTypeName = typeName - .toLowerCase() - .replace(attributes.connectorName, "") - .toUpperCase(); - - // Filtering classifications based on classificationNames - let classificationsObj = classifications.filter(({ name }) => - classificationNames.includes(name) - ); + logger.withInfo( + "Rendering Downstream Assets...", + integrationName, + headSHA, + "renderDownstreamAssetsComment" + ); + try { + let impactedData = downstreamAssets.entities.map( + ({ + displayText, + guid, + typeName, + attributes, + meanings, + classificationNames, + }) => { + // Modifying the typeName and getting the readableTypeName + let readableTypeName = typeName + .toLowerCase() + .replace(attributes.connectorName, "") + .toUpperCase(); + + // Filtering classifications based on classificationNames + let classificationsObj = classifications.filter(({ name }) => + classificationNames.includes(name) + ); + + // Modifying the readableTypeName + readableTypeName = + readableTypeName.charAt(0).toUpperCase() + + readableTypeName.slice(1).toLowerCase(); + + return [ + guid, + truncate(displayText), + truncate(attributes.connectorName), + truncate(readableTypeName), + truncate( + attributes?.userDescription || attributes?.description || "" + ), + attributes?.certificateStatus || "", + truncate( + [...attributes?.ownerUsers, ...attributes?.ownerGroups] || [] + ), + truncate( + meanings.map( + ({ displayText, termGuid }) => + `[${displayText}](${ATLAN_INSTANCE_URL}/assets/${termGuid}/overview?utm_source=dbt_github_action)` + ) + ), + truncate( + classificationsObj?.map( + ({ name, displayName }) => `\`${displayName}\`` + ) + ), + attributes?.sourceURL || "", + ]; + } + ); - // Modifying the readableTypeName - readableTypeName = - readableTypeName.charAt(0).toUpperCase() + - readableTypeName.slice(1).toLowerCase(); + // Sorting the impactedData first by typeName and then by connectorName + impactedData = impactedData.sort((a, b) => a[3].localeCompare(b[3])); + impactedData = impactedData.sort((a, b) => a[2].localeCompare(b[2])); - return [ + // Creating rows for the downstream table + let rows = impactedData.map( + ([ guid, - truncate(displayText), - truncate(attributes.connectorName), - truncate(readableTypeName), - truncate( - attributes?.userDescription || attributes?.description || "" - ), - attributes?.certificateStatus || "", - truncate( - [...attributes?.ownerUsers, ...attributes?.ownerGroups] || [] - ), - truncate( - meanings.map( - ({ displayText, termGuid }) => - `[${displayText}](${ATLAN_INSTANCE_URL}/assets/${termGuid}/overview?utm_source=dbt_github_action)` - ) - ), - truncate( - classificationsObj?.map( - ({ name, displayName }) => `\`${displayName}\`` - ) - ), - attributes?.sourceURL || "", - ]; - } - ); - - // Sorting the impactedData first by typeName and then by connectorName - impactedData = impactedData.sort((a, b) => a[3].localeCompare(b[3])); - impactedData = impactedData.sort((a, b) => a[2].localeCompare(b[2])); - - // Creating rows for the downstream table - let rows = impactedData.map( - ([ - guid, - displayText, - connectorName, - typeName, - description, - certificateStatus, - owners, - meanings, - classifications, - sourceUrl, - ]) => { - // Getting connector and certification images - const connectorImage = getConnectorImage(connectorName); - const certificationImage = certificateStatus - ? getCertificationImage(certificateStatus) - : ""; - - return [ - `${connectorImage} [${displayText}](${ATLAN_INSTANCE_URL}/assets/${guid}/overview?utm_source=dbt_github_action) ${certificationImage}`, - `\`${typeName}\``, + displayText, + connectorName, + typeName, description, + certificateStatus, owners, meanings, classifications, - sourceUrl ? `[Open in ${connectorName}](${sourceUrl})` : " ", - ]; - } - ); + sourceUrl, + ]) => { + // Getting connector and certification images + const connectorImage = getConnectorImage(connectorName); + const certificationImage = certificateStatus + ? getCertificationImage(certificateStatus) + : ""; + + return [ + `${connectorImage} [${displayText}](${ATLAN_INSTANCE_URL}/assets/${guid}/overview?utm_source=dbt_github_action) ${certificationImage}`, + `\`${typeName}\``, + description, + owners, + meanings, + classifications, + sourceUrl ? `[Open in ${connectorName}](${sourceUrl})` : " ", + ]; + } + ); - const environmentName = - materialisedAsset?.attributes?.assetDbtEnvironmentName; - const projectName = materialisedAsset?.attributes?.assetDbtProjectName; - // Generating asset information - const assetInfo = `### ${getConnectorImage( - asset.attributes.connectorName - )} [${asset.displayText}](${ATLAN_INSTANCE_URL}/assets/${ - asset.guid - }/overview?utm_source=dbt_github_action) ${ - asset.attributes?.certificateStatus - ? getCertificationImage(asset.attributes.certificateStatus) - : "" - } -Materialised asset: ${getConnectorImage( - materialisedAsset.attributes.connectorName - )} [${materialisedAsset.attributes.name}](${ATLAN_INSTANCE_URL}/assets/${ - materialisedAsset.guid - }/overview?utm_source=dbt_github_action) ${ - materialisedAsset.attributes?.certificateStatus - ? getCertificationImage(materialisedAsset.attributes.certificateStatus) - : "" - }${environmentName ? ` | Environment Name: \`${environmentName}\`` : ""}${ - projectName ? ` | Project Name: \`${projectName}\`` : "" - }`; - - // Generating the downstream table - const downstreamTable = `
${ - downstreamAssets.entityCount - } downstream assets 👇
- -Name | Type | Description | Owners | Terms | Classifications | Source URL ---- | --- | --- | --- | --- | --- | --- -${rows - .map((row) => - row.map((i) => i.replace(/\|/g, "•").replace(/\n/g, "")).join(" | ") - ) - .join("\n")} - -${ - downstreamAssets.hasMore - ? `[See more downstream assets at Atlan](${ATLAN_INSTANCE_URL}/assets/${materialisedAsset.guid}/lineage?utm_source=dbt_github_action)` - : "" -} + const environmentName = + materialisedAsset?.attributes?.assetDbtEnvironmentName; + const projectName = materialisedAsset?.attributes?.assetDbtProjectName; + // Generating asset information -
`; + const assetInfo = getAssetInfo( + ATLAN_INSTANCE_URL, + asset, + materialisedAsset, + environmentName, + projectName + ); + + // Generating the downstream table + const downstreamTable = getDownstreamTable( + ATLAN_INSTANCE_URL, + downstreamAssets, + rows, + materialisedAsset + ); - // Generating the "View asset in Atlan" button - const viewAssetButton = `${getImageURL( - "atlan-logo", - 15, - 15 - )} [View asset in Atlan](${ATLAN_INSTANCE_URL}/assets/${ - asset.guid - }/overview?utm_source=dbt_github_action)`; + // Generating the "View asset in Atlan" button + const viewAssetButton = getViewAssetButton(ATLAN_INSTANCE_URL, asset); - // Generating the final comment based on the presence of downstream assets - if (downstreamAssets.entities.length > 0) { - return `${assetInfo} + // Generating the final comment based on the presence of downstream assets + if (downstreamAssets.entities.length > 0) { + return `${assetInfo} ${downstreamTable} ${viewAssetButton}`; - } else { - return `${assetInfo} + } else { + return `${assetInfo} No downstream assets found. ${viewAssetButton}`; + } + } catch (error) { + logger.withError( + `Error rendering Downstream Assets: ${error.message}`, + integrationName, + headSHA, + "renderDownstreamAssetsComment" + ); + throw error; } } } diff --git a/adapters/integrations/gitlab-integration.js b/adapters/integrations/gitlab-integration.js index 87506db..8110b19 100644 --- a/adapters/integrations/gitlab-integration.js +++ b/adapters/integrations/gitlab-integration.js @@ -1,6 +1,4 @@ // gitlabIntegration.js -import dotenv from "dotenv"; - import IntegrationInterface from "./contract/contract.js"; import { Gitlab } from "@gitbeaker/rest"; import { @@ -8,16 +6,44 @@ import { getAsset, getDownstreamAssets, sendSegmentEvent, -} from "../../src/api/index.js"; -import { getImageURL, auth } from "../../src/utils/index.js"; -import { getGitLabEnvironments } from "../../src/utils/get-environment-variables.js"; -import { getConnectorImage } from "../../src/utils/index.js"; -import { getCertificationImage } from "../../src/utils/index.js"; + getClassifications, +} from "../api/index.js"; +import { + auth, + getConnectorImage, + getCertificationImage, + getGitLabEnvironments, + truncate, +} from "../utils/index.js"; import stringify from "json-stringify-safe"; - -dotenv.config(); -const ATLAN_INSTANCE_URL = process.env.ATLAN_INSTANCE_URL; -const { IS_DEV } = process.env; +import { + getSetResourceOnAssetComment, + getErrorResponseStatus401, + getErrorResponseStatusUndefined, + getAssetInfo, + getDownstreamTable, + getViewAssetButton, + getMDCommentForModel, + getMDCommentForMaterialisedView, + getTableMD, +} from "../templates/gitlab-integration.js"; +import { getNewModelAddedComment, getBaseComment } from "../templates/atlan.js"; +import { + IS_DEV, + ATLAN_INSTANCE_URL, + CI_PROJECT_PATH, + CI_PROJECT_ID, + CI_JOB_URL, + IGNORE_MODEL_ALIAS_MATCHING, + CI_COMMIT_MESSAGE, + CI_PROJECT_NAME, + CI_COMMIT_SHA, + getCIMergeRequestIID, + CI_PROJECT_NAMESPACE, +} from "../utils/get-environment-variables.js"; +import logger from "../logger/logger.js"; +const integrationName = "gitlab"; +var CI_MERGE_REQUEST_IID; export default class GitLabIntegration extends IntegrationInterface { constructor(token) { @@ -25,290 +51,576 @@ export default class GitLabIntegration extends IntegrationInterface { } async run() { - //Done - console.log("Run Gitlab"); - const timeStart = Date.now(); + try { + const timeStart = Date.now(); + const gitlab = new Gitlab({ + host: "https://gitlab.com", + token: this.token, + }); - const gitlab = new Gitlab({ - host: "https://gitlab.com", - token: this.token, - }); + CI_MERGE_REQUEST_IID = await getCIMergeRequestIID( + gitlab, + CI_PROJECT_ID, + CI_COMMIT_SHA + ); - const { CI_PROJECT_PATH, CI_MERGE_REQUEST_IID } = process.env; + var mergeRequestCommit = await gitlab.Commits.allMergeRequests( + CI_PROJECT_ID, + CI_COMMIT_SHA + ); - if (!(await this.authIntegration({ gitlab }))) - //Done - throw { message: "Wrong API Token" }; + logger.withInfo( + "GitLab Integration is running...", + integrationName, + CI_COMMIT_SHA, + "run" + ); - const { state, web_url, source_branch } = await gitlab.MergeRequests.show( - CI_PROJECT_PATH, - CI_MERGE_REQUEST_IID - ); - console.log("At line 47", state, web_url, source_branch); - let total_assets = 0; + if (!(await this.authIntegration({ gitlab }))) { + logger.withError( + "Authentication failed. Wrong API Token.", + integrationName, + CI_COMMIT_SHA, + "run" + ); + throw { message: "Wrong API Token" }; + } - if (state === "opened") { - total_assets = await this.printDownstreamAssets({ - gitlab, - source_branch, - }); - } else if (state === "merged") { - total_assets = await this.setResourceOnAsset({ - gitlab, - web_url, - source_branch, - }); - } + let total_assets = 0; + + if ( + mergeRequestCommit.length && + mergeRequestCommit[0]?.state == "merged" + ) { + const { web_url, target_branch, diff_refs } = + await gitlab.MergeRequests.show( + CI_PROJECT_PATH, + mergeRequestCommit[0]?.iid + ); + total_assets = await this.setResourceOnAsset({ + gitlab, + web_url, + target_branch, + diff_refs, + }); + } else { + const { target_branch, diff_refs } = await gitlab.MergeRequests.show( + CI_PROJECT_PATH, + CI_MERGE_REQUEST_IID + ); + + total_assets = await this.printDownstreamAssets({ + gitlab, + target_branch, + diff_refs, + }); + } - if (total_assets !== 0) - this.sendSegmentEventOfIntegration("dbt_ci_action_run", { - asset_count: total_assets, - total_time: Date.now() - timeStart, - }); + if (total_assets !== 0) + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_run", + properties: { + asset_count: total_assets, + total_time: Date.now() - timeStart, + }, + }); + + logger.withInfo( + "Successfully Completed DBT_CI_PIPELINE", + integrationName, + CI_COMMIT_SHA, + "run" + ); + } catch (error) { + logger.withError( + `Error in run(): ${error.message}`, + integrationName, + CI_COMMIT_SHA, + "run" + ); + throw error; + } } - async printDownstreamAssets({ gitlab, source_branch }) { - //Done - // Implementation for printing impact on GitHub - // Use this.token to access the token - console.log("At line 74 inside printDownstreamAssets"); - const changedFiles = await this.getChangedFiles({ gitlab }); //Complete - console.log("At line 75", changedFiles); - let comments = ``; - let totalChangedFiles = 0; - - for (const { fileName, filePath, headSHA } of changedFiles) { - const assetName = await this.getAssetName({ - //Complete - gitlab, - fileName, - filePath, - headSHA, - }); - console.log("At line 88"); - const environments = getGitLabEnvironments(); - console.log("At line 90", environments); - let environment = null; - for (const [baseBranchName, environmentName] of environments) { - if (baseBranchName === source_branch) { - environment = environmentName; - break; + async printDownstreamAssets({ gitlab, target_branch, diff_refs }) { + logger.withInfo( + "Printing downstream assets...", + integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + + try { + const changedFiles = await this.getChangedFiles({ gitlab, diff_refs }); + + let comments = ``; + let totalChangedFiles = 0; + + for (const { fileName, filePath, headSHA, status } of changedFiles) { + logger.withInfo( + `Processing file: ${fileName}`, + integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + const aliasName = await this.getAssetName({ + gitlab, + fileName, + filePath, + headSHA, + }); + const assetName = IGNORE_MODEL_ALIAS_MATCHING ? fileName : aliasName; + + const environments = getGitLabEnvironments(); + let environment = null; + for (const baseBranchName of Object.keys(environments)) { + const environmentName = environments[baseBranchName]; + if (baseBranchName === target_branch) { + environment = environmentName; + break; + } } - } - console.log("At line 98", environment); - const asset = await getAsset({ - //Complete - name: assetName, - sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, - environment: environment, - integration: "gitlab", - }); - if (asset.error) { - comments += asset.error; - totalChangedFiles++; - continue; - } - console.log("At line 112", asset); - //Cross-check this part once with Jaagrav. - - const totalModifiedFiles = changedFiles.filter( - (i) => i.status === "modified" - ).length; - console.log("At line 118", totalModifiedFiles); - const { guid } = asset.attributes.sqlAsset; - const timeStart = Date.now(); - const downstreamAssets = await getDownstreamAssets( - //Done - asset, - guid, - totalModifiedFiles, - this.sendSegmentEventOfIntegration, - "gitlab" - ); - console.log("At line 129 Completed getDownstreamAssets"); - if (totalChangedFiles !== 0) comments += "\n\n---\n\n"; + logger.withInfo( + `Processing asset: ${assetName} in environment: ${environment}`, + integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + + const asset = await getAsset({ + name: assetName, + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + environment: environment, + integration: "gitlab", + }); + + if (totalChangedFiles !== 0) comments += "\n\n---\n\n"; + + if (status === "added") { + logger.withInfo( + `New model added: ${fileName}`, + integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + comments += getNewModelAddedComment(fileName); + totalChangedFiles++; + continue; + } + + if (asset.error) { + logger.withError( + `Asset error for ${assetName}: ${asset.error}`, + integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + comments += asset.error; + totalChangedFiles++; + continue; + } + + const materialisedAsset = asset?.attributes?.dbtModelSqlAssets?.[0]; + const timeStart = Date.now(); + + const totalModifiedFiles = changedFiles.filter( + (i) => i.status === "modified" + ).length; + + const { guid } = asset; + + const downstreamAssets = await getDownstreamAssets( + asset, + materialisedAsset.guid, + totalModifiedFiles, + this.sendSegmentEventOfIntegration, + "gitlab" + ); + + if (downstreamAssets.error) { + logger.withError( + `Downstream assets error for ${assetName}: ${downstreamAssets.error}`, + integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + comments += downstreamAssets.error; + totalChangedFiles++; + continue; + } + + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_downstream_unfurl", + properties: { + asset_guid: asset.guid, + asset_type: asset.typeName, + downstream_count: downstreamAssets.entities.length, + total_fetch_time: Date.now() - timeStart, + }, + }); + + const classifications = await getClassifications({ + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + }); + + const comment = await this.renderDownstreamAssetsComment({ + asset, + downstreamAssets, + classifications, + materialisedAsset, + }); + + comments += comment; - if (downstreamAssets.error) { - comments += downstreamAssets.error; totalChangedFiles++; - continue; } - this.sendSegmentEventOfIntegration("dbt_ci_action_downstream_unfurl", { - //Complete - asset_guid: asset.guid, - asset_type: asset.typeName, - downstream_count: downstreamAssets.length, - total_fetch_time: Date.now() - timeStart, - }); + comments = getBaseComment(totalChangedFiles, comments); - const comment = await this.renderDownstreamAssetsComment({ - //Complete - asset, - downstreamAssets, - }); + const existingComment = await this.checkCommentExists({ gitlab }); - comments += comment; + logger.withInfo( + `Existing Comment: ${existingComment?.id}`, + integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + + if (totalChangedFiles > 0) + await this.createIssueComment({ + gitlab, + content: comments, + comment_id: existingComment?.id, + }); + + if (totalChangedFiles === 0 && existingComment) + await this.deleteComment({ gitlab, comment_id: existingComment?.id }); + + logger.withInfo( + "Successfully printed Downstream Assets", + integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); - totalChangedFiles++; + return totalChangedFiles; + } catch (error) { + logger.withError( + `Error in printDownstreamAssets: ${error.message}`, + integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + throw error; } + } - comments = `### ${getImageURL("atlan-logo", 15, 15)} Atlan impact analysis -Here is your downstream impact analysis for **${totalChangedFiles} ${ - totalChangedFiles > 1 ? "models" : "model" - }** you have edited. - -${comments}`; - console.log("At line 163 in printDownstreamAssets"); - const existingComment = await this.checkCommentExists({ gitlab }); //Complete - console.log("At line 165 after checkCommentExists"); - if (totalChangedFiles > 0) - await this.createIssueComment({ - //Complete - gitlab, - comments, - comment_id: existingComment?.id, - }); + async setResourceOnAsset({ gitlab, web_url, target_branch, diff_refs }) { + logger.withInfo( + "Setting resources on assets...", + integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); - if (totalChangedFiles === 0 && existingComment) - await this.deleteComment({ gitlab, comment_id: existingComment.id }); //Complete - console.log("Completed PrintDownstreamAssets"); - return totalChangedFiles; - } + try { + const changedFiles = await this.getChangedFiles({ gitlab, diff_refs }); + + var totalChangedFiles = 0; + let tableMd = ``; + let setResourceFailed = false; + if (changedFiles.length === 0) { + logger.withInfo( + "No changed files found. Skipping resource setup.", + integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + return totalChangedFiles; + } - async setResourceOnAsset({ gitlab, web_url, source_branch }) { - //Done - // Implementation for setting resources on GitHub - // Use this.token to access the token - const changedFiles = await this.getChangedFiles({ gitlab }); //Done - var totalChangedFiles = 0; + for (const { fileName, filePath, headSHA } of changedFiles) { + const aliasName = await this.getAssetName({ + gitlab, + fileName, + filePath, + headSHA, + }); + + const assetName = IGNORE_MODEL_ALIAS_MATCHING ? fileName : aliasName; + + logger.withInfo( + `Resolved asset name: ${assetName}`, + integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + + const environments = getGitLabEnvironments(); + let environment = null; + for (const baseBranchName of Object.keys(environments)) { + const environmentName = environments[baseBranchName]; + if (baseBranchName === target_branch) { + environment = environmentName; + break; + } + } - if (changedFiles.length === 0) return; + logger.withInfo( + `Processing asset: ${assetName} in environment: ${environment}`, + integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + + const asset = await getAsset({ + name: assetName, + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + environment: environment, + integration: "gitlab", + }); + + if (asset.error) { + logger.withError( + `Failed to retrieve asset: ${assetName}, Error: ${asset.error}`, + integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + continue; + } - for (const { fileName, filePath, headSHA } of changedFiles) { - const assetName = await this.getAssetName({ - gitlab, - fileName, - filePath, - headSHA, - }); + const materialisedAsset = asset?.attributes?.dbtModelSqlAssets?.[0]; + const timeStart = Date.now(); + + const totalModifiedFiles = changedFiles.filter( + (i) => i.status === "modified" + ).length; + + const { guid } = asset; + + const downstreamAssets = await getDownstreamAssets( + asset, + materialisedAsset.guid, + totalModifiedFiles, + this.sendSegmentEventOfIntegration, + "gitlab" + ); + + if (downstreamAssets.error) { + logger.withError( + `Failed to retrieve downstream assets for: ${assetName}, Error: ${downstreamAssets.error}`, + integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + continue; + } - const environments = getGitLabEnvironments(); + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_downstream_unfurl", + properties: { + asset_guid: asset.guid, + asset_type: asset.typeName, + downstream_count: downstreamAssets.entities.length, + total_fetch_time: Date.now() - timeStart, + }, + }); + + const model = asset; + const materialisedView = asset?.attributes?.dbtModelSqlAssets?.[0]; + + var lines = CI_COMMIT_MESSAGE.split("\n"); + var CI_MERGE_REQUEST_TITLE = lines[2]; + + if (downstreamAssets.entityCount != 0) { + if (model) { + const { guid: modelGuid } = model; + const resp = await createResource( + modelGuid, + CI_MERGE_REQUEST_TITLE, + web_url, + this.sendSegmentEventOfIntegration + ); + const md = getMDCommentForModel(ATLAN_INSTANCE_URL, model); + tableMd += getTableMD(md, resp); + if (!resp) { + setResourceFailed = true; + logger.withError( + `Setting resource failed for model: ${modelGuid}`, + integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + } + } - let environment = null; - for (const [baseBranchName, environmentName] of environments) { - if (baseBranchName === source_branch) { - environment = environmentName; - break; + if (materialisedView) { + const { guid: tableAssetGuid } = materialisedView; + const resp = await createResource( + tableAssetGuid, + CI_MERGE_REQUEST_TITLE, + web_url, + this.sendSegmentEventOfIntegration + ); + const md = getMDCommentForMaterialisedView( + ATLAN_INSTANCE_URL, + materialisedView + ); + tableMd += getTableMD(md, resp); + if (!resp) { + setResourceFailed = true; + logger.withError( + `Setting resource failed for materialized view: ${tableAssetGuid}`, + integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + } + } } + + totalChangedFiles++; } - const asset = await getAsset({ - //Done - name: assetName, - sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, - environment: environment, - integration: "gitlab", + const comment = await this.createIssueComment({ + gitlab, + content: getSetResourceOnAssetComment(tableMd, setResourceFailed), + comment_id: null, + forceNewComment: true, }); - if (!asset) continue; - - const { guid: modelGuid } = asset; - const { guid: tableAssetGuid } = asset.attributes.sqlAsset; - - await createResource( - //Done - //Complete - modelGuid, - "Pull Request on GitLab", - web_url, - this.sendSegmentEventOfIntegration - ); - await createResource( - //Done - tableAssetGuid, - "Pull Request on GitLab", - web_url, - this.sendSegmentEventOfIntegration + logger.withInfo( + "Successfully set the resource on the asset", + integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" ); - totalChangedFiles++; + return totalChangedFiles; + } catch (error) { + logger.withError( + `Error in setResourceOnAsset: ${error}`, + integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + throw error; } - - const comment = await this.createIssueComment({ - //Done - //Complete - gitlab, - content: `🎊 Congrats on the merge! - -This pull request has been added as a resource to all the assets modified. ✅ -`, - comment_id: null, - forceNewComment: true, - }); - - return totalChangedFiles; } async authIntegration({ gitlab }) { - //Done - const response = await auth(); + logger.withInfo( + "Authenticating with Atlan", + integrationName, + CI_COMMIT_SHA, + "authIntegration" + ); - if (response?.status === 401) { - //Complete - await this.createIssueComment( - gitlab, - `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Bearer Token as \`ATLAN_API_TOKEN\` in your .gitlab-ci.yml file. + try { + const response = await auth(); -Atlan Instance URL: ${ATLAN_INSTANCE_URL}` - ); - return false; - } + const existingComment = await this.checkCommentExists({ gitlab }); - if (response === undefined) { - await this.createIssueComment( - gitlab, - `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Instance URL as \`ATLAN_INSTANCE_URL\` in your .gitlab-ci.yml file. - -Atlan Instance URL: ${ATLAN_INSTANCE_URL} + logger.withInfo( + `Existing Comment: ${existingComment?.id}`, + integrationName, + CI_COMMIT_SHA, + "authIntegration" + ); -Make sure your Atlan Instance URL is set in the following format. -\`https://tenant.atlan.com\` + if (response?.status === 401) { + logger.withError( + "Authentication failed: Status 401", + integrationName, + CI_COMMIT_SHA, + "authIntegration" + ); + await this.createIssueComment({ + gitlab, + content: getErrorResponseStatus401( + ATLAN_INSTANCE_URL, + CI_PROJECT_NAME, + CI_PROJECT_NAMESPACE + ), + comment_id: existingComment?.id, + }); + return false; + } -` + if (response === undefined) { + logger.withError( + "Authentication failed: Undefined response", + integrationName, + CI_COMMIT_SHA, + "authIntegration" + ); + await this.createIssueComment({ + gitlab, + content: getErrorResponseStatusUndefined( + ATLAN_INSTANCE_URL, + CI_PROJECT_NAME, + CI_PROJECT_NAMESPACE + ), + comment_id: existingComment?.id, + }); + return false; + } + logger.withInfo( + "Successfully Authenticated with Atlan", + integrationName, + CI_COMMIT_SHA, + "authIntegration" + ); + return true; + } catch (error) { + logger.withError( + `Error in authIntegration: ${error.message}`, + integrationName, + CI_COMMIT_SHA, + "authIntegration" ); - return false; + throw error; } - - return true; } async createIssueComment({ - //Done - //Complete gitlab, content, comment_id = null, forceNewComment = false, }) { - console.log("At line 295 inside createIssueComment"); - const { CI_PROJECT_PATH, CI_MERGE_REQUEST_IID } = process.env; + logger.withInfo( + "Creating an issue comment...", + integrationName, + CI_COMMIT_SHA, + "createIssueComment" + ); content = ` ${content}`; - console.log("At line 301 inside createIssueComment", content); - - if (IS_DEV) return content; + if (IS_DEV) { + logger.withInfo( + "Development mode enabled. Skipping comment creation.", + integrationName, + CI_COMMIT_SHA, + "createIssueComment" + ); + return content; + } - if (comment_id && !forceNewComment) + if (comment_id && !forceNewComment) { return await gitlab.MergeRequestNotes.edit( - CI_PROJECT_PATH, + CI_PROJECT_ID, CI_MERGE_REQUEST_IID, comment_id, - content + { + body: content, + } ); + } return await gitlab.MergeRequestNotes.create( CI_PROJECT_PATH, CI_MERGE_REQUEST_IID, @@ -317,154 +629,277 @@ ${content}`; } async sendSegmentEventOfIntegration({ action, properties }) { - //Done - //Complete - // Implement your sendSegmentEvent logic here - // IMPORT ATLAN_INSTANCE_URL. - console.log("At line 324 inside sendSegmentOfIntegration"); - const domain = new URL(ATLAN_INSTANCE_URL).hostname; - const { CI_PROJECT_PATH, CI_JOB_URL } = process.env; - - const raw = stringify({ - category: "integration", - object: "gitlab", - action, - userId: "atlan-annonymous-github", - properties: { - ...properties, - gitlab_job_id: CI_JOB_URL, - domain, - }, - }); - // IMPORT SEGMENTEVENT - return sendSegmentEvent(action, raw); + try { + const domain = new URL(ATLAN_INSTANCE_URL).hostname; + logger.withInfo( + `Sending Segment event for action: ${action}`, + integrationName, + CI_COMMIT_SHA, + "sendSegmentEventOfIntegration" + ); + + const raw = stringify({ + category: "integration", + object: "gitlab", + action, + userId: "atlan-annonymous-github", + properties: { + ...properties, + gitlab_job_id: CI_JOB_URL, + domain, + }, + }); + + return sendSegmentEvent(action, raw); + } catch (error) { + logger.withError( + `Error sending Segment event for action: ${action} - ${error.message}`, + integrationName, + CI_COMMIT_SHA, + "sendSegmentEventOfIntegration" + ); + throw error; + } } - async getChangedFiles({ gitlab }) { - //Done - //Complete - console.log("At line 344 Inside getChangedFiles"); - const { CI_PROJECT_PATH, CI_MERGE_REQUEST_IID } = process.env; + async getChangedFiles({ gitlab, diff_refs }) { + try { + logger.withInfo( + "Fetching changed files...", + integrationName, + CI_COMMIT_SHA, + "getChangedFiles" + ); - const { changes, diff_refs } = await gitlab.MergeRequests.changes( - CI_PROJECT_PATH, - CI_MERGE_REQUEST_IID - ); - console.log("At line 351 Inside getChangedFiles"); - var changedFiles = changes - .map(({ new_path, old_path }) => { - try { - const [modelName] = new_path - .match(/.*models\/(.*)\.sql/)[1] - .split("/") - .reverse()[0] - .split("."); - - //Cross-check this with Jaagrav. ### - if (modelName) { - if (old_path === null) { - return { - fileName: modelName, - filePath: new_path, - headSHA: diff_refs.head_sha, - status: "added", - }; - } else if (new_path !== old_path) { - // File is renamed or moved - return { - fileName: modelName, - filePath: new_path, - headSHA: diff_refs.head_sha, - status: "renamed_or_moved", - }; - } else { - // File is modified - return { - fileName: modelName, - filePath: new_path, - headSHA: diff_refs.head_sha, - status: "modified", - }; + var changes = await gitlab.MergeRequests.allDiffs( + CI_PROJECT_PATH, + CI_MERGE_REQUEST_IID + ); + + var changedFiles = changes + .map(({ new_path, old_path, new_file }) => { + try { + const [modelName] = new_path + .match(/.*models\/(.*)\.sql/)[1] + .split("/") + .reverse()[0] + .split("."); + + if (modelName) { + if (new_file) { + return { + fileName: modelName, + filePath: new_path, + headSHA: diff_refs.head_sha, + status: "added", + }; + } else if (new_path !== old_path) { + // File is renamed or moved + return { + fileName: modelName, + filePath: new_path, + headSHA: diff_refs.head_sha, + status: "renamed_or_moved", + }; + } else { + // File is modified + return { + fileName: modelName, + filePath: new_path, + headSHA: diff_refs.head_sha, + status: "modified", + }; + } } + } catch (e) { + logger.withError( + `Error processing file`, + integrationName, + CI_COMMIT_SHA, + "getChangedFiles" + ); } - } catch (e) {} - }) - .filter((i) => i !== undefined); + }) + .filter((i) => i !== undefined); + + changedFiles = changedFiles.filter((item, index) => { + return ( + changedFiles.findIndex((obj) => obj.fileName === item.fileName) === + index + ); + }); - changedFiles = changedFiles.filter((item, index) => { - return ( - changedFiles.findIndex((obj) => obj.fileName === item.fileName) === - index + logger.withInfo( + "Successfully fetched changed files", + integrationName, + CI_COMMIT_SHA, + "getChangedFiles" ); - }); - - console.log("At line 399 Completed getChangedFiles ", changedFiles); - return changedFiles; + return changedFiles; + } catch (error) { + logger.withError( + `Error fetching changed files - ${error.message}`, + integrationName, + CI_COMMIT_SHA, + "getChangedFiles" + ); + throw error; + } } async getAssetName({ gitlab, fileName, filePath, headSHA }) { - //Done - //Complete - console.log("At line 407 inside getAssetName"); - var regExp = /config\(.*alias=\'([^']+)\'.*\)/im; - var fileContents = await this.getFileContents({ - gitlab, - filePath, - headSHA, - }); - console.log("At line 414 inside getAssetName"); - var matches = regExp.exec(fileContents); - console.log("At line 416"); - if (matches) { - return matches[1]; + try { + logger.withInfo( + "Getting asset name...", + integrationName, + CI_COMMIT_SHA, + "getAssetName" + ); + + var regExp = + /{{\s*config\s*\(\s*(?:[^,]*,)*\s*alias\s*=\s*['"]([^'"]+)['"](?:\s*,[^,]*)*\s*\)\s*}}/im; + var fileContents = await this.getFileContents({ + gitlab, + filePath, + headSHA, + }); + + if (fileContents) { + var matches = regExp.exec(fileContents); + if (matches) { + logger.withInfo( + `Found a match: ${matches[1].trim()}`, + integrationName, + CI_COMMIT_SHA, + "getAssetName" + ); + return matches[1].trim(); + } + } + + logger.withInfo( + `Using filename as asset name: ${fileName}`, + integrationName, + CI_COMMIT_SHA, + "getAssetName" + ); + + return fileName; + } catch (error) { + logger.withError( + `Error getting asset name - ${error.message}`, + integrationName, + CI_COMMIT_SHA, + "getAssetName" + ); + throw error; } - console.log("At line 420 completed getAssetName"); - return fileName; } async getFileContents({ gitlab, filePath, headSHA }) { - //Done - //Complete - console.log("At line 427 inside getFileContents"); - const { CI_PROJECT_PATH } = process.env; - const { content } = await gitlab.RepositoryFiles.show( - CI_PROJECT_PATH, - filePath, - headSHA - ); - const buff = Buffer.from(content, "base64"); - console.log("At line 435 inside getFileContents"); - return buff.toString("utf8"); + try { + logger.withInfo( + "Fetching file contents...", + integrationName, + CI_COMMIT_SHA, + "getFileContents" + ); + + const { content } = await gitlab.RepositoryFiles.show( + CI_PROJECT_PATH, + filePath, + headSHA + ); + const buff = Buffer.from(content, "base64"); + + logger.withInfo( + "Successfully fetched file contents", + integrationName, + CI_COMMIT_SHA, + "getFileContents" + ); + + return buff.toString("utf8"); + } catch (error) { + logger.withError( + `Error in getFileContents: ${error.message}`, + integrationName, + CI_COMMIT_SHA, + "getFileContents" + ); + throw error; + } } async checkCommentExists({ gitlab }) { - //Done - //Complete - console.log("At line 442 inside checkCommentExists"); - const { CI_PROJECT_PATH, CI_MERGE_REQUEST_IID } = process.env; - if (IS_DEV) return null; - - const comments = await gitlab.MergeRequestNotes.all( - CI_PROJECT_PATH, - CI_MERGE_REQUEST_IID + logger.withInfo( + "Checking for existing comments...", + integrationName, + CI_COMMIT_SHA, + "checkCommentExists" ); - return comments.find( - // Why here we have hardocded value? What should be over here inplace of this. - (comment) => - comment.author.username === "Jaagrav" && - comment.body.includes( - "" - ) - ); + if (IS_DEV) { + logger.withInfo( + "Development mode enabled. Skipping comment check.", + integrationName, + CI_COMMIT_SHA, + "checkCommentExists" + ); + return null; + } + + try { + const comments = await gitlab.MergeRequestNotes.all( + CI_PROJECT_PATH, + CI_MERGE_REQUEST_IID + ); + + const identifier = `project_${CI_PROJECT_ID}_bot_`; + + const existingComment = comments.find( + (comment) => + comment.author.username.includes(identifier) && + comment.body.includes( + "" + ) + ); + if (existingComment) { + logger.withInfo( + "Found existing comment: " + existingComment?.id, + integrationName, + CI_COMMIT_SHA, + "checkCommentExists" + ); + } else { + logger.withInfo( + "No existing comment found", + integrationName, + CI_COMMIT_SHA, + "checkCommentExists" + ); + } + + return existingComment; + } catch (error) { + logger.withError( + "Error checking for existing comments: " + error.message, + integrationName, + CI_COMMIT_SHA, + "checkCommentExists" + ); + throw error; + } } async deleteComment({ gitlab, comment_id }) { - //Done - //Complete - console.log("At line 464 inside deleteComment"); - const { CI_PROJECT_PATH, CI_MERGE_REQUEST_IID } = process.env; + logger.withInfo( + `Deleted comment with ID ${comment_id}`, + integrationName, + CI_COMMIT_SHA, + "deleteComment" + ); return await gitlab.MergeRequestNotes.remove( CI_PROJECT_PATH, @@ -473,95 +908,154 @@ ${content}`; ); } - async renderDownstreamAssetsComment({ asset, downstreamAssets }) { - //Done - console.log("At line 474 inside renderDownstreamAssetsComment"); - let impactedData = downstreamAssets.map( - ({ displayText, guid, typeName, attributes, meanings }) => { - let readableTypeName = typeName - .toLowerCase() - .replace(attributes.connectorName, "") - .toUpperCase(); - readableTypeName = - readableTypeName.charAt(0).toUpperCase() + - readableTypeName.slice(1).toLowerCase(); - return [ + async renderDownstreamAssetsComment({ + asset, + downstreamAssets, + classifications, + materialisedAsset, + }) { + logger.withInfo( + "Rendering Downstream Assets...", + integrationName, + CI_COMMIT_SHA, + "renderDownstreamAssetsComment" + ); + + try { + let impactedData = downstreamAssets.entities.map( + ({ + displayText, + guid, + typeName, + attributes, + meanings, + classificationNames, + }) => { + // Modifying the typeName and getting the readableTypeName + let readableTypeName = typeName + .toLowerCase() + .replace(attributes.connectorName, "") + .toUpperCase(); + + // Filtering classifications based on classificationNames + let classificationsObj = classifications.filter(({ name }) => + classificationNames.includes(name) + ); + + // Modifying the readableTypeName + readableTypeName = + readableTypeName.charAt(0).toUpperCase() + + readableTypeName.slice(1).toLowerCase(); + + return [ + guid, + truncate(displayText), + truncate(attributes.connectorName), + truncate(readableTypeName), + truncate( + attributes?.userDescription || attributes?.description || "" + ), + attributes?.certificateStatus || "", + truncate( + [...attributes?.ownerUsers, ...attributes?.ownerGroups] || [] + ), + truncate( + meanings.map( + ({ displayText, termGuid }) => + `[${displayText}](${ATLAN_INSTANCE_URL}/assets/${termGuid}/overview?utm_source=dbt_gitlab_action)` + ) + ), + truncate( + classificationsObj?.map( + ({ name, displayName }) => `\`${displayName}\`` + ) + ), + attributes?.sourceURL || "", + ]; + } + ); + + // Sorting the impactedData first by typeName and then by connectorName + impactedData = impactedData.sort((a, b) => a[3].localeCompare(b[3])); + impactedData = impactedData.sort((a, b) => a[2].localeCompare(b[2])); + + // Creating rows for the downstream table + let rows = impactedData.map( + ([ guid, displayText, - attributes.connectorName, - readableTypeName, - attributes?.userDescription || attributes?.description || "", - attributes?.certificateStatus || "", - [...attributes?.ownerUsers, ...attributes?.ownerGroups] || [], - meanings - .map( - ({ displayText, termGuid }) => - `[${displayText}](${ATLAN_INSTANCE_URL}/assets/${termGuid}?utm_source=dbt_gitlab_action)` - ) - ?.join(", ") || " ", - attributes?.sourceURL || "", - ]; - } - ); - console.log("At line 502 in renderDownstreamAsset"); - impactedData = impactedData.sort((a, b) => a[3].localeCompare(b[3])); // Sort by typeName - impactedData = impactedData.sort((a, b) => a[2].localeCompare(b[2])); // Sort by connectorName - - let rows = impactedData.map( - ([ - guid, - displayText, - connectorName, - typeName, - description, - certificateStatus, - owners, - meanings, - sourceUrl, - ]) => { - const connectorImage = getConnectorImage(connectorName), - certificationImage = certificateStatus + connectorName, + typeName, + description, + certificateStatus, + owners, + meanings, + classifications, + sourceUrl, + ]) => { + // Getting connector and certification images + const connectorImage = getConnectorImage(connectorName); + const certificationImage = certificateStatus ? getCertificationImage(certificateStatus) : ""; - return [ - `${connectorImage} [${displayText}](${ATLAN_INSTANCE_URL}/assets/${guid}?utm_source=dbt_gitlab_action) ${certificationImage}`, - `\`${typeName}\``, - description, - owners.join(", ") || " ", - meanings, - sourceUrl ? `[Open in ${connectorName}](${sourceUrl})` : " ", - ]; - } - ); + return [ + `${connectorImage} [${displayText}](${ATLAN_INSTANCE_URL}/assets/${guid}/overview?utm_source=dbt_gitlab_action) ${certificationImage}`, + `\`${typeName}\``, + description, + owners, + meanings, + classifications, + sourceUrl ? `[Open in ${connectorName}](${sourceUrl})` : " ", + ]; + } + ); - const comment = `### ${getConnectorImage( - asset.attributes.connectorName - )} [${asset.displayText}](${ATLAN_INSTANCE_URL}/assets/${ - asset.guid - }?utm_source=dbt_gitlab_action) ${ - asset.attributes?.certificateStatus - ? getCertificationImage(asset.attributes.certificateStatus) - : "" - } + const environmentName = + materialisedAsset?.attributes?.assetDbtEnvironmentName; + const projectName = materialisedAsset?.attributes?.assetDbtProjectName; + // Generating asset information + const assetInfo = getAssetInfo( + ATLAN_INSTANCE_URL, + asset, + materialisedAsset, + environmentName, + projectName + ); - **${downstreamAssets.length} downstream assets** 👇 - Name | Type | Description | Owners | Terms | Source URL - --- | --- | --- | --- | --- | --- - ${rows - .map((row) => - row.map((i) => i.replace(/\|/g, "•").replace(/\n/g, "")).join(" | ") - ) - .join("\n")} - - ${getImageURL( - "atlan-logo", - 15, - 15 - )} [View asset in Atlan](${ATLAN_INSTANCE_URL}/assets/${ - asset.guid - }?utm_source=dbt_gitlab_action)`; - console.log("At line 560 Completed renderDownstream"); - return comment; + // Generating the downstream table + const downstreamTable = getDownstreamTable( + ATLAN_INSTANCE_URL, + downstreamAssets, + rows, + materialisedAsset + ); + + // Generating the "View asset in Atlan" button + const viewAssetButton = getViewAssetButton(ATLAN_INSTANCE_URL, asset); + + // Generating the final comment based on the presence of downstream assets + if (downstreamAssets.entities.length > 0) { + return `${assetInfo} + +${downstreamTable} + +${viewAssetButton}`; + } else { + return `${assetInfo} + +No downstream assets found. + +${viewAssetButton}`; + } + } catch (error) { + logger.withError( + `Error rendering Downstream Assets: ${error.message}`, + integrationName, + CI_COMMIT_SHA, + "renderDownstreamAssetsComment" + ); + throw error; + } } } diff --git a/adapters/logger/logger.js b/adapters/logger/logger.js new file mode 100644 index 0000000..ef36876 --- /dev/null +++ b/adapters/logger/logger.js @@ -0,0 +1,65 @@ +// logger.js + +function getCurrentTimestamp() { + const now = new Date(); + return now.toISOString(); +} + +function logInfo(message, method) { + const timestamp = getCurrentTimestamp(); + const logEntry = { + level: "ERROR", + timestamp, + method, + message, + }; + console.error(logEntry); +} + +function withInfo(message, vcs, sha, method) { + const timestamp = getCurrentTimestamp(); + const logEntry = { + level: "INFO", + timestamp, + vcs, + sha, + method, + message, + }; + console.log(logEntry); +} + +function withError(message, vcs, sha, method) { + const timestamp = getCurrentTimestamp(); + const logEntry = { + level: "ERROR", + timestamp, + vcs, + sha, + method, + message, + }; + console.error(logEntry); +} + +function debug(message, vcs, sha, method) { + const timestamp = getCurrentTimestamp(); + const logEntry = { + level: "DEBUG", + timestamp, + vcs, + sha, + method, + message, + }; + console.debug(logEntry); +} + +const logger = { + withInfo, + withError, + debug, + logInfo, +}; + +export default logger; diff --git a/adapters/templates/atlan.js b/adapters/templates/atlan.js new file mode 100644 index 0000000..d84e475 --- /dev/null +++ b/adapters/templates/atlan.js @@ -0,0 +1,32 @@ +import {getConnectorImage, getImageURL} from "../utils/index.js" + +export function getErrorModelNotFound(name) { + return ` +
❌ Model with name **${name}** could not be found or is deleted

+ `; +} + +export function getErrorDoesNotMaterialize( + name, + ATLAN_INSTANCE_URL, + response, + integration +) { + + return ` +
❌ Model with name [${name}](${ATLAN_INSTANCE_URL}/assets/${response.entities[0].guid}/overview?utm_source=dbt_${integration}_action) does not materialise any asset

`; +} + +export function getNewModelAddedComment(fileName) { + return `### ${getConnectorImage("dbt")} ${fileName} 🆕 + Its a new model and not present in Atlan yet, you'll see the downstream impact for it after its present in Atlan.` +} + +export function getBaseComment(totalChangedFiles, comments) { + return `### ${getImageURL("atlan-logo", 15, 15)} Atlan impact analysis + Here is your downstream impact analysis for **${totalChangedFiles} ${ + totalChangedFiles > 1 ? "models" : "model" + }** you have edited. + + ${comments}` +} \ No newline at end of file diff --git a/adapters/templates/github-integration.js b/adapters/templates/github-integration.js new file mode 100644 index 0000000..8e0cb8e --- /dev/null +++ b/adapters/templates/github-integration.js @@ -0,0 +1,102 @@ +import { getImageURL, getConnectorImage, getCertificationImage } from "../utils/index.js"; + +export function getErrorResponseStatus401 (ATLAN_INSTANCE_URL, context) { + return `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Bearer Token as \`ATLAN_API_TOKEN\` as this repository's action secret. + +Atlan Instance URL: ${ATLAN_INSTANCE_URL} + +Set your repository action secrets [here](https://github.com/${context.payload.repository.full_name}/settings/secrets/actions). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).` +} + +export function getErrorResponseStatusUndefined(ATLAN_INSTANCE_URL, context) { + return `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Instance URL as \`ATLAN_INSTANCE_URL\` as this repository's action secret. + +Atlan Instance URL: ${ATLAN_INSTANCE_URL} + +Make sure your Atlan Instance URL is set in the following format. +\`https://tenant.atlan.com\` + +Set your repository action secrets [here](https://github.com/${context.payload.repository.full_name}/settings/secrets/actions). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).` +} + +export function getSetResourceOnAssetComment(tableMd, setResourceFailed) { + return `## 🎊 Congrats on the merge! + +This pull request has been added as a resource to the following assets: +Name | Resource set successfully +--- | --- +${tableMd} +${setResourceFailed ? '> Seems like we were unable to set the resources for some of the assets due to insufficient permissions. To ensure that the pull request is linked as a resource, you will need to assign the right persona with requisite permissions to the API token.' : ''} +` +} + +export function getAssetInfo(ATLAN_INSTANCE_URL, asset, materialisedAsset, environmentName, projectName) { + return `### ${getConnectorImage( + asset.attributes.connectorName + )} [${asset.displayText}](${ATLAN_INSTANCE_URL}/assets/${ + asset.guid + }/overview?utm_source=dbt_github_action) ${ + asset.attributes?.certificateStatus + ? getCertificationImage(asset.attributes.certificateStatus) + : "" + } + Materialised asset: ${getConnectorImage( + materialisedAsset.attributes.connectorName + )} [${materialisedAsset.attributes.name}](${ATLAN_INSTANCE_URL}/assets/${ + materialisedAsset.guid + }/overview?utm_source=dbt_github_action) ${ + materialisedAsset.attributes?.certificateStatus + ? getCertificationImage(materialisedAsset.attributes.certificateStatus) + : "" + }${environmentName ? ` | Environment Name: \`${environmentName}\`` : ""}${ + projectName ? ` | Project Name: \`${projectName}\`` : "" + }` +} + +export function getDownstreamTable(ATLAN_INSTANCE_URL, downstreamAssets, rows, materialisedAsset) { + return `
${ + downstreamAssets.entityCount + } downstream assets 👇
+ + Name | Type | Description | Owners | Terms | Classifications | Source URL + --- | --- | --- | --- | --- | --- | --- + ${rows + .map((row) => + row.map((i) => i.replace(/\|/g, "•").replace(/\n/g, "")).join(" | ") + ) + .join("\n")} + + ${ + downstreamAssets.hasMore + ? `[See more downstream assets at Atlan](${ATLAN_INSTANCE_URL}/assets/${materialisedAsset.guid}/lineage?utm_source=dbt_github_action)` + : "" + } + +
` +} + +export function getViewAssetButton(ATLAN_INSTANCE_URL, asset) { + return `${getImageURL( + "atlan-logo", + 15, + 15 + )} [View asset in Atlan](${ATLAN_INSTANCE_URL}/assets/${ + asset.guid + }/overview?utm_source=dbt_github_action)` +} + +export function getMDCommentForModel(ATLAN_INSTANCE_URL, model) { + return `${getConnectorImage(model?.attributes?.connectorName)} [${ + model?.displayText + }](${ATLAN_INSTANCE_URL}/assets/${model?.guid}/overview?utm_source=dbt_github_action)` +} + +export function getMDCommentForMaterialisedView(ATLAN_INSTANCE_URL, materialisedView) { + return `${getConnectorImage(materialisedView?.attributes?.connectorName)} [${ + materialisedView?.attributes?.name + }](${ATLAN_INSTANCE_URL}/assets/${materialisedView?.guid}/overview?utm_source=dbt_github_action)` +} + +export function getTableMD(md, resp) { + return `${md} | ${resp ? '✅' : '❌'} \n` +} \ No newline at end of file diff --git a/adapters/templates/gitlab-integration.js b/adapters/templates/gitlab-integration.js new file mode 100644 index 0000000..e5f547d --- /dev/null +++ b/adapters/templates/gitlab-integration.js @@ -0,0 +1,102 @@ +import { getImageURL, getConnectorImage, getCertificationImage } from "../utils/index.js"; + +export function getErrorResponseStatus401 (ATLAN_INSTANCE_URL, CI_PROJECT_NAME, CI_PROJECT_NAMESPACE) { + return `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Bearer Token as \`ATLAN_API_TOKEN\` as this repository's CI/CD variable. + +Atlan Instance URL: ${ATLAN_INSTANCE_URL} + +Set your CI/CD variables [here](https://gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}/-/settings/ci_cd). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).` +} + +export function getErrorResponseStatusUndefined(ATLAN_INSTANCE_URL, CI_PROJECT_NAME, CI_PROJECT_NAMESPACE) { + return `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Instance URL as \`ATLAN_INSTANCE_URL\` as this repository's CI/CD variable. + +Atlan Instance URL: ${ATLAN_INSTANCE_URL} + +Make sure your Atlan Instance URL is set in the following format. +\`https://tenant.atlan.com\` + +Set your CI/CD variables [here](https://gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}/-/settings/ci_cd). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).` +} + +export function getSetResourceOnAssetComment(tableMd, setResourceFailed) { + return `## 🎊 Congrats on the merge! + + This pull request has been added as a resource to the following assets: + Name | Resource set successfully + --- | --- + ${tableMd} + ${setResourceFailed ? '> Seems like we were unable to set the resources for some of the assets due to insufficient permissions. To ensure that the pull request is linked as a resource, you will need to assign the right persona with requisite permissions to the API token.' : ''} + ` +} + +export function getAssetInfo(ATLAN_INSTANCE_URL, asset, materialisedAsset, environmentName, projectName) { + return `### ${getConnectorImage( + asset.attributes.connectorName + )} [${asset.displayText}](${ATLAN_INSTANCE_URL}/assets/${ + asset.guid + }/overview?utm_source=dbt_gitlab_action) ${ + asset.attributes?.certificateStatus + ? getCertificationImage(asset.attributes.certificateStatus) + : "" + } +Materialised asset: ${getConnectorImage( + materialisedAsset.attributes.connectorName + )} [${materialisedAsset.attributes.name}](${ATLAN_INSTANCE_URL}/assets/${ + materialisedAsset.guid + }/overview?utm_source=dbt_gitlab_action) ${ + materialisedAsset.attributes?.certificateStatus + ? getCertificationImage(materialisedAsset.attributes.certificateStatus) + : "" + }${environmentName ? ` | Environment Name: \`${environmentName}\`` : ""}${ + projectName ? ` | Project Name: \`${projectName}\`` : "" + }` +} + +export function getDownstreamTable(ATLAN_INSTANCE_URL, downstreamAssets, rows, materialisedAsset) { + return `
${ + downstreamAssets.entityCount + } downstream assets 👇
+ +Name | Type | Description | Owners | Terms | Classifications | Source URL +--- | --- | --- | --- | --- | --- | --- +${rows + .map((row) => + row.map((i) => i.replace(/\|/g, "•").replace(/\n/g, "")).join(" | ") + ) + .join("\n")} + +${ + downstreamAssets.hasMore + ? `[See more downstream assets at Atlan](${ATLAN_INSTANCE_URL}/assets/${materialisedAsset.guid}/lineage?utm_source=dbt_gitlab_action)` + : "" +} + +
` +} + +export function getViewAssetButton(ATLAN_INSTANCE_URL, asset) { + return `${getImageURL( + "atlan-logo", + 15, + 15 + )} [View asset in Atlan](${ATLAN_INSTANCE_URL}/assets/${ + asset.guid + }/overview?utm_source=dbt_gitlab_action)` +} + +export function getMDCommentForModel(ATLAN_INSTANCE_URL, model) { + return `${getConnectorImage(model?.attributes?.connectorName)} [${ + model?.displayText + }](${ATLAN_INSTANCE_URL}/assets/${model?.guid}/overview?utm_source=dbt_gitlab_action)` +} + +export function getMDCommentForMaterialisedView(ATLAN_INSTANCE_URL, materialisedView) { + return `${getConnectorImage(materialisedView?.attributes?.connectorName)} [${ + materialisedView?.attributes?.name + }](${ATLAN_INSTANCE_URL}/assets/${materialisedView?.guid}/overview?utm_source=dbt_gitlab_action)` +} + +export function getTableMD(md, resp) { + return `${md} | ${resp ? '✅' : '❌'} \n` +} \ No newline at end of file diff --git a/src/utils/auth.js b/adapters/utils/auth.js similarity index 51% rename from src/utils/auth.js rename to adapters/utils/auth.js index 029a168..ba09b6e 100644 --- a/src/utils/auth.js +++ b/adapters/utils/auth.js @@ -1,17 +1,10 @@ import fetch from "node-fetch"; -import dotenv from "dotenv"; -import core from "@actions/core"; - -dotenv.config(); - -const ATLAN_INSTANCE_URL = - core.getInput("ATLAN_INSTANCE_URL") || process.env.ATLAN_INSTANCE_URL; -const ATLAN_API_TOKEN = - core.getInput("ATLAN_API_TOKEN") || process.env.ATLAN_API_TOKEN; +import { + ATLAN_INSTANCE_URL, + ATLAN_API_TOKEN, +} from "./get-environment-variables.js"; export async function auth() { - //Done - console.log("Inside auth in utils.js"); var myHeaders = { authorization: `Bearer ${ATLAN_API_TOKEN}`, "content-type": "application/json", @@ -26,6 +19,6 @@ export async function auth() { `${ATLAN_INSTANCE_URL}/api/meta`, requestOptions ).catch((err) => {}); - console.log("Completed auth in utils.js"); + return response; } diff --git a/adapters/utils/create-comment.js b/adapters/utils/create-comment.js new file mode 100644 index 0000000..e07e89a --- /dev/null +++ b/adapters/utils/create-comment.js @@ -0,0 +1,9 @@ +export function truncate(value) { + if (typeof value === "string") + return value.length > 100 ? value.substring(0, 100) + "..." : value; + if (Array.isArray(value)) + return value.length > 10 + ? value.slice(0, 10).join(", ") + "..." + : value.join(", "); + return ""; +} diff --git a/adapters/utils/get-environment-variables.js b/adapters/utils/get-environment-variables.js new file mode 100644 index 0000000..30416a3 --- /dev/null +++ b/adapters/utils/get-environment-variables.js @@ -0,0 +1,84 @@ +import dotenv from "dotenv"; +import core from "@actions/core"; +dotenv.config(); + +//Common env variables +export const ATLAN_INSTANCE_URL = new URL( + process.env.ATLAN_INSTANCE_URL || core.getInput("ATLAN_INSTANCE_URL") +).origin; + +export const ATLAN_API_TOKEN = + process.env.ATLAN_API_TOKEN || core.getInput("ATLAN_API_TOKEN"); + +export const IS_DEV = process.env.IS_DEV; + +export const IGNORE_MODEL_ALIAS_MATCHING = + (process.env.IGNORE_MODEL_ALIAS_MATCHING || + core.getInput("IGNORE_MODEL_ALIAS_MATCHING")) == "true"; + +//GITLAB SPECIFIC ENV VARIABLES +export async function getCIMergeRequestIID( + gitlab, + CI_PROJECT_ID, + CI_COMMIT_SHA +) { + if (!process.env.CI_MERGE_REQUEST_IID) { + const mergeRequestCommit = await gitlab.Commits.allMergeRequests( + CI_PROJECT_ID, + CI_COMMIT_SHA + ); + + const firstMergeRequest = mergeRequestCommit[0]; + if (firstMergeRequest) { + return firstMergeRequest.iid; + } + } + + return process.env.CI_MERGE_REQUEST_IID; +} + +export const { + CI_PROJECT_PATH, + CI_PROJECT_ID, + CI_JOB_URL, + GITLAB_TOKEN, + CI_COMMIT_MESSAGE, + GITLAB_USER_LOGIN, + CI_PROJECT_NAME, + CI_COMMIT_SHA, + CI_PROJECT_NAMESPACE, +} = process.env; + +export function getGitLabEnvironments() { + const { DBT_ENVIRONMENT_BRANCH_MAP } = process.env; + + if (DBT_ENVIRONMENT_BRANCH_MAP) { + const environmentLines = DBT_ENVIRONMENT_BRANCH_MAP.split("\n"); + const environmentMap = {}; + + environmentLines.forEach((line) => { + const [environment, branch] = line.split(":").map((item) => item.trim()); + if (environment && branch) { + environmentMap[environment] = branch; + } + }); + + return environmentMap; + } else { + return {}; + } +} + +//GITHUB SPECIFIC ENV VARIABLES +export const GITHUB_TOKEN = + core.getInput("GITHUB_TOKEN") || process.env.GITHUB_TOKEN; + +export const getEnvironments = () => { + return ( + core + .getInput("DBT_ENVIRONMENT_BRANCH_MAP") + ?.trim() + ?.split("\n") + ?.map((i) => i.split(":").map((i) => i.trim())) ?? [] + ); +}; diff --git a/src/utils/get-image-url.js b/adapters/utils/get-image-url.js similarity index 100% rename from src/utils/get-image-url.js rename to adapters/utils/get-image-url.js diff --git a/src/utils/hosted-images.js b/adapters/utils/hosted-images.js similarity index 100% rename from src/utils/hosted-images.js rename to adapters/utils/hosted-images.js diff --git a/adapters/utils/index.js b/adapters/utils/index.js new file mode 100644 index 0000000..68c4e24 --- /dev/null +++ b/adapters/utils/index.js @@ -0,0 +1,12 @@ +export { + getImageURL, + getConnectorImage, + getCertificationImage, +} from "./get-image-url.js"; +export { default as hostedImages } from "./hosted-images.js"; +export { truncate } from "./create-comment.js"; +export { auth } from "./auth.js"; +export { + getGitLabEnvironments, + getEnvironments, +} from "./get-environment-variables.js"; diff --git a/dist/index.js b/dist/index.js index 387470f..40a1a98 100644 --- a/dist/index.js +++ b/dist/index.js @@ -6760,6 +6760,176 @@ exports.Response = Response; exports.FetchError = FetchError; +/***/ }), + +/***/ 1884: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + + +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.RateLimit = exports.Sema = void 0; +const events_1 = __importDefault(__nccwpck_require__(2361)); +function arrayMove(src, srcIndex, dst, dstIndex, len) { + for (let j = 0; j < len; ++j) { + dst[j + dstIndex] = src[j + srcIndex]; + src[j + srcIndex] = void 0; + } +} +function pow2AtLeast(n) { + n = n >>> 0; + n = n - 1; + n = n | (n >> 1); + n = n | (n >> 2); + n = n | (n >> 4); + n = n | (n >> 8); + n = n | (n >> 16); + return n + 1; +} +function getCapacity(capacity) { + return pow2AtLeast(Math.min(Math.max(16, capacity), 1073741824)); +} +// Deque is based on https://github.com/petkaantonov/deque/blob/master/js/deque.js +// Released under the MIT License: https://github.com/petkaantonov/deque/blob/6ef4b6400ad3ba82853fdcc6531a38eb4f78c18c/LICENSE +class Deque { + constructor(capacity) { + this._capacity = getCapacity(capacity); + this._length = 0; + this._front = 0; + this.arr = []; + } + push(item) { + const length = this._length; + this.checkCapacity(length + 1); + const i = (this._front + length) & (this._capacity - 1); + this.arr[i] = item; + this._length = length + 1; + return length + 1; + } + pop() { + const length = this._length; + if (length === 0) { + return void 0; + } + const i = (this._front + length - 1) & (this._capacity - 1); + const ret = this.arr[i]; + this.arr[i] = void 0; + this._length = length - 1; + return ret; + } + shift() { + const length = this._length; + if (length === 0) { + return void 0; + } + const front = this._front; + const ret = this.arr[front]; + this.arr[front] = void 0; + this._front = (front + 1) & (this._capacity - 1); + this._length = length - 1; + return ret; + } + get length() { + return this._length; + } + checkCapacity(size) { + if (this._capacity < size) { + this.resizeTo(getCapacity(this._capacity * 1.5 + 16)); + } + } + resizeTo(capacity) { + const oldCapacity = this._capacity; + this._capacity = capacity; + const front = this._front; + const length = this._length; + if (front + length > oldCapacity) { + const moveItemsCount = (front + length) & (oldCapacity - 1); + arrayMove(this.arr, 0, this.arr, oldCapacity, moveItemsCount); + } + } +} +class ReleaseEmitter extends events_1.default { +} +function isFn(x) { + return typeof x === 'function'; +} +function defaultInit() { + return '1'; +} +class Sema { + constructor(nr, { initFn = defaultInit, pauseFn, resumeFn, capacity = 10, } = {}) { + if (isFn(pauseFn) !== isFn(resumeFn)) { + throw new Error('pauseFn and resumeFn must be both set for pausing'); + } + this.nrTokens = nr; + this.free = new Deque(nr); + this.waiting = new Deque(capacity); + this.releaseEmitter = new ReleaseEmitter(); + this.noTokens = initFn === defaultInit; + this.pauseFn = pauseFn; + this.resumeFn = resumeFn; + this.paused = false; + this.releaseEmitter.on('release', (token) => { + const p = this.waiting.shift(); + if (p) { + p.resolve(token); + } + else { + if (this.resumeFn && this.paused) { + this.paused = false; + this.resumeFn(); + } + this.free.push(token); + } + }); + for (let i = 0; i < nr; i++) { + this.free.push(initFn()); + } + } + tryAcquire() { + return this.free.pop(); + } + async acquire() { + let token = this.tryAcquire(); + if (token !== void 0) { + return token; + } + return new Promise((resolve, reject) => { + if (this.pauseFn && !this.paused) { + this.paused = true; + this.pauseFn(); + } + this.waiting.push({ resolve, reject }); + }); + } + release(token) { + this.releaseEmitter.emit('release', this.noTokens ? '1' : token); + } + drain() { + const a = new Array(this.nrTokens); + for (let i = 0; i < this.nrTokens; i++) { + a[i] = this.acquire(); + } + return Promise.all(a); + } + nrWaiting() { + return this.waiting.length; + } +} +exports.Sema = Sema; +function RateLimit(rps, { timeUnit = 1000, uniformDistribution = false, } = {}) { + const sema = new Sema(uniformDistribution ? 1 : rps); + const delay = uniformDistribution ? timeUnit / rps : timeUnit; + return async function rl() { + await sema.acquire(); + setTimeout(() => sema.release(), delay); + }; +} +exports.RateLimit = RateLimit; + + /***/ }), /***/ 3682: @@ -6943,1434 +7113,5227 @@ function removeHook(state, name, method) { /***/ }), -/***/ 8803: +/***/ 610: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -var GetIntrinsic = __nccwpck_require__(4538); +const stringify = __nccwpck_require__(8750); +const compile = __nccwpck_require__(9434); +const expand = __nccwpck_require__(5873); +const parse = __nccwpck_require__(6477); -var callBind = __nccwpck_require__(2977); +/** + * Expand the given pattern or create a regex-compatible string. + * + * ```js + * const braces = require('braces'); + * console.log(braces('{a,b,c}', { compile: true })); //=> ['(a|b|c)'] + * console.log(braces('{a,b,c}')); //=> ['a', 'b', 'c'] + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {String} + * @api public + */ -var $indexOf = callBind(GetIntrinsic('String.prototype.indexOf')); +const braces = (input, options = {}) => { + let output = []; -module.exports = function callBoundIntrinsic(name, allowMissing) { - var intrinsic = GetIntrinsic(name, !!allowMissing); - if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) { - return callBind(intrinsic); - } - return intrinsic; + if (Array.isArray(input)) { + for (let pattern of input) { + let result = braces.create(pattern, options); + if (Array.isArray(result)) { + output.push(...result); + } else { + output.push(result); + } + } + } else { + output = [].concat(braces.create(input, options)); + } + + if (options && options.expand === true && options.nodupes === true) { + output = [...new Set(output)]; + } + return output; }; +/** + * Parse the given `str` with the given `options`. + * + * ```js + * // braces.parse(pattern, [, options]); + * const ast = braces.parse('a/{b,c}/d'); + * console.log(ast); + * ``` + * @param {String} pattern Brace pattern to parse + * @param {Object} options + * @return {Object} Returns an AST + * @api public + */ -/***/ }), +braces.parse = (input, options = {}) => parse(input, options); -/***/ 2977: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +/** + * Creates a braces string from an AST, or an AST node. + * + * ```js + * const braces = require('braces'); + * let ast = braces.parse('foo/{a,b}/bar'); + * console.log(stringify(ast.nodes[2])); //=> '{a,b}' + * ``` + * @param {String} `input` Brace pattern or AST. + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ + +braces.stringify = (input, options = {}) => { + if (typeof input === 'string') { + return stringify(braces.parse(input, options), options); + } + return stringify(input, options); +}; +/** + * Compiles a brace pattern into a regex-compatible, optimized string. + * This method is called by the main [braces](#braces) function by default. + * + * ```js + * const braces = require('braces'); + * console.log(braces.compile('a/{b,c}/d')); + * //=> ['a/(b|c)/d'] + * ``` + * @param {String} `input` Brace pattern or AST. + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ +braces.compile = (input, options = {}) => { + if (typeof input === 'string') { + input = braces.parse(input, options); + } + return compile(input, options); +}; -var bind = __nccwpck_require__(8334); -var GetIntrinsic = __nccwpck_require__(4538); +/** + * Expands a brace pattern into an array. This method is called by the + * main [braces](#braces) function when `options.expand` is true. Before + * using this method it's recommended that you read the [performance notes](#performance)) + * and advantages of using [.compile](#compile) instead. + * + * ```js + * const braces = require('braces'); + * console.log(braces.expand('a/{b,c}/d')); + * //=> ['a/b/d', 'a/c/d']; + * ``` + * @param {String} `pattern` Brace pattern + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ -var $apply = GetIntrinsic('%Function.prototype.apply%'); -var $call = GetIntrinsic('%Function.prototype.call%'); -var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply); +braces.expand = (input, options = {}) => { + if (typeof input === 'string') { + input = braces.parse(input, options); + } -var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true); -var $defineProperty = GetIntrinsic('%Object.defineProperty%', true); -var $max = GetIntrinsic('%Math.max%'); + let result = expand(input, options); -if ($defineProperty) { - try { - $defineProperty({}, 'a', { value: 1 }); - } catch (e) { - // IE 8 has a broken defineProperty - $defineProperty = null; - } -} + // filter out empty strings if specified + if (options.noempty === true) { + result = result.filter(Boolean); + } -module.exports = function callBind(originalFunction) { - var func = $reflectApply(bind, $call, arguments); - if ($gOPD && $defineProperty) { - var desc = $gOPD(func, 'length'); - if (desc.configurable) { - // original length, plus the receiver, minus any additional arguments (after the receiver) - $defineProperty( - func, - 'length', - { value: 1 + $max(0, originalFunction.length - (arguments.length - 1)) } - ); - } - } - return func; + // filter out duplicates if specified + if (options.nodupes === true) { + result = [...new Set(result)]; + } + + return result; }; -var applyBind = function applyBind() { - return $reflectApply(bind, $apply, arguments); +/** + * Processes a brace pattern and returns either an expanded array + * (if `options.expand` is true), a highly optimized regex-compatible string. + * This method is called by the main [braces](#braces) function. + * + * ```js + * const braces = require('braces'); + * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}')) + * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)' + * ``` + * @param {String} `pattern` Brace pattern + * @param {Object} `options` + * @return {Array} Returns an array of expanded values. + * @api public + */ + +braces.create = (input, options = {}) => { + if (input === '' || input.length < 3) { + return [input]; + } + + return options.expand !== true + ? braces.compile(input, options) + : braces.expand(input, options); }; -if ($defineProperty) { - $defineProperty(module.exports, 'apply', { value: applyBind }); -} else { - module.exports.apply = applyBind; -} +/** + * Expose "braces" + */ + +module.exports = braces; /***/ }), -/***/ 8932: -/***/ ((__unused_webpack_module, exports) => { +/***/ 9434: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -Object.defineProperty(exports, "__esModule", ({ value: true })); +const fill = __nccwpck_require__(6330); +const utils = __nccwpck_require__(5207); -class Deprecation extends Error { - constructor(message) { - super(message); // Maintains proper stack trace (only available on V8) +const compile = (ast, options = {}) => { + let walk = (node, parent = {}) => { + let invalidBlock = utils.isInvalidBrace(parent); + let invalidNode = node.invalid === true && options.escapeInvalid === true; + let invalid = invalidBlock === true || invalidNode === true; + let prefix = options.escapeInvalid === true ? '\\' : ''; + let output = ''; - /* istanbul ignore next */ + if (node.isOpen === true) { + return prefix + node.value; + } + if (node.isClose === true) { + return prefix + node.value; + } - if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); + if (node.type === 'open') { + return invalid ? (prefix + node.value) : '('; } - this.name = 'Deprecation'; - } + if (node.type === 'close') { + return invalid ? (prefix + node.value) : ')'; + } -} + if (node.type === 'comma') { + return node.prev.type === 'comma' ? '' : (invalid ? node.value : '|'); + } -exports.Deprecation = Deprecation; + if (node.value) { + return node.value; + } + + if (node.nodes && node.ranges > 0) { + let args = utils.reduce(node.nodes); + let range = fill(...args, { ...options, wrap: false, toRegex: true }); + + if (range.length !== 0) { + return args.length > 1 && range.length > 1 ? `(${range})` : range; + } + } + + if (node.nodes) { + for (let child of node.nodes) { + output += walk(child, node); + } + } + return output; + }; + + return walk(ast); +}; + +module.exports = compile; /***/ }), -/***/ 2437: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +/***/ 8774: +/***/ ((module) => { -const fs = __nccwpck_require__(7147) -const path = __nccwpck_require__(1017) -const os = __nccwpck_require__(2037) -const packageJson = __nccwpck_require__(9968) -const version = packageJson.version -const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg +module.exports = { + MAX_LENGTH: 1024 * 64, + + // Digits + CHAR_0: '0', /* 0 */ + CHAR_9: '9', /* 9 */ + + // Alphabet chars. + CHAR_UPPERCASE_A: 'A', /* A */ + CHAR_LOWERCASE_A: 'a', /* a */ + CHAR_UPPERCASE_Z: 'Z', /* Z */ + CHAR_LOWERCASE_Z: 'z', /* z */ + + CHAR_LEFT_PARENTHESES: '(', /* ( */ + CHAR_RIGHT_PARENTHESES: ')', /* ) */ + + CHAR_ASTERISK: '*', /* * */ + + // Non-alphabetic chars. + CHAR_AMPERSAND: '&', /* & */ + CHAR_AT: '@', /* @ */ + CHAR_BACKSLASH: '\\', /* \ */ + CHAR_BACKTICK: '`', /* ` */ + CHAR_CARRIAGE_RETURN: '\r', /* \r */ + CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */ + CHAR_COLON: ':', /* : */ + CHAR_COMMA: ',', /* , */ + CHAR_DOLLAR: '$', /* . */ + CHAR_DOT: '.', /* . */ + CHAR_DOUBLE_QUOTE: '"', /* " */ + CHAR_EQUAL: '=', /* = */ + CHAR_EXCLAMATION_MARK: '!', /* ! */ + CHAR_FORM_FEED: '\f', /* \f */ + CHAR_FORWARD_SLASH: '/', /* / */ + CHAR_HASH: '#', /* # */ + CHAR_HYPHEN_MINUS: '-', /* - */ + CHAR_LEFT_ANGLE_BRACKET: '<', /* < */ + CHAR_LEFT_CURLY_BRACE: '{', /* { */ + CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */ + CHAR_LINE_FEED: '\n', /* \n */ + CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */ + CHAR_PERCENT: '%', /* % */ + CHAR_PLUS: '+', /* + */ + CHAR_QUESTION_MARK: '?', /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */ + CHAR_RIGHT_CURLY_BRACE: '}', /* } */ + CHAR_RIGHT_SQUARE_BRACKET: ']', /* ] */ + CHAR_SEMICOLON: ';', /* ; */ + CHAR_SINGLE_QUOTE: '\'', /* ' */ + CHAR_SPACE: ' ', /* */ + CHAR_TAB: '\t', /* \t */ + CHAR_UNDERSCORE: '_', /* _ */ + CHAR_VERTICAL_LINE: '|', /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF' /* \uFEFF */ +}; -// Parser src into an Object -function parse (src) { - const obj = {} - // Convert buffer to string - let lines = src.toString() +/***/ }), - // Convert line breaks to same format - lines = lines.replace(/\r\n?/mg, '\n') +/***/ 5873: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - let match - while ((match = LINE.exec(lines)) != null) { - const key = match[1] - // Default undefined or null to empty string - let value = (match[2] || '') - // Remove whitespace - value = value.trim() +const fill = __nccwpck_require__(6330); +const stringify = __nccwpck_require__(8750); +const utils = __nccwpck_require__(5207); - // Check if double quoted - const maybeQuote = value[0] +const append = (queue = '', stash = '', enclose = false) => { + let result = []; - // Remove surrounding quotes - value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2') + queue = [].concat(queue); + stash = [].concat(stash); - // Expand newlines if double quoted - if (maybeQuote === '"') { - value = value.replace(/\\n/g, '\n') - value = value.replace(/\\r/g, '\r') - } + if (!stash.length) return queue; + if (!queue.length) { + return enclose ? utils.flatten(stash).map(ele => `{${ele}}`) : stash; + } - // Add to object - obj[key] = value + for (let item of queue) { + if (Array.isArray(item)) { + for (let value of item) { + result.push(append(value, stash, enclose)); + } + } else { + for (let ele of stash) { + if (enclose === true && typeof ele === 'string') ele = `{${ele}}`; + result.push(Array.isArray(ele) ? append(item, ele, enclose) : (item + ele)); + } + } } + return utils.flatten(result); +}; - return obj -} +const expand = (ast, options = {}) => { + let rangeLimit = options.rangeLimit === void 0 ? 1000 : options.rangeLimit; -function _log (message) { - console.log(`[dotenv@${version}][DEBUG] ${message}`) -} + let walk = (node, parent = {}) => { + node.queue = []; -function _resolveHome (envPath) { - return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath -} + let p = parent; + let q = parent.queue; -// Populates process.env from .env file -function config (options) { - let dotenvPath = path.resolve(process.cwd(), '.env') - let encoding = 'utf8' - const debug = Boolean(options && options.debug) - const override = Boolean(options && options.override) + while (p.type !== 'brace' && p.type !== 'root' && p.parent) { + p = p.parent; + q = p.queue; + } - if (options) { - if (options.path != null) { - dotenvPath = _resolveHome(options.path) + if (node.invalid || node.dollar) { + q.push(append(q.pop(), stringify(node, options))); + return; } - if (options.encoding != null) { - encoding = options.encoding + + if (node.type === 'brace' && node.invalid !== true && node.nodes.length === 2) { + q.push(append(q.pop(), ['{}'])); + return; } - } - try { - // Specifying an encoding returns a string instead of a buffer - const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding })) + if (node.nodes && node.ranges > 0) { + let args = utils.reduce(node.nodes); - Object.keys(parsed).forEach(function (key) { - if (!Object.prototype.hasOwnProperty.call(process.env, key)) { - process.env[key] = parsed[key] - } else { - if (override === true) { - process.env[key] = parsed[key] - } + if (utils.exceedsLimit(...args, options.step, rangeLimit)) { + throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.'); + } - if (debug) { - if (override === true) { - _log(`"${key}" is already defined in \`process.env\` and WAS overwritten`) - } else { - _log(`"${key}" is already defined in \`process.env\` and was NOT overwritten`) - } - } + let range = fill(...args, options); + if (range.length === 0) { + range = stringify(node, options); } - }) - return { parsed } - } catch (e) { - if (debug) { - _log(`Failed to load ${dotenvPath} ${e.message}`) + q.push(append(q.pop(), range)); + node.nodes = []; + return; } - return { error: e } - } -} + let enclose = utils.encloseBrace(node); + let queue = node.queue; + let block = node; -const DotenvModule = { - config, - parse -} + while (block.type !== 'brace' && block.type !== 'root' && block.parent) { + block = block.parent; + queue = block.queue; + } -module.exports.config = DotenvModule.config -module.exports.parse = DotenvModule.parse -module.exports = DotenvModule + for (let i = 0; i < node.nodes.length; i++) { + let child = node.nodes[i]; + + if (child.type === 'comma' && node.type === 'brace') { + if (i === 1) queue.push(''); + queue.push(''); + continue; + } + + if (child.type === 'close') { + q.push(append(q.pop(), queue, enclose)); + continue; + } + + if (child.value && child.type !== 'open') { + queue.push(append(queue.pop(), child.value)); + continue; + } + + if (child.nodes) { + walk(child, node); + } + } + + return queue; + }; + + return utils.flatten(walk(ast)); +}; + +module.exports = expand; /***/ }), -/***/ 9320: -/***/ ((module) => { +/***/ 6477: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -/* eslint no-invalid-this: 1 */ +const stringify = __nccwpck_require__(8750); -var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; -var slice = Array.prototype.slice; -var toStr = Object.prototype.toString; -var funcType = '[object Function]'; +/** + * Constants + */ -module.exports = function bind(that) { - var target = this; - if (typeof target !== 'function' || toStr.call(target) !== funcType) { - throw new TypeError(ERROR_MESSAGE + target); +const { + MAX_LENGTH, + CHAR_BACKSLASH, /* \ */ + CHAR_BACKTICK, /* ` */ + CHAR_COMMA, /* , */ + CHAR_DOT, /* . */ + CHAR_LEFT_PARENTHESES, /* ( */ + CHAR_RIGHT_PARENTHESES, /* ) */ + CHAR_LEFT_CURLY_BRACE, /* { */ + CHAR_RIGHT_CURLY_BRACE, /* } */ + CHAR_LEFT_SQUARE_BRACKET, /* [ */ + CHAR_RIGHT_SQUARE_BRACKET, /* ] */ + CHAR_DOUBLE_QUOTE, /* " */ + CHAR_SINGLE_QUOTE, /* ' */ + CHAR_NO_BREAK_SPACE, + CHAR_ZERO_WIDTH_NOBREAK_SPACE +} = __nccwpck_require__(8774); + +/** + * parse + */ + +const parse = (input, options = {}) => { + if (typeof input !== 'string') { + throw new TypeError('Expected a string'); + } + + let opts = options || {}; + let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + if (input.length > max) { + throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`); + } + + let ast = { type: 'root', input, nodes: [] }; + let stack = [ast]; + let block = ast; + let prev = ast; + let brackets = 0; + let length = input.length; + let index = 0; + let depth = 0; + let value; + let memo = {}; + + /** + * Helpers + */ + + const advance = () => input[index++]; + const push = node => { + if (node.type === 'text' && prev.type === 'dot') { + prev.type = 'text'; } - var args = slice.call(arguments, 1); - var bound; - var binder = function () { - if (this instanceof bound) { - var result = target.apply( - this, - args.concat(slice.call(arguments)) - ); - if (Object(result) === result) { - return result; - } - return this; - } else { - return target.apply( - that, - args.concat(slice.call(arguments)) - ); + if (prev && prev.type === 'text' && node.type === 'text') { + prev.value += node.value; + return; + } + + block.nodes.push(node); + node.parent = block; + node.prev = prev; + prev = node; + return node; + }; + + push({ type: 'bos' }); + + while (index < length) { + block = stack[stack.length - 1]; + value = advance(); + + /** + * Invalid chars + */ + + if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) { + continue; + } + + /** + * Escaped chars + */ + + if (value === CHAR_BACKSLASH) { + push({ type: 'text', value: (options.keepEscaping ? value : '') + advance() }); + continue; + } + + /** + * Right square bracket (literal): ']' + */ + + if (value === CHAR_RIGHT_SQUARE_BRACKET) { + push({ type: 'text', value: '\\' + value }); + continue; + } + + /** + * Left square bracket: '[' + */ + + if (value === CHAR_LEFT_SQUARE_BRACKET) { + brackets++; + + let closed = true; + let next; + + while (index < length && (next = advance())) { + value += next; + + if (next === CHAR_LEFT_SQUARE_BRACKET) { + brackets++; + continue; } - }; - var boundLength = Math.max(0, target.length - args.length); - var boundArgs = []; - for (var i = 0; i < boundLength; i++) { - boundArgs.push('$' + i); + if (next === CHAR_BACKSLASH) { + value += advance(); + continue; + } + + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + brackets--; + + if (brackets === 0) { + break; + } + } + } + + push({ type: 'text', value }); + continue; } - bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); + /** + * Parentheses + */ - if (target.prototype) { - var Empty = function Empty() {}; - Empty.prototype = target.prototype; - bound.prototype = new Empty(); - Empty.prototype = null; + if (value === CHAR_LEFT_PARENTHESES) { + block = push({ type: 'paren', nodes: [] }); + stack.push(block); + push({ type: 'text', value }); + continue; } - return bound; -}; + if (value === CHAR_RIGHT_PARENTHESES) { + if (block.type !== 'paren') { + push({ type: 'text', value }); + continue; + } + block = stack.pop(); + push({ type: 'text', value }); + block = stack[stack.length - 1]; + continue; + } + /** + * Quotes: '|"|` + */ -/***/ }), + if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) { + let open = value; + let next; -/***/ 8334: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + if (options.keepQuotes !== true) { + value = ''; + } + while (index < length && (next = advance())) { + if (next === CHAR_BACKSLASH) { + value += next + advance(); + continue; + } + if (next === open) { + if (options.keepQuotes === true) value += next; + break; + } -var implementation = __nccwpck_require__(9320); + value += next; + } -module.exports = Function.prototype.bind || implementation; + push({ type: 'text', value }); + continue; + } + + /** + * Left curly brace: '{' + */ + + if (value === CHAR_LEFT_CURLY_BRACE) { + depth++; + + let dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true; + let brace = { + type: 'brace', + open: true, + close: false, + dollar, + depth, + commas: 0, + ranges: 0, + nodes: [] + }; + + block = push(brace); + stack.push(block); + push({ type: 'open', value }); + continue; + } + + /** + * Right curly brace: '}' + */ + + if (value === CHAR_RIGHT_CURLY_BRACE) { + if (block.type !== 'brace') { + push({ type: 'text', value }); + continue; + } + + let type = 'close'; + block = stack.pop(); + block.close = true; + + push({ type, value }); + depth--; + + block = stack[stack.length - 1]; + continue; + } + + /** + * Comma: ',' + */ + + if (value === CHAR_COMMA && depth > 0) { + if (block.ranges > 0) { + block.ranges = 0; + let open = block.nodes.shift(); + block.nodes = [open, { type: 'text', value: stringify(block) }]; + } + + push({ type: 'comma', value }); + block.commas++; + continue; + } + + /** + * Dot: '.' + */ + + if (value === CHAR_DOT && depth > 0 && block.commas === 0) { + let siblings = block.nodes; + + if (depth === 0 || siblings.length === 0) { + push({ type: 'text', value }); + continue; + } + + if (prev.type === 'dot') { + block.range = []; + prev.value += value; + prev.type = 'range'; + + if (block.nodes.length !== 3 && block.nodes.length !== 5) { + block.invalid = true; + block.ranges = 0; + prev.type = 'text'; + continue; + } + + block.ranges++; + block.args = []; + continue; + } + + if (prev.type === 'range') { + siblings.pop(); + + let before = siblings[siblings.length - 1]; + before.value += prev.value + value; + prev = before; + block.ranges--; + continue; + } + + push({ type: 'dot', value }); + continue; + } + + /** + * Text + */ + + push({ type: 'text', value }); + } + + // Mark imbalanced braces and brackets as invalid + do { + block = stack.pop(); + + if (block.type !== 'root') { + block.nodes.forEach(node => { + if (!node.nodes) { + if (node.type === 'open') node.isOpen = true; + if (node.type === 'close') node.isClose = true; + if (!node.nodes) node.type = 'text'; + node.invalid = true; + } + }); + + // get the location of the block on parent.nodes (block's siblings) + let parent = stack[stack.length - 1]; + let index = parent.nodes.indexOf(block); + // replace the (invalid) block with it's nodes + parent.nodes.splice(index, 1, ...block.nodes); + } + } while (stack.length > 0); + + push({ type: 'eos' }); + return ast; +}; + +module.exports = parse; /***/ }), -/***/ 4538: +/***/ 8750: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -var undefined; +const utils = __nccwpck_require__(5207); -var $SyntaxError = SyntaxError; -var $Function = Function; -var $TypeError = TypeError; +module.exports = (ast, options = {}) => { + let stringify = (node, parent = {}) => { + let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent); + let invalidNode = node.invalid === true && options.escapeInvalid === true; + let output = ''; -// eslint-disable-next-line consistent-return -var getEvalledConstructor = function (expressionSyntax) { - try { - return $Function('"use strict"; return (' + expressionSyntax + ').constructor;')(); - } catch (e) {} + if (node.value) { + if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) { + return '\\' + node.value; + } + return node.value; + } + + if (node.value) { + return node.value; + } + + if (node.nodes) { + for (let child of node.nodes) { + output += stringify(child); + } + } + return output; + }; + + return stringify(ast); }; -var $gOPD = Object.getOwnPropertyDescriptor; -if ($gOPD) { - try { - $gOPD({}, ''); - } catch (e) { - $gOPD = null; // this is IE 8, which has a broken gOPD - } -} -var throwTypeError = function () { - throw new $TypeError(); + +/***/ }), + +/***/ 5207: +/***/ ((__unused_webpack_module, exports) => { + + + +exports.isInteger = num => { + if (typeof num === 'number') { + return Number.isInteger(num); + } + if (typeof num === 'string' && num.trim() !== '') { + return Number.isInteger(Number(num)); + } + return false; }; -var ThrowTypeError = $gOPD - ? (function () { - try { - // eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties - arguments.callee; // IE 8 does not throw here - return throwTypeError; - } catch (calleeThrows) { - try { - // IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '') - return $gOPD(arguments, 'callee').get; - } catch (gOPDthrows) { - return throwTypeError; - } - } - }()) - : throwTypeError; -var hasSymbols = __nccwpck_require__(587)(); -var hasProto = __nccwpck_require__(5894)(); +/** + * Find a node of the given type + */ -var getProto = Object.getPrototypeOf || ( - hasProto - ? function (x) { return x.__proto__; } // eslint-disable-line no-proto - : null -); +exports.find = (node, type) => node.nodes.find(node => node.type === type); -var needsEval = {}; +/** + * Find a node of the given type + */ -var TypedArray = typeof Uint8Array === 'undefined' || !getProto ? undefined : getProto(Uint8Array); +exports.exceedsLimit = (min, max, step = 1, limit) => { + if (limit === false) return false; + if (!exports.isInteger(min) || !exports.isInteger(max)) return false; + return ((Number(max) - Number(min)) / Number(step)) >= limit; +}; -var INTRINSICS = { - '%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError, - '%Array%': Array, - '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer, - '%ArrayIteratorPrototype%': hasSymbols && getProto ? getProto([][Symbol.iterator]()) : undefined, - '%AsyncFromSyncIteratorPrototype%': undefined, - '%AsyncFunction%': needsEval, - '%AsyncGenerator%': needsEval, - '%AsyncGeneratorFunction%': needsEval, - '%AsyncIteratorPrototype%': needsEval, - '%Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics, - '%BigInt%': typeof BigInt === 'undefined' ? undefined : BigInt, - '%BigInt64Array%': typeof BigInt64Array === 'undefined' ? undefined : BigInt64Array, - '%BigUint64Array%': typeof BigUint64Array === 'undefined' ? undefined : BigUint64Array, - '%Boolean%': Boolean, - '%DataView%': typeof DataView === 'undefined' ? undefined : DataView, - '%Date%': Date, - '%decodeURI%': decodeURI, - '%decodeURIComponent%': decodeURIComponent, - '%encodeURI%': encodeURI, - '%encodeURIComponent%': encodeURIComponent, - '%Error%': Error, - '%eval%': eval, // eslint-disable-line no-eval - '%EvalError%': EvalError, - '%Float32Array%': typeof Float32Array === 'undefined' ? undefined : Float32Array, - '%Float64Array%': typeof Float64Array === 'undefined' ? undefined : Float64Array, - '%FinalizationRegistry%': typeof FinalizationRegistry === 'undefined' ? undefined : FinalizationRegistry, - '%Function%': $Function, - '%GeneratorFunction%': needsEval, - '%Int8Array%': typeof Int8Array === 'undefined' ? undefined : Int8Array, - '%Int16Array%': typeof Int16Array === 'undefined' ? undefined : Int16Array, - '%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array, - '%isFinite%': isFinite, - '%isNaN%': isNaN, - '%IteratorPrototype%': hasSymbols && getProto ? getProto(getProto([][Symbol.iterator]())) : undefined, - '%JSON%': typeof JSON === 'object' ? JSON : undefined, - '%Map%': typeof Map === 'undefined' ? undefined : Map, - '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols || !getProto ? undefined : getProto(new Map()[Symbol.iterator]()), - '%Math%': Math, - '%Number%': Number, - '%Object%': Object, - '%parseFloat%': parseFloat, - '%parseInt%': parseInt, - '%Promise%': typeof Promise === 'undefined' ? undefined : Promise, - '%Proxy%': typeof Proxy === 'undefined' ? undefined : Proxy, - '%RangeError%': RangeError, - '%ReferenceError%': ReferenceError, - '%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect, - '%RegExp%': RegExp, - '%Set%': typeof Set === 'undefined' ? undefined : Set, - '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols || !getProto ? undefined : getProto(new Set()[Symbol.iterator]()), - '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer, - '%String%': String, - '%StringIteratorPrototype%': hasSymbols && getProto ? getProto(''[Symbol.iterator]()) : undefined, - '%Symbol%': hasSymbols ? Symbol : undefined, - '%SyntaxError%': $SyntaxError, - '%ThrowTypeError%': ThrowTypeError, - '%TypedArray%': TypedArray, - '%TypeError%': $TypeError, - '%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined : Uint8Array, - '%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray, - '%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined : Uint16Array, - '%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined : Uint32Array, - '%URIError%': URIError, - '%WeakMap%': typeof WeakMap === 'undefined' ? undefined : WeakMap, - '%WeakRef%': typeof WeakRef === 'undefined' ? undefined : WeakRef, - '%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet +/** + * Escape the given node with '\\' before node.value + */ + +exports.escapeNode = (block, n = 0, type) => { + let node = block.nodes[n]; + if (!node) return; + + if ((type && node.type === type) || node.type === 'open' || node.type === 'close') { + if (node.escaped !== true) { + node.value = '\\' + node.value; + node.escaped = true; + } + } }; -if (getProto) { +/** + * Returns true if the given brace node should be enclosed in literal braces + */ + +exports.encloseBrace = node => { + if (node.type !== 'brace') return false; + if ((node.commas >> 0 + node.ranges >> 0) === 0) { + node.invalid = true; + return true; + } + return false; +}; + +/** + * Returns true if a brace node is invalid. + */ + +exports.isInvalidBrace = block => { + if (block.type !== 'brace') return false; + if (block.invalid === true || block.dollar) return true; + if ((block.commas >> 0 + block.ranges >> 0) === 0) { + block.invalid = true; + return true; + } + if (block.open !== true || block.close !== true) { + block.invalid = true; + return true; + } + return false; +}; + +/** + * Returns true if a node is an open or close node + */ + +exports.isOpenOrClose = node => { + if (node.type === 'open' || node.type === 'close') { + return true; + } + return node.open === true || node.close === true; +}; + +/** + * Reduce an array of text nodes. + */ + +exports.reduce = nodes => nodes.reduce((acc, node) => { + if (node.type === 'text') acc.push(node.value); + if (node.type === 'range') node.type = 'text'; + return acc; +}, []); + +/** + * Flatten an array + */ + +exports.flatten = (...args) => { + const result = []; + const flat = arr => { + for (let i = 0; i < arr.length; i++) { + let ele = arr[i]; + Array.isArray(ele) ? flat(ele, result) : ele !== void 0 && result.push(ele); + } + return result; + }; + flat(args); + return result; +}; + + +/***/ }), + +/***/ 8803: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + + + +var GetIntrinsic = __nccwpck_require__(4538); + +var callBind = __nccwpck_require__(2977); + +var $indexOf = callBind(GetIntrinsic('String.prototype.indexOf')); + +module.exports = function callBoundIntrinsic(name, allowMissing) { + var intrinsic = GetIntrinsic(name, !!allowMissing); + if (typeof intrinsic === 'function' && $indexOf(name, '.prototype.') > -1) { + return callBind(intrinsic); + } + return intrinsic; +}; + + +/***/ }), + +/***/ 2977: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + + + +var bind = __nccwpck_require__(8334); +var GetIntrinsic = __nccwpck_require__(4538); + +var $apply = GetIntrinsic('%Function.prototype.apply%'); +var $call = GetIntrinsic('%Function.prototype.call%'); +var $reflectApply = GetIntrinsic('%Reflect.apply%', true) || bind.call($call, $apply); + +var $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true); +var $defineProperty = GetIntrinsic('%Object.defineProperty%', true); +var $max = GetIntrinsic('%Math.max%'); + +if ($defineProperty) { try { - null.error; // eslint-disable-line no-unused-expressions + $defineProperty({}, 'a', { value: 1 }); } catch (e) { - // https://github.com/tc39/proposal-shadowrealm/pull/384#issuecomment-1364264229 - var errorProto = getProto(getProto(e)); - INTRINSICS['%Error.prototype%'] = errorProto; + // IE 8 has a broken defineProperty + $defineProperty = null; } } -var doEval = function doEval(name) { - var value; - if (name === '%AsyncFunction%') { - value = getEvalledConstructor('async function () {}'); - } else if (name === '%GeneratorFunction%') { - value = getEvalledConstructor('function* () {}'); - } else if (name === '%AsyncGeneratorFunction%') { - value = getEvalledConstructor('async function* () {}'); - } else if (name === '%AsyncGenerator%') { - var fn = doEval('%AsyncGeneratorFunction%'); - if (fn) { - value = fn.prototype; - } - } else if (name === '%AsyncIteratorPrototype%') { - var gen = doEval('%AsyncGenerator%'); - if (gen && getProto) { - value = getProto(gen.prototype); +module.exports = function callBind(originalFunction) { + var func = $reflectApply(bind, $call, arguments); + if ($gOPD && $defineProperty) { + var desc = $gOPD(func, 'length'); + if (desc.configurable) { + // original length, plus the receiver, minus any additional arguments (after the receiver) + $defineProperty( + func, + 'length', + { value: 1 + $max(0, originalFunction.length - (arguments.length - 1)) } + ); } } + return func; +}; - INTRINSICS[name] = value; +var applyBind = function applyBind() { + return $reflectApply(bind, $apply, arguments); +}; + +if ($defineProperty) { + $defineProperty(module.exports, 'apply', { value: applyBind }); +} else { + module.exports.apply = applyBind; +} + + +/***/ }), + +/***/ 8932: +/***/ ((__unused_webpack_module, exports) => { + + + +Object.defineProperty(exports, "__esModule", ({ value: true })); + +class Deprecation extends Error { + constructor(message) { + super(message); // Maintains proper stack trace (only available on V8) + + /* istanbul ignore next */ + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + this.name = 'Deprecation'; + } + +} + +exports.Deprecation = Deprecation; + + +/***/ }), + +/***/ 2437: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const fs = __nccwpck_require__(7147) +const path = __nccwpck_require__(1017) +const os = __nccwpck_require__(2037) +const packageJson = __nccwpck_require__(9968) + +const version = packageJson.version + +const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg + +// Parser src into an Object +function parse (src) { + const obj = {} + + // Convert buffer to string + let lines = src.toString() + + // Convert line breaks to same format + lines = lines.replace(/\r\n?/mg, '\n') + + let match + while ((match = LINE.exec(lines)) != null) { + const key = match[1] + + // Default undefined or null to empty string + let value = (match[2] || '') + + // Remove whitespace + value = value.trim() + + // Check if double quoted + const maybeQuote = value[0] + + // Remove surrounding quotes + value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2') + + // Expand newlines if double quoted + if (maybeQuote === '"') { + value = value.replace(/\\n/g, '\n') + value = value.replace(/\\r/g, '\r') + } + + // Add to object + obj[key] = value + } + + return obj +} + +function _log (message) { + console.log(`[dotenv@${version}][DEBUG] ${message}`) +} + +function _resolveHome (envPath) { + return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath +} + +// Populates process.env from .env file +function config (options) { + let dotenvPath = path.resolve(process.cwd(), '.env') + let encoding = 'utf8' + const debug = Boolean(options && options.debug) + const override = Boolean(options && options.override) + + if (options) { + if (options.path != null) { + dotenvPath = _resolveHome(options.path) + } + if (options.encoding != null) { + encoding = options.encoding + } + } + + try { + // Specifying an encoding returns a string instead of a buffer + const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding })) + + Object.keys(parsed).forEach(function (key) { + if (!Object.prototype.hasOwnProperty.call(process.env, key)) { + process.env[key] = parsed[key] + } else { + if (override === true) { + process.env[key] = parsed[key] + } + + if (debug) { + if (override === true) { + _log(`"${key}" is already defined in \`process.env\` and WAS overwritten`) + } else { + _log(`"${key}" is already defined in \`process.env\` and was NOT overwritten`) + } + } + } + }) + + return { parsed } + } catch (e) { + if (debug) { + _log(`Failed to load ${dotenvPath} ${e.message}`) + } + + return { error: e } + } +} + +const DotenvModule = { + config, + parse +} + +module.exports.config = DotenvModule.config +module.exports.parse = DotenvModule.parse +module.exports = DotenvModule + + +/***/ }), + +/***/ 6330: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +/*! + * fill-range + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Licensed under the MIT License. + */ + + + +const util = __nccwpck_require__(3837); +const toRegexRange = __nccwpck_require__(1861); + +const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); + +const transform = toNumber => { + return value => toNumber === true ? Number(value) : String(value); +}; + +const isValidValue = value => { + return typeof value === 'number' || (typeof value === 'string' && value !== ''); +}; + +const isNumber = num => Number.isInteger(+num); + +const zeros = input => { + let value = `${input}`; + let index = -1; + if (value[0] === '-') value = value.slice(1); + if (value === '0') return false; + while (value[++index] === '0'); + return index > 0; +}; + +const stringify = (start, end, options) => { + if (typeof start === 'string' || typeof end === 'string') { + return true; + } + return options.stringify === true; +}; + +const pad = (input, maxLength, toNumber) => { + if (maxLength > 0) { + let dash = input[0] === '-' ? '-' : ''; + if (dash) input = input.slice(1); + input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0')); + } + if (toNumber === false) { + return String(input); + } + return input; +}; + +const toMaxLen = (input, maxLength) => { + let negative = input[0] === '-' ? '-' : ''; + if (negative) { + input = input.slice(1); + maxLength--; + } + while (input.length < maxLength) input = '0' + input; + return negative ? ('-' + input) : input; +}; + +const toSequence = (parts, options) => { + parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); + parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0); + + let prefix = options.capture ? '' : '?:'; + let positives = ''; + let negatives = ''; + let result; + + if (parts.positives.length) { + positives = parts.positives.join('|'); + } + + if (parts.negatives.length) { + negatives = `-(${prefix}${parts.negatives.join('|')})`; + } + + if (positives && negatives) { + result = `${positives}|${negatives}`; + } else { + result = positives || negatives; + } + + if (options.wrap) { + return `(${prefix}${result})`; + } + + return result; +}; + +const toRange = (a, b, isNumbers, options) => { + if (isNumbers) { + return toRegexRange(a, b, { wrap: false, ...options }); + } + + let start = String.fromCharCode(a); + if (a === b) return start; + + let stop = String.fromCharCode(b); + return `[${start}-${stop}]`; +}; + +const toRegex = (start, end, options) => { + if (Array.isArray(start)) { + let wrap = options.wrap === true; + let prefix = options.capture ? '' : '?:'; + return wrap ? `(${prefix}${start.join('|')})` : start.join('|'); + } + return toRegexRange(start, end, options); +}; + +const rangeError = (...args) => { + return new RangeError('Invalid range arguments: ' + util.inspect(...args)); +}; + +const invalidRange = (start, end, options) => { + if (options.strictRanges === true) throw rangeError([start, end]); + return []; +}; + +const invalidStep = (step, options) => { + if (options.strictRanges === true) { + throw new TypeError(`Expected step "${step}" to be a number`); + } + return []; +}; + +const fillNumbers = (start, end, step = 1, options = {}) => { + let a = Number(start); + let b = Number(end); + + if (!Number.isInteger(a) || !Number.isInteger(b)) { + if (options.strictRanges === true) throw rangeError([start, end]); + return []; + } + + // fix negative zero + if (a === 0) a = 0; + if (b === 0) b = 0; + + let descending = a > b; + let startString = String(start); + let endString = String(end); + let stepString = String(step); + step = Math.max(Math.abs(step), 1); + + let padded = zeros(startString) || zeros(endString) || zeros(stepString); + let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0; + let toNumber = padded === false && stringify(start, end, options) === false; + let format = options.transform || transform(toNumber); + + if (options.toRegex && step === 1) { + return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options); + } + + let parts = { negatives: [], positives: [] }; + let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num)); + let range = []; + let index = 0; + + while (descending ? a >= b : a <= b) { + if (options.toRegex === true && step > 1) { + push(a); + } else { + range.push(pad(format(a, index), maxLen, toNumber)); + } + a = descending ? a - step : a + step; + index++; + } + + if (options.toRegex === true) { + return step > 1 + ? toSequence(parts, options) + : toRegex(range, null, { wrap: false, ...options }); + } + + return range; +}; + +const fillLetters = (start, end, step = 1, options = {}) => { + if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) { + return invalidRange(start, end, options); + } + + + let format = options.transform || (val => String.fromCharCode(val)); + let a = `${start}`.charCodeAt(0); + let b = `${end}`.charCodeAt(0); + + let descending = a > b; + let min = Math.min(a, b); + let max = Math.max(a, b); + + if (options.toRegex && step === 1) { + return toRange(min, max, false, options); + } + + let range = []; + let index = 0; + + while (descending ? a >= b : a <= b) { + range.push(format(a, index)); + a = descending ? a - step : a + step; + index++; + } + + if (options.toRegex === true) { + return toRegex(range, null, { wrap: false, options }); + } + + return range; +}; + +const fill = (start, end, step, options = {}) => { + if (end == null && isValidValue(start)) { + return [start]; + } + + if (!isValidValue(start) || !isValidValue(end)) { + return invalidRange(start, end, options); + } + + if (typeof step === 'function') { + return fill(start, end, 1, { transform: step }); + } + + if (isObject(step)) { + return fill(start, end, 0, step); + } + + let opts = { ...options }; + if (opts.capture === true) opts.wrap = true; + step = step || opts.step || 1; + + if (!isNumber(step)) { + if (step != null && !isObject(step)) return invalidStep(step, opts); + return fill(start, end, 1, step); + } + + if (isNumber(start) && isNumber(end)) { + return fillNumbers(start, end, step, opts); + } + + return fillLetters(start, end, Math.max(Math.abs(step), 1), opts); +}; + +module.exports = fill; + + +/***/ }), + +/***/ 9320: +/***/ ((module) => { + + + +/* eslint no-invalid-this: 1 */ + +var ERROR_MESSAGE = 'Function.prototype.bind called on incompatible '; +var slice = Array.prototype.slice; +var toStr = Object.prototype.toString; +var funcType = '[object Function]'; + +module.exports = function bind(that) { + var target = this; + if (typeof target !== 'function' || toStr.call(target) !== funcType) { + throw new TypeError(ERROR_MESSAGE + target); + } + var args = slice.call(arguments, 1); + + var bound; + var binder = function () { + if (this instanceof bound) { + var result = target.apply( + this, + args.concat(slice.call(arguments)) + ); + if (Object(result) === result) { + return result; + } + return this; + } else { + return target.apply( + that, + args.concat(slice.call(arguments)) + ); + } + }; + + var boundLength = Math.max(0, target.length - args.length); + var boundArgs = []; + for (var i = 0; i < boundLength; i++) { + boundArgs.push('$' + i); + } + + bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this,arguments); }')(binder); + + if (target.prototype) { + var Empty = function Empty() {}; + Empty.prototype = target.prototype; + bound.prototype = new Empty(); + Empty.prototype = null; + } + + return bound; +}; + + +/***/ }), + +/***/ 8334: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + + + +var implementation = __nccwpck_require__(9320); + +module.exports = Function.prototype.bind || implementation; + + +/***/ }), + +/***/ 4538: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + + + +var undefined; + +var $SyntaxError = SyntaxError; +var $Function = Function; +var $TypeError = TypeError; + +// eslint-disable-next-line consistent-return +var getEvalledConstructor = function (expressionSyntax) { + try { + return $Function('"use strict"; return (' + expressionSyntax + ').constructor;')(); + } catch (e) {} +}; + +var $gOPD = Object.getOwnPropertyDescriptor; +if ($gOPD) { + try { + $gOPD({}, ''); + } catch (e) { + $gOPD = null; // this is IE 8, which has a broken gOPD + } +} + +var throwTypeError = function () { + throw new $TypeError(); +}; +var ThrowTypeError = $gOPD + ? (function () { + try { + // eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties + arguments.callee; // IE 8 does not throw here + return throwTypeError; + } catch (calleeThrows) { + try { + // IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '') + return $gOPD(arguments, 'callee').get; + } catch (gOPDthrows) { + return throwTypeError; + } + } + }()) + : throwTypeError; + +var hasSymbols = __nccwpck_require__(587)(); +var hasProto = __nccwpck_require__(5894)(); + +var getProto = Object.getPrototypeOf || ( + hasProto + ? function (x) { return x.__proto__; } // eslint-disable-line no-proto + : null +); + +var needsEval = {}; + +var TypedArray = typeof Uint8Array === 'undefined' || !getProto ? undefined : getProto(Uint8Array); + +var INTRINSICS = { + '%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError, + '%Array%': Array, + '%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer, + '%ArrayIteratorPrototype%': hasSymbols && getProto ? getProto([][Symbol.iterator]()) : undefined, + '%AsyncFromSyncIteratorPrototype%': undefined, + '%AsyncFunction%': needsEval, + '%AsyncGenerator%': needsEval, + '%AsyncGeneratorFunction%': needsEval, + '%AsyncIteratorPrototype%': needsEval, + '%Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics, + '%BigInt%': typeof BigInt === 'undefined' ? undefined : BigInt, + '%BigInt64Array%': typeof BigInt64Array === 'undefined' ? undefined : BigInt64Array, + '%BigUint64Array%': typeof BigUint64Array === 'undefined' ? undefined : BigUint64Array, + '%Boolean%': Boolean, + '%DataView%': typeof DataView === 'undefined' ? undefined : DataView, + '%Date%': Date, + '%decodeURI%': decodeURI, + '%decodeURIComponent%': decodeURIComponent, + '%encodeURI%': encodeURI, + '%encodeURIComponent%': encodeURIComponent, + '%Error%': Error, + '%eval%': eval, // eslint-disable-line no-eval + '%EvalError%': EvalError, + '%Float32Array%': typeof Float32Array === 'undefined' ? undefined : Float32Array, + '%Float64Array%': typeof Float64Array === 'undefined' ? undefined : Float64Array, + '%FinalizationRegistry%': typeof FinalizationRegistry === 'undefined' ? undefined : FinalizationRegistry, + '%Function%': $Function, + '%GeneratorFunction%': needsEval, + '%Int8Array%': typeof Int8Array === 'undefined' ? undefined : Int8Array, + '%Int16Array%': typeof Int16Array === 'undefined' ? undefined : Int16Array, + '%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array, + '%isFinite%': isFinite, + '%isNaN%': isNaN, + '%IteratorPrototype%': hasSymbols && getProto ? getProto(getProto([][Symbol.iterator]())) : undefined, + '%JSON%': typeof JSON === 'object' ? JSON : undefined, + '%Map%': typeof Map === 'undefined' ? undefined : Map, + '%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols || !getProto ? undefined : getProto(new Map()[Symbol.iterator]()), + '%Math%': Math, + '%Number%': Number, + '%Object%': Object, + '%parseFloat%': parseFloat, + '%parseInt%': parseInt, + '%Promise%': typeof Promise === 'undefined' ? undefined : Promise, + '%Proxy%': typeof Proxy === 'undefined' ? undefined : Proxy, + '%RangeError%': RangeError, + '%ReferenceError%': ReferenceError, + '%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect, + '%RegExp%': RegExp, + '%Set%': typeof Set === 'undefined' ? undefined : Set, + '%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols || !getProto ? undefined : getProto(new Set()[Symbol.iterator]()), + '%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer, + '%String%': String, + '%StringIteratorPrototype%': hasSymbols && getProto ? getProto(''[Symbol.iterator]()) : undefined, + '%Symbol%': hasSymbols ? Symbol : undefined, + '%SyntaxError%': $SyntaxError, + '%ThrowTypeError%': ThrowTypeError, + '%TypedArray%': TypedArray, + '%TypeError%': $TypeError, + '%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined : Uint8Array, + '%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray, + '%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined : Uint16Array, + '%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined : Uint32Array, + '%URIError%': URIError, + '%WeakMap%': typeof WeakMap === 'undefined' ? undefined : WeakMap, + '%WeakRef%': typeof WeakRef === 'undefined' ? undefined : WeakRef, + '%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet +}; + +if (getProto) { + try { + null.error; // eslint-disable-line no-unused-expressions + } catch (e) { + // https://github.com/tc39/proposal-shadowrealm/pull/384#issuecomment-1364264229 + var errorProto = getProto(getProto(e)); + INTRINSICS['%Error.prototype%'] = errorProto; + } +} + +var doEval = function doEval(name) { + var value; + if (name === '%AsyncFunction%') { + value = getEvalledConstructor('async function () {}'); + } else if (name === '%GeneratorFunction%') { + value = getEvalledConstructor('function* () {}'); + } else if (name === '%AsyncGeneratorFunction%') { + value = getEvalledConstructor('async function* () {}'); + } else if (name === '%AsyncGenerator%') { + var fn = doEval('%AsyncGeneratorFunction%'); + if (fn) { + value = fn.prototype; + } + } else if (name === '%AsyncIteratorPrototype%') { + var gen = doEval('%AsyncGenerator%'); + if (gen && getProto) { + value = getProto(gen.prototype); + } + } + + INTRINSICS[name] = value; + + return value; +}; + +var LEGACY_ALIASES = { + '%ArrayBufferPrototype%': ['ArrayBuffer', 'prototype'], + '%ArrayPrototype%': ['Array', 'prototype'], + '%ArrayProto_entries%': ['Array', 'prototype', 'entries'], + '%ArrayProto_forEach%': ['Array', 'prototype', 'forEach'], + '%ArrayProto_keys%': ['Array', 'prototype', 'keys'], + '%ArrayProto_values%': ['Array', 'prototype', 'values'], + '%AsyncFunctionPrototype%': ['AsyncFunction', 'prototype'], + '%AsyncGenerator%': ['AsyncGeneratorFunction', 'prototype'], + '%AsyncGeneratorPrototype%': ['AsyncGeneratorFunction', 'prototype', 'prototype'], + '%BooleanPrototype%': ['Boolean', 'prototype'], + '%DataViewPrototype%': ['DataView', 'prototype'], + '%DatePrototype%': ['Date', 'prototype'], + '%ErrorPrototype%': ['Error', 'prototype'], + '%EvalErrorPrototype%': ['EvalError', 'prototype'], + '%Float32ArrayPrototype%': ['Float32Array', 'prototype'], + '%Float64ArrayPrototype%': ['Float64Array', 'prototype'], + '%FunctionPrototype%': ['Function', 'prototype'], + '%Generator%': ['GeneratorFunction', 'prototype'], + '%GeneratorPrototype%': ['GeneratorFunction', 'prototype', 'prototype'], + '%Int8ArrayPrototype%': ['Int8Array', 'prototype'], + '%Int16ArrayPrototype%': ['Int16Array', 'prototype'], + '%Int32ArrayPrototype%': ['Int32Array', 'prototype'], + '%JSONParse%': ['JSON', 'parse'], + '%JSONStringify%': ['JSON', 'stringify'], + '%MapPrototype%': ['Map', 'prototype'], + '%NumberPrototype%': ['Number', 'prototype'], + '%ObjectPrototype%': ['Object', 'prototype'], + '%ObjProto_toString%': ['Object', 'prototype', 'toString'], + '%ObjProto_valueOf%': ['Object', 'prototype', 'valueOf'], + '%PromisePrototype%': ['Promise', 'prototype'], + '%PromiseProto_then%': ['Promise', 'prototype', 'then'], + '%Promise_all%': ['Promise', 'all'], + '%Promise_reject%': ['Promise', 'reject'], + '%Promise_resolve%': ['Promise', 'resolve'], + '%RangeErrorPrototype%': ['RangeError', 'prototype'], + '%ReferenceErrorPrototype%': ['ReferenceError', 'prototype'], + '%RegExpPrototype%': ['RegExp', 'prototype'], + '%SetPrototype%': ['Set', 'prototype'], + '%SharedArrayBufferPrototype%': ['SharedArrayBuffer', 'prototype'], + '%StringPrototype%': ['String', 'prototype'], + '%SymbolPrototype%': ['Symbol', 'prototype'], + '%SyntaxErrorPrototype%': ['SyntaxError', 'prototype'], + '%TypedArrayPrototype%': ['TypedArray', 'prototype'], + '%TypeErrorPrototype%': ['TypeError', 'prototype'], + '%Uint8ArrayPrototype%': ['Uint8Array', 'prototype'], + '%Uint8ClampedArrayPrototype%': ['Uint8ClampedArray', 'prototype'], + '%Uint16ArrayPrototype%': ['Uint16Array', 'prototype'], + '%Uint32ArrayPrototype%': ['Uint32Array', 'prototype'], + '%URIErrorPrototype%': ['URIError', 'prototype'], + '%WeakMapPrototype%': ['WeakMap', 'prototype'], + '%WeakSetPrototype%': ['WeakSet', 'prototype'] +}; + +var bind = __nccwpck_require__(8334); +var hasOwn = __nccwpck_require__(6339); +var $concat = bind.call(Function.call, Array.prototype.concat); +var $spliceApply = bind.call(Function.apply, Array.prototype.splice); +var $replace = bind.call(Function.call, String.prototype.replace); +var $strSlice = bind.call(Function.call, String.prototype.slice); +var $exec = bind.call(Function.call, RegExp.prototype.exec); + +/* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */ +var rePropName = /[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g; +var reEscapeChar = /\\(\\)?/g; /** Used to match backslashes in property paths. */ +var stringToPath = function stringToPath(string) { + var first = $strSlice(string, 0, 1); + var last = $strSlice(string, -1); + if (first === '%' && last !== '%') { + throw new $SyntaxError('invalid intrinsic syntax, expected closing `%`'); + } else if (last === '%' && first !== '%') { + throw new $SyntaxError('invalid intrinsic syntax, expected opening `%`'); + } + var result = []; + $replace(string, rePropName, function (match, number, quote, subString) { + result[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : number || match; + }); + return result; +}; +/* end adaptation */ + +var getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) { + var intrinsicName = name; + var alias; + if (hasOwn(LEGACY_ALIASES, intrinsicName)) { + alias = LEGACY_ALIASES[intrinsicName]; + intrinsicName = '%' + alias[0] + '%'; + } + + if (hasOwn(INTRINSICS, intrinsicName)) { + var value = INTRINSICS[intrinsicName]; + if (value === needsEval) { + value = doEval(intrinsicName); + } + if (typeof value === 'undefined' && !allowMissing) { + throw new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!'); + } + + return { + alias: alias, + name: intrinsicName, + value: value + }; + } + + throw new $SyntaxError('intrinsic ' + name + ' does not exist!'); +}; + +module.exports = function GetIntrinsic(name, allowMissing) { + if (typeof name !== 'string' || name.length === 0) { + throw new $TypeError('intrinsic name must be a non-empty string'); + } + if (arguments.length > 1 && typeof allowMissing !== 'boolean') { + throw new $TypeError('"allowMissing" argument must be a boolean'); + } + + if ($exec(/^%?[^%]*%?$/, name) === null) { + throw new $SyntaxError('`%` may not be present anywhere but at the beginning and end of the intrinsic name'); + } + var parts = stringToPath(name); + var intrinsicBaseName = parts.length > 0 ? parts[0] : ''; + + var intrinsic = getBaseIntrinsic('%' + intrinsicBaseName + '%', allowMissing); + var intrinsicRealName = intrinsic.name; + var value = intrinsic.value; + var skipFurtherCaching = false; + + var alias = intrinsic.alias; + if (alias) { + intrinsicBaseName = alias[0]; + $spliceApply(parts, $concat([0, 1], alias)); + } + + for (var i = 1, isOwn = true; i < parts.length; i += 1) { + var part = parts[i]; + var first = $strSlice(part, 0, 1); + var last = $strSlice(part, -1); + if ( + ( + (first === '"' || first === "'" || first === '`') + || (last === '"' || last === "'" || last === '`') + ) + && first !== last + ) { + throw new $SyntaxError('property names with quotes must have matching quotes'); + } + if (part === 'constructor' || !isOwn) { + skipFurtherCaching = true; + } + + intrinsicBaseName += '.' + part; + intrinsicRealName = '%' + intrinsicBaseName + '%'; + + if (hasOwn(INTRINSICS, intrinsicRealName)) { + value = INTRINSICS[intrinsicRealName]; + } else if (value != null) { + if (!(part in value)) { + if (!allowMissing) { + throw new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.'); + } + return void undefined; + } + if ($gOPD && (i + 1) >= parts.length) { + var desc = $gOPD(value, part); + isOwn = !!desc; + + // By convention, when a data property is converted to an accessor + // property to emulate a data property that does not suffer from + // the override mistake, that accessor's getter is marked with + // an `originalValue` property. Here, when we detect this, we + // uphold the illusion by pretending to see that original data + // property, i.e., returning the value rather than the getter + // itself. + if (isOwn && 'get' in desc && !('originalValue' in desc.get)) { + value = desc.get; + } else { + value = value[part]; + } + } else { + isOwn = hasOwn(value, part); + value = value[part]; + } + + if (isOwn && !skipFurtherCaching) { + INTRINSICS[intrinsicRealName] = value; + } + } + } + return value; +}; + + +/***/ }), + +/***/ 5894: +/***/ ((module) => { + + + +var test = { + foo: {} +}; + +var $Object = Object; + +module.exports = function hasProto() { + return { __proto__: test }.foo === test.foo && !({ __proto__: null } instanceof $Object); +}; + + +/***/ }), + +/***/ 587: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + + + +var origSymbol = typeof Symbol !== 'undefined' && Symbol; +var hasSymbolSham = __nccwpck_require__(7747); + +module.exports = function hasNativeSymbols() { + if (typeof origSymbol !== 'function') { return false; } + if (typeof Symbol !== 'function') { return false; } + if (typeof origSymbol('foo') !== 'symbol') { return false; } + if (typeof Symbol('bar') !== 'symbol') { return false; } + + return hasSymbolSham(); +}; + + +/***/ }), + +/***/ 7747: +/***/ ((module) => { + + + +/* eslint complexity: [2, 18], max-statements: [2, 33] */ +module.exports = function hasSymbols() { + if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; } + if (typeof Symbol.iterator === 'symbol') { return true; } + + var obj = {}; + var sym = Symbol('test'); + var symObj = Object(sym); + if (typeof sym === 'string') { return false; } + + if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; } + if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; } + + // temp disabled per https://github.com/ljharb/object.assign/issues/17 + // if (sym instanceof Symbol) { return false; } + // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4 + // if (!(symObj instanceof Symbol)) { return false; } + + // if (typeof Symbol.prototype.toString !== 'function') { return false; } + // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; } + + var symVal = 42; + obj[sym] = symVal; + for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax, no-unreachable-loop + if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; } + + if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; } + + var syms = Object.getOwnPropertySymbols(obj); + if (syms.length !== 1 || syms[0] !== sym) { return false; } + + if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; } + + if (typeof Object.getOwnPropertyDescriptor === 'function') { + var descriptor = Object.getOwnPropertyDescriptor(obj, sym); + if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; } + } + + return true; +}; + + +/***/ }), + +/***/ 6339: +/***/ ((module) => { + + + +var hasOwnProperty = {}.hasOwnProperty; +var call = Function.prototype.call; + +module.exports = call.bind ? call.bind(hasOwnProperty) : function (O, P) { + return call.call(hasOwnProperty, O, P); +}; + + +/***/ }), + +/***/ 5680: +/***/ ((module) => { + +/*! + * is-number + * + * Copyright (c) 2014-present, Jon Schlinkert. + * Released under the MIT License. + */ + + + +module.exports = function(num) { + if (typeof num === 'number') { + return num - num === 0; + } + if (typeof num === 'string' && num.trim() !== '') { + return Number.isFinite ? Number.isFinite(+num) : isFinite(+num); + } + return false; +}; + + +/***/ }), + +/***/ 3287: +/***/ ((__unused_webpack_module, exports) => { + + + +Object.defineProperty(exports, "__esModule", ({ value: true })); + +/*! + * is-plain-object + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ + +function isObject(o) { + return Object.prototype.toString.call(o) === '[object Object]'; +} + +function isPlainObject(o) { + var ctor,prot; + + if (isObject(o) === false) return false; + + // If has modified constructor + ctor = o.constructor; + if (ctor === undefined) return true; + + // If has modified prototype + prot = ctor.prototype; + if (isObject(prot) === false) return false; + + // If constructor does not have an Object-specific method + if (prot.hasOwnProperty('isPrototypeOf') === false) { + return false; + } + + // Most likely a plain Object + return true; +} + +exports.isPlainObject = isPlainObject; + + +/***/ }), + +/***/ 7073: +/***/ ((module, exports) => { + +exports = module.exports = stringify +exports.getSerialize = serializer + +function stringify(obj, replacer, spaces, cycleReplacer) { + return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces) +} + +function serializer(replacer, cycleReplacer) { + var stack = [], keys = [] + + if (cycleReplacer == null) cycleReplacer = function(key, value) { + if (stack[0] === value) return "[Circular ~]" + return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]" + } + + return function(key, value) { + if (stack.length > 0) { + var thisPos = stack.indexOf(this) + ~thisPos ? stack.splice(thisPos + 1) : stack.push(this) + ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key) + if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value) + } + else stack.push(value) + + return replacer == null ? value : replacer.call(this, key, value) + } +} + + +/***/ }), + +/***/ 6228: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + + + +const util = __nccwpck_require__(3837); +const braces = __nccwpck_require__(610); +const picomatch = __nccwpck_require__(8569); +const utils = __nccwpck_require__(479); +const isEmptyString = val => val === '' || val === './'; + +/** + * Returns an array of strings that match one or more glob patterns. + * + * ```js + * const mm = require('micromatch'); + * // mm(list, patterns[, options]); + * + * console.log(mm(['a.js', 'a.txt'], ['*.js'])); + * //=> [ 'a.js' ] + * ``` + * @param {String|Array} `list` List of strings to match. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) + * @return {Array} Returns an array of matches + * @summary false + * @api public + */ + +const micromatch = (list, patterns, options) => { + patterns = [].concat(patterns); + list = [].concat(list); + + let omit = new Set(); + let keep = new Set(); + let items = new Set(); + let negatives = 0; + + let onResult = state => { + items.add(state.output); + if (options && options.onResult) { + options.onResult(state); + } + }; + + for (let i = 0; i < patterns.length; i++) { + let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true); + let negated = isMatch.state.negated || isMatch.state.negatedExtglob; + if (negated) negatives++; + + for (let item of list) { + let matched = isMatch(item, true); + + let match = negated ? !matched.isMatch : matched.isMatch; + if (!match) continue; + + if (negated) { + omit.add(matched.output); + } else { + omit.delete(matched.output); + keep.add(matched.output); + } + } + } + + let result = negatives === patterns.length ? [...items] : [...keep]; + let matches = result.filter(item => !omit.has(item)); + + if (options && matches.length === 0) { + if (options.failglob === true) { + throw new Error(`No matches found for "${patterns.join(', ')}"`); + } + + if (options.nonull === true || options.nullglob === true) { + return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns; + } + } + + return matches; +}; + +/** + * Backwards compatibility + */ + +micromatch.match = micromatch; + +/** + * Returns a matcher function from the given glob `pattern` and `options`. + * The returned function takes a string to match as its only argument and returns + * true if the string is a match. + * + * ```js + * const mm = require('micromatch'); + * // mm.matcher(pattern[, options]); + * + * const isMatch = mm.matcher('*.!(*a)'); + * console.log(isMatch('a.a')); //=> false + * console.log(isMatch('a.b')); //=> true + * ``` + * @param {String} `pattern` Glob pattern + * @param {Object} `options` + * @return {Function} Returns a matcher function. + * @api public + */ + +micromatch.matcher = (pattern, options) => picomatch(pattern, options); + +/** + * Returns true if **any** of the given glob `patterns` match the specified `string`. + * + * ```js + * const mm = require('micromatch'); + * // mm.isMatch(string, patterns[, options]); + * + * console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true + * console.log(mm.isMatch('a.a', 'b.*')); //=> false + * ``` + * @param {String} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `[options]` See available [options](#options). + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ + +micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); + +/** + * Backwards compatibility + */ + +micromatch.any = micromatch.isMatch; + +/** + * Returns a list of strings that _**do not match any**_ of the given `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.not(list, patterns[, options]); + * + * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a')); + * //=> ['b.b', 'c.c'] + * ``` + * @param {Array} `list` Array of strings to match. + * @param {String|Array} `patterns` One or more glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array} Returns an array of strings that **do not match** the given patterns. + * @api public + */ + +micromatch.not = (list, patterns, options = {}) => { + patterns = [].concat(patterns).map(String); + let result = new Set(); + let items = []; + + let onResult = state => { + if (options.onResult) options.onResult(state); + items.push(state.output); + }; + + let matches = new Set(micromatch(list, patterns, { ...options, onResult })); + + for (let item of items) { + if (!matches.has(item)) { + result.add(item); + } + } + return [...result]; +}; + +/** + * Returns true if the given `string` contains the given pattern. Similar + * to [.isMatch](#isMatch) but the pattern can match any part of the string. + * + * ```js + * var mm = require('micromatch'); + * // mm.contains(string, pattern[, options]); + * + * console.log(mm.contains('aa/bb/cc', '*b')); + * //=> true + * console.log(mm.contains('aa/bb/cc', '*d')); + * //=> false + * ``` + * @param {String} `str` The string to match. + * @param {String|Array} `patterns` Glob pattern to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any of the patterns matches any part of `str`. + * @api public + */ + +micromatch.contains = (str, pattern, options) => { + if (typeof str !== 'string') { + throw new TypeError(`Expected a string: "${util.inspect(str)}"`); + } + + if (Array.isArray(pattern)) { + return pattern.some(p => micromatch.contains(str, p, options)); + } + + if (typeof pattern === 'string') { + if (isEmptyString(str) || isEmptyString(pattern)) { + return false; + } + + if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) { + return true; + } + } + + return micromatch.isMatch(str, pattern, { ...options, contains: true }); +}; + +/** + * Filter the keys of the given object with the given `glob` pattern + * and `options`. Does not attempt to match nested keys. If you need this feature, + * use [glob-object][] instead. + * + * ```js + * const mm = require('micromatch'); + * // mm.matchKeys(object, patterns[, options]); + * + * const obj = { aa: 'a', ab: 'b', ac: 'c' }; + * console.log(mm.matchKeys(obj, '*b')); + * //=> { ab: 'b' } + * ``` + * @param {Object} `object` The object with keys to filter. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Object} Returns an object with only keys that match the given patterns. + * @api public + */ + +micromatch.matchKeys = (obj, patterns, options) => { + if (!utils.isObject(obj)) { + throw new TypeError('Expected the first argument to be an object'); + } + let keys = micromatch(Object.keys(obj), patterns, options); + let res = {}; + for (let key of keys) res[key] = obj[key]; + return res; +}; + +/** + * Returns true if some of the strings in the given `list` match any of the given glob `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.some(list, patterns[, options]); + * + * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // true + * console.log(mm.some(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any `patterns` matches any of the strings in `list` + * @api public + */ + +micromatch.some = (list, patterns, options) => { + let items = [].concat(list); + + for (let pattern of [].concat(patterns)) { + let isMatch = picomatch(String(pattern), options); + if (items.some(item => isMatch(item))) { + return true; + } + } + return false; +}; + +/** + * Returns true if every string in the given `list` matches + * any of the given glob `patterns`. + * + * ```js + * const mm = require('micromatch'); + * // mm.every(list, patterns[, options]); + * + * console.log(mm.every('foo.js', ['foo.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js'])); + * // true + * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js'])); + * // false + * console.log(mm.every(['foo.js'], ['*.js', '!foo.js'])); + * // false + * ``` + * @param {String|Array} `list` The string or array of strings to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if all `patterns` matches all of the strings in `list` + * @api public + */ + +micromatch.every = (list, patterns, options) => { + let items = [].concat(list); + + for (let pattern of [].concat(patterns)) { + let isMatch = picomatch(String(pattern), options); + if (!items.every(item => isMatch(item))) { + return false; + } + } + return true; +}; + +/** + * Returns true if **all** of the given `patterns` match + * the specified string. + * + * ```js + * const mm = require('micromatch'); + * // mm.all(string, patterns[, options]); + * + * console.log(mm.all('foo.js', ['foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', '!foo.js'])); + * // false + * + * console.log(mm.all('foo.js', ['*.js', 'foo.js'])); + * // true + * + * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js'])); + * // true + * ``` + * @param {String|Array} `str` The string to test. + * @param {String|Array} `patterns` One or more glob patterns to use for matching. + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ + +micromatch.all = (str, patterns, options) => { + if (typeof str !== 'string') { + throw new TypeError(`Expected a string: "${util.inspect(str)}"`); + } + + return [].concat(patterns).every(p => picomatch(p, options)(str)); +}; + +/** + * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match. + * + * ```js + * const mm = require('micromatch'); + * // mm.capture(pattern, string[, options]); + * + * console.log(mm.capture('test/*.js', 'test/foo.js')); + * //=> ['foo'] + * console.log(mm.capture('test/*.js', 'foo/bar.css')); + * //=> null + * ``` + * @param {String} `glob` Glob pattern to use for matching. + * @param {String} `input` String to match + * @param {Object} `options` See available [options](#options) for changing how matches are performed + * @return {Array|null} Returns an array of captures if the input matches the glob pattern, otherwise `null`. + * @api public + */ + +micromatch.capture = (glob, input, options) => { + let posix = utils.isWindows(options); + let regex = picomatch.makeRe(String(glob), { ...options, capture: true }); + let match = regex.exec(posix ? utils.toPosixSlashes(input) : input); + + if (match) { + return match.slice(1).map(v => v === void 0 ? '' : v); + } +}; + +/** + * Create a regular expression from the given glob `pattern`. + * + * ```js + * const mm = require('micromatch'); + * // mm.makeRe(pattern[, options]); + * + * console.log(mm.makeRe('*.js')); + * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/ + * ``` + * @param {String} `pattern` A glob pattern to convert to regex. + * @param {Object} `options` + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ + +micromatch.makeRe = (...args) => picomatch.makeRe(...args); + +/** + * Scan a glob pattern to separate the pattern into segments. Used + * by the [split](#split) method. + * + * ```js + * const mm = require('micromatch'); + * const state = mm.scan(pattern[, options]); + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} Returns an object with + * @api public + */ + +micromatch.scan = (...args) => picomatch.scan(...args); + +/** + * Parse a glob pattern to create the source string for a regular + * expression. + * + * ```js + * const mm = require('micromatch'); + * const state = mm.parse(pattern[, options]); + * ``` + * @param {String} `glob` + * @param {Object} `options` + * @return {Object} Returns an object with useful properties and output to be used as regex source string. + * @api public + */ + +micromatch.parse = (patterns, options) => { + let res = []; + for (let pattern of [].concat(patterns || [])) { + for (let str of braces(String(pattern), options)) { + res.push(picomatch.parse(str, options)); + } + } + return res; +}; + +/** + * Process the given brace `pattern`. + * + * ```js + * const { braces } = require('micromatch'); + * console.log(braces('foo/{a,b,c}/bar')); + * //=> [ 'foo/(a|b|c)/bar' ] + * + * console.log(braces('foo/{a,b,c}/bar', { expand: true })); + * //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ] + * ``` + * @param {String} `pattern` String with brace pattern to process. + * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options. + * @return {Array} + * @api public + */ + +micromatch.braces = (pattern, options) => { + if (typeof pattern !== 'string') throw new TypeError('Expected a string'); + if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) { + return [pattern]; + } + return braces(pattern, options); +}; + +/** + * Expand braces + */ + +micromatch.braceExpand = (pattern, options) => { + if (typeof pattern !== 'string') throw new TypeError('Expected a string'); + return micromatch.braces(pattern, { ...options, expand: true }); +}; + +/** + * Expose micromatch + */ + +module.exports = micromatch; + + +/***/ }), + +/***/ 7760: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +/*! node-domexception. MIT License. Jimmy Wärting */ + +if (!globalThis.DOMException) { + try { + const { MessageChannel } = __nccwpck_require__(1267), + port = new MessageChannel().port1, + ab = new ArrayBuffer() + port.postMessage(ab, [ab, ab]) + } catch (err) { + err.constructor.name === 'DOMException' && ( + globalThis.DOMException = err.constructor + ) + } +} + +module.exports = globalThis.DOMException + + +/***/ }), + +/***/ 504: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +var hasMap = typeof Map === 'function' && Map.prototype; +var mapSizeDescriptor = Object.getOwnPropertyDescriptor && hasMap ? Object.getOwnPropertyDescriptor(Map.prototype, 'size') : null; +var mapSize = hasMap && mapSizeDescriptor && typeof mapSizeDescriptor.get === 'function' ? mapSizeDescriptor.get : null; +var mapForEach = hasMap && Map.prototype.forEach; +var hasSet = typeof Set === 'function' && Set.prototype; +var setSizeDescriptor = Object.getOwnPropertyDescriptor && hasSet ? Object.getOwnPropertyDescriptor(Set.prototype, 'size') : null; +var setSize = hasSet && setSizeDescriptor && typeof setSizeDescriptor.get === 'function' ? setSizeDescriptor.get : null; +var setForEach = hasSet && Set.prototype.forEach; +var hasWeakMap = typeof WeakMap === 'function' && WeakMap.prototype; +var weakMapHas = hasWeakMap ? WeakMap.prototype.has : null; +var hasWeakSet = typeof WeakSet === 'function' && WeakSet.prototype; +var weakSetHas = hasWeakSet ? WeakSet.prototype.has : null; +var hasWeakRef = typeof WeakRef === 'function' && WeakRef.prototype; +var weakRefDeref = hasWeakRef ? WeakRef.prototype.deref : null; +var booleanValueOf = Boolean.prototype.valueOf; +var objectToString = Object.prototype.toString; +var functionToString = Function.prototype.toString; +var $match = String.prototype.match; +var $slice = String.prototype.slice; +var $replace = String.prototype.replace; +var $toUpperCase = String.prototype.toUpperCase; +var $toLowerCase = String.prototype.toLowerCase; +var $test = RegExp.prototype.test; +var $concat = Array.prototype.concat; +var $join = Array.prototype.join; +var $arrSlice = Array.prototype.slice; +var $floor = Math.floor; +var bigIntValueOf = typeof BigInt === 'function' ? BigInt.prototype.valueOf : null; +var gOPS = Object.getOwnPropertySymbols; +var symToString = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? Symbol.prototype.toString : null; +var hasShammedSymbols = typeof Symbol === 'function' && typeof Symbol.iterator === 'object'; +// ie, `has-tostringtag/shams +var toStringTag = typeof Symbol === 'function' && Symbol.toStringTag && (typeof Symbol.toStringTag === hasShammedSymbols ? 'object' : 'symbol') + ? Symbol.toStringTag + : null; +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var gPO = (typeof Reflect === 'function' ? Reflect.getPrototypeOf : Object.getPrototypeOf) || ( + [].__proto__ === Array.prototype // eslint-disable-line no-proto + ? function (O) { + return O.__proto__; // eslint-disable-line no-proto + } + : null +); + +function addNumericSeparator(num, str) { + if ( + num === Infinity + || num === -Infinity + || num !== num + || (num && num > -1000 && num < 1000) + || $test.call(/e/, str) + ) { + return str; + } + var sepRegex = /[0-9](?=(?:[0-9]{3})+(?![0-9]))/g; + if (typeof num === 'number') { + var int = num < 0 ? -$floor(-num) : $floor(num); // trunc(num) + if (int !== num) { + var intStr = String(int); + var dec = $slice.call(str, intStr.length + 1); + return $replace.call(intStr, sepRegex, '$&_') + '.' + $replace.call($replace.call(dec, /([0-9]{3})/g, '$&_'), /_$/, ''); + } + } + return $replace.call(str, sepRegex, '$&_'); +} + +var utilInspect = __nccwpck_require__(7265); +var inspectCustom = utilInspect.custom; +var inspectSymbol = isSymbol(inspectCustom) ? inspectCustom : null; + +module.exports = function inspect_(obj, options, depth, seen) { + var opts = options || {}; + + if (has(opts, 'quoteStyle') && (opts.quoteStyle !== 'single' && opts.quoteStyle !== 'double')) { + throw new TypeError('option "quoteStyle" must be "single" or "double"'); + } + if ( + has(opts, 'maxStringLength') && (typeof opts.maxStringLength === 'number' + ? opts.maxStringLength < 0 && opts.maxStringLength !== Infinity + : opts.maxStringLength !== null + ) + ) { + throw new TypeError('option "maxStringLength", if provided, must be a positive integer, Infinity, or `null`'); + } + var customInspect = has(opts, 'customInspect') ? opts.customInspect : true; + if (typeof customInspect !== 'boolean' && customInspect !== 'symbol') { + throw new TypeError('option "customInspect", if provided, must be `true`, `false`, or `\'symbol\'`'); + } + + if ( + has(opts, 'indent') + && opts.indent !== null + && opts.indent !== '\t' + && !(parseInt(opts.indent, 10) === opts.indent && opts.indent > 0) + ) { + throw new TypeError('option "indent" must be "\\t", an integer > 0, or `null`'); + } + if (has(opts, 'numericSeparator') && typeof opts.numericSeparator !== 'boolean') { + throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`'); + } + var numericSeparator = opts.numericSeparator; + + if (typeof obj === 'undefined') { + return 'undefined'; + } + if (obj === null) { + return 'null'; + } + if (typeof obj === 'boolean') { + return obj ? 'true' : 'false'; + } + + if (typeof obj === 'string') { + return inspectString(obj, opts); + } + if (typeof obj === 'number') { + if (obj === 0) { + return Infinity / obj > 0 ? '0' : '-0'; + } + var str = String(obj); + return numericSeparator ? addNumericSeparator(obj, str) : str; + } + if (typeof obj === 'bigint') { + var bigIntStr = String(obj) + 'n'; + return numericSeparator ? addNumericSeparator(obj, bigIntStr) : bigIntStr; + } + + var maxDepth = typeof opts.depth === 'undefined' ? 5 : opts.depth; + if (typeof depth === 'undefined') { depth = 0; } + if (depth >= maxDepth && maxDepth > 0 && typeof obj === 'object') { + return isArray(obj) ? '[Array]' : '[Object]'; + } + + var indent = getIndent(opts, depth); + + if (typeof seen === 'undefined') { + seen = []; + } else if (indexOf(seen, obj) >= 0) { + return '[Circular]'; + } + + function inspect(value, from, noIndent) { + if (from) { + seen = $arrSlice.call(seen); + seen.push(from); + } + if (noIndent) { + var newOpts = { + depth: opts.depth + }; + if (has(opts, 'quoteStyle')) { + newOpts.quoteStyle = opts.quoteStyle; + } + return inspect_(value, newOpts, depth + 1, seen); + } + return inspect_(value, opts, depth + 1, seen); + } + + if (typeof obj === 'function' && !isRegExp(obj)) { // in older engines, regexes are callable + var name = nameOf(obj); + var keys = arrObjKeys(obj, inspect); + return '[Function' + (name ? ': ' + name : ' (anonymous)') + ']' + (keys.length > 0 ? ' { ' + $join.call(keys, ', ') + ' }' : ''); + } + if (isSymbol(obj)) { + var symString = hasShammedSymbols ? $replace.call(String(obj), /^(Symbol\(.*\))_[^)]*$/, '$1') : symToString.call(obj); + return typeof obj === 'object' && !hasShammedSymbols ? markBoxed(symString) : symString; + } + if (isElement(obj)) { + var s = '<' + $toLowerCase.call(String(obj.nodeName)); + var attrs = obj.attributes || []; + for (var i = 0; i < attrs.length; i++) { + s += ' ' + attrs[i].name + '=' + wrapQuotes(quote(attrs[i].value), 'double', opts); + } + s += '>'; + if (obj.childNodes && obj.childNodes.length) { s += '...'; } + s += ''; + return s; + } + if (isArray(obj)) { + if (obj.length === 0) { return '[]'; } + var xs = arrObjKeys(obj, inspect); + if (indent && !singleLineValues(xs)) { + return '[' + indentedJoin(xs, indent) + ']'; + } + return '[ ' + $join.call(xs, ', ') + ' ]'; + } + if (isError(obj)) { + var parts = arrObjKeys(obj, inspect); + if (!('cause' in Error.prototype) && 'cause' in obj && !isEnumerable.call(obj, 'cause')) { + return '{ [' + String(obj) + '] ' + $join.call($concat.call('[cause]: ' + inspect(obj.cause), parts), ', ') + ' }'; + } + if (parts.length === 0) { return '[' + String(obj) + ']'; } + return '{ [' + String(obj) + '] ' + $join.call(parts, ', ') + ' }'; + } + if (typeof obj === 'object' && customInspect) { + if (inspectSymbol && typeof obj[inspectSymbol] === 'function' && utilInspect) { + return utilInspect(obj, { depth: maxDepth - depth }); + } else if (customInspect !== 'symbol' && typeof obj.inspect === 'function') { + return obj.inspect(); + } + } + if (isMap(obj)) { + var mapParts = []; + if (mapForEach) { + mapForEach.call(obj, function (value, key) { + mapParts.push(inspect(key, obj, true) + ' => ' + inspect(value, obj)); + }); + } + return collectionOf('Map', mapSize.call(obj), mapParts, indent); + } + if (isSet(obj)) { + var setParts = []; + if (setForEach) { + setForEach.call(obj, function (value) { + setParts.push(inspect(value, obj)); + }); + } + return collectionOf('Set', setSize.call(obj), setParts, indent); + } + if (isWeakMap(obj)) { + return weakCollectionOf('WeakMap'); + } + if (isWeakSet(obj)) { + return weakCollectionOf('WeakSet'); + } + if (isWeakRef(obj)) { + return weakCollectionOf('WeakRef'); + } + if (isNumber(obj)) { + return markBoxed(inspect(Number(obj))); + } + if (isBigInt(obj)) { + return markBoxed(inspect(bigIntValueOf.call(obj))); + } + if (isBoolean(obj)) { + return markBoxed(booleanValueOf.call(obj)); + } + if (isString(obj)) { + return markBoxed(inspect(String(obj))); + } + if (!isDate(obj) && !isRegExp(obj)) { + var ys = arrObjKeys(obj, inspect); + var isPlainObject = gPO ? gPO(obj) === Object.prototype : obj instanceof Object || obj.constructor === Object; + var protoTag = obj instanceof Object ? '' : 'null prototype'; + var stringTag = !isPlainObject && toStringTag && Object(obj) === obj && toStringTag in obj ? $slice.call(toStr(obj), 8, -1) : protoTag ? 'Object' : ''; + var constructorTag = isPlainObject || typeof obj.constructor !== 'function' ? '' : obj.constructor.name ? obj.constructor.name + ' ' : ''; + var tag = constructorTag + (stringTag || protoTag ? '[' + $join.call($concat.call([], stringTag || [], protoTag || []), ': ') + '] ' : ''); + if (ys.length === 0) { return tag + '{}'; } + if (indent) { + return tag + '{' + indentedJoin(ys, indent) + '}'; + } + return tag + '{ ' + $join.call(ys, ', ') + ' }'; + } + return String(obj); +}; + +function wrapQuotes(s, defaultStyle, opts) { + var quoteChar = (opts.quoteStyle || defaultStyle) === 'double' ? '"' : "'"; + return quoteChar + s + quoteChar; +} + +function quote(s) { + return $replace.call(String(s), /"/g, '"'); +} + +function isArray(obj) { return toStr(obj) === '[object Array]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } +function isDate(obj) { return toStr(obj) === '[object Date]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } +function isRegExp(obj) { return toStr(obj) === '[object RegExp]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } +function isError(obj) { return toStr(obj) === '[object Error]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } +function isString(obj) { return toStr(obj) === '[object String]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } +function isNumber(obj) { return toStr(obj) === '[object Number]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } +function isBoolean(obj) { return toStr(obj) === '[object Boolean]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } + +// Symbol and BigInt do have Symbol.toStringTag by spec, so that can't be used to eliminate false positives +function isSymbol(obj) { + if (hasShammedSymbols) { + return obj && typeof obj === 'object' && obj instanceof Symbol; + } + if (typeof obj === 'symbol') { + return true; + } + if (!obj || typeof obj !== 'object' || !symToString) { + return false; + } + try { + symToString.call(obj); + return true; + } catch (e) {} + return false; +} + +function isBigInt(obj) { + if (!obj || typeof obj !== 'object' || !bigIntValueOf) { + return false; + } + try { + bigIntValueOf.call(obj); + return true; + } catch (e) {} + return false; +} + +var hasOwn = Object.prototype.hasOwnProperty || function (key) { return key in this; }; +function has(obj, key) { + return hasOwn.call(obj, key); +} + +function toStr(obj) { + return objectToString.call(obj); +} + +function nameOf(f) { + if (f.name) { return f.name; } + var m = $match.call(functionToString.call(f), /^function\s*([\w$]+)/); + if (m) { return m[1]; } + return null; +} + +function indexOf(xs, x) { + if (xs.indexOf) { return xs.indexOf(x); } + for (var i = 0, l = xs.length; i < l; i++) { + if (xs[i] === x) { return i; } + } + return -1; +} + +function isMap(x) { + if (!mapSize || !x || typeof x !== 'object') { + return false; + } + try { + mapSize.call(x); + try { + setSize.call(x); + } catch (s) { + return true; + } + return x instanceof Map; // core-js workaround, pre-v2.5.0 + } catch (e) {} + return false; +} + +function isWeakMap(x) { + if (!weakMapHas || !x || typeof x !== 'object') { + return false; + } + try { + weakMapHas.call(x, weakMapHas); + try { + weakSetHas.call(x, weakSetHas); + } catch (s) { + return true; + } + return x instanceof WeakMap; // core-js workaround, pre-v2.5.0 + } catch (e) {} + return false; +} + +function isWeakRef(x) { + if (!weakRefDeref || !x || typeof x !== 'object') { + return false; + } + try { + weakRefDeref.call(x); + return true; + } catch (e) {} + return false; +} + +function isSet(x) { + if (!setSize || !x || typeof x !== 'object') { + return false; + } + try { + setSize.call(x); + try { + mapSize.call(x); + } catch (m) { + return true; + } + return x instanceof Set; // core-js workaround, pre-v2.5.0 + } catch (e) {} + return false; +} + +function isWeakSet(x) { + if (!weakSetHas || !x || typeof x !== 'object') { + return false; + } + try { + weakSetHas.call(x, weakSetHas); + try { + weakMapHas.call(x, weakMapHas); + } catch (s) { + return true; + } + return x instanceof WeakSet; // core-js workaround, pre-v2.5.0 + } catch (e) {} + return false; +} + +function isElement(x) { + if (!x || typeof x !== 'object') { return false; } + if (typeof HTMLElement !== 'undefined' && x instanceof HTMLElement) { + return true; + } + return typeof x.nodeName === 'string' && typeof x.getAttribute === 'function'; +} + +function inspectString(str, opts) { + if (str.length > opts.maxStringLength) { + var remaining = str.length - opts.maxStringLength; + var trailer = '... ' + remaining + ' more character' + (remaining > 1 ? 's' : ''); + return inspectString($slice.call(str, 0, opts.maxStringLength), opts) + trailer; + } + // eslint-disable-next-line no-control-regex + var s = $replace.call($replace.call(str, /(['\\])/g, '\\$1'), /[\x00-\x1f]/g, lowbyte); + return wrapQuotes(s, 'single', opts); +} + +function lowbyte(c) { + var n = c.charCodeAt(0); + var x = { + 8: 'b', + 9: 't', + 10: 'n', + 12: 'f', + 13: 'r' + }[n]; + if (x) { return '\\' + x; } + return '\\x' + (n < 0x10 ? '0' : '') + $toUpperCase.call(n.toString(16)); +} + +function markBoxed(str) { + return 'Object(' + str + ')'; +} + +function weakCollectionOf(type) { + return type + ' { ? }'; +} + +function collectionOf(type, size, entries, indent) { + var joinedEntries = indent ? indentedJoin(entries, indent) : $join.call(entries, ', '); + return type + ' (' + size + ') {' + joinedEntries + '}'; +} + +function singleLineValues(xs) { + for (var i = 0; i < xs.length; i++) { + if (indexOf(xs[i], '\n') >= 0) { + return false; + } + } + return true; +} + +function getIndent(opts, depth) { + var baseIndent; + if (opts.indent === '\t') { + baseIndent = '\t'; + } else if (typeof opts.indent === 'number' && opts.indent > 0) { + baseIndent = $join.call(Array(opts.indent + 1), ' '); + } else { + return null; + } + return { + base: baseIndent, + prev: $join.call(Array(depth + 1), baseIndent) + }; +} + +function indentedJoin(xs, indent) { + if (xs.length === 0) { return ''; } + var lineJoiner = '\n' + indent.prev + indent.base; + return lineJoiner + $join.call(xs, ',' + lineJoiner) + '\n' + indent.prev; +} + +function arrObjKeys(obj, inspect) { + var isArr = isArray(obj); + var xs = []; + if (isArr) { + xs.length = obj.length; + for (var i = 0; i < obj.length; i++) { + xs[i] = has(obj, i) ? inspect(obj[i], obj) : ''; + } + } + var syms = typeof gOPS === 'function' ? gOPS(obj) : []; + var symMap; + if (hasShammedSymbols) { + symMap = {}; + for (var k = 0; k < syms.length; k++) { + symMap['$' + syms[k]] = syms[k]; + } + } + + for (var key in obj) { // eslint-disable-line no-restricted-syntax + if (!has(obj, key)) { continue; } // eslint-disable-line no-restricted-syntax, no-continue + if (isArr && String(Number(key)) === key && key < obj.length) { continue; } // eslint-disable-line no-restricted-syntax, no-continue + if (hasShammedSymbols && symMap['$' + key] instanceof Symbol) { + // this is to prevent shammed Symbols, which are stored as strings, from being included in the string key section + continue; // eslint-disable-line no-restricted-syntax, no-continue + } else if ($test.call(/[^\w$]/, key)) { + xs.push(inspect(key, obj) + ': ' + inspect(obj[key], obj)); + } else { + xs.push(key + ': ' + inspect(obj[key], obj)); + } + } + if (typeof gOPS === 'function') { + for (var j = 0; j < syms.length; j++) { + if (isEnumerable.call(obj, syms[j])) { + xs.push('[' + inspect(syms[j]) + ']: ' + inspect(obj[syms[j]], obj)); + } + } + } + return xs; +} + + +/***/ }), + +/***/ 7265: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +module.exports = __nccwpck_require__(3837).inspect; + + +/***/ }), + +/***/ 1223: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +var wrappy = __nccwpck_require__(2940) +module.exports = wrappy(once) +module.exports.strict = wrappy(onceStrict) + +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) + + Object.defineProperty(Function.prototype, 'onceStrict', { + value: function () { + return onceStrict(this) + }, + configurable: true + }) +}) + +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) + } + f.called = false + return f +} + +function onceStrict (fn) { + var f = function () { + if (f.called) + throw new Error(f.onceError) + f.called = true + return f.value = fn.apply(this, arguments) + } + var name = fn.name || 'Function wrapped with `once`' + f.onceError = name + " shouldn't be called more than once" + f.called = false + return f +} + + +/***/ }), + +/***/ 8569: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + + + +module.exports = __nccwpck_require__(3322); + + +/***/ }), + +/***/ 6099: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + + + +const path = __nccwpck_require__(1017); +const WIN_SLASH = '\\\\/'; +const WIN_NO_SLASH = `[^${WIN_SLASH}]`; + +/** + * Posix glob regex + */ + +const DOT_LITERAL = '\\.'; +const PLUS_LITERAL = '\\+'; +const QMARK_LITERAL = '\\?'; +const SLASH_LITERAL = '\\/'; +const ONE_CHAR = '(?=.)'; +const QMARK = '[^/]'; +const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`; +const START_ANCHOR = `(?:^|${SLASH_LITERAL})`; +const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`; +const NO_DOT = `(?!${DOT_LITERAL})`; +const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`; +const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`; +const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`; +const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`; +const STAR = `${QMARK}*?`; + +const POSIX_CHARS = { + DOT_LITERAL, + PLUS_LITERAL, + QMARK_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + QMARK, + END_ANCHOR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK_NO_DOT, + STAR, + START_ANCHOR +}; + +/** + * Windows glob regex + */ + +const WINDOWS_CHARS = { + ...POSIX_CHARS, + + SLASH_LITERAL: `[${WIN_SLASH}]`, + QMARK: WIN_NO_SLASH, + STAR: `${WIN_NO_SLASH}*?`, + DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`, + NO_DOT: `(?!${DOT_LITERAL})`, + NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`, + NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`, + QMARK_NO_DOT: `[^.${WIN_SLASH}]`, + START_ANCHOR: `(?:^|[${WIN_SLASH}])`, + END_ANCHOR: `(?:[${WIN_SLASH}]|$)` +}; + +/** + * POSIX Bracket Regex + */ + +const POSIX_REGEX_SOURCE = { + alnum: 'a-zA-Z0-9', + alpha: 'a-zA-Z', + ascii: '\\x00-\\x7F', + blank: ' \\t', + cntrl: '\\x00-\\x1F\\x7F', + digit: '0-9', + graph: '\\x21-\\x7E', + lower: 'a-z', + print: '\\x20-\\x7E ', + punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~', + space: ' \\t\\r\\n\\v\\f', + upper: 'A-Z', + word: 'A-Za-z0-9_', + xdigit: 'A-Fa-f0-9' +}; + +module.exports = { + MAX_LENGTH: 1024 * 64, + POSIX_REGEX_SOURCE, + + // regular expressions + REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g, + REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/, + REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/, + REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g, + REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g, + REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g, + + // Replace globs with equivalent patterns to reduce parsing time. + REPLACEMENTS: { + '***': '*', + '**/**': '**', + '**/**/**': '**' + }, + + // Digits + CHAR_0: 48, /* 0 */ + CHAR_9: 57, /* 9 */ + + // Alphabet chars. + CHAR_UPPERCASE_A: 65, /* A */ + CHAR_LOWERCASE_A: 97, /* a */ + CHAR_UPPERCASE_Z: 90, /* Z */ + CHAR_LOWERCASE_Z: 122, /* z */ + + CHAR_LEFT_PARENTHESES: 40, /* ( */ + CHAR_RIGHT_PARENTHESES: 41, /* ) */ + + CHAR_ASTERISK: 42, /* * */ + + // Non-alphabetic chars. + CHAR_AMPERSAND: 38, /* & */ + CHAR_AT: 64, /* @ */ + CHAR_BACKWARD_SLASH: 92, /* \ */ + CHAR_CARRIAGE_RETURN: 13, /* \r */ + CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */ + CHAR_COLON: 58, /* : */ + CHAR_COMMA: 44, /* , */ + CHAR_DOT: 46, /* . */ + CHAR_DOUBLE_QUOTE: 34, /* " */ + CHAR_EQUAL: 61, /* = */ + CHAR_EXCLAMATION_MARK: 33, /* ! */ + CHAR_FORM_FEED: 12, /* \f */ + CHAR_FORWARD_SLASH: 47, /* / */ + CHAR_GRAVE_ACCENT: 96, /* ` */ + CHAR_HASH: 35, /* # */ + CHAR_HYPHEN_MINUS: 45, /* - */ + CHAR_LEFT_ANGLE_BRACKET: 60, /* < */ + CHAR_LEFT_CURLY_BRACE: 123, /* { */ + CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */ + CHAR_LINE_FEED: 10, /* \n */ + CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */ + CHAR_PERCENT: 37, /* % */ + CHAR_PLUS: 43, /* + */ + CHAR_QUESTION_MARK: 63, /* ? */ + CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */ + CHAR_RIGHT_CURLY_BRACE: 125, /* } */ + CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */ + CHAR_SEMICOLON: 59, /* ; */ + CHAR_SINGLE_QUOTE: 39, /* ' */ + CHAR_SPACE: 32, /* */ + CHAR_TAB: 9, /* \t */ + CHAR_UNDERSCORE: 95, /* _ */ + CHAR_VERTICAL_LINE: 124, /* | */ + CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */ + + SEP: path.sep, + + /** + * Create EXTGLOB_CHARS + */ + + extglobChars(chars) { + return { + '!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` }, + '?': { type: 'qmark', open: '(?:', close: ')?' }, + '+': { type: 'plus', open: '(?:', close: ')+' }, + '*': { type: 'star', open: '(?:', close: ')*' }, + '@': { type: 'at', open: '(?:', close: ')' } + }; + }, + + /** + * Create GLOB_CHARS + */ + + globChars(win32) { + return win32 === true ? WINDOWS_CHARS : POSIX_CHARS; + } +}; + + +/***/ }), + +/***/ 2139: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + + + +const constants = __nccwpck_require__(6099); +const utils = __nccwpck_require__(479); + +/** + * Constants + */ + +const { + MAX_LENGTH, + POSIX_REGEX_SOURCE, + REGEX_NON_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_BACKREF, + REPLACEMENTS +} = constants; + +/** + * Helpers + */ + +const expandRange = (args, options) => { + if (typeof options.expandRange === 'function') { + return options.expandRange(...args, options); + } + + args.sort(); + const value = `[${args.join('-')}]`; + + try { + /* eslint-disable-next-line no-new */ + new RegExp(value); + } catch (ex) { + return args.map(v => utils.escapeRegex(v)).join('..'); + } + + return value; +}; + +/** + * Create the message for a syntax error + */ + +const syntaxError = (type, char) => { + return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`; +}; + +/** + * Parse the given input string. + * @param {String} input + * @param {Object} options + * @return {Object} + */ + +const parse = (input, options) => { + if (typeof input !== 'string') { + throw new TypeError('Expected a string'); + } + + input = REPLACEMENTS[input] || input; + + const opts = { ...options }; + const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + + let len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + + const bos = { type: 'bos', value: '', output: opts.prepend || '' }; + const tokens = [bos]; + + const capture = opts.capture ? '' : '?:'; + const win32 = utils.isWindows(options); + + // create constants based on platform, for windows or posix + const PLATFORM_CHARS = constants.globChars(win32); + const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS); + + const { + DOT_LITERAL, + PLUS_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOT_SLASH, + NO_DOTS_SLASH, + QMARK, + QMARK_NO_DOT, + STAR, + START_ANCHOR + } = PLATFORM_CHARS; + + const globstar = opts => { + return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + + const nodot = opts.dot ? '' : NO_DOT; + const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT; + let star = opts.bash === true ? globstar(opts) : STAR; + + if (opts.capture) { + star = `(${star})`; + } + + // minimatch options support + if (typeof opts.noext === 'boolean') { + opts.noextglob = opts.noext; + } + + const state = { + input, + index: -1, + start: 0, + dot: opts.dot === true, + consumed: '', + output: '', + prefix: '', + backtrack: false, + negated: false, + brackets: 0, + braces: 0, + parens: 0, + quotes: 0, + globstar: false, + tokens + }; + + input = utils.removePrefix(input, state); + len = input.length; + + const extglobs = []; + const braces = []; + const stack = []; + let prev = bos; + let value; + + /** + * Tokenizing helpers + */ + + const eos = () => state.index === len - 1; + const peek = state.peek = (n = 1) => input[state.index + n]; + const advance = state.advance = () => input[++state.index] || ''; + const remaining = () => input.slice(state.index + 1); + const consume = (value = '', num = 0) => { + state.consumed += value; + state.index += num; + }; + + const append = token => { + state.output += token.output != null ? token.output : token.value; + consume(token.value); + }; + + const negate = () => { + let count = 1; + + while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) { + advance(); + state.start++; + count++; + } + + if (count % 2 === 0) { + return false; + } + + state.negated = true; + state.start++; + return true; + }; + + const increment = type => { + state[type]++; + stack.push(type); + }; + + const decrement = type => { + state[type]--; + stack.pop(); + }; + + /** + * Push tokens onto the tokens array. This helper speeds up + * tokenizing by 1) helping us avoid backtracking as much as possible, + * and 2) helping us avoid creating extra tokens when consecutive + * characters are plain text. This improves performance and simplifies + * lookbehinds. + */ + + const push = tok => { + if (prev.type === 'globstar') { + const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace'); + const isExtglob = tok.extglob === true || (extglobs.length && (tok.type === 'pipe' || tok.type === 'paren')); + + if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) { + state.output = state.output.slice(0, -prev.output.length); + prev.type = 'star'; + prev.value = '*'; + prev.output = star; + state.output += prev.output; + } + } + + if (extglobs.length && tok.type !== 'paren') { + extglobs[extglobs.length - 1].inner += tok.value; + } + + if (tok.value || tok.output) append(tok); + if (prev && prev.type === 'text' && tok.type === 'text') { + prev.value += tok.value; + prev.output = (prev.output || '') + tok.value; + return; + } + + tok.prev = prev; + tokens.push(tok); + prev = tok; + }; + + const extglobOpen = (type, value) => { + const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' }; + + token.prev = prev; + token.parens = state.parens; + token.output = state.output; + const output = (opts.capture ? '(' : '') + token.open; + + increment('parens'); + push({ type, value, output: state.output ? '' : ONE_CHAR }); + push({ type: 'paren', extglob: true, value: advance(), output }); + extglobs.push(token); + }; + + const extglobClose = token => { + let output = token.close + (opts.capture ? ')' : ''); + let rest; + + if (token.type === 'negate') { + let extglobStar = star; + + if (token.inner && token.inner.length > 1 && token.inner.includes('/')) { + extglobStar = globstar(opts); + } + + if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) { + output = token.close = `)$))${extglobStar}`; + } + + if (token.inner.includes('*') && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) { + // Any non-magical string (`.ts`) or even nested expression (`.{ts,tsx}`) can follow after the closing parenthesis. + // In this case, we need to parse the string and use it in the output of the original pattern. + // Suitable patterns: `/!(*.d).ts`, `/!(*.d).{ts,tsx}`, `**/!(*-dbg).@(js)`. + // + // Disabling the `fastpaths` option due to a problem with parsing strings as `.ts` in the pattern like `**/!(*.d).ts`. + const expression = parse(rest, { ...options, fastpaths: false }).output; + + output = token.close = `)${expression})${extglobStar})`; + } + + if (token.prev.type === 'bos') { + state.negatedExtglob = true; + } + } + + push({ type: 'paren', extglob: true, value, output }); + decrement('parens'); + }; + + /** + * Fast paths + */ + + if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) { + let backslashes = false; + + let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => { + if (first === '\\') { + backslashes = true; + return m; + } + + if (first === '?') { + if (esc) { + return esc + first + (rest ? QMARK.repeat(rest.length) : ''); + } + if (index === 0) { + return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : ''); + } + return QMARK.repeat(chars.length); + } + + if (first === '.') { + return DOT_LITERAL.repeat(chars.length); + } + + if (first === '*') { + if (esc) { + return esc + first + (rest ? star : ''); + } + return star; + } + return esc ? m : `\\${m}`; + }); + + if (backslashes === true) { + if (opts.unescape === true) { + output = output.replace(/\\/g, ''); + } else { + output = output.replace(/\\+/g, m => { + return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : ''); + }); + } + } + + if (output === input && opts.contains === true) { + state.output = input; + return state; + } + + state.output = utils.wrapOutput(output, state, options); + return state; + } + + /** + * Tokenize input until we reach end-of-string + */ + + while (!eos()) { + value = advance(); + + if (value === '\u0000') { + continue; + } + + /** + * Escaped characters + */ + + if (value === '\\') { + const next = peek(); + + if (next === '/' && opts.bash !== true) { + continue; + } + + if (next === '.' || next === ';') { + continue; + } + + if (!next) { + value += '\\'; + push({ type: 'text', value }); + continue; + } + + // collapse slashes to reduce potential for exploits + const match = /^\\+/.exec(remaining()); + let slashes = 0; + + if (match && match[0].length > 2) { + slashes = match[0].length; + state.index += slashes; + if (slashes % 2 !== 0) { + value += '\\'; + } + } + + if (opts.unescape === true) { + value = advance(); + } else { + value += advance(); + } + + if (state.brackets === 0) { + push({ type: 'text', value }); + continue; + } + } + + /** + * If we're inside a regex character class, continue + * until we reach the closing bracket. + */ + + if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) { + if (opts.posix !== false && value === ':') { + const inner = prev.value.slice(1); + if (inner.includes('[')) { + prev.posix = true; + + if (inner.includes(':')) { + const idx = prev.value.lastIndexOf('['); + const pre = prev.value.slice(0, idx); + const rest = prev.value.slice(idx + 2); + const posix = POSIX_REGEX_SOURCE[rest]; + if (posix) { + prev.value = pre + posix; + state.backtrack = true; + advance(); + + if (!bos.output && tokens.indexOf(prev) === 1) { + bos.output = ONE_CHAR; + } + continue; + } + } + } + } + + if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) { + value = `\\${value}`; + } + + if (value === ']' && (prev.value === '[' || prev.value === '[^')) { + value = `\\${value}`; + } + + if (opts.posix === true && value === '!' && prev.value === '[') { + value = '^'; + } + + prev.value += value; + append({ value }); + continue; + } + + /** + * If we're inside a quoted string, continue + * until we reach the closing double quote. + */ + + if (state.quotes === 1 && value !== '"') { + value = utils.escapeRegex(value); + prev.value += value; + append({ value }); + continue; + } + + /** + * Double quotes + */ + + if (value === '"') { + state.quotes = state.quotes === 1 ? 0 : 1; + if (opts.keepQuotes === true) { + push({ type: 'text', value }); + } + continue; + } + + /** + * Parentheses + */ + + if (value === '(') { + increment('parens'); + push({ type: 'paren', value }); + continue; + } + + if (value === ')') { + if (state.parens === 0 && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('opening', '(')); + } + + const extglob = extglobs[extglobs.length - 1]; + if (extglob && state.parens === extglob.parens + 1) { + extglobClose(extglobs.pop()); + continue; + } + + push({ type: 'paren', value, output: state.parens ? ')' : '\\)' }); + decrement('parens'); + continue; + } + + /** + * Square brackets + */ + + if (value === '[') { + if (opts.nobracket === true || !remaining().includes(']')) { + if (opts.nobracket !== true && opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('closing', ']')); + } + + value = `\\${value}`; + } else { + increment('brackets'); + } + + push({ type: 'bracket', value }); + continue; + } + + if (value === ']') { + if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) { + push({ type: 'text', value, output: `\\${value}` }); + continue; + } + + if (state.brackets === 0) { + if (opts.strictBrackets === true) { + throw new SyntaxError(syntaxError('opening', '[')); + } + + push({ type: 'text', value, output: `\\${value}` }); + continue; + } + + decrement('brackets'); + + const prevValue = prev.value.slice(1); + if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) { + value = `/${value}`; + } + + prev.value += value; + append({ value }); + + // when literal brackets are explicitly disabled + // assume we should match with a regex character class + if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) { + continue; + } + + const escaped = utils.escapeRegex(prev.value); + state.output = state.output.slice(0, -prev.value.length); + + // when literal brackets are explicitly enabled + // assume we should escape the brackets to match literal characters + if (opts.literalBrackets === true) { + state.output += escaped; + prev.value = escaped; + continue; + } + + // when the user specifies nothing, try to match both + prev.value = `(${capture}${escaped}|${prev.value})`; + state.output += prev.value; + continue; + } + + /** + * Braces + */ + + if (value === '{' && opts.nobrace !== true) { + increment('braces'); + + const open = { + type: 'brace', + value, + output: '(', + outputIndex: state.output.length, + tokensIndex: state.tokens.length + }; + + braces.push(open); + push(open); + continue; + } + + if (value === '}') { + const brace = braces[braces.length - 1]; + + if (opts.nobrace === true || !brace) { + push({ type: 'text', value, output: value }); + continue; + } + + let output = ')'; + + if (brace.dots === true) { + const arr = tokens.slice(); + const range = []; + + for (let i = arr.length - 1; i >= 0; i--) { + tokens.pop(); + if (arr[i].type === 'brace') { + break; + } + if (arr[i].type !== 'dots') { + range.unshift(arr[i].value); + } + } + + output = expandRange(range, opts); + state.backtrack = true; + } + + if (brace.comma !== true && brace.dots !== true) { + const out = state.output.slice(0, brace.outputIndex); + const toks = state.tokens.slice(brace.tokensIndex); + brace.value = brace.output = '\\{'; + value = output = '\\}'; + state.output = out; + for (const t of toks) { + state.output += (t.output || t.value); + } + } + + push({ type: 'brace', value, output }); + decrement('braces'); + braces.pop(); + continue; + } + + /** + * Pipes + */ + + if (value === '|') { + if (extglobs.length > 0) { + extglobs[extglobs.length - 1].conditions++; + } + push({ type: 'text', value }); + continue; + } + + /** + * Commas + */ + + if (value === ',') { + let output = value; + + const brace = braces[braces.length - 1]; + if (brace && stack[stack.length - 1] === 'braces') { + brace.comma = true; + output = '|'; + } + + push({ type: 'comma', value, output }); + continue; + } + + /** + * Slashes + */ + + if (value === '/') { + // if the beginning of the glob is "./", advance the start + // to the current index, and don't add the "./" characters + // to the state. This greatly simplifies lookbehinds when + // checking for BOS characters like "!" and "." (not "./") + if (prev.type === 'dot' && state.index === state.start + 1) { + state.start = state.index + 1; + state.consumed = ''; + state.output = ''; + tokens.pop(); + prev = bos; // reset "prev" to the first token + continue; + } + + push({ type: 'slash', value, output: SLASH_LITERAL }); + continue; + } + + /** + * Dots + */ + + if (value === '.') { + if (state.braces > 0 && prev.type === 'dot') { + if (prev.value === '.') prev.output = DOT_LITERAL; + const brace = braces[braces.length - 1]; + prev.type = 'dots'; + prev.output += value; + prev.value += value; + brace.dots = true; + continue; + } + + if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') { + push({ type: 'text', value, output: DOT_LITERAL }); + continue; + } + + push({ type: 'dot', value, output: DOT_LITERAL }); + continue; + } + + /** + * Question marks + */ + + if (value === '?') { + const isGroup = prev && prev.value === '('; + if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + extglobOpen('qmark', value); + continue; + } + + if (prev && prev.type === 'paren') { + const next = peek(); + let output = value; + + if (next === '<' && !utils.supportsLookbehinds()) { + throw new Error('Node.js v10 or higher is required for regex lookbehinds'); + } + + if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) { + output = `\\${value}`; + } + + push({ type: 'text', value, output }); + continue; + } + + if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) { + push({ type: 'qmark', value, output: QMARK_NO_DOT }); + continue; + } + + push({ type: 'qmark', value, output: QMARK }); + continue; + } + + /** + * Exclamation + */ + + if (value === '!') { + if (opts.noextglob !== true && peek() === '(') { + if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) { + extglobOpen('negate', value); + continue; + } + } + + if (opts.nonegate !== true && state.index === 0) { + negate(); + continue; + } + } + + /** + * Plus + */ + + if (value === '+') { + if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + extglobOpen('plus', value); + continue; + } + + if ((prev && prev.value === '(') || opts.regex === false) { + push({ type: 'plus', value, output: PLUS_LITERAL }); + continue; + } + + if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) { + push({ type: 'plus', value }); + continue; + } + + push({ type: 'plus', value: PLUS_LITERAL }); + continue; + } + + /** + * Plain text + */ + + if (value === '@') { + if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') { + push({ type: 'at', extglob: true, value, output: '' }); + continue; + } + + push({ type: 'text', value }); + continue; + } + + /** + * Plain text + */ + + if (value !== '*') { + if (value === '$' || value === '^') { + value = `\\${value}`; + } + + const match = REGEX_NON_SPECIAL_CHARS.exec(remaining()); + if (match) { + value += match[0]; + state.index += match[0].length; + } + + push({ type: 'text', value }); + continue; + } + + /** + * Stars + */ + + if (prev && (prev.type === 'globstar' || prev.star === true)) { + prev.type = 'star'; + prev.star = true; + prev.value += value; + prev.output = star; + state.backtrack = true; + state.globstar = true; + consume(value); + continue; + } + + let rest = remaining(); + if (opts.noextglob !== true && /^\([^?]/.test(rest)) { + extglobOpen('star', value); + continue; + } + + if (prev.type === 'star') { + if (opts.noglobstar === true) { + consume(value); + continue; + } - return value; -}; + const prior = prev.prev; + const before = prior.prev; + const isStart = prior.type === 'slash' || prior.type === 'bos'; + const afterStar = before && (before.type === 'star' || before.type === 'globstar'); -var LEGACY_ALIASES = { - '%ArrayBufferPrototype%': ['ArrayBuffer', 'prototype'], - '%ArrayPrototype%': ['Array', 'prototype'], - '%ArrayProto_entries%': ['Array', 'prototype', 'entries'], - '%ArrayProto_forEach%': ['Array', 'prototype', 'forEach'], - '%ArrayProto_keys%': ['Array', 'prototype', 'keys'], - '%ArrayProto_values%': ['Array', 'prototype', 'values'], - '%AsyncFunctionPrototype%': ['AsyncFunction', 'prototype'], - '%AsyncGenerator%': ['AsyncGeneratorFunction', 'prototype'], - '%AsyncGeneratorPrototype%': ['AsyncGeneratorFunction', 'prototype', 'prototype'], - '%BooleanPrototype%': ['Boolean', 'prototype'], - '%DataViewPrototype%': ['DataView', 'prototype'], - '%DatePrototype%': ['Date', 'prototype'], - '%ErrorPrototype%': ['Error', 'prototype'], - '%EvalErrorPrototype%': ['EvalError', 'prototype'], - '%Float32ArrayPrototype%': ['Float32Array', 'prototype'], - '%Float64ArrayPrototype%': ['Float64Array', 'prototype'], - '%FunctionPrototype%': ['Function', 'prototype'], - '%Generator%': ['GeneratorFunction', 'prototype'], - '%GeneratorPrototype%': ['GeneratorFunction', 'prototype', 'prototype'], - '%Int8ArrayPrototype%': ['Int8Array', 'prototype'], - '%Int16ArrayPrototype%': ['Int16Array', 'prototype'], - '%Int32ArrayPrototype%': ['Int32Array', 'prototype'], - '%JSONParse%': ['JSON', 'parse'], - '%JSONStringify%': ['JSON', 'stringify'], - '%MapPrototype%': ['Map', 'prototype'], - '%NumberPrototype%': ['Number', 'prototype'], - '%ObjectPrototype%': ['Object', 'prototype'], - '%ObjProto_toString%': ['Object', 'prototype', 'toString'], - '%ObjProto_valueOf%': ['Object', 'prototype', 'valueOf'], - '%PromisePrototype%': ['Promise', 'prototype'], - '%PromiseProto_then%': ['Promise', 'prototype', 'then'], - '%Promise_all%': ['Promise', 'all'], - '%Promise_reject%': ['Promise', 'reject'], - '%Promise_resolve%': ['Promise', 'resolve'], - '%RangeErrorPrototype%': ['RangeError', 'prototype'], - '%ReferenceErrorPrototype%': ['ReferenceError', 'prototype'], - '%RegExpPrototype%': ['RegExp', 'prototype'], - '%SetPrototype%': ['Set', 'prototype'], - '%SharedArrayBufferPrototype%': ['SharedArrayBuffer', 'prototype'], - '%StringPrototype%': ['String', 'prototype'], - '%SymbolPrototype%': ['Symbol', 'prototype'], - '%SyntaxErrorPrototype%': ['SyntaxError', 'prototype'], - '%TypedArrayPrototype%': ['TypedArray', 'prototype'], - '%TypeErrorPrototype%': ['TypeError', 'prototype'], - '%Uint8ArrayPrototype%': ['Uint8Array', 'prototype'], - '%Uint8ClampedArrayPrototype%': ['Uint8ClampedArray', 'prototype'], - '%Uint16ArrayPrototype%': ['Uint16Array', 'prototype'], - '%Uint32ArrayPrototype%': ['Uint32Array', 'prototype'], - '%URIErrorPrototype%': ['URIError', 'prototype'], - '%WeakMapPrototype%': ['WeakMap', 'prototype'], - '%WeakSetPrototype%': ['WeakSet', 'prototype'] -}; + if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) { + push({ type: 'star', value, output: '' }); + continue; + } -var bind = __nccwpck_require__(8334); -var hasOwn = __nccwpck_require__(6339); -var $concat = bind.call(Function.call, Array.prototype.concat); -var $spliceApply = bind.call(Function.apply, Array.prototype.splice); -var $replace = bind.call(Function.call, String.prototype.replace); -var $strSlice = bind.call(Function.call, String.prototype.slice); -var $exec = bind.call(Function.call, RegExp.prototype.exec); + const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace'); + const isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren'); + if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) { + push({ type: 'star', value, output: '' }); + continue; + } -/* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */ -var rePropName = /[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g; -var reEscapeChar = /\\(\\)?/g; /** Used to match backslashes in property paths. */ -var stringToPath = function stringToPath(string) { - var first = $strSlice(string, 0, 1); - var last = $strSlice(string, -1); - if (first === '%' && last !== '%') { - throw new $SyntaxError('invalid intrinsic syntax, expected closing `%`'); - } else if (last === '%' && first !== '%') { - throw new $SyntaxError('invalid intrinsic syntax, expected opening `%`'); - } - var result = []; - $replace(string, rePropName, function (match, number, quote, subString) { - result[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : number || match; - }); - return result; -}; -/* end adaptation */ + // strip consecutive `/**/` + while (rest.slice(0, 3) === '/**') { + const after = input[state.index + 4]; + if (after && after !== '/') { + break; + } + rest = rest.slice(3); + consume('/**', 3); + } -var getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) { - var intrinsicName = name; - var alias; - if (hasOwn(LEGACY_ALIASES, intrinsicName)) { - alias = LEGACY_ALIASES[intrinsicName]; - intrinsicName = '%' + alias[0] + '%'; - } + if (prior.type === 'bos' && eos()) { + prev.type = 'globstar'; + prev.value += value; + prev.output = globstar(opts); + state.output = prev.output; + state.globstar = true; + consume(value); + continue; + } - if (hasOwn(INTRINSICS, intrinsicName)) { - var value = INTRINSICS[intrinsicName]; - if (value === needsEval) { - value = doEval(intrinsicName); - } - if (typeof value === 'undefined' && !allowMissing) { - throw new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!'); - } + if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) { + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; - return { - alias: alias, - name: intrinsicName, - value: value - }; - } + prev.type = 'globstar'; + prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)'); + prev.value += value; + state.globstar = true; + state.output += prior.output + prev.output; + consume(value); + continue; + } - throw new $SyntaxError('intrinsic ' + name + ' does not exist!'); -}; + if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') { + const end = rest[1] !== void 0 ? '|$' : ''; -module.exports = function GetIntrinsic(name, allowMissing) { - if (typeof name !== 'string' || name.length === 0) { - throw new $TypeError('intrinsic name must be a non-empty string'); - } - if (arguments.length > 1 && typeof allowMissing !== 'boolean') { - throw new $TypeError('"allowMissing" argument must be a boolean'); - } + state.output = state.output.slice(0, -(prior.output + prev.output).length); + prior.output = `(?:${prior.output}`; - if ($exec(/^%?[^%]*%?$/, name) === null) { - throw new $SyntaxError('`%` may not be present anywhere but at the beginning and end of the intrinsic name'); - } - var parts = stringToPath(name); - var intrinsicBaseName = parts.length > 0 ? parts[0] : ''; + prev.type = 'globstar'; + prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`; + prev.value += value; - var intrinsic = getBaseIntrinsic('%' + intrinsicBaseName + '%', allowMissing); - var intrinsicRealName = intrinsic.name; - var value = intrinsic.value; - var skipFurtherCaching = false; + state.output += prior.output + prev.output; + state.globstar = true; - var alias = intrinsic.alias; - if (alias) { - intrinsicBaseName = alias[0]; - $spliceApply(parts, $concat([0, 1], alias)); - } + consume(value + advance()); - for (var i = 1, isOwn = true; i < parts.length; i += 1) { - var part = parts[i]; - var first = $strSlice(part, 0, 1); - var last = $strSlice(part, -1); - if ( - ( - (first === '"' || first === "'" || first === '`') - || (last === '"' || last === "'" || last === '`') - ) - && first !== last - ) { - throw new $SyntaxError('property names with quotes must have matching quotes'); - } - if (part === 'constructor' || !isOwn) { - skipFurtherCaching = true; - } + push({ type: 'slash', value: '/', output: '' }); + continue; + } - intrinsicBaseName += '.' + part; - intrinsicRealName = '%' + intrinsicBaseName + '%'; + if (prior.type === 'bos' && rest[0] === '/') { + prev.type = 'globstar'; + prev.value += value; + prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`; + state.output = prev.output; + state.globstar = true; + consume(value + advance()); + push({ type: 'slash', value: '/', output: '' }); + continue; + } - if (hasOwn(INTRINSICS, intrinsicRealName)) { - value = INTRINSICS[intrinsicRealName]; - } else if (value != null) { - if (!(part in value)) { - if (!allowMissing) { - throw new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.'); - } - return void undefined; - } - if ($gOPD && (i + 1) >= parts.length) { - var desc = $gOPD(value, part); - isOwn = !!desc; + // remove single star from output + state.output = state.output.slice(0, -prev.output.length); - // By convention, when a data property is converted to an accessor - // property to emulate a data property that does not suffer from - // the override mistake, that accessor's getter is marked with - // an `originalValue` property. Here, when we detect this, we - // uphold the illusion by pretending to see that original data - // property, i.e., returning the value rather than the getter - // itself. - if (isOwn && 'get' in desc && !('originalValue' in desc.get)) { - value = desc.get; - } else { - value = value[part]; - } - } else { - isOwn = hasOwn(value, part); - value = value[part]; - } + // reset previous token to globstar + prev.type = 'globstar'; + prev.output = globstar(opts); + prev.value += value; - if (isOwn && !skipFurtherCaching) { - INTRINSICS[intrinsicRealName] = value; - } - } - } - return value; -}; + // reset output with globstar + state.output += prev.output; + state.globstar = true; + consume(value); + continue; + } + const token = { type: 'star', value, output: star }; -/***/ }), + if (opts.bash === true) { + token.output = '.*?'; + if (prev.type === 'bos' || prev.type === 'slash') { + token.output = nodot + token.output; + } + push(token); + continue; + } -/***/ 5894: -/***/ ((module) => { + if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) { + token.output = value; + push(token); + continue; + } + if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') { + if (prev.type === 'dot') { + state.output += NO_DOT_SLASH; + prev.output += NO_DOT_SLASH; + } else if (opts.dot === true) { + state.output += NO_DOTS_SLASH; + prev.output += NO_DOTS_SLASH; -var test = { - foo: {} -}; + } else { + state.output += nodot; + prev.output += nodot; + } -var $Object = Object; + if (peek() !== '*') { + state.output += ONE_CHAR; + prev.output += ONE_CHAR; + } + } -module.exports = function hasProto() { - return { __proto__: test }.foo === test.foo && !({ __proto__: null } instanceof $Object); -}; + push(token); + } + while (state.brackets > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']')); + state.output = utils.escapeLast(state.output, '['); + decrement('brackets'); + } -/***/ }), + while (state.parens > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')')); + state.output = utils.escapeLast(state.output, '('); + decrement('parens'); + } -/***/ 587: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + while (state.braces > 0) { + if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}')); + state.output = utils.escapeLast(state.output, '{'); + decrement('braces'); + } + if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) { + push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` }); + } + // rebuild the output if we had to backtrack at any point + if (state.backtrack === true) { + state.output = ''; -var origSymbol = typeof Symbol !== 'undefined' && Symbol; -var hasSymbolSham = __nccwpck_require__(7747); + for (const token of state.tokens) { + state.output += token.output != null ? token.output : token.value; -module.exports = function hasNativeSymbols() { - if (typeof origSymbol !== 'function') { return false; } - if (typeof Symbol !== 'function') { return false; } - if (typeof origSymbol('foo') !== 'symbol') { return false; } - if (typeof Symbol('bar') !== 'symbol') { return false; } + if (token.suffix) { + state.output += token.suffix; + } + } + } - return hasSymbolSham(); + return state; }; +/** + * Fast paths for creating regular expressions for common glob patterns. + * This can significantly speed up processing and has very little downside + * impact when none of the fast paths match. + */ -/***/ }), - -/***/ 7747: -/***/ ((module) => { +parse.fastpaths = (input, options) => { + const opts = { ...options }; + const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH; + const len = input.length; + if (len > max) { + throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`); + } + + input = REPLACEMENTS[input] || input; + const win32 = utils.isWindows(options); + + // create constants based on platform, for windows or posix + const { + DOT_LITERAL, + SLASH_LITERAL, + ONE_CHAR, + DOTS_SLASH, + NO_DOT, + NO_DOTS, + NO_DOTS_SLASH, + STAR, + START_ANCHOR + } = constants.globChars(win32); + + const nodot = opts.dot ? NO_DOTS : NO_DOT; + const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT; + const capture = opts.capture ? '' : '?:'; + const state = { negated: false, prefix: '' }; + let star = opts.bash === true ? '.*?' : STAR; + + if (opts.capture) { + star = `(${star})`; + } + + const globstar = opts => { + if (opts.noglobstar === true) return star; + return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`; + }; + const create = str => { + switch (str) { + case '*': + return `${nodot}${ONE_CHAR}${star}`; + case '.*': + return `${DOT_LITERAL}${ONE_CHAR}${star}`; -/* eslint complexity: [2, 18], max-statements: [2, 33] */ -module.exports = function hasSymbols() { - if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; } - if (typeof Symbol.iterator === 'symbol') { return true; } + case '*.*': + return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - var obj = {}; - var sym = Symbol('test'); - var symObj = Object(sym); - if (typeof sym === 'string') { return false; } + case '*/*': + return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`; - if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; } - if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; } + case '**': + return nodot + globstar(opts); - // temp disabled per https://github.com/ljharb/object.assign/issues/17 - // if (sym instanceof Symbol) { return false; } - // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4 - // if (!(symObj instanceof Symbol)) { return false; } + case '**/*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`; - // if (typeof Symbol.prototype.toString !== 'function') { return false; } - // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; } + case '**/*.*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`; - var symVal = 42; - obj[sym] = symVal; - for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax, no-unreachable-loop - if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; } + case '**/.*': + return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`; - if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; } + default: { + const match = /^(.*?)\.(\w+)$/.exec(str); + if (!match) return; - var syms = Object.getOwnPropertySymbols(obj); - if (syms.length !== 1 || syms[0] !== sym) { return false; } + const source = create(match[1]); + if (!source) return; - if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; } + return source + DOT_LITERAL + match[2]; + } + } + }; - if (typeof Object.getOwnPropertyDescriptor === 'function') { - var descriptor = Object.getOwnPropertyDescriptor(obj, sym); - if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; } - } + const output = utils.removePrefix(input, state); + let source = create(output); - return true; + if (source && opts.strictSlashes !== true) { + source += `${SLASH_LITERAL}?`; + } + + return source; }; +module.exports = parse; + /***/ }), -/***/ 6339: +/***/ 3322: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -var bind = __nccwpck_require__(8334); +const path = __nccwpck_require__(1017); +const scan = __nccwpck_require__(2429); +const parse = __nccwpck_require__(2139); +const utils = __nccwpck_require__(479); +const constants = __nccwpck_require__(6099); +const isObject = val => val && typeof val === 'object' && !Array.isArray(val); -module.exports = bind.call(Function.call, Object.prototype.hasOwnProperty); +/** + * Creates a matcher function from one or more glob patterns. The + * returned function takes a string to match as its first argument, + * and returns true if the string is a match. The returned matcher + * function also takes a boolean as the second argument that, when true, + * returns an object with additional information. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch(glob[, options]); + * + * const isMatch = picomatch('*.!(*a)'); + * console.log(isMatch('a.a')); //=> false + * console.log(isMatch('a.b')); //=> true + * ``` + * @name picomatch + * @param {String|Array} `globs` One or more glob patterns. + * @param {Object=} `options` + * @return {Function=} Returns a matcher function. + * @api public + */ +const picomatch = (glob, options, returnState = false) => { + if (Array.isArray(glob)) { + const fns = glob.map(input => picomatch(input, options, returnState)); + const arrayMatcher = str => { + for (const isMatch of fns) { + const state = isMatch(str); + if (state) return state; + } + return false; + }; + return arrayMatcher; + } -/***/ }), + const isState = isObject(glob) && glob.tokens && glob.input; -/***/ 3287: -/***/ ((__unused_webpack_module, exports) => { + if (glob === '' || (typeof glob !== 'string' && !isState)) { + throw new TypeError('Expected pattern to be a non-empty string'); + } + const opts = options || {}; + const posix = utils.isWindows(options); + const regex = isState + ? picomatch.compileRe(glob, options) + : picomatch.makeRe(glob, options, false, true); + const state = regex.state; + delete regex.state; -Object.defineProperty(exports, "__esModule", ({ value: true })); + let isIgnored = () => false; + if (opts.ignore) { + const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null }; + isIgnored = picomatch(opts.ignore, ignoreOpts, returnState); + } -/*! - * is-plain-object + const matcher = (input, returnObject = false) => { + const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix }); + const result = { glob, state, regex, posix, input, output, match, isMatch }; + + if (typeof opts.onResult === 'function') { + opts.onResult(result); + } + + if (isMatch === false) { + result.isMatch = false; + return returnObject ? result : false; + } + + if (isIgnored(input)) { + if (typeof opts.onIgnore === 'function') { + opts.onIgnore(result); + } + result.isMatch = false; + return returnObject ? result : false; + } + + if (typeof opts.onMatch === 'function') { + opts.onMatch(result); + } + return returnObject ? result : true; + }; + + if (returnState) { + matcher.state = state; + } + + return matcher; +}; + +/** + * Test `input` with the given `regex`. This is used by the main + * `picomatch()` function to test the input string. * - * Copyright (c) 2014-2017, Jon Schlinkert. - * Released under the MIT License. + * ```js + * const picomatch = require('picomatch'); + * // picomatch.test(input, regex[, options]); + * + * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/)); + * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' } + * ``` + * @param {String} `input` String to test. + * @param {RegExp} `regex` + * @return {Object} Returns an object with matching info. + * @api public */ -function isObject(o) { - return Object.prototype.toString.call(o) === '[object Object]'; -} - -function isPlainObject(o) { - var ctor,prot; +picomatch.test = (input, regex, options, { glob, posix } = {}) => { + if (typeof input !== 'string') { + throw new TypeError('Expected input to be a string'); + } - if (isObject(o) === false) return false; + if (input === '') { + return { isMatch: false, output: '' }; + } - // If has modified constructor - ctor = o.constructor; - if (ctor === undefined) return true; + const opts = options || {}; + const format = opts.format || (posix ? utils.toPosixSlashes : null); + let match = input === glob; + let output = (match && format) ? format(input) : input; - // If has modified prototype - prot = ctor.prototype; - if (isObject(prot) === false) return false; + if (match === false) { + output = format ? format(input) : input; + match = output === glob; + } - // If constructor does not have an Object-specific method - if (prot.hasOwnProperty('isPrototypeOf') === false) { - return false; + if (match === false || opts.capture === true) { + if (opts.matchBase === true || opts.basename === true) { + match = picomatch.matchBase(input, regex, options, posix); + } else { + match = regex.exec(output); + } } - // Most likely a plain Object - return true; -} + return { isMatch: Boolean(match), match, output }; +}; -exports.isPlainObject = isPlainObject; +/** + * Match the basename of a filepath. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.matchBase(input, glob[, options]); + * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true + * ``` + * @param {String} `input` String to test. + * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe). + * @return {Boolean} + * @api public + */ +picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => { + const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options); + return regex.test(path.basename(input)); +}; -/***/ }), +/** + * Returns true if **any** of the given glob `patterns` match the specified `string`. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.isMatch(string, patterns[, options]); + * + * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true + * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false + * ``` + * @param {String|Array} str The string to test. + * @param {String|Array} patterns One or more glob patterns to use for matching. + * @param {Object} [options] See available [options](#options). + * @return {Boolean} Returns true if any patterns match `str` + * @api public + */ -/***/ 7073: -/***/ ((module, exports) => { +picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str); -exports = module.exports = stringify -exports.getSerialize = serializer +/** + * Parse a glob pattern to create the source string for a regular + * expression. + * + * ```js + * const picomatch = require('picomatch'); + * const result = picomatch.parse(pattern[, options]); + * ``` + * @param {String} `pattern` + * @param {Object} `options` + * @return {Object} Returns an object with useful properties and output to be used as a regex source string. + * @api public + */ -function stringify(obj, replacer, spaces, cycleReplacer) { - return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces) -} +picomatch.parse = (pattern, options) => { + if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options)); + return parse(pattern, { ...options, fastpaths: false }); +}; -function serializer(replacer, cycleReplacer) { - var stack = [], keys = [] +/** + * Scan a glob pattern to separate the pattern into segments. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.scan(input[, options]); + * + * const result = picomatch.scan('!./foo/*.js'); + * console.log(result); + * { prefix: '!./', + * input: '!./foo/*.js', + * start: 3, + * base: 'foo', + * glob: '*.js', + * isBrace: false, + * isBracket: false, + * isGlob: true, + * isExtglob: false, + * isGlobstar: false, + * negated: true } + * ``` + * @param {String} `input` Glob pattern to scan. + * @param {Object} `options` + * @return {Object} Returns an object with + * @api public + */ - if (cycleReplacer == null) cycleReplacer = function(key, value) { - if (stack[0] === value) return "[Circular ~]" - return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]" +picomatch.scan = (input, options) => scan(input, options); + +/** + * Compile a regular expression from the `state` object returned by the + * [parse()](#parse) method. + * + * @param {Object} `state` + * @param {Object} `options` + * @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser. + * @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging. + * @return {RegExp} + * @api public + */ + +picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => { + if (returnOutput === true) { + return state.output; } - return function(key, value) { - if (stack.length > 0) { - var thisPos = stack.indexOf(this) - ~thisPos ? stack.splice(thisPos + 1) : stack.push(this) - ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key) - if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value) - } - else stack.push(value) + const opts = options || {}; + const prepend = opts.contains ? '' : '^'; + const append = opts.contains ? '' : '$'; - return replacer == null ? value : replacer.call(this, key, value) + let source = `${prepend}(?:${state.output})${append}`; + if (state && state.negated === true) { + source = `^(?!${source}).*$`; } -} + const regex = picomatch.toRegex(source, options); + if (returnState === true) { + regex.state = state; + } -/***/ }), + return regex; +}; -/***/ 7760: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +/** + * Create a regular expression from a parsed glob pattern. + * + * ```js + * const picomatch = require('picomatch'); + * const state = picomatch.parse('*.js'); + * // picomatch.compileRe(state[, options]); + * + * console.log(picomatch.compileRe(state)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `state` The object returned from the `.parse` method. + * @param {Object} `options` + * @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result. + * @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression. + * @return {RegExp} Returns a regex created from the given pattern. + * @api public + */ -/*! node-domexception. MIT License. Jimmy Wärting */ +picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => { + if (!input || typeof input !== 'string') { + throw new TypeError('Expected a non-empty string'); + } -if (!globalThis.DOMException) { - try { - const { MessageChannel } = __nccwpck_require__(1267), - port = new MessageChannel().port1, - ab = new ArrayBuffer() - port.postMessage(ab, [ab, ab]) - } catch (err) { - err.constructor.name === 'DOMException' && ( - globalThis.DOMException = err.constructor - ) + let parsed = { negated: false, fastpaths: true }; + + if (options.fastpaths !== false && (input[0] === '.' || input[0] === '*')) { + parsed.output = parse.fastpaths(input, options); } -} -module.exports = globalThis.DOMException + if (!parsed.output) { + parsed = parse(input, options); + } + return picomatch.compileRe(parsed, options, returnOutput, returnState); +}; -/***/ }), +/** + * Create a regular expression from the given regex source string. + * + * ```js + * const picomatch = require('picomatch'); + * // picomatch.toRegex(source[, options]); + * + * const { output } = picomatch.parse('*.js'); + * console.log(picomatch.toRegex(output)); + * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/ + * ``` + * @param {String} `source` Regular expression source string. + * @param {Object} `options` + * @return {RegExp} + * @api public + */ -/***/ 504: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +picomatch.toRegex = (source, options) => { + try { + const opts = options || {}; + return new RegExp(source, opts.flags || (opts.nocase ? 'i' : '')); + } catch (err) { + if (options && options.debug === true) throw err; + return /$^/; + } +}; -var hasMap = typeof Map === 'function' && Map.prototype; -var mapSizeDescriptor = Object.getOwnPropertyDescriptor && hasMap ? Object.getOwnPropertyDescriptor(Map.prototype, 'size') : null; -var mapSize = hasMap && mapSizeDescriptor && typeof mapSizeDescriptor.get === 'function' ? mapSizeDescriptor.get : null; -var mapForEach = hasMap && Map.prototype.forEach; -var hasSet = typeof Set === 'function' && Set.prototype; -var setSizeDescriptor = Object.getOwnPropertyDescriptor && hasSet ? Object.getOwnPropertyDescriptor(Set.prototype, 'size') : null; -var setSize = hasSet && setSizeDescriptor && typeof setSizeDescriptor.get === 'function' ? setSizeDescriptor.get : null; -var setForEach = hasSet && Set.prototype.forEach; -var hasWeakMap = typeof WeakMap === 'function' && WeakMap.prototype; -var weakMapHas = hasWeakMap ? WeakMap.prototype.has : null; -var hasWeakSet = typeof WeakSet === 'function' && WeakSet.prototype; -var weakSetHas = hasWeakSet ? WeakSet.prototype.has : null; -var hasWeakRef = typeof WeakRef === 'function' && WeakRef.prototype; -var weakRefDeref = hasWeakRef ? WeakRef.prototype.deref : null; -var booleanValueOf = Boolean.prototype.valueOf; -var objectToString = Object.prototype.toString; -var functionToString = Function.prototype.toString; -var $match = String.prototype.match; -var $slice = String.prototype.slice; -var $replace = String.prototype.replace; -var $toUpperCase = String.prototype.toUpperCase; -var $toLowerCase = String.prototype.toLowerCase; -var $test = RegExp.prototype.test; -var $concat = Array.prototype.concat; -var $join = Array.prototype.join; -var $arrSlice = Array.prototype.slice; -var $floor = Math.floor; -var bigIntValueOf = typeof BigInt === 'function' ? BigInt.prototype.valueOf : null; -var gOPS = Object.getOwnPropertySymbols; -var symToString = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? Symbol.prototype.toString : null; -var hasShammedSymbols = typeof Symbol === 'function' && typeof Symbol.iterator === 'object'; -// ie, `has-tostringtag/shams -var toStringTag = typeof Symbol === 'function' && Symbol.toStringTag && (typeof Symbol.toStringTag === hasShammedSymbols ? 'object' : 'symbol') - ? Symbol.toStringTag - : null; -var isEnumerable = Object.prototype.propertyIsEnumerable; +/** + * Picomatch constants. + * @return {Object} + */ -var gPO = (typeof Reflect === 'function' ? Reflect.getPrototypeOf : Object.getPrototypeOf) || ( - [].__proto__ === Array.prototype // eslint-disable-line no-proto - ? function (O) { - return O.__proto__; // eslint-disable-line no-proto - } - : null -); +picomatch.constants = constants; -function addNumericSeparator(num, str) { - if ( - num === Infinity - || num === -Infinity - || num !== num - || (num && num > -1000 && num < 1000) - || $test.call(/e/, str) - ) { - return str; - } - var sepRegex = /[0-9](?=(?:[0-9]{3})+(?![0-9]))/g; - if (typeof num === 'number') { - var int = num < 0 ? -$floor(-num) : $floor(num); // trunc(num) - if (int !== num) { - var intStr = String(int); - var dec = $slice.call(str, intStr.length + 1); - return $replace.call(intStr, sepRegex, '$&_') + '.' + $replace.call($replace.call(dec, /([0-9]{3})/g, '$&_'), /_$/, ''); - } - } - return $replace.call(str, sepRegex, '$&_'); -} +/** + * Expose "picomatch" + */ -var utilInspect = __nccwpck_require__(7265); -var inspectCustom = utilInspect.custom; -var inspectSymbol = isSymbol(inspectCustom) ? inspectCustom : null; +module.exports = picomatch; -module.exports = function inspect_(obj, options, depth, seen) { - var opts = options || {}; - if (has(opts, 'quoteStyle') && (opts.quoteStyle !== 'single' && opts.quoteStyle !== 'double')) { - throw new TypeError('option "quoteStyle" must be "single" or "double"'); - } - if ( - has(opts, 'maxStringLength') && (typeof opts.maxStringLength === 'number' - ? opts.maxStringLength < 0 && opts.maxStringLength !== Infinity - : opts.maxStringLength !== null - ) - ) { - throw new TypeError('option "maxStringLength", if provided, must be a positive integer, Infinity, or `null`'); - } - var customInspect = has(opts, 'customInspect') ? opts.customInspect : true; - if (typeof customInspect !== 'boolean' && customInspect !== 'symbol') { - throw new TypeError('option "customInspect", if provided, must be `true`, `false`, or `\'symbol\'`'); - } +/***/ }), - if ( - has(opts, 'indent') - && opts.indent !== null - && opts.indent !== '\t' - && !(parseInt(opts.indent, 10) === opts.indent && opts.indent > 0) - ) { - throw new TypeError('option "indent" must be "\\t", an integer > 0, or `null`'); - } - if (has(opts, 'numericSeparator') && typeof opts.numericSeparator !== 'boolean') { - throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`'); - } - var numericSeparator = opts.numericSeparator; +/***/ 2429: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { - if (typeof obj === 'undefined') { - return 'undefined'; - } - if (obj === null) { - return 'null'; - } - if (typeof obj === 'boolean') { - return obj ? 'true' : 'false'; - } - if (typeof obj === 'string') { - return inspectString(obj, opts); - } - if (typeof obj === 'number') { - if (obj === 0) { - return Infinity / obj > 0 ? '0' : '-0'; - } - var str = String(obj); - return numericSeparator ? addNumericSeparator(obj, str) : str; - } - if (typeof obj === 'bigint') { - var bigIntStr = String(obj) + 'n'; - return numericSeparator ? addNumericSeparator(obj, bigIntStr) : bigIntStr; - } - var maxDepth = typeof opts.depth === 'undefined' ? 5 : opts.depth; - if (typeof depth === 'undefined') { depth = 0; } - if (depth >= maxDepth && maxDepth > 0 && typeof obj === 'object') { - return isArray(obj) ? '[Array]' : '[Object]'; - } +const utils = __nccwpck_require__(479); +const { + CHAR_ASTERISK, /* * */ + CHAR_AT, /* @ */ + CHAR_BACKWARD_SLASH, /* \ */ + CHAR_COMMA, /* , */ + CHAR_DOT, /* . */ + CHAR_EXCLAMATION_MARK, /* ! */ + CHAR_FORWARD_SLASH, /* / */ + CHAR_LEFT_CURLY_BRACE, /* { */ + CHAR_LEFT_PARENTHESES, /* ( */ + CHAR_LEFT_SQUARE_BRACKET, /* [ */ + CHAR_PLUS, /* + */ + CHAR_QUESTION_MARK, /* ? */ + CHAR_RIGHT_CURLY_BRACE, /* } */ + CHAR_RIGHT_PARENTHESES, /* ) */ + CHAR_RIGHT_SQUARE_BRACKET /* ] */ +} = __nccwpck_require__(6099); - var indent = getIndent(opts, depth); +const isPathSeparator = code => { + return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH; +}; - if (typeof seen === 'undefined') { - seen = []; - } else if (indexOf(seen, obj) >= 0) { - return '[Circular]'; - } +const depth = token => { + if (token.isPrefix !== true) { + token.depth = token.isGlobstar ? Infinity : 1; + } +}; - function inspect(value, from, noIndent) { - if (from) { - seen = $arrSlice.call(seen); - seen.push(from); - } - if (noIndent) { - var newOpts = { - depth: opts.depth - }; - if (has(opts, 'quoteStyle')) { - newOpts.quoteStyle = opts.quoteStyle; - } - return inspect_(value, newOpts, depth + 1, seen); - } - return inspect_(value, opts, depth + 1, seen); - } +/** + * Quickly scans a glob pattern and returns an object with a handful of + * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists), + * `glob` (the actual pattern), `negated` (true if the path starts with `!` but not + * with `!(`) and `negatedExtglob` (true if the path starts with `!(`). + * + * ```js + * const pm = require('picomatch'); + * console.log(pm.scan('foo/bar/*.js')); + * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' } + * ``` + * @param {String} `str` + * @param {Object} `options` + * @return {Object} Returns an object with tokens and regex source string. + * @api public + */ - if (typeof obj === 'function' && !isRegExp(obj)) { // in older engines, regexes are callable - var name = nameOf(obj); - var keys = arrObjKeys(obj, inspect); - return '[Function' + (name ? ': ' + name : ' (anonymous)') + ']' + (keys.length > 0 ? ' { ' + $join.call(keys, ', ') + ' }' : ''); - } - if (isSymbol(obj)) { - var symString = hasShammedSymbols ? $replace.call(String(obj), /^(Symbol\(.*\))_[^)]*$/, '$1') : symToString.call(obj); - return typeof obj === 'object' && !hasShammedSymbols ? markBoxed(symString) : symString; - } - if (isElement(obj)) { - var s = '<' + $toLowerCase.call(String(obj.nodeName)); - var attrs = obj.attributes || []; - for (var i = 0; i < attrs.length; i++) { - s += ' ' + attrs[i].name + '=' + wrapQuotes(quote(attrs[i].value), 'double', opts); - } - s += '>'; - if (obj.childNodes && obj.childNodes.length) { s += '...'; } - s += ''; - return s; - } - if (isArray(obj)) { - if (obj.length === 0) { return '[]'; } - var xs = arrObjKeys(obj, inspect); - if (indent && !singleLineValues(xs)) { - return '[' + indentedJoin(xs, indent) + ']'; - } - return '[ ' + $join.call(xs, ', ') + ' ]'; - } - if (isError(obj)) { - var parts = arrObjKeys(obj, inspect); - if (!('cause' in Error.prototype) && 'cause' in obj && !isEnumerable.call(obj, 'cause')) { - return '{ [' + String(obj) + '] ' + $join.call($concat.call('[cause]: ' + inspect(obj.cause), parts), ', ') + ' }'; - } - if (parts.length === 0) { return '[' + String(obj) + ']'; } - return '{ [' + String(obj) + '] ' + $join.call(parts, ', ') + ' }'; - } - if (typeof obj === 'object' && customInspect) { - if (inspectSymbol && typeof obj[inspectSymbol] === 'function' && utilInspect) { - return utilInspect(obj, { depth: maxDepth - depth }); - } else if (customInspect !== 'symbol' && typeof obj.inspect === 'function') { - return obj.inspect(); - } - } - if (isMap(obj)) { - var mapParts = []; - if (mapForEach) { - mapForEach.call(obj, function (value, key) { - mapParts.push(inspect(key, obj, true) + ' => ' + inspect(value, obj)); - }); - } - return collectionOf('Map', mapSize.call(obj), mapParts, indent); +const scan = (input, options) => { + const opts = options || {}; + + const length = input.length - 1; + const scanToEnd = opts.parts === true || opts.scanToEnd === true; + const slashes = []; + const tokens = []; + const parts = []; + + let str = input; + let index = -1; + let start = 0; + let lastIndex = 0; + let isBrace = false; + let isBracket = false; + let isGlob = false; + let isExtglob = false; + let isGlobstar = false; + let braceEscaped = false; + let backslashes = false; + let negated = false; + let negatedExtglob = false; + let finished = false; + let braces = 0; + let prev; + let code; + let token = { value: '', depth: 0, isGlob: false }; + + const eos = () => index >= length; + const peek = () => str.charCodeAt(index + 1); + const advance = () => { + prev = code; + return str.charCodeAt(++index); + }; + + while (index < length) { + code = advance(); + let next; + + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + + if (code === CHAR_LEFT_CURLY_BRACE) { + braceEscaped = true; + } + continue; } - if (isSet(obj)) { - var setParts = []; - if (setForEach) { - setForEach.call(obj, function (value) { - setParts.push(inspect(value, obj)); - }); + + if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) { + braces++; + + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; } - return collectionOf('Set', setSize.call(obj), setParts, indent); - } - if (isWeakMap(obj)) { - return weakCollectionOf('WeakMap'); - } - if (isWeakSet(obj)) { - return weakCollectionOf('WeakSet'); - } - if (isWeakRef(obj)) { - return weakCollectionOf('WeakRef'); - } - if (isNumber(obj)) { - return markBoxed(inspect(Number(obj))); - } - if (isBigInt(obj)) { - return markBoxed(inspect(bigIntValueOf.call(obj))); - } - if (isBoolean(obj)) { - return markBoxed(booleanValueOf.call(obj)); - } - if (isString(obj)) { - return markBoxed(inspect(String(obj))); - } - if (!isDate(obj) && !isRegExp(obj)) { - var ys = arrObjKeys(obj, inspect); - var isPlainObject = gPO ? gPO(obj) === Object.prototype : obj instanceof Object || obj.constructor === Object; - var protoTag = obj instanceof Object ? '' : 'null prototype'; - var stringTag = !isPlainObject && toStringTag && Object(obj) === obj && toStringTag in obj ? $slice.call(toStr(obj), 8, -1) : protoTag ? 'Object' : ''; - var constructorTag = isPlainObject || typeof obj.constructor !== 'function' ? '' : obj.constructor.name ? obj.constructor.name + ' ' : ''; - var tag = constructorTag + (stringTag || protoTag ? '[' + $join.call($concat.call([], stringTag || [], protoTag || []), ': ') + '] ' : ''); - if (ys.length === 0) { return tag + '{}'; } - if (indent) { - return tag + '{' + indentedJoin(ys, indent) + '}'; + + if (code === CHAR_LEFT_CURLY_BRACE) { + braces++; + continue; } - return tag + '{ ' + $join.call(ys, ', ') + ' }'; - } - return String(obj); -}; -function wrapQuotes(s, defaultStyle, opts) { - var quoteChar = (opts.quoteStyle || defaultStyle) === 'double' ? '"' : "'"; - return quoteChar + s + quoteChar; -} + if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; -function quote(s) { - return $replace.call(String(s), /"/g, '"'); -} + if (scanToEnd === true) { + continue; + } -function isArray(obj) { return toStr(obj) === '[object Array]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } -function isDate(obj) { return toStr(obj) === '[object Date]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } -function isRegExp(obj) { return toStr(obj) === '[object RegExp]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } -function isError(obj) { return toStr(obj) === '[object Error]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } -function isString(obj) { return toStr(obj) === '[object String]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } -function isNumber(obj) { return toStr(obj) === '[object Number]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } -function isBoolean(obj) { return toStr(obj) === '[object Boolean]' && (!toStringTag || !(typeof obj === 'object' && toStringTag in obj)); } + break; + } -// Symbol and BigInt do have Symbol.toStringTag by spec, so that can't be used to eliminate false positives -function isSymbol(obj) { - if (hasShammedSymbols) { - return obj && typeof obj === 'object' && obj instanceof Symbol; - } - if (typeof obj === 'symbol') { - return true; - } - if (!obj || typeof obj !== 'object' || !symToString) { - return false; - } - try { - symToString.call(obj); - return true; - } catch (e) {} - return false; -} + if (braceEscaped !== true && code === CHAR_COMMA) { + isBrace = token.isBrace = true; + isGlob = token.isGlob = true; + finished = true; -function isBigInt(obj) { - if (!obj || typeof obj !== 'object' || !bigIntValueOf) { - return false; - } - try { - bigIntValueOf.call(obj); - return true; - } catch (e) {} - return false; -} + if (scanToEnd === true) { + continue; + } -var hasOwn = Object.prototype.hasOwnProperty || function (key) { return key in this; }; -function has(obj, key) { - return hasOwn.call(obj, key); -} + break; + } -function toStr(obj) { - return objectToString.call(obj); -} + if (code === CHAR_RIGHT_CURLY_BRACE) { + braces--; -function nameOf(f) { - if (f.name) { return f.name; } - var m = $match.call(functionToString.call(f), /^function\s*([\w$]+)/); - if (m) { return m[1]; } - return null; -} + if (braces === 0) { + braceEscaped = false; + isBrace = token.isBrace = true; + finished = true; + break; + } + } + } -function indexOf(xs, x) { - if (xs.indexOf) { return xs.indexOf(x); } - for (var i = 0, l = xs.length; i < l; i++) { - if (xs[i] === x) { return i; } + if (scanToEnd === true) { + continue; + } + + break; } - return -1; -} -function isMap(x) { - if (!mapSize || !x || typeof x !== 'object') { - return false; + if (code === CHAR_FORWARD_SLASH) { + slashes.push(index); + tokens.push(token); + token = { value: '', depth: 0, isGlob: false }; + + if (finished === true) continue; + if (prev === CHAR_DOT && index === (start + 1)) { + start += 2; + continue; + } + + lastIndex = index + 1; + continue; } - try { - mapSize.call(x); - try { - setSize.call(x); - } catch (s) { - return true; + + if (opts.noext !== true) { + const isExtglobChar = code === CHAR_PLUS + || code === CHAR_AT + || code === CHAR_ASTERISK + || code === CHAR_QUESTION_MARK + || code === CHAR_EXCLAMATION_MARK; + + if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; + isExtglob = token.isExtglob = true; + finished = true; + if (code === CHAR_EXCLAMATION_MARK && index === start) { + negatedExtglob = true; } - return x instanceof Map; // core-js workaround, pre-v2.5.0 - } catch (e) {} - return false; -} -function isWeakMap(x) { - if (!weakMapHas || !x || typeof x !== 'object') { - return false; - } - try { - weakMapHas.call(x, weakMapHas); - try { - weakSetHas.call(x, weakSetHas); - } catch (s) { - return true; + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } + + if (code === CHAR_RIGHT_PARENTHESES) { + isGlob = token.isGlob = true; + finished = true; + break; + } + } + continue; } - return x instanceof WeakMap; // core-js workaround, pre-v2.5.0 - } catch (e) {} - return false; -} + break; + } + } -function isWeakRef(x) { - if (!weakRefDeref || !x || typeof x !== 'object') { - return false; + if (code === CHAR_ASTERISK) { + if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true; + isGlob = token.isGlob = true; + finished = true; + + if (scanToEnd === true) { + continue; + } + break; } - try { - weakRefDeref.call(x); - return true; - } catch (e) {} - return false; -} -function isSet(x) { - if (!setSize || !x || typeof x !== 'object') { - return false; + if (code === CHAR_QUESTION_MARK) { + isGlob = token.isGlob = true; + finished = true; + + if (scanToEnd === true) { + continue; + } + break; } - try { - setSize.call(x); - try { - mapSize.call(x); - } catch (m) { - return true; + + if (code === CHAR_LEFT_SQUARE_BRACKET) { + while (eos() !== true && (next = advance())) { + if (next === CHAR_BACKWARD_SLASH) { + backslashes = token.backslashes = true; + advance(); + continue; } - return x instanceof Set; // core-js workaround, pre-v2.5.0 - } catch (e) {} - return false; -} -function isWeakSet(x) { - if (!weakSetHas || !x || typeof x !== 'object') { - return false; - } - try { - weakSetHas.call(x, weakSetHas); - try { - weakMapHas.call(x, weakMapHas); - } catch (s) { - return true; + if (next === CHAR_RIGHT_SQUARE_BRACKET) { + isBracket = token.isBracket = true; + isGlob = token.isGlob = true; + finished = true; + break; } - return x instanceof WeakSet; // core-js workaround, pre-v2.5.0 - } catch (e) {} - return false; -} + } -function isElement(x) { - if (!x || typeof x !== 'object') { return false; } - if (typeof HTMLElement !== 'undefined' && x instanceof HTMLElement) { - return true; + if (scanToEnd === true) { + continue; + } + + break; } - return typeof x.nodeName === 'string' && typeof x.getAttribute === 'function'; -} -function inspectString(str, opts) { - if (str.length > opts.maxStringLength) { - var remaining = str.length - opts.maxStringLength; - var trailer = '... ' + remaining + ' more character' + (remaining > 1 ? 's' : ''); - return inspectString($slice.call(str, 0, opts.maxStringLength), opts) + trailer; + if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) { + negated = token.negated = true; + start++; + continue; } - // eslint-disable-next-line no-control-regex - var s = $replace.call($replace.call(str, /(['\\])/g, '\\$1'), /[\x00-\x1f]/g, lowbyte); - return wrapQuotes(s, 'single', opts); -} -function lowbyte(c) { - var n = c.charCodeAt(0); - var x = { - 8: 'b', - 9: 't', - 10: 'n', - 12: 'f', - 13: 'r' - }[n]; - if (x) { return '\\' + x; } - return '\\x' + (n < 0x10 ? '0' : '') + $toUpperCase.call(n.toString(16)); -} + if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) { + isGlob = token.isGlob = true; -function markBoxed(str) { - return 'Object(' + str + ')'; -} + if (scanToEnd === true) { + while (eos() !== true && (code = advance())) { + if (code === CHAR_LEFT_PARENTHESES) { + backslashes = token.backslashes = true; + code = advance(); + continue; + } -function weakCollectionOf(type) { - return type + ' { ? }'; -} + if (code === CHAR_RIGHT_PARENTHESES) { + finished = true; + break; + } + } + continue; + } + break; + } -function collectionOf(type, size, entries, indent) { - var joinedEntries = indent ? indentedJoin(entries, indent) : $join.call(entries, ', '); - return type + ' (' + size + ') {' + joinedEntries + '}'; -} + if (isGlob === true) { + finished = true; -function singleLineValues(xs) { - for (var i = 0; i < xs.length; i++) { - if (indexOf(xs[i], '\n') >= 0) { - return false; - } + if (scanToEnd === true) { + continue; + } + + break; } - return true; -} + } -function getIndent(opts, depth) { - var baseIndent; - if (opts.indent === '\t') { - baseIndent = '\t'; - } else if (typeof opts.indent === 'number' && opts.indent > 0) { - baseIndent = $join.call(Array(opts.indent + 1), ' '); - } else { - return null; + if (opts.noext === true) { + isExtglob = false; + isGlob = false; + } + + let base = str; + let prefix = ''; + let glob = ''; + + if (start > 0) { + prefix = str.slice(0, start); + str = str.slice(start); + lastIndex -= start; + } + + if (base && isGlob === true && lastIndex > 0) { + base = str.slice(0, lastIndex); + glob = str.slice(lastIndex); + } else if (isGlob === true) { + base = ''; + glob = str; + } else { + base = str; + } + + if (base && base !== '' && base !== '/' && base !== str) { + if (isPathSeparator(base.charCodeAt(base.length - 1))) { + base = base.slice(0, -1); } - return { - base: baseIndent, - prev: $join.call(Array(depth + 1), baseIndent) - }; -} + } -function indentedJoin(xs, indent) { - if (xs.length === 0) { return ''; } - var lineJoiner = '\n' + indent.prev + indent.base; - return lineJoiner + $join.call(xs, ',' + lineJoiner) + '\n' + indent.prev; -} + if (opts.unescape === true) { + if (glob) glob = utils.removeBackslashes(glob); -function arrObjKeys(obj, inspect) { - var isArr = isArray(obj); - var xs = []; - if (isArr) { - xs.length = obj.length; - for (var i = 0; i < obj.length; i++) { - xs[i] = has(obj, i) ? inspect(obj[i], obj) : ''; - } + if (base && backslashes === true) { + base = utils.removeBackslashes(base); } - var syms = typeof gOPS === 'function' ? gOPS(obj) : []; - var symMap; - if (hasShammedSymbols) { - symMap = {}; - for (var k = 0; k < syms.length; k++) { - symMap['$' + syms[k]] = syms[k]; - } + } + + const state = { + prefix, + input, + start, + base, + glob, + isBrace, + isBracket, + isGlob, + isExtglob, + isGlobstar, + negated, + negatedExtglob + }; + + if (opts.tokens === true) { + state.maxDepth = 0; + if (!isPathSeparator(code)) { + tokens.push(token); } + state.tokens = tokens; + } - for (var key in obj) { // eslint-disable-line no-restricted-syntax - if (!has(obj, key)) { continue; } // eslint-disable-line no-restricted-syntax, no-continue - if (isArr && String(Number(key)) === key && key < obj.length) { continue; } // eslint-disable-line no-restricted-syntax, no-continue - if (hasShammedSymbols && symMap['$' + key] instanceof Symbol) { - // this is to prevent shammed Symbols, which are stored as strings, from being included in the string key section - continue; // eslint-disable-line no-restricted-syntax, no-continue - } else if ($test.call(/[^\w$]/, key)) { - xs.push(inspect(key, obj) + ': ' + inspect(obj[key], obj)); + if (opts.parts === true || opts.tokens === true) { + let prevIndex; + + for (let idx = 0; idx < slashes.length; idx++) { + const n = prevIndex ? prevIndex + 1 : start; + const i = slashes[idx]; + const value = input.slice(n, i); + if (opts.tokens) { + if (idx === 0 && start !== 0) { + tokens[idx].isPrefix = true; + tokens[idx].value = prefix; } else { - xs.push(key + ': ' + inspect(obj[key], obj)); - } - } - if (typeof gOPS === 'function') { - for (var j = 0; j < syms.length; j++) { - if (isEnumerable.call(obj, syms[j])) { - xs.push('[' + inspect(syms[j]) + ']: ' + inspect(obj[syms[j]], obj)); - } + tokens[idx].value = value; } + depth(tokens[idx]); + state.maxDepth += tokens[idx].depth; + } + if (idx !== 0 || value !== '') { + parts.push(value); + } + prevIndex = i; } - return xs; -} + if (prevIndex && prevIndex + 1 < input.length) { + const value = input.slice(prevIndex + 1); + parts.push(value); -/***/ }), + if (opts.tokens) { + tokens[tokens.length - 1].value = value; + depth(tokens[tokens.length - 1]); + state.maxDepth += tokens[tokens.length - 1].depth; + } + } -/***/ 7265: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + state.slashes = slashes; + state.parts = parts; + } -module.exports = __nccwpck_require__(3837).inspect; + return state; +}; + +module.exports = scan; /***/ }), -/***/ 1223: -/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { +/***/ 479: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -var wrappy = __nccwpck_require__(2940) -module.exports = wrappy(once) -module.exports.strict = wrappy(onceStrict) -once.proto = once(function () { - Object.defineProperty(Function.prototype, 'once', { - value: function () { - return once(this) - }, - configurable: true - }) - Object.defineProperty(Function.prototype, 'onceStrict', { - value: function () { - return onceStrict(this) - }, - configurable: true - }) -}) +const path = __nccwpck_require__(1017); +const win32 = process.platform === 'win32'; +const { + REGEX_BACKSLASH, + REGEX_REMOVE_BACKSLASH, + REGEX_SPECIAL_CHARS, + REGEX_SPECIAL_CHARS_GLOBAL +} = __nccwpck_require__(6099); + +exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val); +exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str); +exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str); +exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1'); +exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/'); + +exports.removeBackslashes = str => { + return str.replace(REGEX_REMOVE_BACKSLASH, match => { + return match === '\\' ? '' : match; + }); +}; -function once (fn) { - var f = function () { - if (f.called) return f.value - f.called = true - return f.value = fn.apply(this, arguments) +exports.supportsLookbehinds = () => { + const segs = process.version.slice(1).split('.').map(Number); + if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) { + return true; + } + return false; +}; + +exports.isWindows = options => { + if (options && typeof options.windows === 'boolean') { + return options.windows; + } + return win32 === true || path.sep === '\\'; +}; + +exports.escapeLast = (input, char, lastIdx) => { + const idx = input.lastIndexOf(char, lastIdx); + if (idx === -1) return input; + if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1); + return `${input.slice(0, idx)}\\${input.slice(idx)}`; +}; + +exports.removePrefix = (input, state = {}) => { + let output = input; + if (output.startsWith('./')) { + output = output.slice(2); + state.prefix = './'; } - f.called = false - return f -} + return output; +}; -function onceStrict (fn) { - var f = function () { - if (f.called) - throw new Error(f.onceError) - f.called = true - return f.value = fn.apply(this, arguments) +exports.wrapOutput = (input, state = {}, options = {}) => { + const prepend = options.contains ? '' : '^'; + const append = options.contains ? '' : '$'; + + let output = `${prepend}(?:${input})${append}`; + if (state.negated === true) { + output = `(?:^(?!${output}).*$)`; } - var name = fn.name || 'Function wrapped with `once`' - f.onceError = name + " shouldn't be called more than once" - f.called = false - return f -} + return output; +}; /***/ }), @@ -9409,6 +13372,301 @@ module.exports = function getSideChannel() { }; +/***/ }), + +/***/ 1861: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +/*! + * to-regex-range + * + * Copyright (c) 2015-present, Jon Schlinkert. + * Released under the MIT License. + */ + + + +const isNumber = __nccwpck_require__(5680); + +const toRegexRange = (min, max, options) => { + if (isNumber(min) === false) { + throw new TypeError('toRegexRange: expected the first argument to be a number'); + } + + if (max === void 0 || min === max) { + return String(min); + } + + if (isNumber(max) === false) { + throw new TypeError('toRegexRange: expected the second argument to be a number.'); + } + + let opts = { relaxZeros: true, ...options }; + if (typeof opts.strictZeros === 'boolean') { + opts.relaxZeros = opts.strictZeros === false; + } + + let relax = String(opts.relaxZeros); + let shorthand = String(opts.shorthand); + let capture = String(opts.capture); + let wrap = String(opts.wrap); + let cacheKey = min + ':' + max + '=' + relax + shorthand + capture + wrap; + + if (toRegexRange.cache.hasOwnProperty(cacheKey)) { + return toRegexRange.cache[cacheKey].result; + } + + let a = Math.min(min, max); + let b = Math.max(min, max); + + if (Math.abs(a - b) === 1) { + let result = min + '|' + max; + if (opts.capture) { + return `(${result})`; + } + if (opts.wrap === false) { + return result; + } + return `(?:${result})`; + } + + let isPadded = hasPadding(min) || hasPadding(max); + let state = { min, max, a, b }; + let positives = []; + let negatives = []; + + if (isPadded) { + state.isPadded = isPadded; + state.maxLen = String(state.max).length; + } + + if (a < 0) { + let newMin = b < 0 ? Math.abs(b) : 1; + negatives = splitToPatterns(newMin, Math.abs(a), state, opts); + a = state.a = 0; + } + + if (b >= 0) { + positives = splitToPatterns(a, b, state, opts); + } + + state.negatives = negatives; + state.positives = positives; + state.result = collatePatterns(negatives, positives, opts); + + if (opts.capture === true) { + state.result = `(${state.result})`; + } else if (opts.wrap !== false && (positives.length + negatives.length) > 1) { + state.result = `(?:${state.result})`; + } + + toRegexRange.cache[cacheKey] = state; + return state.result; +}; + +function collatePatterns(neg, pos, options) { + let onlyNegative = filterPatterns(neg, pos, '-', false, options) || []; + let onlyPositive = filterPatterns(pos, neg, '', false, options) || []; + let intersected = filterPatterns(neg, pos, '-?', true, options) || []; + let subpatterns = onlyNegative.concat(intersected).concat(onlyPositive); + return subpatterns.join('|'); +} + +function splitToRanges(min, max) { + let nines = 1; + let zeros = 1; + + let stop = countNines(min, nines); + let stops = new Set([max]); + + while (min <= stop && stop <= max) { + stops.add(stop); + nines += 1; + stop = countNines(min, nines); + } + + stop = countZeros(max + 1, zeros) - 1; + + while (min < stop && stop <= max) { + stops.add(stop); + zeros += 1; + stop = countZeros(max + 1, zeros) - 1; + } + + stops = [...stops]; + stops.sort(compare); + return stops; +} + +/** + * Convert a range to a regex pattern + * @param {Number} `start` + * @param {Number} `stop` + * @return {String} + */ + +function rangeToPattern(start, stop, options) { + if (start === stop) { + return { pattern: start, count: [], digits: 0 }; + } + + let zipped = zip(start, stop); + let digits = zipped.length; + let pattern = ''; + let count = 0; + + for (let i = 0; i < digits; i++) { + let [startDigit, stopDigit] = zipped[i]; + + if (startDigit === stopDigit) { + pattern += startDigit; + + } else if (startDigit !== '0' || stopDigit !== '9') { + pattern += toCharacterClass(startDigit, stopDigit, options); + + } else { + count++; + } + } + + if (count) { + pattern += options.shorthand === true ? '\\d' : '[0-9]'; + } + + return { pattern, count: [count], digits }; +} + +function splitToPatterns(min, max, tok, options) { + let ranges = splitToRanges(min, max); + let tokens = []; + let start = min; + let prev; + + for (let i = 0; i < ranges.length; i++) { + let max = ranges[i]; + let obj = rangeToPattern(String(start), String(max), options); + let zeros = ''; + + if (!tok.isPadded && prev && prev.pattern === obj.pattern) { + if (prev.count.length > 1) { + prev.count.pop(); + } + + prev.count.push(obj.count[0]); + prev.string = prev.pattern + toQuantifier(prev.count); + start = max + 1; + continue; + } + + if (tok.isPadded) { + zeros = padZeros(max, tok, options); + } + + obj.string = zeros + obj.pattern + toQuantifier(obj.count); + tokens.push(obj); + start = max + 1; + prev = obj; + } + + return tokens; +} + +function filterPatterns(arr, comparison, prefix, intersection, options) { + let result = []; + + for (let ele of arr) { + let { string } = ele; + + // only push if _both_ are negative... + if (!intersection && !contains(comparison, 'string', string)) { + result.push(prefix + string); + } + + // or _both_ are positive + if (intersection && contains(comparison, 'string', string)) { + result.push(prefix + string); + } + } + return result; +} + +/** + * Zip strings + */ + +function zip(a, b) { + let arr = []; + for (let i = 0; i < a.length; i++) arr.push([a[i], b[i]]); + return arr; +} + +function compare(a, b) { + return a > b ? 1 : b > a ? -1 : 0; +} + +function contains(arr, key, val) { + return arr.some(ele => ele[key] === val); +} + +function countNines(min, len) { + return Number(String(min).slice(0, -len) + '9'.repeat(len)); +} + +function countZeros(integer, zeros) { + return integer - (integer % Math.pow(10, zeros)); +} + +function toQuantifier(digits) { + let [start = 0, stop = ''] = digits; + if (stop || start > 1) { + return `{${start + (stop ? ',' + stop : '')}}`; + } + return ''; +} + +function toCharacterClass(a, b, options) { + return `[${a}${(b - a === 1) ? '' : '-'}${b}]`; +} + +function hasPadding(str) { + return /^-?(0+)\d/.test(str); +} + +function padZeros(value, tok, options) { + if (!tok.isPadded) { + return value; + } + + let diff = Math.abs(tok.maxLen - String(value).length); + let relax = options.relaxZeros !== false; + + switch (diff) { + case 0: + return ''; + case 1: + return relax ? '0?' : '0'; + case 2: + return relax ? '0{0,2}' : '00'; + default: { + return relax ? `0{0,${diff}}` : `0{${diff}}`; + } + } +} + +/** + * Cache + */ + +toRegexRange.cache = {}; +toRegexRange.clearCache = () => (toRegexRange.cache = {}); + +/** + * Expose `toRegexRange` + */ + +module.exports = toRegexRange; + + /***/ }), /***/ 4256: @@ -17952,6 +22210,7 @@ __nccwpck_require__.d(core_dist_namespaceObject, { "Avatar": () => (Avatar), "Branches": () => (Branches), "BroadcastMessages": () => (BroadcastMessages), + "CodeSuggestions": () => (CodeSuggestions), "CommitDiscussions": () => (CommitDiscussions), "Commits": () => (Commits), "Composer": () => (Composer), @@ -17981,6 +22240,7 @@ __nccwpck_require__.d(core_dist_namespaceObject, { "FeatureFlags": () => (FeatureFlags), "FreezePeriods": () => (FreezePeriods), "GeoNodes": () => (GeoNodes), + "GeoSites": () => (GeoSites), "GitLabCIYMLTemplates": () => (GitLabCIYMLTemplates), "GitignoreTemplates": () => (GitignoreTemplates), "Gitlab": () => (Gitlab), @@ -17992,6 +22252,7 @@ __nccwpck_require__.d(core_dist_namespaceObject, { "GroupBadges": () => (GroupBadges), "GroupCustomAttributes": () => (GroupCustomAttributes), "GroupDORA4Metrics": () => (GroupDORA4Metrics), + "GroupEpicBoards": () => (GroupEpicBoards), "GroupHooks": () => (GroupHooks), "GroupImportExports": () => (GroupImportExports), "GroupInvitations": () => (GroupInvitations), @@ -18008,6 +22269,7 @@ __nccwpck_require__.d(core_dist_namespaceObject, { "GroupReleases": () => (GroupReleases), "GroupRepositoryStorageMoves": () => (GroupRepositoryStorageMoves), "GroupSAMLIdentities": () => (GroupSAMLIdentities), + "GroupSAMLLinks": () => (GroupSAMLLinks), "GroupSCIMIdentities": () => (GroupSCIMIdentities), "GroupVariables": () => (GroupVariables), "GroupWikis": () => (GroupWikis), @@ -18104,6 +22366,7 @@ __nccwpck_require__.d(core_dist_namespaceObject, { "RubyGems": () => (RubyGems), "Runners": () => (Runners), "Search": () => (Search), + "SearchAdmin": () => (SearchAdmin), "SecureFiles": () => (SecureFiles), "ServiceData": () => (ServiceData), "SidekiqMetrics": () => (SidekiqMetrics), @@ -18124,17 +22387,83 @@ __nccwpck_require__.d(core_dist_namespaceObject, { "Users": () => (Users) }); -// EXTERNAL MODULE: ./node_modules/dotenv/lib/main.js -var main = __nccwpck_require__(2437); +;// CONCATENATED MODULE: ./adapters/logger/logger.js +// logger.js + +function getCurrentTimestamp() { + const now = new Date(); + return now.toISOString(); +} + +function logInfo(message, method) { + const timestamp = getCurrentTimestamp(); + const logEntry = { + level: "ERROR", + timestamp, + method, + message, + }; + console.error(logEntry); +} + +function withInfo(message, vcs, sha, method) { + const timestamp = getCurrentTimestamp(); + const logEntry = { + level: "INFO", + timestamp, + vcs, + sha, + method, + message, + }; + console.log(logEntry); +} + +function withError(message, vcs, sha, method) { + const timestamp = getCurrentTimestamp(); + const logEntry = { + level: "ERROR", + timestamp, + vcs, + sha, + method, + message, + }; + console.error(logEntry); +} + +function debug(message, vcs, sha, method) { + const timestamp = getCurrentTimestamp(); + const logEntry = { + level: "DEBUG", + timestamp, + vcs, + sha, + method, + message, + }; + console.debug(logEntry); +} + +const logger = { + withInfo, + withError, + debug, + logInfo, +}; + +/* harmony default export */ const logger_logger = (logger); + ;// CONCATENATED MODULE: ./adapters/gateway.js // Common Gateway for all integrations + async function runAction(token, integrationModule) { if (token === undefined) { - console.log("Token not provided."); + logger_logger.logInfo("Token not provided.", "runAction"); return; } const integration = new integrationModule(token); - await integration.run(); // Add error part + await integration.run(); } ;// CONCATENATED MODULE: ./adapters/integrations/contract/contract.js @@ -18195,62 +22524,29 @@ class IntegrationInterface { // EXTERNAL MODULE: ./node_modules/@actions/github/lib/github.js var github = __nccwpck_require__(5438); -// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js -var core = __nccwpck_require__(2186); -;// CONCATENATED MODULE: ./src/utils/get-environment-variables.js - - - -main.config(); - -const { - IS_DEV, - ATLAN_INSTANCE_URL, - ATLAN_API_TOKEN, - IGNORE_MODEL_ALIAS_MATCHING, -} = process.env; - -const isDev = () => IS_DEV === "true"; -const getInstanceUrl = () => { - if (ATLAN_INSTANCE_URL) return new URL(ATLAN_INSTANCE_URL).origin; - return new URL(core.getInput("ATLAN_INSTANCE_URL")).origin; -}; -const getAPIToken = () => { - if (ATLAN_API_TOKEN) return ATLAN_API_TOKEN; - return core.getInput("ATLAN_API_TOKEN"); -}; -const getEnvironments = () => { - return core.getInput("DBT_ENVIRONMENT_BRANCH_MAP") - ? core.getInput("DBT_ENVIRONMENT_BRANCH_MAP") - .trim() - ?.split("\n") - ?.map((i) => i.split(":").map((i) => i.trim())) - : []; -}; -const isIgnoreModelAliasMatching = () => - core.getInput("IGNORE_MODEL_ALIAS_MATCHING") === "true"; +// EXTERNAL MODULE: ./node_modules/json-stringify-safe/stringify.js +var stringify = __nccwpck_require__(7073); +;// CONCATENATED MODULE: ./adapters/utils/get-image-url.js -function getGitLabEnvironments() { - const { DBT_ENVIRONMENT_BRANCH_MAP } = process.env; - if (DBT_ENVIRONMENT_BRANCH_MAP) { - const environmentLines = DBT_ENVIRONMENT_BRANCH_MAP.split("\n"); - const environmentMap = {}; +function getImageURL(name, height = 20, width = 20) { + try { + return `${hosted_images[name].alt}`; + } catch (e) { + console.log(name); + return ''; + } +} - environmentLines.forEach((line) => { - const [environment, branch] = line.split(":").map((item) => item.trim()); - if (environment && branch) { - environmentMap[environment] = branch; - } - }); +function getConnectorImage(connectorName) { + return getImageURL(`connector-${connectorName.toLowerCase()}`, 15, 15); +} - return environmentMap; - } else { - return {}; - } +function getCertificationImage(certificationStatus) { + return getImageURL(`certification-${certificationStatus.toLowerCase()}`, 15, 15); } -;// CONCATENATED MODULE: ./src/utils/hosted-images.js +;// CONCATENATED MODULE: ./adapters/utils/hosted-images.js /* harmony default export */ const hosted_images = ({ "atlan-logo": { alt: "Atlan Logo", @@ -18373,197 +22669,24 @@ function getGitLabEnvironments() { url: "https://assets.atlan.com/assets/tableau.svg", }, "connector-mode": { - alt: "Connector Mode", - url: "https://iili.io/HVTAlgs.png" - }, - "connector-sigma": { - alt: "Connector Sigma", - url: "https://iili.io/HVTA1dG.png" - } -}); - -;// CONCATENATED MODULE: ./src/utils/create-comment.js - - - -const create_comment_IS_DEV = isDev(); -const create_comment_ATLAN_INSTANCE_URL = - getInstanceUrl(); - -function truncate(value) { - if (typeof value === 'string') - return value.length > 100 ? value.substring(0, 100) + "..." : value; - if (Array.isArray(value)) - return value.length > 10 ? value.slice(0, 10).join(", ") + "..." : value.join(", "); - return "" -} - -async function renderDownstreamAssetsComment( - octokit, - context, - asset, - materialisedAsset, - downstreamAssets, - classifications -) { - // Mapping the downstream assets data - let impactedData = downstreamAssets.entities.map( - ({ - displayText, - guid, - typeName, - attributes, - meanings, - classificationNames - }) => { - // Modifying the typeName and getting the readableTypeName - let readableTypeName = typeName - .toLowerCase() - .replace(attributes.connectorName, "") - .toUpperCase(); - - // Filtering classifications based on classificationNames - let classificationsObj = classifications.filter(({name}) => - classificationNames.includes(name) - ); - - // Modifying the readableTypeName - readableTypeName = readableTypeName.charAt(0).toUpperCase() + readableTypeName.slice(1).toLowerCase(); - - return [ - guid, - truncate(displayText), - truncate(attributes.connectorName), - truncate(readableTypeName), - truncate(attributes?.userDescription || attributes?.description || ""), - attributes?.certificateStatus || "", - truncate([...attributes?.ownerUsers, ...attributes?.ownerGroups] || []), - truncate(meanings.map(({displayText, termGuid}) => - `[${displayText}](${create_comment_ATLAN_INSTANCE_URL}/assets/${termGuid}/overview?utm_source=dbt_github_action)` - )), - truncate(classificationsObj?.map(({name, displayName}) => - `\`${displayName}\`` - )), - attributes?.sourceURL || "" - ]; - } - ); - - // Sorting the impactedData first by typeName and then by connectorName - impactedData = impactedData.sort((a, b) => a[3].localeCompare(b[3])); - impactedData = impactedData.sort((a, b) => a[2].localeCompare(b[2])); - - // Creating rows for the downstream table - let rows = impactedData.map( - ([guid, displayText, connectorName, typeName, description, certificateStatus, owners, meanings, classifications, sourceUrl]) => { - // Getting connector and certification images - const connectorImage = getConnectorImage(connectorName); - const certificationImage = certificateStatus ? getCertificationImage(certificateStatus) : ""; - - return [ - `${connectorImage} [${displayText}](${create_comment_ATLAN_INSTANCE_URL}/assets/${guid}/overview?utm_source=dbt_github_action) ${certificationImage}`, - `\`${typeName}\``, - description, - owners, - meanings, - classifications, - sourceUrl ? `[Open in ${connectorName}](${sourceUrl})` : " " - ]; - } - ); - - const environmentName = materialisedAsset?.attributes?.assetDbtEnvironmentName - const projectName = materialisedAsset?.attributes?.assetDbtProjectName - // Generating asset information - const assetInfo = `### ${getConnectorImage(asset.attributes.connectorName)} [${ - asset.displayText - }](${create_comment_ATLAN_INSTANCE_URL}/assets/${asset.guid}/overview?utm_source=dbt_github_action) ${ - asset.attributes?.certificateStatus - ? getCertificationImage(asset.attributes.certificateStatus) - : "" - } -Materialised asset: ${getConnectorImage(materialisedAsset.attributes.connectorName)} [${ - materialisedAsset.attributes.name - }](${create_comment_ATLAN_INSTANCE_URL}/assets/${materialisedAsset.guid}/overview?utm_source=dbt_github_action) ${ - materialisedAsset.attributes?.certificateStatus - ? getCertificationImage(materialisedAsset.attributes.certificateStatus) - : "" - }${environmentName ? ` | Environment Name: \`${environmentName}\`` : ''}${projectName ? ` | Project Name: \`${projectName}\`` : ''}`; - - // Generating the downstream table - const downstreamTable = `
${downstreamAssets.entityCount} downstream assets 👇
- -Name | Type | Description | Owners | Terms | Classifications | Source URL ---- | --- | --- | --- | --- | --- | --- -${rows.map((row) => row.map(i => i.replace(/\|/g, "•").replace(/\n/g, "")).join(" | ")).join("\n")} - -${downstreamAssets.hasMore ? `[See more downstream assets at Atlan](${create_comment_ATLAN_INSTANCE_URL}/assets/${materialisedAsset.guid}/lineage?utm_source=dbt_github_action)` : ""} - -
`; - - // Generating the "View asset in Atlan" button - const viewAssetButton = `${getImageURL("atlan-logo", 15, 15)} [View asset in Atlan](${create_comment_ATLAN_INSTANCE_URL}/assets/${asset.guid}/overview?utm_source=dbt_github_action)`; - - // Generating the final comment based on the presence of downstream assets - if (downstreamAssets.entities.length > 0) { - return `${assetInfo} - -${downstreamTable} - -${viewAssetButton}`; - } else { - return `${assetInfo} - -No downstream assets found. - -${viewAssetButton}`; + alt: "Connector Mode", + url: "https://iili.io/HVTAlgs.png" + }, + "connector-sigma": { + alt: "Connector Sigma", + url: "https://iili.io/HVTA1dG.png" } -} - - -async function checkCommentExists(octokit, context) { - if (create_comment_IS_DEV) return null; - - const {pull_request} = context.payload; - - const comments = await octokit.rest.issues.listComments({ - ...context.repo, - issue_number: pull_request.number, - }); - - return comments.data.find( - (comment) => comment.user.login === "github-actions[bot]" && comment.body.includes("") - ); -} - -async function createIssueComment(octokit, context, content, comment_id = null, forceNewComment = false) { - const {pull_request} = context.payload; - - content = ` -${content}` - - const commentObj = { - ...context.repo, - issue_number: pull_request.number, - body: content, - }; - - console.log(content, content.length) - - if (create_comment_IS_DEV) return content; - - if (comment_id && !forceNewComment) return octokit.rest.issues.updateComment({...commentObj, comment_id}); - return octokit.rest.issues.createComment(commentObj); -} - -async function deleteComment(octokit, context, comment_id) { - const {pull_request} = context.payload; +}); - return octokit.rest.issues.deleteComment({ - ...context.repo, - issue_number: pull_request.number, - comment_id, - }); +;// CONCATENATED MODULE: ./adapters/utils/create-comment.js +function truncate(value) { + if (typeof value === "string") + return value.length > 100 ? value.substring(0, 100) + "..." : value; + if (Array.isArray(value)) + return value.length > 10 + ? value.slice(0, 10).join(", ") + "..." + : value.join(", "); + return ""; } ;// CONCATENATED MODULE: external "node:http" @@ -20715,23 +24838,102 @@ function fixResponseChunkedTransferBadEnding(request, errorCallback) { }); } -;// CONCATENATED MODULE: ./src/utils/auth.js +// EXTERNAL MODULE: ./node_modules/dotenv/lib/main.js +var main = __nccwpck_require__(2437); +// EXTERNAL MODULE: ./node_modules/@actions/core/lib/core.js +var core = __nccwpck_require__(2186); +;// CONCATENATED MODULE: ./adapters/utils/get-environment-variables.js + + +main.config(); +//Common env variables +const ATLAN_INSTANCE_URL = new URL( + process.env.ATLAN_INSTANCE_URL || core.getInput("ATLAN_INSTANCE_URL") +).origin; +const ATLAN_API_TOKEN = + process.env.ATLAN_API_TOKEN || core.getInput("ATLAN_API_TOKEN"); +const IS_DEV = process.env.IS_DEV; + +const IGNORE_MODEL_ALIAS_MATCHING = + (process.env.IGNORE_MODEL_ALIAS_MATCHING || + core.getInput("IGNORE_MODEL_ALIAS_MATCHING")) == "true"; + +//GITLAB SPECIFIC ENV VARIABLES +async function getCIMergeRequestIID( + gitlab, + CI_PROJECT_ID, + CI_COMMIT_SHA +) { + if (!process.env.CI_MERGE_REQUEST_IID) { + const mergeRequestCommit = await gitlab.Commits.allMergeRequests( + CI_PROJECT_ID, + CI_COMMIT_SHA + ); + + const firstMergeRequest = mergeRequestCommit[0]; + if (firstMergeRequest) { + return firstMergeRequest.iid; + } + } + + return process.env.CI_MERGE_REQUEST_IID; +} + +const { + CI_PROJECT_PATH, + CI_PROJECT_ID, + CI_JOB_URL, + GITLAB_TOKEN, + CI_COMMIT_MESSAGE, + GITLAB_USER_LOGIN, + CI_PROJECT_NAME, + CI_COMMIT_SHA, + CI_PROJECT_NAMESPACE, +} = process.env; + +function getGitLabEnvironments() { + const { DBT_ENVIRONMENT_BRANCH_MAP } = process.env; + + if (DBT_ENVIRONMENT_BRANCH_MAP) { + const environmentLines = DBT_ENVIRONMENT_BRANCH_MAP.split("\n"); + const environmentMap = {}; + + environmentLines.forEach((line) => { + const [environment, branch] = line.split(":").map((item) => item.trim()); + if (environment && branch) { + environmentMap[environment] = branch; + } + }); + + return environmentMap; + } else { + return {}; + } +} + +//GITHUB SPECIFIC ENV VARIABLES +const GITHUB_TOKEN = + core.getInput("GITHUB_TOKEN") || process.env.GITHUB_TOKEN; + +const getEnvironments = () => { + return ( + core.getInput("DBT_ENVIRONMENT_BRANCH_MAP") + ?.trim() + ?.split("\n") + ?.map((i) => i.split(":").map((i) => i.trim())) ?? [] + ); +}; + +;// CONCATENATED MODULE: ./adapters/utils/auth.js -main.config(); -const auth_ATLAN_INSTANCE_URL = - core.getInput("ATLAN_INSTANCE_URL") || process.env.ATLAN_INSTANCE_URL; -const auth_ATLAN_API_TOKEN = - core.getInput("ATLAN_API_TOKEN") || process.env.ATLAN_API_TOKEN; async function auth() { - //Done - console.log("Inside auth in utils.js"); var myHeaders = { - authorization: `Bearer ${auth_ATLAN_API_TOKEN}`, + authorization: `Bearer ${ATLAN_API_TOKEN}`, "content-type": "application/json", }; @@ -20741,59 +24943,29 @@ async function auth() { }; var response = await src_fetch( - `${auth_ATLAN_INSTANCE_URL}/api/meta`, + `${ATLAN_INSTANCE_URL}/api/meta`, requestOptions ).catch((err) => {}); - console.log("Completed auth in utils.js"); + return response; } -;// CONCATENATED MODULE: ./src/utils/index.js - - - - - - -;// CONCATENATED MODULE: ./src/utils/get-image-url.js - - -function get_image_url_getImageURL(name, height = 20, width = 20) { - try { - return `${hosted_images[name].alt}`; - } catch (e) { - console.log(name); - return ''; - } -} +;// CONCATENATED MODULE: ./adapters/utils/index.js -function get_image_url_getConnectorImage(connectorName) { - return get_image_url_getImageURL(`connector-${connectorName.toLowerCase()}`, 15, 15); -} -function get_image_url_getCertificationImage(certificationStatus) { - return get_image_url_getImageURL(`certification-${certificationStatus.toLowerCase()}`, 15, 15); -} -// EXTERNAL MODULE: ./node_modules/json-stringify-safe/stringify.js -var stringify = __nccwpck_require__(7073); -;// CONCATENATED MODULE: ./src/api/get-downstream-assets.js +;// CONCATENATED MODULE: ./adapters/api/get-downstream-assets.js -main.config(); -const get_downstream_assets_ATLAN_INSTANCE_URL = - core.getInput("ATLAN_INSTANCE_URL") || process.env.ATLAN_INSTANCE_URL; -const get_downstream_assets_ATLAN_API_TOKEN = - core.getInput("ATLAN_API_TOKEN") || process.env.ATLAN_API_TOKEN; const ASSETS_LIMIT = 100; -async function getDownstreamAssets( //Done +async function getDownstreamAssets( asset, guid, totalModifiedFiles, @@ -20801,7 +24973,7 @@ async function getDownstreamAssets( //Done integration ) { var myHeaders = { - authorization: `Bearer ${get_downstream_assets_ATLAN_API_TOKEN}`, + authorization: `Bearer ${ATLAN_API_TOKEN}`, "content-type": "application/json", }; @@ -20852,39 +25024,42 @@ async function getDownstreamAssets( //Done }; var handleError = (err) => { - const comment = `### ${get_image_url_getConnectorImage( + const comment = `### ${getConnectorImage( asset.attributes.connectorName - )} [${asset.displayText}](${get_downstream_assets_ATLAN_INSTANCE_URL}/assets/${ + )} [${asset.displayText}](${ATLAN_INSTANCE_URL}/assets/${ asset.guid }/overview?utm_source=dbt_${integration}_action) ${ asset.attributes?.certificateStatus - ? get_image_url_getCertificationImage(asset.attributes.certificateStatus) + ? getCertificationImage(asset.attributes.certificateStatus) : "" } _Failed to fetch impacted assets._ -${get_image_url_getImageURL( +${getImageURL( "atlan-logo", 15, 15 -)} [View lineage in Atlan](${get_downstream_assets_ATLAN_INSTANCE_URL}/assets/${ +)} [View lineage in Atlan](${ATLAN_INSTANCE_URL}/assets/${ asset.guid }/lineage/overview?utm_source=dbt_${integration}_action)`; - sendSegmentEventOfIntegration("dbt_ci_action_failure", { - reason: "failed_to_fetch_lineage", - asset_guid: asset.guid, - asset_name: asset.name, - asset_typeName: asset.typeName, - msg: err, + sendSegmentEventOfIntegration({ + action: "dbt_ci_action_failure", + properties: { + reason: "failed_to_fetch_lineage", + asset_guid: asset.guid, + asset_name: asset.name, + asset_typeName: asset.typeName, + msg: err, + }, }); return comment; }; var response = await src_fetch( - `${get_downstream_assets_ATLAN_INSTANCE_URL}/api/meta/lineage/list`, + `${ATLAN_INSTANCE_URL}/api/meta/lineage/list`, requestOptions ) .then((e) => { @@ -20899,34 +25074,58 @@ ${get_image_url_getImageURL( error: handleError(err), }; }); - if (response.error) return response; return response; } -;// CONCATENATED MODULE: ./src/api/get-asset.js +;// CONCATENATED MODULE: ./adapters/templates/atlan.js + + +function getErrorModelNotFound(name) { + return ` +
❌ Model with name **${name}** could not be found or is deleted

+ `; +} + +function getErrorDoesNotMaterialize( + name, + ATLAN_INSTANCE_URL, + response, + integration +) { + + return ` +
❌ Model with name [${name}](${ATLAN_INSTANCE_URL}/assets/${response.entities[0].guid}/overview?utm_source=dbt_${integration}_action) does not materialise any asset

`; +} +function getNewModelAddedComment(fileName) { + return `### ${getConnectorImage("dbt")} ${fileName} 🆕 + Its a new model and not present in Atlan yet, you'll see the downstream impact for it after its present in Atlan.` +} +function getBaseComment(totalChangedFiles, comments) { + return `### ${getImageURL("atlan-logo", 15, 15)} Atlan impact analysis + Here is your downstream impact analysis for **${totalChangedFiles} ${ + totalChangedFiles > 1 ? "models" : "model" + }** you have edited. + + ${comments}` +} +;// CONCATENATED MODULE: ./adapters/api/get-asset.js -main.config(); -const get_asset_ATLAN_INSTANCE_URL = - core.getInput("ATLAN_INSTANCE_URL") || process.env.ATLAN_INSTANCE_URL; -const get_asset_ATLAN_API_TOKEN = - core.getInput("ATLAN_API_TOKEN") || process.env.ATLAN_API_TOKEN; async function getAsset({ - //Done name, sendSegmentEventOfIntegration, environment, integration, }) { var myHeaders = { - Authorization: `Bearer ${get_asset_ATLAN_API_TOKEN}`, + Authorization: `Bearer ${ATLAN_API_TOKEN}`, "Content-Type": "application/json", }; @@ -20996,9 +25195,9 @@ async function getAsset({ headers: myHeaders, body: raw, }; - console.log("Before SendSegmentEventOfIntegration"); + var response = await src_fetch( - `${get_asset_ATLAN_INSTANCE_URL}/api/meta/search/indexsearch#findAssetByExactName`, + `${ATLAN_INSTANCE_URL}/api/meta/search/indexsearch#findAssetByExactName`, requestOptions ) .then((e) => e.json()) @@ -21012,35 +25211,61 @@ async function getAsset({ }, }); }); - console.log("<><><><><><><><><><><><><>"); - console.log(response); - if (!response?.entities?.length) + + if (!response?.entities?.length) { return { - error: `❌ Model with name **${name}** could not be found or is deleted

`, + error: getErrorModelNotFound(name), }; + } + + if (Array.isArray(response.entities)) { + response.entities.sort((entityA, entityB) => { + const hasDbtModelSqlAssetsA = + entityA.attributes.dbtModelSqlAssets && + entityA.attributes.dbtModelSqlAssets.length > 0; + const hasDbtModelSqlAssetsB = + entityB.attributes.dbtModelSqlAssets && + entityB.attributes.dbtModelSqlAssets.length > 0; + + if (hasDbtModelSqlAssetsA && !hasDbtModelSqlAssetsB) { + return -1; // entityA comes before entityB + } else if (!hasDbtModelSqlAssetsA && hasDbtModelSqlAssetsB) { + return 1; // entityB comes before entityA + } + + // Primary sorting criterion: Latest createTime comes first + if (entityA.createTime > entityB.createTime) { + return -1; + } else if (entityA.createTime < entityB.createTime) { + return 1; + } + + return 0; // No difference in sorting for these two entities + }); + } if (!response?.entities[0]?.attributes?.dbtModelSqlAssets?.length > 0) return { - error: `❌ Model with name [${name}](${get_asset_ATLAN_INSTANCE_URL}/assets/${response.entities[0].guid}/overview?utm_source=dbt_${integration}_action) does not materialise any asset

`, + error: getErrorDoesNotMaterialize( + name, + ATLAN_INSTANCE_URL, + response, + integration + ), }; return response.entities[0]; } -;// CONCATENATED MODULE: ./src/api/get-classifications.js - - - +;// CONCATENATED MODULE: ./adapters/api/get-classifications.js -const get_classifications_ATLAN_INSTANCE_URL = getInstanceUrl(); -const get_classifications_ATLAN_API_TOKEN = getAPIToken(); async function getClassifications({ sendSegmentEventOfIntegration, }) { var myHeaders = { - Authorization: `Bearer ${get_classifications_ATLAN_API_TOKEN}`, + Authorization: `Bearer ${ATLAN_API_TOKEN}`, "Content-Type": "application/json", }; @@ -21051,14 +25276,17 @@ async function getClassifications({ }; var response = await src_fetch( - `${get_classifications_ATLAN_INSTANCE_URL}/api/meta/types/typedefs?type=classification`, + `${ATLAN_INSTANCE_URL}/api/meta/types/typedefs?type=classification`, requestOptions ) .then((e) => e.json()) .catch((err) => { - sendSegmentEventOfIntegration("dbt_ci_action_failure", { - reason: "failed_to_get_classifications", - msg: err, + sendSegmentEventOfIntegration({ + action: "dbt_ci_action_failure", + properties: { + reason: "failed_to_get_classifications", + msg: err, + }, }); }); @@ -21079,23 +25307,20 @@ const validate = uuid_dist/* validate */.Gu; const wrapper_stringify = uuid_dist/* stringify */.Pz; const parse = uuid_dist/* parse */.Qc; -;// CONCATENATED MODULE: ./src/api/create-resource.js +;// CONCATENATED MODULE: ./adapters/api/create-resource.js -const create_resource_ATLAN_INSTANCE_URL = getInstanceUrl(); -const create_resource_ATLAN_API_TOKEN = getAPIToken(); - -async function createResource( //Done +async function createResource( guid, name, link, sendSegmentEventOfIntegration ) { var myHeaders = { - Authorization: `Bearer ${create_resource_ATLAN_API_TOKEN}`, + Authorization: `Bearer ${ATLAN_API_TOKEN}`, "Content-Type": "application/json", }; @@ -21125,40 +25350,32 @@ async function createResource( //Done }; var response = await src_fetch( - `${create_resource_ATLAN_INSTANCE_URL}/api/meta/entity/bulk`, + `${ATLAN_INSTANCE_URL}/api/meta/entity/bulk`, requestOptions ) .then((e) => e.json()) .catch((err) => { console.log(err); - sendSegmentEventOfIntegration("dbt_ci_action_failure", { - reason: "failed_to_create_resource", - asset_name: name, - msg: err, + sendSegmentEventOfIntegration({ + action: "dbt_ci_action_failure", + properties: { + reason: "failed_to_create_resource", + asset_name: name, // This should change + msg: err, + }, }); }); - console.log("Created Resource:", response); - return response; } -;// CONCATENATED MODULE: ./src/api/segment.js - - +;// CONCATENATED MODULE: ./adapters/api/segment.js -main.config(); - -const { IS_DEV: segment_IS_DEV } = process.env; -const segment_ATLAN_INSTANCE_URL = - core.getInput("ATLAN_INSTANCE_URL") || process.env.ATLAN_INSTANCE_URL; -const segment_ATLAN_API_TOKEN = - core.getInput("ATLAN_API_TOKEN") || process.env.ATLAN_API_TOKEN; async function sendSegmentEvent(action, body) { const myHeaders = { - authorization: `Bearer ${segment_ATLAN_API_TOKEN}`, + authorization: `Bearer ${ATLAN_API_TOKEN}`, "content-type": "application/json", }; @@ -21169,9 +25386,9 @@ async function sendSegmentEvent(action, body) { }; var response = null; - if (!segment_IS_DEV) { + if (!IS_DEV) { response = await src_fetch( - `${segment_ATLAN_INSTANCE_URL}/api/service/segment/track`, + `${ATLAN_INSTANCE_URL}/api/service/segment/track`, requestOptions ) .then(() => { @@ -21187,7 +25404,7 @@ async function sendSegmentEvent(action, body) { return response; } -;// CONCATENATED MODULE: ./src/api/index.js +;// CONCATENATED MODULE: ./adapters/api/index.js @@ -21195,14 +25412,111 @@ async function sendSegmentEvent(action, body) { -;// CONCATENATED MODULE: ./adapters/integrations/github-integration.js -// githubIntegration.js - // Check do we actually need it or not +;// CONCATENATED MODULE: ./adapters/templates/github-integration.js +function getErrorResponseStatus401 (ATLAN_INSTANCE_URL, context) { + return `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Bearer Token as \`ATLAN_API_TOKEN\` as this repository's action secret. +Atlan Instance URL: ${ATLAN_INSTANCE_URL} + +Set your repository action secrets [here](https://github.com/${context.payload.repository.full_name}/settings/secrets/actions). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).` +} + +function getErrorResponseStatusUndefined(ATLAN_INSTANCE_URL, context) { + return `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Instance URL as \`ATLAN_INSTANCE_URL\` as this repository's action secret. + +Atlan Instance URL: ${ATLAN_INSTANCE_URL} + +Make sure your Atlan Instance URL is set in the following format. +\`https://tenant.atlan.com\` + +Set your repository action secrets [here](https://github.com/${context.payload.repository.full_name}/settings/secrets/actions). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).` +} + +function getSetResourceOnAssetComment(tableMd, setResourceFailed) { + return `## 🎊 Congrats on the merge! + +This pull request has been added as a resource to the following assets: +Name | Resource set successfully +--- | --- +${tableMd} +${setResourceFailed ? '> Seems like we were unable to set the resources for some of the assets due to insufficient permissions. To ensure that the pull request is linked as a resource, you will need to assign the right persona with requisite permissions to the API token.' : ''} +` +} + +function getAssetInfo(ATLAN_INSTANCE_URL, asset, materialisedAsset, environmentName, projectName) { + return `### ${getConnectorImage( + asset.attributes.connectorName + )} [${asset.displayText}](${ATLAN_INSTANCE_URL}/assets/${ + asset.guid + }/overview?utm_source=dbt_github_action) ${ + asset.attributes?.certificateStatus + ? getCertificationImage(asset.attributes.certificateStatus) + : "" + } + Materialised asset: ${getConnectorImage( + materialisedAsset.attributes.connectorName + )} [${materialisedAsset.attributes.name}](${ATLAN_INSTANCE_URL}/assets/${ + materialisedAsset.guid + }/overview?utm_source=dbt_github_action) ${ + materialisedAsset.attributes?.certificateStatus + ? getCertificationImage(materialisedAsset.attributes.certificateStatus) + : "" + }${environmentName ? ` | Environment Name: \`${environmentName}\`` : ""}${ + projectName ? ` | Project Name: \`${projectName}\`` : "" + }` +} + +function getDownstreamTable(ATLAN_INSTANCE_URL, downstreamAssets, rows, materialisedAsset) { + return `
${ + downstreamAssets.entityCount + } downstream assets 👇
+ + Name | Type | Description | Owners | Terms | Classifications | Source URL + --- | --- | --- | --- | --- | --- | --- + ${rows + .map((row) => + row.map((i) => i.replace(/\|/g, "•").replace(/\n/g, "")).join(" | ") + ) + .join("\n")} + + ${ + downstreamAssets.hasMore + ? `[See more downstream assets at Atlan](${ATLAN_INSTANCE_URL}/assets/${materialisedAsset.guid}/lineage?utm_source=dbt_github_action)` + : "" + } + +
` +} + +function getViewAssetButton(ATLAN_INSTANCE_URL, asset) { + return `${getImageURL( + "atlan-logo", + 15, + 15 + )} [View asset in Atlan](${ATLAN_INSTANCE_URL}/assets/${ + asset.guid + }/overview?utm_source=dbt_github_action)` +} +function getMDCommentForModel(ATLAN_INSTANCE_URL, model) { + return `${getConnectorImage(model?.attributes?.connectorName)} [${ + model?.displayText + }](${ATLAN_INSTANCE_URL}/assets/${model?.guid}/overview?utm_source=dbt_github_action)` +} + +function getMDCommentForMaterialisedView(ATLAN_INSTANCE_URL, materialisedView) { + return `${getConnectorImage(materialisedView?.attributes?.connectorName)} [${ + materialisedView?.attributes?.name + }](${ATLAN_INSTANCE_URL}/assets/${materialisedView?.guid}/overview?utm_source=dbt_github_action)` +} +function getTableMD(md, resp) { + return `${md} | ${resp ? '✅' : '❌'} \n` +} +;// CONCATENATED MODULE: ./adapters/integrations/github-integration.js +// githubIntegration.js @@ -21210,470 +25524,816 @@ async function sendSegmentEvent(action, body) { -const github_integration_IS_DEV = isDev(); -const github_integration_ATLAN_INSTANCE_URL = getInstanceUrl(); -main.config(); +var headSHA; +const integrationName = "github"; class GitHubIntegration extends IntegrationInterface { constructor(token) { super(token); } async run() { - //Done - //Complete - console.log("Run Github"); - console.log(github_integration_IS_DEV); - const timeStart = Date.now(); - const { context } = github; - console.log("Context:", context); - const octokit = github.getOctokit(this.token); - const { pull_request } = context?.payload; - console.log(pull_request, "hii"); - - console.log("Interesting"); - const { state, merged } = pull_request; - console.log("state", state); - console.log("merged", merged); - if (!(await this.authIntegration({ octokit, context }))) { - //DONE - //Complete - throw { message: "Wrong API Token" }; - } - - let total_assets = 0; - console.log("After auth Integration"); - if (state === "open") { - total_assets = await this.printDownstreamAssets({ octokit, context }); - } else if (state === "closed" && merged) { - console.log("Hmm"); - total_assets = await this.setResourceOnAsset({ octokit, context }); - } - - if (total_assets !== 0) { - this.sendSegmentEventOfIntegration("dbt_ci_action_run", { - //Complete - asset_count: total_assets, - total_time: Date.now() - timeStart, - }); - } - } + try { + const timeStart = Date.now(); + const { context } = github; - async printDownstreamAssets({ octokit, context }) { - //Done - console.log("Brother"); - // const changedFiles = await this.getChangedFiles({ octokit, context }); //Complete - var changedFiles = [ - { - fileName: "instacart_beverages_order_customer", - filePath: "instacart_beverages_order_customer.sql", - status: "modified", - }, - ]; - let comments = ``; - let totalChangedFiles = 0; + const octokit = github.getOctokit(this.token); + const { pull_request } = context?.payload; + const { state, merged } = pull_request; + headSHA = pull_request?.head?.sha; - for (const { fileName, filePath, status } of changedFiles) { - const aliasName = await this.getAssetName({ - //Complete - octokit, - context, - fileName, - filePath, - }); - const assetName = isIgnoreModelAliasMatching() ? fileName : aliasName; //Complete - console.log("acha2"); - const environments = getEnvironments(); - - let environment = null; - for (const [baseBranchName, environmentName] of environments) { - if (baseBranchName === context.payload.pull_request.base.ref) { - environment = environmentName; - break; - } + logger_logger.withInfo( + "GitHub Integration is running...", + integrationName, + headSHA, + "run" + ); + + if (!(await this.authIntegration({ octokit, context }))) { + logger_logger.withError( + "Authentication failed. Wrong API Token.", + integrationName, + headSHA, + "run" + ); + throw { message: "Wrong API Token" }; } - console.log("Before getAsset"); - const asset = await getAsset({ - //Done - name: assetName, - sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, - environment: environment, - integration: "github", - }); - console.log("After getAsset"); - if (totalChangedFiles !== 0) comments += "\n\n---\n\n"; - console.log("Status: ", status); - if (status === "added") { - comments += `### ${get_image_url_getConnectorImage("dbt")} ${fileName} 🆕 -Its a new model and not present in Atlan yet, you'll see the downstream impact for it after its present in Atlan.`; - totalChangedFiles++; - continue; + + let total_assets = 0; + + if (state === "open") { + total_assets = await this.printDownstreamAssets({ octokit, context }); + } else if (state === "closed" && merged) { + total_assets = await this.setResourceOnAsset({ octokit, context }); } - console.log("Before filtering"); - console.log("Asset", asset); - if (asset.error) { - comments += asset.error; - totalChangedFiles++; - continue; + + if (total_assets !== 0) { + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_run", + properties: { + asset_count: total_assets, + total_time: Date.now() - timeStart, + }, + }); } - const materialisedAsset = asset.attributes.dbtModelSqlAssets[0]; - const timeStart = Date.now(); - const totalModifiedFiles = changedFiles.filter( - (i) => i.status === "modified" - ).length; - console.log("Before getDownstreamAssets"); - const downstreamAssets = await getDownstreamAssets( - //Complete - asset, - materialisedAsset.guid, - totalModifiedFiles + + logger_logger.withInfo( + "Successfully Completed DBT_CI_ACTION", + integrationName, + headSHA, + "run" ); - console.log("After getDownstreamAssets"); - if (downstreamAssets.error) { - comments += downstreamAssets.error; - totalChangedFiles++; - continue; - } - console.log("At line 139 after getDownstreamAssets in printDownstream"); - this.sendSegmentEventOfIntegration("dbt_ci_action_downstream_unfurl", { - //Complete - asset_guid: asset.guid, - asset_type: asset.typeName, - downstream_count: downstreamAssets.entities.length, - total_fetch_time: Date.now() - timeStart, - }); - console.log("At line 147 after getDownstreamAssets in printDownstream"); + } catch (error) { + logger_logger.withError( + `Error in run(): ${error.message}`, + integrationName, + headSHA, + "run" + ); + throw error; + } + } - const classifications = await getClassifications({ - //Complete - sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, - }); + async printDownstreamAssets({ octokit, context }) { + logger_logger.withInfo( + "Printing downstream assets...", + integrationName, + headSHA, + "printDownstreamAssets" + ); - const comment = await this.renderDownstreamAssetsComment({ - //Done - //Complete - octokit, - context, - asset, - materialisedAsset, - downstreamAssets, - classifications, - }); + try { + const changedFiles = await this.getChangedFiles({ octokit, context }); + let comments = ``; + let totalChangedFiles = 0; + + for (const { fileName, filePath, status } of changedFiles) { + logger_logger.withInfo( + `Processing file: ${fileName}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + const aliasName = await this.getAssetName({ + octokit, + context, + fileName, + filePath, + }); + const assetName = IGNORE_MODEL_ALIAS_MATCHING ? fileName : aliasName; + + const environments = getEnvironments(); + let environment = null; + for (const [baseBranchName, environmentName] of environments) { + if (baseBranchName === context.payload.pull_request.base.ref) { + environment = environmentName; + break; + } + } - comments += comment; + logger_logger.withInfo( + `Processing asset: ${assetName} in environment: ${environment}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + const asset = await getAsset({ + name: assetName, + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + environment: environment, + integration: "github", + }); - totalChangedFiles++; - } + if (totalChangedFiles !== 0) comments += "\n\n---\n\n"; + + if (status === "added") { + logger_logger.withInfo( + `New model added: ${fileName}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + comments += getNewModelAddedComment(fileName); + totalChangedFiles++; + continue; + } + + if (asset.error) { + logger_logger.withError( + `Asset error for ${assetName}: ${asset.error}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + comments += asset.error; + totalChangedFiles++; + continue; + } + + const materialisedAsset = asset?.attributes?.dbtModelSqlAssets?.[0]; + const timeStart = Date.now(); + + const totalModifiedFiles = changedFiles.filter( + (i) => i.status === "modified" + ).length; + + const downstreamAssets = await getDownstreamAssets( + asset, + materialisedAsset.guid, + totalModifiedFiles, + this.sendSegmentEventOfIntegration, + "github" + ); + + if (downstreamAssets.error) { + logger_logger.withError( + `Downstream assets error for ${assetName}: ${downstreamAssets.error}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + comments += downstreamAssets.error; + totalChangedFiles++; + continue; + } + + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_downstream_unfurl", + properties: { + asset_guid: asset.guid, + asset_type: asset.typeName, + downstream_count: downstreamAssets.entities.length, + total_fetch_time: Date.now() - timeStart, + }, + }); - comments = `### ${get_image_url_getImageURL("atlan-logo", 15, 15)} Atlan impact analysis -Here is your downstream impact analysis for **${totalChangedFiles} ${ - totalChangedFiles > 1 ? "models" : "model" - }** you have edited. + const classifications = await getClassifications({ + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + }); -${comments}`; + const comment = await this.renderDownstreamAssetsComment({ + octokit, + context, + asset, + materialisedAsset, + downstreamAssets, + classifications, + }); - const existingComment = await this.checkCommentExists({ octokit, context }); //Complete + comments += comment; - if (totalChangedFiles > 0) - await this.createIssueComment({ - //Complete - octokit, - context, - content: comments, - comment_id: existingComment?.id, - }); + totalChangedFiles++; + } - if (totalChangedFiles === 0 && existingComment) - await this.deleteComment({ - //Complete + comments = getBaseComment(totalChangedFiles, comments); + + const existingComment = await this.checkCommentExists({ octokit, context, - comment_id: existingComment.id, }); - return totalChangedFiles; - } + logger_logger.withInfo( + `Existing Comment: ${existingComment?.id}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); - async setResourceOnAsset({ octokit, context }) { - //Done - //Complete - const changedFiles = await this.getChangedFiles({ octokit, context }); //Complete - const { pull_request } = context.payload; - var totalChangedFiles = 0; + if (totalChangedFiles > 0) + await this.createIssueComment({ + octokit, + context, + content: comments, + comment_id: existingComment?.id, + }); + + if (totalChangedFiles === 0 && existingComment) + await this.deleteComment({ + octokit, + context, + comment_id: existingComment?.id, + }); - if (changedFiles.length === 0) return; + logger_logger.withInfo( + "Successfully printed Downstream Assets", + integrationName, + headSHA, + "printDownstreamAssets" + ); - for (const { fileName, filePath } of changedFiles) { - const assetName = await this.getAssetName({ - //Complete - octokit, - context, - fileName, - filePath, - }); + return totalChangedFiles; + } catch (error) { + logger_logger.withError( + `Error in printDownstreamAssets: ${error.message}`, + integrationName, + headSHA, + "printDownstreamAssets" + ); + throw error; + } + } - const environments = getEnvironments(); + async setResourceOnAsset({ octokit, context }) { + logger_logger.withInfo( + "Setting resources on assets...", + integrationName, + headSHA, + "setResourceOnAsset" + ); - let environment = null; - for (const [baseBranchName, environmentName] of environments) { - if (baseBranchName === context.payload.pull_request.base.ref) { - environment = environmentName; - break; - } + try { + const changedFiles = await this.getChangedFiles({ octokit, context }); + const { pull_request } = context.payload; + var totalChangedFiles = 0; + let tableMd = ``; + let setResourceFailed = false; + + if (changedFiles.length === 0) { + logger_logger.withInfo( + "No changed files found. Skipping resource setup.", + integrationName, + headSHA, + "setResourceOnAsset" + ); + return totalChangedFiles; } - const asset = await getAsset({ - //Done - name: assetName, - sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, - environment: environment, - integration: "github", - }); + for (const { fileName, filePath } of changedFiles) { + logger_logger.withInfo( + `Processing file: ${fileName}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + const aliasName = await this.getAssetName({ + octokit, + context, + fileName, + filePath, + }); + + const assetName = IGNORE_MODEL_ALIAS_MATCHING ? fileName : aliasName; - if (asset.error) continue; + logger_logger.withInfo( + `Resolved asset name: ${assetName}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); - const { guid: modelGuid } = asset; - const { guid: tableAssetGuid } = - asset?.attributes?.dbtModelSqlAssets?.[0]; + const environments = getEnvironments(); + let environment = null; + for (const [baseBranchName, environmentName] of environments) { + if (baseBranchName === context.payload.pull_request.base.ref) { + environment = environmentName; + break; + } + } - if (modelGuid) - await createResource( - //Complete - modelGuid, - "Pull Request on GitHub", - pull_request.html_url, - this.sendSegmentEventOfIntegration + logger_logger.withInfo( + `Processing asset: ${assetName} in environment: ${environment}`, + integrationName, + headSHA, + "setResourceOnAsset" ); - if (tableAssetGuid) - await createResource( - //Complete - tableAssetGuid, - "Pull Request on GitHub", - pull_request.html_url, - this.sendSegmentEventOfIntegration + const asset = await getAsset({ + name: assetName, + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + environment: environment, + integration: "github", + }); + + if (asset.error) { + logger_logger.withError( + `Failed to retrieve asset: ${assetName}, Error: ${asset.error}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + continue; + } + + const materialisedAsset = asset?.attributes?.dbtModelSqlAssets?.[0]; + const timeStart = Date.now(); + + const totalModifiedFiles = changedFiles.filter( + (i) => i.status === "modified" + ).length; + + const downstreamAssets = await getDownstreamAssets( + asset, + materialisedAsset.guid, + totalModifiedFiles, + this.sendSegmentEventOfIntegration, + "github" ); - totalChangedFiles++; - } + if (downstreamAssets.error) { + logger_logger.withError( + `Failed to retrieve downstream assets for: ${assetName}, Error: ${downstreamAssets.error}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + continue; + } + + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_downstream_unfurl", + properties: { + asset_guid: asset.guid, + asset_type: asset.typeName, + downstream_count: downstreamAssets.entities.length, + total_fetch_time: Date.now() - timeStart, + }, + }); - const comment = await this.createIssueComment({ - //Complete - octokit, - context, - content: `🎊 Congrats on the merge! + const model = asset; + const materialisedView = asset?.attributes?.dbtModelSqlAssets?.[0]; -This pull request has been added as a resource to all the assets modified. ✅ -`, - comment_id: null, - forceNewComment: true, - }); + let PR_TITLE = pull_request.title; - return totalChangedFiles; - } + if (downstreamAssets.entityCount != 0) { + if (model) { + const { guid: modelGuid } = model; + const resp = await createResource( + modelGuid, + PR_TITLE, + pull_request.html_url, + this.sendSegmentEventOfIntegration + ); + const md = getMDCommentForModel(ATLAN_INSTANCE_URL, model); + tableMd += getTableMD(md, resp); + if (!resp) { + setResourceFailed = true; + logger_logger.withError( + `Setting resource failed for model: ${modelGuid}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + } + } - async authIntegration({ octokit, context }) { - //DONE - //COMPLETE - console.log("Here is Context:", context); - const response = await auth(); - console.log("Inside authIntegration befor comment exists"); - const existingComment = await this.checkCommentExists({ octokit, context }); - - console.log("Existing Comment", existingComment); - - if (response?.status === 401) { - console.log("Inside authIntegration befor createIssueComment"); - await this.createIssueComment({ + if (materialisedView) { + const { guid: tableAssetGuid } = materialisedView; + const resp = await createResource( + tableAssetGuid, + PR_TITLE, + pull_request.html_url, + this.sendSegmentEventOfIntegration + ); + const md = getMDCommentForMaterialisedView( + ATLAN_INSTANCE_URL, + materialisedView + ); + tableMd += getTableMD(md, resp); + if (!resp) { + setResourceFailed = true; + logger_logger.withError( + `Setting resource failed for materialized view: ${tableAssetGuid}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + } + } + } + totalChangedFiles++; + } + + const comment = await this.createIssueComment({ octokit, context, - content: `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Bearer Token as \`ATLAN_API_TOKEN\` as this repository's action secret. + content: getSetResourceOnAssetComment(tableMd, setResourceFailed), + comment_id: null, + forceNewComment: true, + }); -Atlan Instance URL: ${github_integration_ATLAN_INSTANCE_URL} + logger_logger.withInfo( + "Successfully set the resource on the asset", + integrationName, + headSHA, + "setResourceOnAsset" + ); -Set your repository action secrets [here](https://github.com/${context.payload.repository.full_name}/settings/secrets/actions). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).`, - comment_id: existingComment?.id, - }); - return false; + return totalChangedFiles; + } catch (error) { + logger_logger.withError( + `Error in setResourceOnAsset: ${error}`, + integrationName, + headSHA, + "setResourceOnAsset" + ); + throw error; } + } - if (response === undefined) { - await this.createIssueComment({ + async authIntegration({ octokit, context }) { + logger_logger.withInfo( + "Authenticating with Atlan", + integrationName, + headSHA, + "authIntegration" + ); + + try { + const response = await auth(); + + const existingComment = await this.checkCommentExists({ octokit, context, - content: `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Instance URL as \`ATLAN_INSTANCE_URL\` as this repository's action secret. + }); -Atlan Instance URL: ${github_integration_ATLAN_INSTANCE_URL} + logger_logger.withInfo( + `Existing Comment: ${existingComment?.id}`, + integrationName, + headSHA, + "authIntegration" + ); -Make sure your Atlan Instance URL is set in the following format. -\`https://tenant.atlan.com\` + if (response?.status === 401) { + logger_logger.withError( + "Authentication failed: Status 401", + integrationName, + headSHA, + "authIntegration" + ); + await this.createIssueComment({ + octokit, + context, + content: getErrorResponseStatus401(ATLAN_INSTANCE_URL, context), + comment_id: existingComment?.id, + }); + return false; + } -Set your repository action secrets [here](https://github.com/${context.payload.repository.full_name}/settings/secrets/actions). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).`, - comment_id: existingComment?.id, - }); - return false; + if (response === undefined) { + logger_logger.withError( + "Authentication failed: Undefined response", + integrationName, + headSHA, + "authIntegration" + ); + await this.createIssueComment({ + octokit, + context, + content: getErrorResponseStatusUndefined(ATLAN_INSTANCE_URL, context), + comment_id: existingComment?.id, + }); + return false; + } + logger_logger.withInfo( + "Successfully Authenticated with Atlan", + integrationName, + headSHA, + "authIntegration" + ); + return true; + } catch (error) { + logger_logger.withError( + `Error in authIntegration: ${error.message}`, + integrationName, + headSHA, + "authIntegration" + ); + throw error; } - - return true; } async sendSegmentEventOfIntegration({ action, properties }) { - //Done - //FullyComplete - // IMPORT ATLAN_INSTANCE_URL. - const domain = new URL(github_integration_ATLAN_INSTANCE_URL).hostname; - - const raw = stringify({ - category: "integration", - object: "github", - action, - userId: "atlan-annonymous-github", - properties: { - ...properties, - //get context for this - // github_action_id: `https://github.com/${context.payload.repository.full_name}/actions/runs/${context.runId}`, - domain, - }, - }); + try { + const domain = new URL(ATLAN_INSTANCE_URL).hostname; + const { context } = github; //confirm this + logger_logger.withInfo( + `Sending Segment event for action: ${action}`, + integrationName, + headSHA, + "sendSegmentEventOfIntegration" + ); + + const raw = stringify({ + category: "integration", + object: "github", + action, + userId: "atlan-annonymous-github", + properties: { + ...properties, + github_action_id: `https://github.com/${context?.payload?.repository?.full_name}/actions/runs/${context?.runId}`, + domain, + }, + }); - return sendSegmentEvent(action, raw); + return sendSegmentEvent(action, raw); + } catch (error) { + logger_logger.withError( + `Error sending Segment event for action: ${action} - ${error.message}`, + integrationName, + headSHA, + "sendSegmentEventOfIntegration" + ); + throw error; + } } async getChangedFiles({ octokit, context }) { - //Done - //FullyComplete - const { repository, pull_request } = context.payload, - owner = repository.owner.login, - repo = repository.name, - pull_number = pull_request.number; - - const res = await octokit.request( - `GET /repos/${owner}/${repo}/pulls/${pull_number}/files`, - { - owner, - repo, - pull_number, - } - ); + try { + logger_logger.withInfo( + "Fetching changed files...", + integrationName, + headSHA, + "getChangedFiles" + ); - var changedFiles = res.data - .map(({ filename, status }) => { - try { - const [modelName] = filename - .match(/.*models\/(.*)\.sql/)[1] - .split("/") - .reverse()[0] - .split("."); + const { repository, pull_request } = context.payload, + owner = repository.owner.login, + repo = repository.name, + pull_number = pull_request.number; - if (modelName) { - return { - fileName: modelName, - filePath: filename, - status, - }; + const res = await octokit.request( + `GET /repos/${owner}/${repo}/pulls/${pull_number}/files`, + { + owner, + repo, + pull_number, + } + ); + + var changedFiles = res.data + .map(({ filename, status }) => { + try { + const [modelName] = filename + .match(/.*models\/(.*)\.sql/)[1] + .split("/") + .reverse()[0] + .split("."); + + if (modelName) { + return { + fileName: modelName, + filePath: filename, + status, + }; + } + } catch (e) { + logger_logger.withError( + `Error processing file: ${filename} - ${e.message}`, + integrationName, + headSHA, + "getChangedFiles" + ); } - } catch (e) {} - }) - .filter((i) => i !== undefined); + }) + .filter((i) => i !== undefined); - changedFiles = changedFiles.filter((item, index) => { - return ( - changedFiles.findIndex((obj) => obj.fileName === item.fileName) === - index - ); - }); + changedFiles = changedFiles.filter((item, index) => { + return ( + changedFiles.findIndex((obj) => obj.fileName === item.fileName) === + index + ); + }); - console.log("Changed Files: ", changedFiles); + logger_logger.withInfo( + "Successfully fetched changed files", + integrationName, + headSHA, + "getChangedFiles" + ); - return changedFiles; + return changedFiles; + } catch (error) { + logger_logger.withError( + `Error fetching changed files - ${error.message}`, + integrationName, + headSHA, + "getChangedFiles" + ); + throw error; + } } async getAssetName({ octokit, context, fileName, filePath }) { - //Done - // FullyComplete - var regExp = - /{{\s*config\s*\(\s*(?:[^,]*,)*\s*alias\s*=\s*['"]([^'"]+)['"](?:\s*,[^,]*)*\s*\)\s*}}/im; - var fileContents = await this.getFileContents({ - octokit, - context, - filePath, - }); + try { + logger_logger.withInfo( + "Getting asset name...", + integrationName, + headSHA, + "getAssetName" + ); - if (fileContents) { - var matches = regExp.exec(fileContents); + var regExp = + /{{\s*config\s*\(\s*(?:[^,]*,)*\s*alias\s*=\s*['"]([^'"]+)['"](?:\s*,[^,]*)*\s*\)\s*}}/im; + var fileContents = await this.getFileContents({ + octokit, + context, + filePath, + }); - if (matches) { - return matches[1].trim(); + if (fileContents) { + var matches = regExp.exec(fileContents); + if (matches) { + logger_logger.withInfo( + `Found a match: ${matches[1].trim()}`, + integrationName, + headSHA, + "getAssetName" + ); + return matches[1].trim(); + } } + logger_logger.withInfo( + `Using filename as asset name: ${fileName}`, + integrationName, + headSHA, + "getAssetName" + ); + return fileName; + } catch (error) { + logger_logger.withError( + `Error getting asset name - ${error.message}`, + integrationName, + headSHA, + "getAssetName" + ); + throw error; } - - return fileName; } async getFileContents({ octokit, context, filePath }) { - //Done - // FullyComplete - const { repository, pull_request } = context.payload, - owner = repository.owner.login, - repo = repository.name, - head_sha = pull_request.head.sha; - - const res = await octokit - .request( - `GET /repos/${owner}/${repo}/contents/${filePath}?ref=${head_sha}`, - { - owner, - repo, - path: filePath, - } - ) - .catch((e) => { - console.log("Error fetching file contents: ", e); - return null; - }); + try { + logger_logger.withInfo( + "Fetching file contents...", + integrationName, + headSHA, + "getFileContents" + ); - if (!res) return null; + const { repository, pull_request } = context.payload, + owner = repository.owner.login, + repo = repository.name, + head_sha = pull_request.head.sha; + + const res = await octokit + .request( + `GET /repos/${owner}/${repo}/contents/${filePath}?ref=${head_sha}`, + { + owner, + repo, + path: filePath, + } + ) + .catch((e) => { + logger_logger.withError( + `Error fetching file contents: ${e.message}`, + integrationName, + headSHA, + "getFileContents" + ); + return null; + }); + + if (!res) return null; - const buff = Buffer.from(res.data.content, "base64"); + const buff = Buffer.from(res.data.content, "base64"); - return buff.toString("utf8"); + logger_logger.withInfo( + "Successfully fetched file contents", + integrationName, + headSHA, + "getFileContents" + ); + + return buff.toString("utf8"); + } catch (error) { + logger_logger.withError( + `Error in getFileContents: ${error.message}`, + integrationName, + headSHA, + "getFileContents" + ); + throw error; + } } async checkCommentExists({ octokit, context }) { - //Done - //FullyComplete - if (github_integration_IS_DEV) return null; + logger_logger.withInfo( + "Checking for existing comments...", + integrationName, + headSHA, + "checkCommentExists" + ); + + if (IS_DEV) { + logger_logger.withInfo( + "Development mode enabled. Skipping comment check.", + integrationName, + headSHA, + "checkCommentExists" + ); + return null; + } const { pull_request } = context.payload; - const comments = await octokit.rest.issues.listComments({ - ...context.repo, - issue_number: pull_request.number, - }); + try { + const comments = await octokit.rest.issues.listComments({ + ...context.repo, + issue_number: pull_request.number, + }); - return comments.data.find( - (comment) => - comment.user.login === "github-actions[bot]" && - comment.body.includes( - "" - ) - ); + const existingComment = comments.data.find( + (comment) => + comment.user.login === "github-actions[bot]" && + comment.body.includes( + "" + ) + ); + if (existingComment) { + logger_logger.withInfo( + "Found existing comment: " + existingComment?.id, + integrationName, + headSHA, + "checkCommentExists" + ); + } else { + logger_logger.withInfo( + "No existing comment found", + integrationName, + headSHA, + "checkCommentExists" + ); + } + + return existingComment; + } catch (error) { + logger_logger.withError( + "Error checking for existing comments: " + error.message, + integrationName, + headSHA, + "checkCommentExists" + ); + throw error; + } } async createIssueComment({ - //Done - // FullyComplete octokit, context, content, comment_id = null, forceNewComment = false, }) { - console.log("Inside CreateIssue:", context); - console.log("Inside CreateIssue Comment"); + logger_logger.withInfo( + "Creating an issue comment...", + integrationName, + headSHA, + "createIssueComment" + ); + const { pull_request } = context?.payload || {}; - console.log("Inside CreateIssue Comment"); + content = ` ${content}`; @@ -21683,10 +26343,15 @@ ${content}`; body: content, }; - console.log(content, content.length); - console.log("Inside CreateIssue Comment Complete"); - - if (github_integration_IS_DEV) return content; + if (IS_DEV) { + logger_logger.withInfo( + "Development mode enabled. Skipping comment creation.", + integrationName, + headSHA, + "createIssueComment" + ); + return content; + } if (comment_id && !forceNewComment) return octokit.rest.issues.updateComment({ ...commentObj, comment_id }); @@ -21694,8 +26359,13 @@ ${content}`; } async deleteComment({ octokit, context, comment_id }) { - //Done - //FullyComplete + logger_logger.withInfo( + `Deleted comment with ID ${comment_id}`, + integrationName, + headSHA, + "deleteComment" + ); + const { pull_request } = context.payload; return octokit.rest.issues.deleteComment({ @@ -21706,8 +26376,6 @@ ${content}`; } async renderDownstreamAssetsComment({ - //Done - //FullyComplete octokit, context, asset, @@ -21715,163 +26383,148 @@ ${content}`; downstreamAssets, classifications, }) { - let impactedData = downstreamAssets.entities.map( - ({ - displayText, - guid, - typeName, - attributes, - meanings, - classificationNames, - }) => { - // Modifying the typeName and getting the readableTypeName - let readableTypeName = typeName - .toLowerCase() - .replace(attributes.connectorName, "") - .toUpperCase(); - - // Filtering classifications based on classificationNames - let classificationsObj = classifications.filter(({ name }) => - classificationNames.includes(name) - ); - - // Modifying the readableTypeName - readableTypeName = - readableTypeName.charAt(0).toUpperCase() + - readableTypeName.slice(1).toLowerCase(); - - return [ - guid, - truncate(displayText), - truncate(attributes.connectorName), - truncate(readableTypeName), - truncate( - attributes?.userDescription || attributes?.description || "" - ), - attributes?.certificateStatus || "", - truncate( - [...attributes?.ownerUsers, ...attributes?.ownerGroups] || [] - ), - truncate( - meanings.map( - ({ displayText, termGuid }) => - `[${displayText}](${github_integration_ATLAN_INSTANCE_URL}/assets/${termGuid}/overview?utm_source=dbt_github_action)` - ) - ), - truncate( - classificationsObj?.map( - ({ name, displayName }) => `\`${displayName}\`` - ) - ), - attributes?.sourceURL || "", - ]; - } + logger_logger.withInfo( + "Rendering Downstream Assets...", + integrationName, + headSHA, + "renderDownstreamAssetsComment" ); - - // Sorting the impactedData first by typeName and then by connectorName - impactedData = impactedData.sort((a, b) => a[3].localeCompare(b[3])); - impactedData = impactedData.sort((a, b) => a[2].localeCompare(b[2])); - - // Creating rows for the downstream table - let rows = impactedData.map( - ([ - guid, - displayText, - connectorName, - typeName, - description, - certificateStatus, - owners, - meanings, - classifications, - sourceUrl, - ]) => { - // Getting connector and certification images - const connectorImage = get_image_url_getConnectorImage(connectorName); - const certificationImage = certificateStatus - ? get_image_url_getCertificationImage(certificateStatus) - : ""; - - return [ - `${connectorImage} [${displayText}](${github_integration_ATLAN_INSTANCE_URL}/assets/${guid}/overview?utm_source=dbt_github_action) ${certificationImage}`, - `\`${typeName}\``, - description, - owners, + try { + let impactedData = downstreamAssets.entities.map( + ({ + displayText, + guid, + typeName, + attributes, meanings, - classifications, - sourceUrl ? `[Open in ${connectorName}](${sourceUrl})` : " ", - ]; - } - ); + classificationNames, + }) => { + // Modifying the typeName and getting the readableTypeName + let readableTypeName = typeName + .toLowerCase() + .replace(attributes.connectorName, "") + .toUpperCase(); + + // Filtering classifications based on classificationNames + let classificationsObj = classifications.filter(({ name }) => + classificationNames.includes(name) + ); + + // Modifying the readableTypeName + readableTypeName = + readableTypeName.charAt(0).toUpperCase() + + readableTypeName.slice(1).toLowerCase(); + + return [ + guid, + truncate(displayText), + truncate(attributes.connectorName), + truncate(readableTypeName), + truncate( + attributes?.userDescription || attributes?.description || "" + ), + attributes?.certificateStatus || "", + truncate( + [...attributes?.ownerUsers, ...attributes?.ownerGroups] || [] + ), + truncate( + meanings.map( + ({ displayText, termGuid }) => + `[${displayText}](${ATLAN_INSTANCE_URL}/assets/${termGuid}/overview?utm_source=dbt_github_action)` + ) + ), + truncate( + classificationsObj?.map( + ({ name, displayName }) => `\`${displayName}\`` + ) + ), + attributes?.sourceURL || "", + ]; + } + ); - const environmentName = - materialisedAsset?.attributes?.assetDbtEnvironmentName; - const projectName = materialisedAsset?.attributes?.assetDbtProjectName; - // Generating asset information - const assetInfo = `### ${get_image_url_getConnectorImage( - asset.attributes.connectorName - )} [${asset.displayText}](${github_integration_ATLAN_INSTANCE_URL}/assets/${ - asset.guid - }/overview?utm_source=dbt_github_action) ${ - asset.attributes?.certificateStatus - ? get_image_url_getCertificationImage(asset.attributes.certificateStatus) - : "" - } -Materialised asset: ${get_image_url_getConnectorImage( - materialisedAsset.attributes.connectorName - )} [${materialisedAsset.attributes.name}](${github_integration_ATLAN_INSTANCE_URL}/assets/${ - materialisedAsset.guid - }/overview?utm_source=dbt_github_action) ${ - materialisedAsset.attributes?.certificateStatus - ? get_image_url_getCertificationImage(materialisedAsset.attributes.certificateStatus) - : "" - }${environmentName ? ` | Environment Name: \`${environmentName}\`` : ""}${ - projectName ? ` | Project Name: \`${projectName}\`` : "" - }`; + // Sorting the impactedData first by typeName and then by connectorName + impactedData = impactedData.sort((a, b) => a[3].localeCompare(b[3])); + impactedData = impactedData.sort((a, b) => a[2].localeCompare(b[2])); + + // Creating rows for the downstream table + let rows = impactedData.map( + ([ + guid, + displayText, + connectorName, + typeName, + description, + certificateStatus, + owners, + meanings, + classifications, + sourceUrl, + ]) => { + // Getting connector and certification images + const connectorImage = getConnectorImage(connectorName); + const certificationImage = certificateStatus + ? getCertificationImage(certificateStatus) + : ""; - // Generating the downstream table - const downstreamTable = `
${ - downstreamAssets.entityCount - } downstream assets 👇
+ return [ + `${connectorImage} [${displayText}](${ATLAN_INSTANCE_URL}/assets/${guid}/overview?utm_source=dbt_github_action) ${certificationImage}`, + `\`${typeName}\``, + description, + owners, + meanings, + classifications, + sourceUrl ? `[Open in ${connectorName}](${sourceUrl})` : " ", + ]; + } + ); -Name | Type | Description | Owners | Terms | Classifications | Source URL ---- | --- | --- | --- | --- | --- | --- -${rows - .map((row) => - row.map((i) => i.replace(/\|/g, "•").replace(/\n/g, "")).join(" | ") - ) - .join("\n")} + const environmentName = + materialisedAsset?.attributes?.assetDbtEnvironmentName; + const projectName = materialisedAsset?.attributes?.assetDbtProjectName; + // Generating asset information -${ - downstreamAssets.hasMore - ? `[See more downstream assets at Atlan](${github_integration_ATLAN_INSTANCE_URL}/assets/${materialisedAsset.guid}/lineage?utm_source=dbt_github_action)` - : "" -} + const assetInfo = getAssetInfo( + ATLAN_INSTANCE_URL, + asset, + materialisedAsset, + environmentName, + projectName + ); -
`; + // Generating the downstream table + const downstreamTable = getDownstreamTable( + ATLAN_INSTANCE_URL, + downstreamAssets, + rows, + materialisedAsset + ); - // Generating the "View asset in Atlan" button - const viewAssetButton = `${get_image_url_getImageURL( - "atlan-logo", - 15, - 15 - )} [View asset in Atlan](${github_integration_ATLAN_INSTANCE_URL}/assets/${ - asset.guid - }/overview?utm_source=dbt_github_action)`; + // Generating the "View asset in Atlan" button + const viewAssetButton = getViewAssetButton(ATLAN_INSTANCE_URL, asset); - // Generating the final comment based on the presence of downstream assets - if (downstreamAssets.entities.length > 0) { - return `${assetInfo} + // Generating the final comment based on the presence of downstream assets + if (downstreamAssets.entities.length > 0) { + return `${assetInfo} ${downstreamTable} ${viewAssetButton}`; - } else { - return `${assetInfo} + } else { + return `${assetInfo} No downstream assets found. ${viewAssetButton}`; + } + } catch (error) { + logger_logger.withError( + `Error rendering Downstream Assets: ${error.message}`, + integrationName, + headSHA, + "renderDownstreamAssetsComment" + ); + throw error; } } } @@ -21880,10 +26533,16 @@ ${viewAssetButton}`; var lib = __nccwpck_require__(2760); // EXTERNAL MODULE: ./node_modules/xcase/es5/index.js var es5 = __nccwpck_require__(7020); +// EXTERNAL MODULE: ./node_modules/async-sema/lib/index.js +var async_sema_lib = __nccwpck_require__(1884); +// EXTERNAL MODULE: ./node_modules/micromatch/index.js +var micromatch = __nccwpck_require__(6228); ;// CONCATENATED MODULE: ./node_modules/@gitbeaker/requester-utils/dist/index.mjs + + // src/RequesterUtils.ts function formatQuery(params = {}) { const decamelized = (0,es5/* decamelizeKeys */.iF)(params); @@ -21926,10 +26585,24 @@ async function defaultOptionsHandler(resourceOptions, { defaultOptions.searchParams = q; return Promise.resolve(defaultOptions); } +function createRateLimiters(rateLimitOptions = {}) { + const rateLimiters = {}; + Object.entries(rateLimitOptions).forEach(([key, config]) => { + if (typeof config === "number") + rateLimiters[key] = (0,async_sema_lib.RateLimit)(config, { timeUnit: 6e4 }); + else + rateLimiters[key] = { + method: config.method.toUpperCase(), + limit: (0,async_sema_lib.RateLimit)(config.limit, { timeUnit: 6e4 }) + }; + }); + return rateLimiters; +} function createRequesterFn(optionsHandler, requestHandler) { const methods = ["get", "post", "put", "patch", "delete"]; return (serviceOptions) => { const requester = {}; + const rateLimiters = createRateLimiters(serviceOptions.rateLimits); methods.forEach((m) => { requester[m] = async (endpoint, options) => { const defaultRequestOptions = await defaultOptionsHandler(serviceOptions, { @@ -21937,13 +26610,13 @@ function createRequesterFn(optionsHandler, requestHandler) { method: m.toUpperCase() }); const requestOptions = await optionsHandler(serviceOptions, defaultRequestOptions); - return requestHandler(endpoint, requestOptions); + return requestHandler(endpoint, { ...requestOptions, rateLimiters }); }; }); return requester; }; } -function extendClass(Base, customConfig = {}) { +function extendClass(Base, customConfig) { return class extends Base { constructor(...options) { const [config, ...opts] = options; @@ -21958,11 +26631,58 @@ function presetResourceArguments(resources, customConfig = {}) { }); return updated; } +function getMatchingRateLimiter(endpoint, rateLimiters = {}, method = "GET") { + const sortedEndpoints = Object.keys(rateLimiters).sort().reverse(); + const match = sortedEndpoints.find((ep) => micromatch.isMatch(endpoint, ep)); + const rateLimitConfig = match && rateLimiters[match]; + if (rateLimitConfig && typeof rateLimitConfig !== "object") { + return rateLimitConfig; + } + if (rateLimitConfig && rateLimitConfig.method.toUpperCase() === method.toUpperCase()) { + return rateLimitConfig.limit; + } + return (0,async_sema_lib.RateLimit)(3e3, { timeUnit: 6e4 }); +} // src/BaseResource.ts function getDynamicToken(tokenArgument) { return tokenArgument instanceof Function ? tokenArgument() : Promise.resolve(tokenArgument); } +var DEFAULT_RATE_LIMITS = Object.freeze({ + // Default rate limit + "**": 3e3, + // Import/Export + "projects/import": 6, + "projects/*/export": 6, + "projects/*/download": 1, + "groups/import": 6, + "groups/*/export": 6, + "groups/*/download": 1, + // Note creation + "projects/*/issues/*/notes": { + method: "post", + limit: 300 + }, + "projects/*/snippets/*/notes": { + method: "post", + limit: 300 + }, + "projects/*/merge_requests/*/notes": { + method: "post", + limit: 300 + }, + "groups/*/epics/*/notes": { + method: "post", + limit: 300 + }, + // Repositories - get file archive + "projects/*/repository/archive*": 5, + // Project Jobs + "projects/*/jobs": 600, + // Member deletion + "projects/*/members": 60, + "groups/*/members": 60 +}); var BaseResource = class { url; requester; @@ -21981,6 +26701,7 @@ var BaseResource = class { prefixUrl = "", rejectUnauthorized = true, queryTimeout = 3e5, + rateLimits = DEFAULT_RATE_LIMITS, ...tokens }) { if (!requesterFn) @@ -22009,7 +26730,7 @@ var BaseResource = class { } if (sudo) this.headers.Sudo = `${sudo}`; - this.requester = requesterFn({ ...this }); + this.requester = requesterFn({ ...this, rateLimits }); } }; @@ -22371,6 +27092,7 @@ var ApplicationPlanLimits = class extends BaseResource { ...opts } = options; return RequestHelper.put()(this, "application/plan_limits", { + ...opts, searchParams: { planName, ciPipelineSize, @@ -22390,8 +27112,7 @@ var ApplicationPlanLimits = class extends BaseResource { pypiMaxFileSize, terraformModuleMaxFileSize, storageSizeLimit - }, - opts + } }); } }; @@ -22487,6 +27208,18 @@ var BroadcastMessages = class extends BaseResource { ); } }; +var CodeSuggestions = class extends BaseResource { + createAccessToken(options) { + return RequestHelper.post()(this, "code_suggestions/tokens", options); + } + generateCompletion(options) { + return RequestHelper.post()( + this, + "code_suggestions/completions", + options + ); + } +}; var Composer = class extends BaseResource { create(projectId, options) { return RequestHelper.post()( @@ -23881,6 +28614,35 @@ var GeoNodes = class extends BaseResource { return RequestHelper.get()(this, `geo_nodes/${geonodeId}/status`, options); } }; +var GeoSites = class extends BaseResource { + all(options) { + return RequestHelper.get()(this, "geo_sites", options); + } + allStatuses(options) { + return RequestHelper.get()(this, "geo_sites/statuses", options); + } + allFailures(options) { + return RequestHelper.get()(this, "geo_sites/current/failures", options); + } + create(name, url12, options) { + return RequestHelper.post()(this, "geo_sites", { name, url: url12, ...options }); + } + edit(geositeId, options) { + return RequestHelper.put()(this, `geo_sites/${geositeId}`, options); + } + repair(geositeId, options) { + return RequestHelper.post()(this, `geo_sites/${geositeId}/repair`, options); + } + remove(geositeId, options) { + return RequestHelper.del()(this, `geo_sites/${geositeId}`, options); + } + show(geositeId, options) { + return RequestHelper.get()(this, `geo_sites/${geositeId}`, options); + } + showStatus(geositeId, options) { + return RequestHelper.get()(this, `geo_sites/${geositeId}/status`, options); + } +}; // src/resources/GitignoreTemplates.ts var GitignoreTemplates = class extends ResourceTemplates { @@ -24391,6 +29153,18 @@ var Search = class extends BaseResource { }); } }; +var SearchAdmin = class extends BaseResource { + all(options) { + return RequestHelper.get()(this, "admin/search/migrations", options); + } + show(versionOrName, options) { + return RequestHelper.get()( + this, + endpoint`admin/search/migrations/${versionOrName}`, + options + ); + } +}; var ServiceData = class extends BaseResource { showMetricDefinitions(options) { return RequestHelper.get()(this, "usage_data/metric_definitions", options); @@ -27905,6 +32679,36 @@ var GroupDORA4Metrics = class extends ResourceDORA4Metrics { super("groups", options); } }; +var GroupEpicBoards = class extends BaseResource { + all(groupId, options) { + return RequestHelper.get()( + this, + endpoint`groups/${groupId}/epic_boards`, + options + ); + } + allLists(groupId, boardId, options) { + return RequestHelper.get()( + this, + endpoint`groups/${groupId}/epic_boards/${boardId}/lists`, + options + ); + } + show(groupId, boardId, options) { + return RequestHelper.get()( + this, + endpoint`groups/${groupId}/epic_boards/${boardId}`, + options + ); + } + showList(groupId, boardId, listId, options) { + return RequestHelper.get()( + this, + endpoint`groups/${groupId}/epic_boards/${boardId}/lists/${listId}`, + options + ); + } +}; // src/resources/GroupHooks.ts var GroupHooks = class extends ResourceHooks { @@ -28259,6 +33063,44 @@ var GroupSAMLIdentities = class extends BaseResource { ); } }; +var GroupSAMLLinks = class extends BaseResource { + all(groupId, options) { + return RequestHelper.get()( + this, + endpoint`groups/${groupId}/saml_group_links`, + options + ); + } + create(groupId, samlGroupName, accessLevel, options) { + return RequestHelper.post()( + this, + endpoint`groups/${groupId}/saml_group_links`, + { + accessLevel, + samlGroupName, + ...options + } + ); + } + remove(groupId, samlGroupName, options) { + return RequestHelper.del()(this, endpoint`groups/${groupId}/saml_group_links`, { + searchParams: { + samlGroupName + }, + ...options + }); + } + show(groupId, samlGroupName, options) { + return RequestHelper.get()( + this, + endpoint`groups/${groupId}/saml_group_links`, + { + samlGroupName, + ...options + } + ); + } +}; var GroupSCIMIdentities = class extends BaseResource { all(groupId, options) { return RequestHelper.get()( @@ -28620,6 +33462,7 @@ var resources = { AuditEvents, Avatar, BroadcastMessages, + CodeSuggestions, Composer, Conan, DashboardAnnotations, @@ -28631,6 +33474,7 @@ var resources = { Events, Experiments, GeoNodes, + GeoSites, GitignoreTemplates, GitLabCIYMLTemplates, Import, @@ -28651,6 +33495,7 @@ var resources = { PyPI, RubyGems, Search, + SearchAdmin, ServiceData, SidekiqMetrics, SidekiqQueues, @@ -28763,6 +33608,7 @@ var resources = { GroupBadges, GroupCustomAttributes, GroupDORA4Metrics, + GroupEpicBoards, GroupHooks, GroupImportExports, GroupInvitations, @@ -28780,6 +33626,7 @@ var resources = { GroupRepositoryStorageMoves, Groups, GroupSAMLIdentities, + GroupSAMLLinks, GroupSCIMIdentities, GroupVariables, GroupWikis, @@ -28871,7 +33718,8 @@ function getConditionalMode(endpoint) { async function defaultRequestHandler(endpoint, options) { const retryCodes = [429, 502]; const maxRetries = 10; - const { prefixUrl, asStream, searchParams, ...opts } = options || {}; + const { prefixUrl, asStream, searchParams, rateLimiters, method, ...opts } = options || {}; + const endpointRateLimit = getMatchingRateLimiter(endpoint, rateLimiters, method); let baseUrl; if (prefixUrl) baseUrl = prefixUrl.endsWith("/") ? prefixUrl : `${prefixUrl}/`; @@ -28879,7 +33727,8 @@ async function defaultRequestHandler(endpoint, options) { url.search = searchParams || ""; const mode = getConditionalMode(endpoint); for (let i = 0; i < maxRetries; i += 1) { - const request = new Request(url, { ...opts, mode }); + const request = new Request(url, { ...opts, method, mode }); + await endpointRateLimit(); const response = await fetch(request).catch((e) => { if (e.name === "TimeoutError" || e.name === "AbortError") { throw new Error("Query timeout was reached"); @@ -28912,6 +33761,7 @@ var { AuditEvents: dist_AuditEvents, Avatar: dist_Avatar, BroadcastMessages: dist_BroadcastMessages, + CodeSuggestions: dist_CodeSuggestions, Composer: dist_Composer, Conan: dist_Conan, DashboardAnnotations: dist_DashboardAnnotations, @@ -28923,6 +33773,7 @@ var { Events: dist_Events, Experiments: dist_Experiments, GeoNodes: dist_GeoNodes, + GeoSites: dist_GeoSites, GitignoreTemplates: dist_GitignoreTemplates, GitLabCIYMLTemplates: dist_GitLabCIYMLTemplates, Import: dist_Import, @@ -28943,6 +33794,7 @@ var { PyPI: dist_PyPI, RubyGems: dist_RubyGems, Search: dist_Search, + SearchAdmin: dist_SearchAdmin, ServiceData: dist_ServiceData, SidekiqMetrics: dist_SidekiqMetrics, SidekiqQueues: dist_SidekiqQueues, @@ -29055,6 +33907,7 @@ var { GroupBadges: dist_GroupBadges, GroupCustomAttributes: dist_GroupCustomAttributes, GroupDORA4Metrics: dist_GroupDORA4Metrics, + GroupEpicBoards: dist_GroupEpicBoards, GroupHooks: dist_GroupHooks, GroupImportExports: dist_GroupImportExports, GroupInvitations: dist_GroupInvitations, @@ -29072,6 +33925,7 @@ var { GroupRepositoryStorageMoves: dist_GroupRepositoryStorageMoves, Groups: dist_Groups, GroupSAMLIdentities: dist_GroupSAMLIdentities, + GroupSAMLLinks: dist_GroupSAMLLinks, GroupSCIMIdentities: dist_GroupSCIMIdentities, GroupVariables: dist_GroupVariables, GroupWikis: dist_GroupWikis, @@ -29087,10 +33941,111 @@ var { -;// CONCATENATED MODULE: ./adapters/integrations/gitlab-integration.js -// gitlabIntegration.js +;// CONCATENATED MODULE: ./adapters/templates/gitlab-integration.js + + +function gitlab_integration_getErrorResponseStatus401 (ATLAN_INSTANCE_URL, CI_PROJECT_NAME, CI_PROJECT_NAMESPACE) { + return `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Bearer Token as \`ATLAN_API_TOKEN\` as this repository's CI/CD variable. + +Atlan Instance URL: ${ATLAN_INSTANCE_URL} + +Set your CI/CD variables [here](https://gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}/-/settings/ci_cd). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).` +} + +function gitlab_integration_getErrorResponseStatusUndefined(ATLAN_INSTANCE_URL, CI_PROJECT_NAME, CI_PROJECT_NAMESPACE) { + return `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Instance URL as \`ATLAN_INSTANCE_URL\` as this repository's CI/CD variable. + +Atlan Instance URL: ${ATLAN_INSTANCE_URL} + +Make sure your Atlan Instance URL is set in the following format. +\`https://tenant.atlan.com\` + +Set your CI/CD variables [here](https://gitlab.com/${CI_PROJECT_NAMESPACE}/${CI_PROJECT_NAME}/-/settings/ci_cd). For more information on how to setup the Atlan dbt Action, please read the [setup documentation here](https://github.com/atlanhq/dbt-action/blob/main/README.md).` +} + +function gitlab_integration_getSetResourceOnAssetComment(tableMd, setResourceFailed) { + return `## 🎊 Congrats on the merge! + + This pull request has been added as a resource to the following assets: + Name | Resource set successfully + --- | --- + ${tableMd} + ${setResourceFailed ? '> Seems like we were unable to set the resources for some of the assets due to insufficient permissions. To ensure that the pull request is linked as a resource, you will need to assign the right persona with requisite permissions to the API token.' : ''} + ` +} + +function gitlab_integration_getAssetInfo(ATLAN_INSTANCE_URL, asset, materialisedAsset, environmentName, projectName) { + return `### ${getConnectorImage( + asset.attributes.connectorName + )} [${asset.displayText}](${ATLAN_INSTANCE_URL}/assets/${ + asset.guid + }/overview?utm_source=dbt_gitlab_action) ${ + asset.attributes?.certificateStatus + ? getCertificationImage(asset.attributes.certificateStatus) + : "" + } +Materialised asset: ${getConnectorImage( + materialisedAsset.attributes.connectorName + )} [${materialisedAsset.attributes.name}](${ATLAN_INSTANCE_URL}/assets/${ + materialisedAsset.guid + }/overview?utm_source=dbt_gitlab_action) ${ + materialisedAsset.attributes?.certificateStatus + ? getCertificationImage(materialisedAsset.attributes.certificateStatus) + : "" + }${environmentName ? ` | Environment Name: \`${environmentName}\`` : ""}${ + projectName ? ` | Project Name: \`${projectName}\`` : "" + }` +} + +function gitlab_integration_getDownstreamTable(ATLAN_INSTANCE_URL, downstreamAssets, rows, materialisedAsset) { + return `
${ + downstreamAssets.entityCount + } downstream assets 👇
+ +Name | Type | Description | Owners | Terms | Classifications | Source URL +--- | --- | --- | --- | --- | --- | --- +${rows + .map((row) => + row.map((i) => i.replace(/\|/g, "•").replace(/\n/g, "")).join(" | ") + ) + .join("\n")} + +${ + downstreamAssets.hasMore + ? `[See more downstream assets at Atlan](${ATLAN_INSTANCE_URL}/assets/${materialisedAsset.guid}/lineage?utm_source=dbt_gitlab_action)` + : "" +} + +
` +} +function gitlab_integration_getViewAssetButton(ATLAN_INSTANCE_URL, asset) { + return `${getImageURL( + "atlan-logo", + 15, + 15 + )} [View asset in Atlan](${ATLAN_INSTANCE_URL}/assets/${ + asset.guid + }/overview?utm_source=dbt_gitlab_action)` +} + +function gitlab_integration_getMDCommentForModel(ATLAN_INSTANCE_URL, model) { + return `${getConnectorImage(model?.attributes?.connectorName)} [${ + model?.displayText + }](${ATLAN_INSTANCE_URL}/assets/${model?.guid}/overview?utm_source=dbt_gitlab_action)` +} + +function gitlab_integration_getMDCommentForMaterialisedView(ATLAN_INSTANCE_URL, materialisedView) { + return `${getConnectorImage(materialisedView?.attributes?.connectorName)} [${ + materialisedView?.attributes?.name + }](${ATLAN_INSTANCE_URL}/assets/${materialisedView?.guid}/overview?utm_source=dbt_gitlab_action)` +} +function gitlab_integration_getTableMD(md, resp) { + return `${md} | ${resp ? '✅' : '❌'} \n` +} +;// CONCATENATED MODULE: ./adapters/integrations/gitlab-integration.js +// gitlabIntegration.js @@ -29100,9 +34055,8 @@ var { -main.config(); -const gitlab_integration_ATLAN_INSTANCE_URL = process.env.ATLAN_INSTANCE_URL; -const { IS_DEV: gitlab_integration_IS_DEV } = process.env; +const gitlab_integration_integrationName = "gitlab"; +var CI_MERGE_REQUEST_IID; class GitLabIntegration extends IntegrationInterface { constructor(token) { @@ -29110,288 +34064,576 @@ class GitLabIntegration extends IntegrationInterface { } async run() { - //Done - console.log("Run Gitlab"); - const timeStart = Date.now(); + try { + const timeStart = Date.now(); + const gitlab = new dist_Gitlab({ + host: "https://gitlab.com", + token: this.token, + }); - const gitlab = new dist_Gitlab({ - host: "https://gitlab.com", - token: this.token, - }); + CI_MERGE_REQUEST_IID = await getCIMergeRequestIID( + gitlab, + CI_PROJECT_ID, + CI_COMMIT_SHA + ); - const { CI_PROJECT_PATH, CI_MERGE_REQUEST_IID } = process.env; + var mergeRequestCommit = await gitlab.Commits.allMergeRequests( + CI_PROJECT_ID, + CI_COMMIT_SHA + ); - if (!(await this.authIntegration({ gitlab }))) - //Done - throw { message: "Wrong API Token" }; + logger_logger.withInfo( + "GitLab Integration is running...", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "run" + ); - const { state, web_url, source_branch } = await gitlab.MergeRequests.show( - CI_PROJECT_PATH, - CI_MERGE_REQUEST_IID - ); + if (!(await this.authIntegration({ gitlab }))) { + logger_logger.withError( + "Authentication failed. Wrong API Token.", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "run" + ); + throw { message: "Wrong API Token" }; + } - let total_assets = 0; + let total_assets = 0; + + if ( + mergeRequestCommit.length && + mergeRequestCommit[0]?.state == "merged" + ) { + const { web_url, target_branch, diff_refs } = + await gitlab.MergeRequests.show( + CI_PROJECT_PATH, + mergeRequestCommit[0]?.iid + ); + total_assets = await this.setResourceOnAsset({ + gitlab, + web_url, + target_branch, + diff_refs, + }); + } else { + const { target_branch, diff_refs } = await gitlab.MergeRequests.show( + CI_PROJECT_PATH, + CI_MERGE_REQUEST_IID + ); - if (state === "opened") { - total_assets = await this.printDownstreamAssets({ - gitlab, - source_branch, - }); - } else if (state === "merged") { - total_assets = await this.setResourceOnAsset({ - gitlab, - web_url, - source_branch, - }); - } + total_assets = await this.printDownstreamAssets({ + gitlab, + target_branch, + diff_refs, + }); + } - if (total_assets !== 0) - this.sendSegmentEventOfIntegration("dbt_ci_action_run", { - asset_count: total_assets, - total_time: Date.now() - timeStart, - }); - } + if (total_assets !== 0) + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_run", + properties: { + asset_count: total_assets, + total_time: Date.now() - timeStart, + }, + }); - async printDownstreamAssets({ gitlab, source_branch }) { - //Done - // Implementation for printing impact on GitHub - // Use this.token to access the token - const changedFiles = await this.getChangedFiles({ gitlab }); //Complete + logger_logger.withInfo( + "Successfully Completed DBT_CI_PIPELINE", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "run" + ); + } catch (error) { + logger_logger.withError( + `Error in run(): ${error.message}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "run" + ); + throw error; + } + } - let comments = ``; - let totalChangedFiles = 0; + async printDownstreamAssets({ gitlab, target_branch, diff_refs }) { + logger_logger.withInfo( + "Printing downstream assets...", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); - for (const { fileName, filePath, headSHA } of changedFiles) { - const assetName = await this.getAssetName({ - //Complete - gitlab, - fileName, - filePath, - headSHA, - }); + try { + const changedFiles = await this.getChangedFiles({ gitlab, diff_refs }); - const environments = getGitLabEnvironments(); + let comments = ``; + let totalChangedFiles = 0; - let environment = null; - for (const [baseBranchName, environmentName] of environments) { - if (baseBranchName === source_branch) { - environment = environmentName; - break; + for (const { fileName, filePath, headSHA, status } of changedFiles) { + logger_logger.withInfo( + `Processing file: ${fileName}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + const aliasName = await this.getAssetName({ + gitlab, + fileName, + filePath, + headSHA, + }); + const assetName = IGNORE_MODEL_ALIAS_MATCHING ? fileName : aliasName; + + const environments = getGitLabEnvironments(); + let environment = null; + for (const baseBranchName of Object.keys(environments)) { + const environmentName = environments[baseBranchName]; + if (baseBranchName === target_branch) { + environment = environmentName; + break; + } } - } - const asset = await getAsset({ - //Complete - name: assetName, - sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, - environment: environment, - integration: "gitlab", - }); + logger_logger.withInfo( + `Processing asset: ${assetName} in environment: ${environment}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + + const asset = await getAsset({ + name: assetName, + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + environment: environment, + integration: "gitlab", + }); + + if (totalChangedFiles !== 0) comments += "\n\n---\n\n"; + + if (status === "added") { + logger_logger.withInfo( + `New model added: ${fileName}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + comments += getNewModelAddedComment(fileName); + totalChangedFiles++; + continue; + } + + if (asset.error) { + logger_logger.withError( + `Asset error for ${assetName}: ${asset.error}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + comments += asset.error; + totalChangedFiles++; + continue; + } + + const materialisedAsset = asset?.attributes?.dbtModelSqlAssets?.[0]; + const timeStart = Date.now(); + + const totalModifiedFiles = changedFiles.filter( + (i) => i.status === "modified" + ).length; + + const { guid } = asset; + + const downstreamAssets = await getDownstreamAssets( + asset, + materialisedAsset.guid, + totalModifiedFiles, + this.sendSegmentEventOfIntegration, + "gitlab" + ); + + if (downstreamAssets.error) { + logger_logger.withError( + `Downstream assets error for ${assetName}: ${downstreamAssets.error}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + comments += downstreamAssets.error; + totalChangedFiles++; + continue; + } + + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_downstream_unfurl", + properties: { + asset_guid: asset.guid, + asset_type: asset.typeName, + downstream_count: downstreamAssets.entities.length, + total_fetch_time: Date.now() - timeStart, + }, + }); + + const classifications = await getClassifications({ + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + }); + + const comment = await this.renderDownstreamAssetsComment({ + asset, + downstreamAssets, + classifications, + materialisedAsset, + }); + + comments += comment; - if (asset.error) { - comments += asset.error; totalChangedFiles++; - continue; } - //Cross-check this part once with Jaagrav. + comments = getBaseComment(totalChangedFiles, comments); - const totalModifiedFiles = changedFiles.filter( - (i) => i.status === "modified" - ).length; + const existingComment = await this.checkCommentExists({ gitlab }); - const { guid } = asset.attributes.sqlAsset; - const timeStart = Date.now(); - const downstreamAssets = await getDownstreamAssets( - //Done - asset, - guid, - totalModifiedFiles, - this.sendSegmentEventOfIntegration, - "gitlab" + logger_logger.withInfo( + `Existing Comment: ${existingComment?.id}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + + if (totalChangedFiles > 0) + await this.createIssueComment({ + gitlab, + content: comments, + comment_id: existingComment?.id, + }); + + if (totalChangedFiles === 0 && existingComment) + await this.deleteComment({ gitlab, comment_id: existingComment?.id }); + + logger_logger.withInfo( + "Successfully printed Downstream Assets", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" + ); + + return totalChangedFiles; + } catch (error) { + logger_logger.withError( + `Error in printDownstreamAssets: ${error.message}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "printDownstreamAssets" ); + throw error; + } + } - if (totalChangedFiles !== 0) comments += "\n\n---\n\n"; + async setResourceOnAsset({ gitlab, web_url, target_branch, diff_refs }) { + logger_logger.withInfo( + "Setting resources on assets...", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); - if (downstreamAssets.error) { - comments += downstreamAssets.error; - totalChangedFiles++; - continue; + try { + const changedFiles = await this.getChangedFiles({ gitlab, diff_refs }); + + var totalChangedFiles = 0; + let tableMd = ``; + let setResourceFailed = false; + if (changedFiles.length === 0) { + logger_logger.withInfo( + "No changed files found. Skipping resource setup.", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + return totalChangedFiles; } - this.sendSegmentEventOfIntegration("dbt_ci_action_downstream_unfurl", { - //Complete - asset_guid: asset.guid, - asset_type: asset.typeName, - downstream_count: downstreamAssets.length, - total_fetch_time: Date.now() - timeStart, - }); + for (const { fileName, filePath, headSHA } of changedFiles) { + const aliasName = await this.getAssetName({ + gitlab, + fileName, + filePath, + headSHA, + }); - const comment = await this.renderDownstreamAssetsComment({ - //Complete - asset, - downstreamAssets, - }); + const assetName = IGNORE_MODEL_ALIAS_MATCHING ? fileName : aliasName; - comments += comment; + logger_logger.withInfo( + `Resolved asset name: ${assetName}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); - totalChangedFiles++; - } + const environments = getGitLabEnvironments(); + let environment = null; + for (const baseBranchName of Object.keys(environments)) { + const environmentName = environments[baseBranchName]; + if (baseBranchName === target_branch) { + environment = environmentName; + break; + } + } - comments = `### ${get_image_url_getImageURL("atlan-logo", 15, 15)} Atlan impact analysis -Here is your downstream impact analysis for **${totalChangedFiles} ${ - totalChangedFiles > 1 ? "models" : "model" - }** you have edited. + logger_logger.withInfo( + `Processing asset: ${assetName} in environment: ${environment}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); -${comments}`; + const asset = await getAsset({ + name: assetName, + sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, + environment: environment, + integration: "gitlab", + }); - const existingComment = await this.checkCommentExists({ gitlab }); //Complete + if (asset.error) { + logger_logger.withError( + `Failed to retrieve asset: ${assetName}, Error: ${asset.error}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + continue; + } - if (totalChangedFiles > 0) - await this.createIssueComment({ - //Complete - gitlab, - comments, - comment_id: existingComment?.id, - }); + const materialisedAsset = asset?.attributes?.dbtModelSqlAssets?.[0]; + const timeStart = Date.now(); - if (totalChangedFiles === 0 && existingComment) - await this.deleteComment({ gitlab, comment_id: existingComment.id }); //Complete + const totalModifiedFiles = changedFiles.filter( + (i) => i.status === "modified" + ).length; - return totalChangedFiles; - } + const { guid } = asset; - async setResourceOnAsset({ gitlab, web_url, source_branch }) { - //Done - // Implementation for setting resources on GitHub - // Use this.token to access the token - const changedFiles = await this.getChangedFiles({ gitlab }); //Done - var totalChangedFiles = 0; + const downstreamAssets = await getDownstreamAssets( + asset, + materialisedAsset.guid, + totalModifiedFiles, + this.sendSegmentEventOfIntegration, + "gitlab" + ); - if (changedFiles.length === 0) return; + if (downstreamAssets.error) { + logger_logger.withError( + `Failed to retrieve downstream assets for: ${assetName}, Error: ${downstreamAssets.error}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + continue; + } + + this.sendSegmentEventOfIntegration({ + action: "dbt_ci_action_downstream_unfurl", + properties: { + asset_guid: asset.guid, + asset_type: asset.typeName, + downstream_count: downstreamAssets.entities.length, + total_fetch_time: Date.now() - timeStart, + }, + }); - for (const { fileName, filePath, headSHA } of changedFiles) { - const assetName = await this.getAssetName({ - gitlab, - fileName, - filePath, - headSHA, - }); + const model = asset; + const materialisedView = asset?.attributes?.dbtModelSqlAssets?.[0]; - const environments = getGitLabEnvironments(); + var lines = CI_COMMIT_MESSAGE.split("\n"); + var CI_MERGE_REQUEST_TITLE = lines[2]; - let environment = null; - for (const [baseBranchName, environmentName] of environments) { - if (baseBranchName === source_branch) { - environment = environmentName; - break; + if (downstreamAssets.entityCount != 0) { + if (model) { + const { guid: modelGuid } = model; + const resp = await createResource( + modelGuid, + CI_MERGE_REQUEST_TITLE, + web_url, + this.sendSegmentEventOfIntegration + ); + const md = gitlab_integration_getMDCommentForModel(ATLAN_INSTANCE_URL, model); + tableMd += gitlab_integration_getTableMD(md, resp); + if (!resp) { + setResourceFailed = true; + logger_logger.withError( + `Setting resource failed for model: ${modelGuid}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + } + } + + if (materialisedView) { + const { guid: tableAssetGuid } = materialisedView; + const resp = await createResource( + tableAssetGuid, + CI_MERGE_REQUEST_TITLE, + web_url, + this.sendSegmentEventOfIntegration + ); + const md = gitlab_integration_getMDCommentForMaterialisedView( + ATLAN_INSTANCE_URL, + materialisedView + ); + tableMd += gitlab_integration_getTableMD(md, resp); + if (!resp) { + setResourceFailed = true; + logger_logger.withError( + `Setting resource failed for materialized view: ${tableAssetGuid}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + } + } } + + totalChangedFiles++; } - const asset = await getAsset({ - //Done - name: assetName, - sendSegmentEventOfIntegration: this.sendSegmentEventOfIntegration, - environment: environment, - integration: "gitlab", + const comment = await this.createIssueComment({ + gitlab, + content: gitlab_integration_getSetResourceOnAssetComment(tableMd, setResourceFailed), + comment_id: null, + forceNewComment: true, }); - if (!asset) continue; - - const { guid: modelGuid } = asset; - const { guid: tableAssetGuid } = asset.attributes.sqlAsset; - - await createResource( - //Done - //Complete - modelGuid, - "Pull Request on GitLab", - web_url, - this.sendSegmentEventOfIntegration - ); - await createResource( - //Done - tableAssetGuid, - "Pull Request on GitLab", - web_url, - this.sendSegmentEventOfIntegration + logger_logger.withInfo( + "Successfully set the resource on the asset", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" ); - totalChangedFiles++; + return totalChangedFiles; + } catch (error) { + logger_logger.withError( + `Error in setResourceOnAsset: ${error}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "setResourceOnAsset" + ); + throw error; } - - const comment = await this.createIssueComment({ - //Done - //Complete - gitlab, - content: `🎊 Congrats on the merge! - -This pull request has been added as a resource to all the assets modified. ✅ -`, - comment_id: null, - forceNewComment: true, - }); - - return totalChangedFiles; } async authIntegration({ gitlab }) { - //Done - const response = await auth(); - - if (response?.status === 401) { - //Complete - await this.createIssueComment( - gitlab, - `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Bearer Token as \`ATLAN_API_TOKEN\` in your .gitlab-ci.yml file. + logger_logger.withInfo( + "Authenticating with Atlan", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "authIntegration" + ); -Atlan Instance URL: ${gitlab_integration_ATLAN_INSTANCE_URL}` - ); - return false; - } + try { + const response = await auth(); - if (response === undefined) { - await this.createIssueComment( - gitlab, - `We couldn't connect to your Atlan Instance, please make sure to set the valid Atlan Instance URL as \`ATLAN_INSTANCE_URL\` in your .gitlab-ci.yml file. + const existingComment = await this.checkCommentExists({ gitlab }); -Atlan Instance URL: ${gitlab_integration_ATLAN_INSTANCE_URL} + logger_logger.withInfo( + `Existing Comment: ${existingComment?.id}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "authIntegration" + ); -Make sure your Atlan Instance URL is set in the following format. -\`https://tenant.atlan.com\` + if (response?.status === 401) { + logger_logger.withError( + "Authentication failed: Status 401", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "authIntegration" + ); + await this.createIssueComment({ + gitlab, + content: gitlab_integration_getErrorResponseStatus401( + ATLAN_INSTANCE_URL, + CI_PROJECT_NAME, + CI_PROJECT_NAMESPACE + ), + comment_id: existingComment?.id, + }); + return false; + } -` + if (response === undefined) { + logger_logger.withError( + "Authentication failed: Undefined response", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "authIntegration" + ); + await this.createIssueComment({ + gitlab, + content: gitlab_integration_getErrorResponseStatusUndefined( + ATLAN_INSTANCE_URL, + CI_PROJECT_NAME, + CI_PROJECT_NAMESPACE + ), + comment_id: existingComment?.id, + }); + return false; + } + logger_logger.withInfo( + "Successfully Authenticated with Atlan", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "authIntegration" ); - return false; + return true; + } catch (error) { + logger_logger.withError( + `Error in authIntegration: ${error.message}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "authIntegration" + ); + throw error; } - - return true; } async createIssueComment({ - //Done - //Complete gitlab, content, comment_id = null, forceNewComment = false, }) { - const { CI_PROJECT_PATH, CI_MERGE_REQUEST_IID } = process.env; + logger_logger.withInfo( + "Creating an issue comment...", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "createIssueComment" + ); content = ` ${content}`; - console.log(content); - - if (gitlab_integration_IS_DEV) return content; + if (IS_DEV) { + logger_logger.withInfo( + "Development mode enabled. Skipping comment creation.", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "createIssueComment" + ); + return content; + } - if (comment_id && !forceNewComment) + if (comment_id && !forceNewComment) { return await gitlab.MergeRequestNotes.edit( - CI_PROJECT_PATH, + CI_PROJECT_ID, CI_MERGE_REQUEST_IID, comment_id, - content + { + body: content, + } ); + } return await gitlab.MergeRequestNotes.create( CI_PROJECT_PATH, CI_MERGE_REQUEST_IID, @@ -29400,147 +34642,277 @@ ${content}`; } async sendSegmentEventOfIntegration({ action, properties }) { - //Done - //Complete - // Implement your sendSegmentEvent logic here - // IMPORT ATLAN_INSTANCE_URL. - const domain = new URL(gitlab_integration_ATLAN_INSTANCE_URL).hostname; - const { CI_PROJECT_PATH, CI_JOB_URL } = process.env; - - const raw = stringify({ - category: "integration", - object: "gitlab", - action, - userId: "atlan-annonymous-github", - properties: { - ...properties, - gitlab_job_id: CI_JOB_URL, - domain, - }, - }); - // IMPORT SEGMENTEVENT - return sendSegmentEvent(action, raw); + try { + const domain = new URL(ATLAN_INSTANCE_URL).hostname; + logger_logger.withInfo( + `Sending Segment event for action: ${action}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "sendSegmentEventOfIntegration" + ); + + const raw = stringify({ + category: "integration", + object: "gitlab", + action, + userId: "atlan-annonymous-github", + properties: { + ...properties, + gitlab_job_id: CI_JOB_URL, + domain, + }, + }); + + return sendSegmentEvent(action, raw); + } catch (error) { + logger_logger.withError( + `Error sending Segment event for action: ${action} - ${error.message}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "sendSegmentEventOfIntegration" + ); + throw error; + } } - async getChangedFiles({ gitlab }) { - //Done - //Complete - const { CI_PROJECT_PATH, CI_MERGE_REQUEST_IID } = process.env; + async getChangedFiles({ gitlab, diff_refs }) { + try { + logger_logger.withInfo( + "Fetching changed files...", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getChangedFiles" + ); - const { changes, diff_refs } = await gitlab.MergeRequests.changes( - CI_PROJECT_PATH, - CI_MERGE_REQUEST_IID - ); - var changedFiles = changes - .map(({ new_path, old_path }) => { - try { - const [modelName] = new_path - .match(/.*models\/(.*)\.sql/)[1] - .split("/") - .reverse()[0] - .split("."); - - //Cross-check this with Jaagrav. ### - if (modelName) { - if (old_path === null) { - return { - fileName: modelName, - filePath: new_path, - headSHA: diff_refs.head_sha, - status: "added", - }; - } else if (new_path !== old_path) { - // File is renamed or moved - return { - fileName: modelName, - filePath: new_path, - headSHA: diff_refs.head_sha, - status: "renamed_or_moved", - }; - } else { - // File is modified - return { - fileName: modelName, - filePath: new_path, - headSHA: diff_refs.head_sha, - status: "modified", - }; + var changes = await gitlab.MergeRequests.allDiffs( + CI_PROJECT_PATH, + CI_MERGE_REQUEST_IID + ); + + var changedFiles = changes + .map(({ new_path, old_path, new_file }) => { + try { + const [modelName] = new_path + .match(/.*models\/(.*)\.sql/)[1] + .split("/") + .reverse()[0] + .split("."); + + if (modelName) { + if (new_file) { + return { + fileName: modelName, + filePath: new_path, + headSHA: diff_refs.head_sha, + status: "added", + }; + } else if (new_path !== old_path) { + // File is renamed or moved + return { + fileName: modelName, + filePath: new_path, + headSHA: diff_refs.head_sha, + status: "renamed_or_moved", + }; + } else { + // File is modified + return { + fileName: modelName, + filePath: new_path, + headSHA: diff_refs.head_sha, + status: "modified", + }; + } } + } catch (e) { + logger_logger.withError( + `Error processing file`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getChangedFiles" + ); } - } catch (e) {} - }) - .filter((i) => i !== undefined); + }) + .filter((i) => i !== undefined); - changedFiles = changedFiles.filter((item, index) => { - return ( - changedFiles.findIndex((obj) => obj.fileName === item.fileName) === - index - ); - }); + changedFiles = changedFiles.filter((item, index) => { + return ( + changedFiles.findIndex((obj) => obj.fileName === item.fileName) === + index + ); + }); - console.log(changedFiles); + logger_logger.withInfo( + "Successfully fetched changed files", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getChangedFiles" + ); - return changedFiles; + return changedFiles; + } catch (error) { + logger_logger.withError( + `Error fetching changed files - ${error.message}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getChangedFiles" + ); + throw error; + } } async getAssetName({ gitlab, fileName, filePath, headSHA }) { - //Done - //Complete - var regExp = /config\(.*alias=\'([^']+)\'.*\)/im; - var fileContents = await this.getFileContents({ - gitlab, - filePath, - headSHA, - }); + try { + logger_logger.withInfo( + "Getting asset name...", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getAssetName" + ); - var matches = regExp.exec(fileContents); + var regExp = + /{{\s*config\s*\(\s*(?:[^,]*,)*\s*alias\s*=\s*['"]([^'"]+)['"](?:\s*,[^,]*)*\s*\)\s*}}/im; + var fileContents = await this.getFileContents({ + gitlab, + filePath, + headSHA, + }); - if (matches) { - return matches[1]; - } + if (fileContents) { + var matches = regExp.exec(fileContents); + if (matches) { + logger_logger.withInfo( + `Found a match: ${matches[1].trim()}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getAssetName" + ); + return matches[1].trim(); + } + } + + logger_logger.withInfo( + `Using filename as asset name: ${fileName}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getAssetName" + ); - return fileName; + return fileName; + } catch (error) { + logger_logger.withError( + `Error getting asset name - ${error.message}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getAssetName" + ); + throw error; + } } async getFileContents({ gitlab, filePath, headSHA }) { - //Done - //Complete - const { CI_PROJECT_PATH } = process.env; - const { content } = await gitlab.RepositoryFiles.show( - CI_PROJECT_PATH, - filePath, - headSHA - ); - const buff = Buffer.from(content, "base64"); + try { + logger_logger.withInfo( + "Fetching file contents...", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getFileContents" + ); + + const { content } = await gitlab.RepositoryFiles.show( + CI_PROJECT_PATH, + filePath, + headSHA + ); + const buff = Buffer.from(content, "base64"); + + logger_logger.withInfo( + "Successfully fetched file contents", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getFileContents" + ); - return buff.toString("utf8"); + return buff.toString("utf8"); + } catch (error) { + logger_logger.withError( + `Error in getFileContents: ${error.message}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "getFileContents" + ); + throw error; + } } async checkCommentExists({ gitlab }) { - //Done - //Complete - const { CI_PROJECT_PATH, CI_MERGE_REQUEST_IID } = process.env; - if (gitlab_integration_IS_DEV) return null; + logger_logger.withInfo( + "Checking for existing comments...", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "checkCommentExists" + ); + + if (IS_DEV) { + logger_logger.withInfo( + "Development mode enabled. Skipping comment check.", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "checkCommentExists" + ); + return null; + } - const comments = await gitlab.MergeRequestNotes.all( - CI_PROJECT_PATH, - CI_MERGE_REQUEST_IID - ); + try { + const comments = await gitlab.MergeRequestNotes.all( + CI_PROJECT_PATH, + CI_MERGE_REQUEST_IID + ); - return comments.find( - // Why here we have hardocded value? What should be over here inplace of this. - (comment) => - comment.author.username === "Jaagrav" && - comment.body.includes( - "" - ) - ); + const identifier = `project_${CI_PROJECT_ID}_bot_`; + + const existingComment = comments.find( + (comment) => + comment.author.username.includes(identifier) && + comment.body.includes( + "" + ) + ); + if (existingComment) { + logger_logger.withInfo( + "Found existing comment: " + existingComment?.id, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "checkCommentExists" + ); + } else { + logger_logger.withInfo( + "No existing comment found", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "checkCommentExists" + ); + } + + return existingComment; + } catch (error) { + logger_logger.withError( + "Error checking for existing comments: " + error.message, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "checkCommentExists" + ); + throw error; + } } async deleteComment({ gitlab, comment_id }) { - //Done - //Complete - const { CI_PROJECT_PATH, CI_MERGE_REQUEST_IID } = process.env; + logger_logger.withInfo( + `Deleted comment with ID ${comment_id}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "deleteComment" + ); return await gitlab.MergeRequestNotes.remove( CI_PROJECT_PATH, @@ -29549,95 +34921,155 @@ ${content}`; ); } - async renderDownstreamAssetsComment({ asset, downstreamAssets }) { - //Done - let impactedData = downstreamAssets.map( - ({ displayText, guid, typeName, attributes, meanings }) => { - let readableTypeName = typeName - .toLowerCase() - .replace(attributes.connectorName, "") - .toUpperCase(); - readableTypeName = - readableTypeName.charAt(0).toUpperCase() + - readableTypeName.slice(1).toLowerCase(); - return [ - guid, - displayText, - attributes.connectorName, - readableTypeName, - attributes?.userDescription || attributes?.description || "", - attributes?.certificateStatus || "", - [...attributes?.ownerUsers, ...attributes?.ownerGroups] || [], - meanings - .map( - ({ displayText, termGuid }) => - `[${displayText}](${gitlab_integration_ATLAN_INSTANCE_URL}/assets/${termGuid}?utm_source=dbt_gitlab_action)` - ) - ?.join(", ") || " ", - attributes?.sourceURL || "", - ]; - } + async renderDownstreamAssetsComment({ + asset, + downstreamAssets, + classifications, + materialisedAsset, + }) { + logger_logger.withInfo( + "Rendering Downstream Assets...", + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "renderDownstreamAssetsComment" ); - impactedData = impactedData.sort((a, b) => a[3].localeCompare(b[3])); // Sort by typeName - impactedData = impactedData.sort((a, b) => a[2].localeCompare(b[2])); // Sort by connectorName + try { + let impactedData = downstreamAssets.entities.map( + ({ + displayText, + guid, + typeName, + attributes, + meanings, + classificationNames, + }) => { + // Modifying the typeName and getting the readableTypeName + let readableTypeName = typeName + .toLowerCase() + .replace(attributes.connectorName, "") + .toUpperCase(); + + // Filtering classifications based on classificationNames + let classificationsObj = classifications.filter(({ name }) => + classificationNames.includes(name) + ); + + // Modifying the readableTypeName + readableTypeName = + readableTypeName.charAt(0).toUpperCase() + + readableTypeName.slice(1).toLowerCase(); + + return [ + guid, + truncate(displayText), + truncate(attributes.connectorName), + truncate(readableTypeName), + truncate( + attributes?.userDescription || attributes?.description || "" + ), + attributes?.certificateStatus || "", + truncate( + [...attributes?.ownerUsers, ...attributes?.ownerGroups] || [] + ), + truncate( + meanings.map( + ({ displayText, termGuid }) => + `[${displayText}](${ATLAN_INSTANCE_URL}/assets/${termGuid}/overview?utm_source=dbt_gitlab_action)` + ) + ), + truncate( + classificationsObj?.map( + ({ name, displayName }) => `\`${displayName}\`` + ) + ), + attributes?.sourceURL || "", + ]; + } + ); - let rows = impactedData.map( - ([ - guid, - displayText, - connectorName, - typeName, - description, - certificateStatus, - owners, - meanings, - sourceUrl, - ]) => { - const connectorImage = get_image_url_getConnectorImage(connectorName), - certificationImage = certificateStatus - ? get_image_url_getCertificationImage(certificateStatus) - : ""; + // Sorting the impactedData first by typeName and then by connectorName + impactedData = impactedData.sort((a, b) => a[3].localeCompare(b[3])); + impactedData = impactedData.sort((a, b) => a[2].localeCompare(b[2])); - return [ - `${connectorImage} [${displayText}](${gitlab_integration_ATLAN_INSTANCE_URL}/assets/${guid}?utm_source=dbt_gitlab_action) ${certificationImage}`, - `\`${typeName}\``, + // Creating rows for the downstream table + let rows = impactedData.map( + ([ + guid, + displayText, + connectorName, + typeName, description, - owners.join(", ") || " ", + certificateStatus, + owners, meanings, - sourceUrl ? `[Open in ${connectorName}](${sourceUrl})` : " ", - ]; - } - ); + classifications, + sourceUrl, + ]) => { + // Getting connector and certification images + const connectorImage = getConnectorImage(connectorName); + const certificationImage = certificateStatus + ? getCertificationImage(certificateStatus) + : ""; - const comment = `### ${get_image_url_getConnectorImage( - asset.attributes.connectorName - )} [${asset.displayText}](${gitlab_integration_ATLAN_INSTANCE_URL}/assets/${ - asset.guid - }?utm_source=dbt_gitlab_action) ${ - asset.attributes?.certificateStatus - ? get_image_url_getCertificationImage(asset.attributes.certificateStatus) - : "" - } + return [ + `${connectorImage} [${displayText}](${ATLAN_INSTANCE_URL}/assets/${guid}/overview?utm_source=dbt_gitlab_action) ${certificationImage}`, + `\`${typeName}\``, + description, + owners, + meanings, + classifications, + sourceUrl ? `[Open in ${connectorName}](${sourceUrl})` : " ", + ]; + } + ); - **${downstreamAssets.length} downstream assets** 👇 - Name | Type | Description | Owners | Terms | Source URL - --- | --- | --- | --- | --- | --- - ${rows - .map((row) => - row.map((i) => i.replace(/\|/g, "•").replace(/\n/g, "")).join(" | ") - ) - .join("\n")} + const environmentName = + materialisedAsset?.attributes?.assetDbtEnvironmentName; + const projectName = materialisedAsset?.attributes?.assetDbtProjectName; + // Generating asset information + const assetInfo = gitlab_integration_getAssetInfo( + ATLAN_INSTANCE_URL, + asset, + materialisedAsset, + environmentName, + projectName + ); - ${get_image_url_getImageURL( - "atlan-logo", - 15, - 15 - )} [View asset in Atlan](${gitlab_integration_ATLAN_INSTANCE_URL}/assets/${ - asset.guid - }?utm_source=dbt_gitlab_action)`; + // Generating the downstream table + const downstreamTable = gitlab_integration_getDownstreamTable( + ATLAN_INSTANCE_URL, + downstreamAssets, + rows, + materialisedAsset + ); - return comment; + // Generating the "View asset in Atlan" button + const viewAssetButton = gitlab_integration_getViewAssetButton(ATLAN_INSTANCE_URL, asset); + + // Generating the final comment based on the presence of downstream assets + if (downstreamAssets.entities.length > 0) { + return `${assetInfo} + +${downstreamTable} + +${viewAssetButton}`; + } else { + return `${assetInfo} + +No downstream assets found. + +${viewAssetButton}`; + } + } catch (error) { + logger_logger.withError( + `Error rendering Downstream Assets: ${error.message}`, + gitlab_integration_integrationName, + CI_COMMIT_SHA, + "renderDownstreamAssetsComment" + ); + throw error; + } } } @@ -29648,15 +35080,8 @@ ${content}`; - -main.config(); - -const GITHUB_TOKEN = core.getInput("GITHUB_TOKEN") || process.env.GITHUB_TOKEN; -const GITLAB_TOKEN = process.env.GITLAB_TOKEN; - async function run() { //Add new integrations over here - console.log("oii"); await runAction(GITHUB_TOKEN, GitHubIntegration); await runAction(GITLAB_TOKEN, GitLabIntegration); } diff --git a/package-lock.json b/package-lock.json index 880e022..5d68496 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,18 @@ "dependencies": { "@actions/core": "^1.10.0", "@actions/github": "^5.1.1", - "@gitbeaker/rest": "^39.17.0", + "@gitbeaker/cli": "^39.19.0", + "@gitbeaker/core": "^39.19.0", + "@gitbeaker/requester-utils": "^39.19.0", + "@gitbeaker/rest": "^39.19.0", "@vercel/ncc": "^0.34.0", "dotenv": "^16.0.3", "json-stringify-safe": "^5.0.1", "node-fetch": "^3.3.0", "uuid": "^9.0.0" + }, + "devDependencies": { + "@types/json-stringify-safe": "^5.0.2" } }, "node_modules/@actions/core": { @@ -55,12 +61,31 @@ "tunnel": "^0.0.6" } }, + "node_modules/@gitbeaker/cli": { + "version": "39.19.0", + "resolved": "https://registry.npmjs.org/@gitbeaker/cli/-/cli-39.19.0.tgz", + "integrity": "sha512-NW3VND/3hZL0g350hB/x5B0q1cDgpxfVel0VefPvlYSwrh0g2BFCqDV/UDCfqWwoLGXNxf/gZfqCZfjWbmJYfw==", + "dependencies": { + "@gitbeaker/core": "^39.19.0", + "@gitbeaker/rest": "^39.19.0", + "chalk": "4.1.2", + "sywac": "^1.3.0", + "xcase": "^2.0.1" + }, + "bin": { + "gb": "dist/index.mjs", + "gitbeaker": "dist/index.mjs" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@gitbeaker/core": { - "version": "39.17.0", - "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-39.17.0.tgz", - "integrity": "sha512-yKlI2qpi6k+aQsfNwlRjmeiKyDcB2xf3NHp5XzMpHdiXUMjwFmNrlmEv8TLj5mdrwDgZWGpUHVHX9xKwHri8eA==", + "version": "39.19.0", + "resolved": "https://registry.npmjs.org/@gitbeaker/core/-/core-39.19.0.tgz", + "integrity": "sha512-v8zZY5S1RsKDAMl0iqjwYwPpLPLYyPfRyYmWxyqyxqkB9SoB3Eqb7LPLMgtTFu2rZ6Atk2N/MXjMYl/f8x10fw==", "dependencies": { - "@gitbeaker/requester-utils": "^39.17.0", + "@gitbeaker/requester-utils": "^39.19.0", "qs": "^6.11.2", "xcase": "^2.0.1" }, @@ -69,10 +94,12 @@ } }, "node_modules/@gitbeaker/requester-utils": { - "version": "39.17.0", - "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-39.17.0.tgz", - "integrity": "sha512-ymy3xA9HZTdZ7GJMR7ewdGQa4JoLAmIdN6T8F5FEvLB4AYBwVqwIyitX4mrnukqI4FlGF/Lt5IUIp4zUDmJXQQ==", + "version": "39.19.0", + "resolved": "https://registry.npmjs.org/@gitbeaker/requester-utils/-/requester-utils-39.19.0.tgz", + "integrity": "sha512-cRPz469TaGUMaxpz/Yht6BlVTrNlWkYiXSuE+vfyo/jyDajeiI+8fswKR3nZO7tRkMu/VAotEb0QXw1MAF+V3w==", "dependencies": { + "async-sema": "^3.1.1", + "micromatch": "^4.0.5", "qs": "^6.11.2", "xcase": "^2.0.1" }, @@ -81,12 +108,12 @@ } }, "node_modules/@gitbeaker/rest": { - "version": "39.17.0", - "resolved": "https://registry.npmjs.org/@gitbeaker/rest/-/rest-39.17.0.tgz", - "integrity": "sha512-P5mg66aKJrMfZag3DXJlqnrFjtRjg6gQgrT3AotN0A7TDQyxXNAOtE19k+etXkXzecaNA01HYNp9aIxXpa5y9A==", + "version": "39.19.0", + "resolved": "https://registry.npmjs.org/@gitbeaker/rest/-/rest-39.19.0.tgz", + "integrity": "sha512-4MaekE0zcelImkM+QbZiuNTDGnBFOd8m/Sge5jjEsQG588B+rjDTCkR4pvmIqRQgkpu5aM8+HSgEFxZ+VRtbmg==", "dependencies": { - "@gitbeaker/core": "^39.17.0", - "@gitbeaker/requester-utils": "^39.17.0" + "@gitbeaker/core": "^39.19.0", + "@gitbeaker/requester-utils": "^39.19.0" }, "engines": { "node": ">=18.0.0" @@ -212,6 +239,12 @@ "@octokit/openapi-types": "^12.11.0" } }, + "node_modules/@types/json-stringify-safe": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/json-stringify-safe/-/json-stringify-safe-5.0.2.tgz", + "integrity": "sha512-NJdNTdatSiYvxdPLwgQImChxTfPECXYkdB6wC71dO3GCz2z/s/ZkkHXHaUlSVvnbsRUQg8Al3cT8Wp08wMF3zA==", + "dev": true + }, "node_modules/@vercel/ncc": { "version": "0.34.0", "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz", @@ -220,11 +253,41 @@ "ncc": "dist/ncc/cli.js" } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/async-sema": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/async-sema/-/async-sema-3.1.1.tgz", + "integrity": "sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==" + }, "node_modules/before-after-hook": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -237,6 +300,37 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -280,6 +374,17 @@ "node": "^12.20 || >= 14.13" } }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -311,16 +416,21 @@ } }, "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" - }, + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", "engines": { "node": ">= 0.4.0" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/has-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", @@ -343,6 +453,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-plain-object": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", @@ -356,6 +474,18 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -407,6 +537,17 @@ "wrappy": "1" } }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/qs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", @@ -434,6 +575,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sywac": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sywac/-/sywac-1.3.0.tgz", + "integrity": "sha512-LDt2stNTp4bVPMgd70Jj9PWrSa4batl+bv+Ea5NLNGT7ufc4oQPtRfQ73wbddNV6RilaPqnEt6y1Wkm5FVTNEg==", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", diff --git a/package.json b/package.json index 6d68730..cf55044 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,17 @@ "dependencies": { "@actions/core": "^1.10.0", "@actions/github": "^5.1.1", - "@gitbeaker/rest": "^39.17.0", + "@gitbeaker/cli": "^39.19.0", + "@gitbeaker/core": "^39.19.0", + "@gitbeaker/requester-utils": "^39.19.0", + "@gitbeaker/rest": "^39.19.0", "@vercel/ncc": "^0.34.0", "dotenv": "^16.0.3", "json-stringify-safe": "^5.0.1", "node-fetch": "^3.3.0", "uuid": "^9.0.0" + }, + "devDependencies": { + "@types/json-stringify-safe": "^5.0.2" } } diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 02b54ac..0000000 --- a/src/index.js +++ /dev/null @@ -1,45 +0,0 @@ -// import dotenv from "dotenv"; -// import core from "@actions/core"; -// import github from "@actions/github"; - -// import { printDownstreamAssets, setResourceOnAsset } from "./main/index.js"; -// import { sendSegmentEvent } from "./api/index.js"; -// import { auth } from "./utils/index.js"; - -// dotenv.config(); - -// const GITHUB_TOKEN = core.getInput("GITHUB_TOKEN") || process.env.GITHUB_TOKEN; - -// async function run() { -// console.log("Hmm"); -// const timeStart = Date.now(); -// const { context } = github; -// const octokit = github.getOctokit(GITHUB_TOKEN); -// const { pull_request } = context.payload; -// const { state, merged } = pull_request; - -// if (!(await auth(octokit, context))) throw { message: "Wrong API Token" }; - -// let total_assets = 0; - -// if (state === "open") { -// total_assets = await printDownstreamAssets({ octokit, context }); -// } else if (state === "closed") { -// if (merged) total_assets = await setResourceOnAsset({ octokit, context }); -// } - -// if (total_assets !== 0) -// sendSegmentEvent("dbt_ci_action_run", { -// asset_count: total_assets, -// total_time: Date.now() - timeStart, -// }); -// } - -// run().catch((err) => { -// sendSegmentEvent("dbt_ci_action_failure", { -// reason: "failed_to_run_action", -// msg: err, -// }); - -// core.setFailed(err.message); -// }); diff --git a/src/main/index.js b/src/main/index.js deleted file mode 100644 index fd65f4b..0000000 --- a/src/main/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as printDownstreamAssets } from "./print-downstream-assets.js"; -export { default as setResourceOnAsset } from "./set-resource-on-asset.js"; diff --git a/src/main/print-downstream-assets.js b/src/main/print-downstream-assets.js deleted file mode 100644 index e24f2d3..0000000 --- a/src/main/print-downstream-assets.js +++ /dev/null @@ -1,105 +0,0 @@ -import { - getAsset, - getClassifications, - getDownstreamAssets, - sendSegmentEvent, -} from "../api/index.js"; -import { - renderDownstreamAssetsComment, - getChangedFiles, - getAssetName, - createIssueComment, - checkCommentExists, - deleteComment, - getImageURL, - getConnectorImage, -} from "../utils/index.js"; -import { isIgnoreModelAliasMatching } from "../utils/get-environment-variables.js"; - -export default async function printDownstreamAssets({ octokit, context }) { - const changedFiles = await getChangedFiles(octokit, context); - let comments = ``; - let totalChangedFiles = 0; - - for (const { fileName, filePath, status } of changedFiles) { - const aliasName = await getAssetName({ - octokit, - context, - fileName, - filePath, - }); - const assetName = isIgnoreModelAliasMatching() ? fileName : aliasName; - const asset = await getAsset({ name: assetName }); - - if (totalChangedFiles !== 0) comments += "\n\n---\n\n"; - - if (status === "added") { - comments += `### ${getConnectorImage("dbt")} ${fileName} 🆕 -Its a new model and not present in Atlan yet, you'll see the downstream impact for it after its present in Atlan.`; - totalChangedFiles++; - continue; - } - - if (asset.error) { - comments += asset.error; - totalChangedFiles++; - continue; - } - - const materialisedAsset = asset.attributes.dbtModelSqlAssets[0]; - const timeStart = Date.now(); - const totalModifiedFiles = changedFiles.filter( - (i) => i.status === "modified" - ).length; - const downstreamAssets = await getDownstreamAssets( - asset, - materialisedAsset.guid, - totalModifiedFiles - ); - - if (downstreamAssets.error) { - comments += downstreamAssets.error; - totalChangedFiles++; - continue; - } - - sendSegmentEvent("dbt_ci_action_downstream_unfurl", { - asset_guid: asset.guid, - asset_type: asset.typeName, - downstream_count: downstreamAssets.entities.length, - total_fetch_time: Date.now() - timeStart, - }); - - const classifications = await getClassifications(); - - const comment = await renderDownstreamAssetsComment( - octokit, - context, - asset, - materialisedAsset, - downstreamAssets, - classifications - ); - - comments += comment; - - totalChangedFiles++; - } - - comments = `### ${getImageURL("atlan-logo", 15, 15)} Atlan impact analysis -Here is your downstream impact analysis for **${totalChangedFiles} ${ - totalChangedFiles > 1 ? "models" : "model" - }** you have edited. - -${comments}`; - - const existingComment = await checkCommentExists(octokit, context); - - if (totalChangedFiles > 0) - await createIssueComment(octokit, context, comments, existingComment?.id); - - if (totalChangedFiles === 0 && existingComment) - await deleteComment(octokit, context, existingComment.id); - - return totalChangedFiles; -} diff --git a/src/main/set-resource-on-asset.js b/src/main/set-resource-on-asset.js deleted file mode 100644 index db493bc..0000000 --- a/src/main/set-resource-on-asset.js +++ /dev/null @@ -1,58 +0,0 @@ -import { getAsset, createResource } from "../api/index.js"; -import { - createIssueComment, - getChangedFiles, - getAssetName, -} from "../utils/index.js"; - -export default async function setResourceOnAsset({ octokit, context }) { - const changedFiles = await getChangedFiles(octokit, context); - const { pull_request } = context.payload; - var totalChangedFiles = 0; - - if (changedFiles.length === 0) return; - - for (const { fileName, filePath } of changedFiles) { - const assetName = await getAssetName({ - octokit, - context, - fileName, - filePath, - }); - const asset = await getAsset({ name: assetName }); - - if (asset.error) continue; - - const { guid: modelGuid } = asset; - const { guid: tableAssetGuid } = asset?.attributes?.dbtModelSqlAssets?.[0]; - - if (modelGuid) - await createResource( - modelGuid, - "Pull Request on GitHub", - pull_request.html_url - ); - - if (tableAssetGuid) - await createResource( - tableAssetGuid, - "Pull Request on GitHub", - pull_request.html_url - ); - - totalChangedFiles++; - } - - const comment = await createIssueComment( - octokit, - context, - `🎊 Congrats on the merge! - -This pull request has been added as a resource to all the assets modified. ✅ -`, - null, - true - ); - - return totalChangedFiles; -} diff --git a/src/utils/create-comment.js b/src/utils/create-comment.js deleted file mode 100644 index 1ed05d9..0000000 --- a/src/utils/create-comment.js +++ /dev/null @@ -1,182 +0,0 @@ -import {getCertificationImage, getConnectorImage, getImageURL,} from "./index.js"; -import {getInstanceUrl, isDev} from "./get-environment-variables.js"; - -const IS_DEV = isDev(); -const ATLAN_INSTANCE_URL = - getInstanceUrl(); - -export function truncate(value) { - if (typeof value === 'string') - return value.length > 100 ? value.substring(0, 100) + "..." : value; - if (Array.isArray(value)) - return value.length > 10 ? value.slice(0, 10).join(", ") + "..." : value.join(", "); - return "" -} - -export default async function renderDownstreamAssetsComment( - octokit, - context, - asset, - materialisedAsset, - downstreamAssets, - classifications -) { - // Mapping the downstream assets data - let impactedData = downstreamAssets.entities.map( - ({ - displayText, - guid, - typeName, - attributes, - meanings, - classificationNames - }) => { - // Modifying the typeName and getting the readableTypeName - let readableTypeName = typeName - .toLowerCase() - .replace(attributes.connectorName, "") - .toUpperCase(); - - // Filtering classifications based on classificationNames - let classificationsObj = classifications.filter(({name}) => - classificationNames.includes(name) - ); - - // Modifying the readableTypeName - readableTypeName = readableTypeName.charAt(0).toUpperCase() + readableTypeName.slice(1).toLowerCase(); - - return [ - guid, - truncate(displayText), - truncate(attributes.connectorName), - truncate(readableTypeName), - truncate(attributes?.userDescription || attributes?.description || ""), - attributes?.certificateStatus || "", - truncate([...attributes?.ownerUsers, ...attributes?.ownerGroups] || []), - truncate(meanings.map(({displayText, termGuid}) => - `[${displayText}](${ATLAN_INSTANCE_URL}/assets/${termGuid}/overview?utm_source=dbt_github_action)` - )), - truncate(classificationsObj?.map(({name, displayName}) => - `\`${displayName}\`` - )), - attributes?.sourceURL || "" - ]; - } - ); - - // Sorting the impactedData first by typeName and then by connectorName - impactedData = impactedData.sort((a, b) => a[3].localeCompare(b[3])); - impactedData = impactedData.sort((a, b) => a[2].localeCompare(b[2])); - - // Creating rows for the downstream table - let rows = impactedData.map( - ([guid, displayText, connectorName, typeName, description, certificateStatus, owners, meanings, classifications, sourceUrl]) => { - // Getting connector and certification images - const connectorImage = getConnectorImage(connectorName); - const certificationImage = certificateStatus ? getCertificationImage(certificateStatus) : ""; - - return [ - `${connectorImage} [${displayText}](${ATLAN_INSTANCE_URL}/assets/${guid}/overview?utm_source=dbt_github_action) ${certificationImage}`, - `\`${typeName}\``, - description, - owners, - meanings, - classifications, - sourceUrl ? `[Open in ${connectorName}](${sourceUrl})` : " " - ]; - } - ); - - const environmentName = materialisedAsset?.attributes?.assetDbtEnvironmentName - const projectName = materialisedAsset?.attributes?.assetDbtProjectName - // Generating asset information - const assetInfo = `### ${getConnectorImage(asset.attributes.connectorName)} [${ - asset.displayText - }](${ATLAN_INSTANCE_URL}/assets/${asset.guid}/overview?utm_source=dbt_github_action) ${ - asset.attributes?.certificateStatus - ? getCertificationImage(asset.attributes.certificateStatus) - : "" - } -Materialised asset: ${getConnectorImage(materialisedAsset.attributes.connectorName)} [${ - materialisedAsset.attributes.name - }](${ATLAN_INSTANCE_URL}/assets/${materialisedAsset.guid}/overview?utm_source=dbt_github_action) ${ - materialisedAsset.attributes?.certificateStatus - ? getCertificationImage(materialisedAsset.attributes.certificateStatus) - : "" - }${environmentName ? ` | Environment Name: \`${environmentName}\`` : ''}${projectName ? ` | Project Name: \`${projectName}\`` : ''}`; - - // Generating the downstream table - const downstreamTable = `
${downstreamAssets.entityCount} downstream assets 👇
- -Name | Type | Description | Owners | Terms | Classifications | Source URL ---- | --- | --- | --- | --- | --- | --- -${rows.map((row) => row.map(i => i.replace(/\|/g, "•").replace(/\n/g, "")).join(" | ")).join("\n")} - -${downstreamAssets.hasMore ? `[See more downstream assets at Atlan](${ATLAN_INSTANCE_URL}/assets/${materialisedAsset.guid}/lineage?utm_source=dbt_github_action)` : ""} - -
`; - - // Generating the "View asset in Atlan" button - const viewAssetButton = `${getImageURL("atlan-logo", 15, 15)} [View asset in Atlan](${ATLAN_INSTANCE_URL}/assets/${asset.guid}/overview?utm_source=dbt_github_action)`; - - // Generating the final comment based on the presence of downstream assets - if (downstreamAssets.entities.length > 0) { - return `${assetInfo} - -${downstreamTable} - -${viewAssetButton}`; - } else { - return `${assetInfo} - -No downstream assets found. - -${viewAssetButton}`; - } -} - - -export async function checkCommentExists(octokit, context) { - if (IS_DEV) return null; - - const {pull_request} = context.payload; - - const comments = await octokit.rest.issues.listComments({ - ...context.repo, - issue_number: pull_request.number, - }); - - return comments.data.find( - (comment) => comment.user.login === "github-actions[bot]" && comment.body.includes("") - ); -} - -export async function createIssueComment(octokit, context, content, comment_id = null, forceNewComment = false) { - const {pull_request} = context.payload; - - content = ` -${content}` - - const commentObj = { - ...context.repo, - issue_number: pull_request.number, - body: content, - }; - - console.log(content, content.length) - - if (IS_DEV) return content; - - if (comment_id && !forceNewComment) return octokit.rest.issues.updateComment({...commentObj, comment_id}); - return octokit.rest.issues.createComment(commentObj); -} - -export async function deleteComment(octokit, context, comment_id) { - const {pull_request} = context.payload; - - return octokit.rest.issues.deleteComment({ - ...context.repo, - issue_number: pull_request.number, - comment_id, - }); -} diff --git a/src/utils/file-system.js b/src/utils/file-system.js deleted file mode 100644 index e799587..0000000 --- a/src/utils/file-system.js +++ /dev/null @@ -1,82 +0,0 @@ -export async function getFileContents(octokit, context, filePath) { - const {repository, pull_request} = context.payload, - owner = repository.owner.login, - repo = repository.name, - head_sha = pull_request.head.sha; - - const res = await octokit.request( - `GET /repos/${owner}/${repo}/contents/${filePath}?ref=${head_sha}`, - { - owner, - repo, - path: filePath, - } - ).catch(e => { - console.log("Error fetching file contents: ", e) - return null - }); - - if (!res) return null - - const buff = Buffer.from(res.data.content, "base64"); - - return buff.toString("utf8"); -} - -export async function getChangedFiles(octokit, context) { - const {repository, pull_request} = context.payload, - owner = repository.owner.login, - repo = repository.name, - pull_number = pull_request.number; - - const res = await octokit.request( - `GET /repos/${owner}/${repo}/pulls/${pull_number}/files`, - { - owner, - repo, - pull_number, - } - ); - - var changedFiles = res.data - .map(({filename, status}) => { - try { - const [modelName] = filename.match(/.*models\/(.*)\.sql/)[1].split('/').reverse()[0].split('.'); - - if (modelName) { - return { - fileName: modelName, - filePath: filename, - status - }; - } - } catch (e) { - - } - }) - .filter((i) => i !== undefined) - - changedFiles = changedFiles - .filter((item, index) => { - return changedFiles.findIndex(obj => obj.fileName === item.fileName) === index; - }) - - console.log("Changed Files: ", changedFiles) - - return changedFiles -} - -export async function getAssetName({octokit, context, fileName, filePath}) { - var regExp = /{{\s*config\s*\(\s*(?:[^,]*,)*\s*alias\s*=\s*['"]([^'"]+)['"](?:\s*,[^,]*)*\s*\)\s*}}/im; - var fileContents = await getFileContents(octokit, context, filePath); - - if (fileContents) { - var matches = regExp.exec(fileContents); - - if (matches) { - return matches[1].trim(); - } - } - - return fileName; -} diff --git a/src/utils/get-environment-variables.js b/src/utils/get-environment-variables.js deleted file mode 100644 index ba30ead..0000000 --- a/src/utils/get-environment-variables.js +++ /dev/null @@ -1,52 +0,0 @@ -import dotenv from "dotenv"; -import core from "@actions/core"; - -dotenv.config(); - -const { - IS_DEV, - ATLAN_INSTANCE_URL, - ATLAN_API_TOKEN, - IGNORE_MODEL_ALIAS_MATCHING, -} = process.env; - -export const isDev = () => IS_DEV === "true"; -export const getInstanceUrl = () => { - if (ATLAN_INSTANCE_URL) return new URL(ATLAN_INSTANCE_URL).origin; - return new URL(core.getInput("ATLAN_INSTANCE_URL")).origin; -}; -export const getAPIToken = () => { - if (ATLAN_API_TOKEN) return ATLAN_API_TOKEN; - return core.getInput("ATLAN_API_TOKEN"); -}; -export const getEnvironments = () => { - return core.getInput("DBT_ENVIRONMENT_BRANCH_MAP") - ? core - .getInput("DBT_ENVIRONMENT_BRANCH_MAP") - .trim() - ?.split("\n") - ?.map((i) => i.split(":").map((i) => i.trim())) - : []; -}; -export const isIgnoreModelAliasMatching = () => - core.getInput("IGNORE_MODEL_ALIAS_MATCHING") === "true"; - -export function getGitLabEnvironments() { - const { DBT_ENVIRONMENT_BRANCH_MAP } = process.env; - - if (DBT_ENVIRONMENT_BRANCH_MAP) { - const environmentLines = DBT_ENVIRONMENT_BRANCH_MAP.split("\n"); - const environmentMap = {}; - - environmentLines.forEach((line) => { - const [environment, branch] = line.split(":").map((item) => item.trim()); - if (environment && branch) { - environmentMap[environment] = branch; - } - }); - - return environmentMap; - } else { - return {}; - } -} diff --git a/src/utils/index.js b/src/utils/index.js deleted file mode 100644 index fc6889a..0000000 --- a/src/utils/index.js +++ /dev/null @@ -1,23 +0,0 @@ -export { - getImageURL, - getConnectorImage, - getCertificationImage, -} from "./get-image-url.js"; -export {default as hostedImages} from "./hosted-images.js"; -export { - default as renderDownstreamAssetsComment, - createIssueComment, checkCommentExists, deleteComment -} from "./create-comment.js"; -export { - getFileContents, - getChangedFiles, - getAssetName, -} from "./file-system.js"; -export { - auth -} from "./auth.js" -export { - getAPIToken, - getInstanceUrl, - isDev -} from "./get-environment-variables.js" \ No newline at end of file