diff --git a/src/extensions/ci-info.extension.js b/src/extensions/ci-info.extension.js index dd06cb6..d775224 100644 --- a/src/extensions/ci-info.extension.js +++ b/src/extensions/ci-info.extension.js @@ -56,19 +56,18 @@ class CIInfoExtension extends BaseExtension { this.#setRepositoryElement(); } if (this.extension.inputs.show_repository_branch) { - if (this.ci.repository_ref.includes('refs/pull')) { + if (this.ci.pull_request_name) { this.#setPullRequestElement(); } else { this.#setRepositoryBranchElement(); } } if (!this.extension.inputs.show_repository && !this.extension.inputs.show_repository_branch && this.extension.inputs.show_repository_non_common) { - if (this.ci.repository_ref.includes('refs/pull')) { + if (this.ci.pull_request_name) { this.#setRepositoryElement(); this.#setPullRequestElement(); } else { - const branch_name = this.ci.repository_ref.replace('refs/heads/', ''); - if (!COMMON_BRANCH_NAMES.includes(branch_name.toLowerCase())) { + if (!COMMON_BRANCH_NAMES.includes(this.ci.branch_name.toLowerCase())) { this.#setRepositoryElement(); this.#setRepositoryBranchElement(); } @@ -81,15 +80,11 @@ class CIInfoExtension extends BaseExtension { } #setPullRequestElement() { - const pr_url = this.ci.repository_url + this.ci.repository_ref.replace('refs/pull/', '/pull/'); - const pr_name = this.ci.repository_ref.replace('refs/pull/', '').replace('/merge', ''); - this.repository_elements.push({ label: 'Pull Request', key: pr_name, value: pr_url, type: 'hyperlink' }); + this.repository_elements.push({ label: 'Pull Request', key: this.ci.pull_request_name, value: this.ci.pull_request_url, type: 'hyperlink' }); } #setRepositoryBranchElement() { - const branch_url = this.ci.repository_url + this.ci.repository_ref.replace('refs/heads/', '/tree/'); - const branch_name = this.ci.repository_ref.replace('refs/heads/', ''); - this.repository_elements.push({ label: 'Branch', key: branch_name, value: branch_url, type: 'hyperlink' }); + this.repository_elements.push({ label: 'Branch', key: this.ci.branch_name, value: this.ci.branch_url, type: 'hyperlink' }); } #setBuildElements() { diff --git a/src/extensions/extensions.d.ts b/src/extensions/extensions.d.ts index 691bc7f..a944a83 100644 --- a/src/extensions/extensions.d.ts +++ b/src/extensions/extensions.d.ts @@ -5,6 +5,10 @@ export type ICIInfo = { repository_name: string repository_ref: string repository_commit_sha: string + branch_url: string + branch_name: string + pull_request_url: string + pull_request_name: string build_url: string build_number: string build_name: string diff --git a/src/helpers/ci.js b/src/helpers/ci.js deleted file mode 100644 index 1aea795..0000000 --- a/src/helpers/ci.js +++ /dev/null @@ -1,140 +0,0 @@ -const os = require('os'); -const pkg = require('../../package.json'); - -const ENV = process.env; - -/** - * @returns {import('../extensions/extensions').ICIInfo} - */ -function getCIInformation() { - const ci_info = getBaseCIInfo(); - const system_info = getSystemInfo(); - return { - ...ci_info, - ...system_info - } -} - -function getBaseCIInfo() { - if (ENV.GITHUB_ACTIONS) { - return getGitHubActionsInformation(); - } - if (ENV.GITLAB_CI) { - return getGitLabInformation(); - } - if (ENV.JENKINS_URL) { - return getJenkinsInformation(); - } - if (ENV.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI) { - return getAzureDevOpsInformation(); - } - return getDefaultInformation(); -} - -function getGitHubActionsInformation() { - return { - ci: 'GITHUB_ACTIONS', - git: 'GITHUB', - repository_url: ENV.GITHUB_SERVER_URL + '/' + ENV.GITHUB_REPOSITORY, - repository_name: ENV.GITHUB_REPOSITORY, - repository_ref: ENV.GITHUB_REF, - repository_commit_sha: ENV.GITHUB_SHA, - build_url: ENV.GITHUB_SERVER_URL + '/' + ENV.GITHUB_REPOSITORY + '/actions/runs/' + ENV.GITHUB_RUN_ID, - build_number: ENV.GITHUB_RUN_NUMBER, - build_name: ENV.GITHUB_WORKFLOW, - build_reason: ENV.GITHUB_EVENT_NAME, - user: ENV.GITHUB_ACTOR, - } -} - -function getAzureDevOpsInformation() { - return { - ci: 'AZURE_DEVOPS_PIPELINES', - git: 'AZURE_DEVOPS_REPOS', - repository_url: ENV.BUILD_REPOSITORY_URI, - repository_name: ENV.BUILD_REPOSITORY_NAME, - repository_ref: ENV.BUILD_SOURCEBRANCH, - repository_commit_sha: ENV.BUILD_SOURCEVERSION, - build_url: ENV.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + ENV.SYSTEM_TEAMPROJECT + '/_build/results?buildId=' + ENV.BUILD_BUILDID, - build_number: ENV.BUILD_BUILDNUMBER, - build_name: ENV.BUILD_DEFINITIONNAME, - build_reason: ENV.BUILD_REASON, - user: ENV.BUILD_REQUESTEDFOR - } -} - -function getJenkinsInformation() { - return { - ci: 'JENKINS', - git: '', - repository_url: ENV.GIT_URL || ENV.GITHUB_URL || ENV.BITBUCKET_URL, - repository_name: ENV.JOB_NAME, - repository_ref: ENV.BRANCH || ENV.BRANCH_NAME, - repository_commit_sha: ENV.GIT_COMMIT || ENV.GIT_COMMIT_SHA || ENV.GITHUB_SHA || ENV.BITBUCKET_COMMIT, - build_url: ENV.BUILD_URL, - build_number: ENV.BUILD_NUMBER, - build_name: ENV.JOB_NAME, - build_reason: ENV.BUILD_CAUSE, - user: ENV.USER || ENV.USERNAME - } -} - -function getGitLabInformation() { - return { - ci: 'GITLAB', - git: 'GITLAB', - repository_url: ENV.CI_PROJECT_URL, - repository_name: ENV.CI_PROJECT_NAME, - repository_ref: ENV.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME || ENV.CI_COMMIT_REF_NAME, - repository_commit_sha:ENV.CI_MERGE_REQUEST_SOURCE_BRANCH_SHA || ENV.CI_COMMIT_SHA, - build_url: ENV.CI_JOB_URL, - build_number: ENV.CI_JOB_ID, - build_name: ENV.CI_JOB_NAME, - build_reason: ENV.CI_PIPELINE_SOURCE, - user: ENV.GITLAB_USER_LOGIN || ENV.CI_COMMIT_AUTHOR - } -} - -function getDefaultInformation() { - return { - ci: ENV.TEST_BEATS_CI_NAME, - git: ENV.TEST_BEATS_CI_GIT, - repository_url: ENV.TEST_BEATS_CI_REPOSITORY_URL, - repository_name: ENV.TEST_BEATS_CI_REPOSITORY_NAME, - repository_ref: ENV.TEST_BEATS_CI_REPOSITORY_REF, - repository_commit_sha: ENV.TEST_BEATS_CI_REPOSITORY_COMMIT_SHA, - build_url: ENV.TEST_BEATS_CI_BUILD_URL, - build_number: ENV.TEST_BEATS_CI_BUILD_NUMBER, - build_name: ENV.TEST_BEATS_CI_BUILD_NAME, - build_reason: ENV.TEST_BEATS_CI_BUILD_REASON, - user: ENV.TEST_BEATS_CI_USER || os.userInfo().username - } -} - -function getSystemInfo() { - function getRuntimeInfo() { - if (typeof process !== 'undefined' && process.versions && process.versions.node) { - return { name: 'node', version: process.versions.node }; - } else if (typeof Deno !== 'undefined') { - return { name: 'deno', version: Deno.version.deno }; - } else if (typeof Bun !== 'undefined') { - return { name: 'bun', version: Bun.version }; - } else { - return { name: 'unknown', version: 'unknown' }; - } - } - - const runtime = getRuntimeInfo(); - - return { - runtime: runtime.name, - runtime_version: runtime.version, - os: os.platform(), - os_version: os.release(), - testbeats_version: pkg.version - } -} - -module.exports = { - getCIInformation -} \ No newline at end of file diff --git a/src/helpers/ci/azure-devops.js b/src/helpers/ci/azure-devops.js new file mode 100644 index 0000000..80dced1 --- /dev/null +++ b/src/helpers/ci/azure-devops.js @@ -0,0 +1,37 @@ +const ENV = process.env; + +/** + * @returns {import('../../extensions/extensions').ICIInfo} + */ +function info() { + const azure_devops = { + ci: 'AZURE_DEVOPS_PIPELINES', + git: 'AZURE_DEVOPS_REPOS', + repository_url: ENV.BUILD_REPOSITORY_URI, + repository_name: ENV.BUILD_REPOSITORY_NAME, + repository_ref: ENV.BUILD_SOURCEBRANCH, + repository_commit_sha: ENV.BUILD_SOURCEVERSION, + branch_url: '', + branch_name: '', + pull_request_url:'', + pull_request_name: '', + build_url: ENV.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + ENV.SYSTEM_TEAMPROJECT + '/_build/results?buildId=' + ENV.BUILD_BUILDID, + build_number: ENV.BUILD_BUILDNUMBER, + build_name: ENV.BUILD_DEFINITIONNAME, + build_reason: ENV.BUILD_REASON, + user: ENV.BUILD_REQUESTEDFOR + } + + azure_devops.branch_url = azure_devops.repository_url + azure_devops.repository_ref.replace('refs/heads/', '/tree/'); + azure_devops.branch_name = azure_devops.repository_ref.replace('refs/heads/', ''); + + if (azure_devops.repository_ref.includes('refs/pull')) { + azure_devops.pull_request_url = azure_devops.repository_url + azure_devops.repository_ref.replace('refs/pull/', '/pull/'); + azure_devops.pull_request_name = azure_devops.repository_ref.replace('refs/pull/', '').replace('/merge', ''); + } + return azure_devops; +} + +module.exports = { + info +} diff --git a/src/helpers/ci/github.js b/src/helpers/ci/github.js new file mode 100644 index 0000000..373661b --- /dev/null +++ b/src/helpers/ci/github.js @@ -0,0 +1,38 @@ +const ENV = process.env; + +/** + * @returns {import('../../extensions/extensions').ICIInfo} + */ +function info() { + const github = { + ci: 'GITHUB_ACTIONS', + git: 'GITHUB', + repository_url: ENV.GITHUB_SERVER_URL + '/' + ENV.GITHUB_REPOSITORY, + repository_name: ENV.GITHUB_REPOSITORY, + repository_ref: ENV.GITHUB_REF, + repository_commit_sha: ENV.GITHUB_SHA, + branch_url: '', + branch_name: '', + pull_request_url:'', + pull_request_name: '', + build_url: ENV.GITHUB_SERVER_URL + '/' + ENV.GITHUB_REPOSITORY + '/actions/runs/' + ENV.GITHUB_RUN_ID, + build_number: ENV.GITHUB_RUN_NUMBER, + build_name: ENV.GITHUB_WORKFLOW, + build_reason: ENV.GITHUB_EVENT_NAME, + user: ENV.GITHUB_ACTOR, + } + + github.branch_url = github.repository_url + github.repository_ref.replace('refs/heads/', '/tree/'); + github.branch_name = github.repository_ref.replace('refs/heads/', ''); + + if (github.repository_ref.includes('refs/pull')) { + github.pull_request_url = github.repository_url + github.repository_ref.replace('refs/pull/', '/pull/'); + github.pull_request_name = github.repository_ref.replace('refs/pull/', '').replace('/merge', ''); + } + + return github +} + +module.exports = { + info +} \ No newline at end of file diff --git a/src/helpers/ci/gitlab.js b/src/helpers/ci/gitlab.js new file mode 100644 index 0000000..e4a4b18 --- /dev/null +++ b/src/helpers/ci/gitlab.js @@ -0,0 +1,36 @@ +const ENV = process.env; + +/** + * @returns {import('../../extensions/extensions').ICIInfo} + */ +function info() { + const gitlab = { + ci: 'GITLAB', + git: 'GITLAB', + repository_url: ENV.CI_PROJECT_URL, + repository_name: ENV.CI_PROJECT_NAME, + repository_ref: '/-/tree/' + (ENV.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME || ENV.CI_COMMIT_REF_NAME), + repository_commit_sha: ENV.CI_MERGE_REQUEST_SOURCE_BRANCH_SHA || ENV.CI_COMMIT_SHA, + branch_url: ENV.CI_PROJECT_URL + '/-/tree/' + (ENV.CI_COMMIT_REF_NAME || ENV.CI_COMMIT_BRANCH), + branch_name: ENV.CI_COMMIT_REF_NAME || ENV.CI_COMMIT_BRANCH, + pull_request_url:'', + pull_request_name: '', + build_url: ENV.CI_JOB_URL, + build_number: ENV.CI_JOB_ID, + build_name: ENV.CI_JOB_NAME, + build_reason: ENV.CI_PIPELINE_SOURCE, + user: ENV.GITLAB_USER_LOGIN || ENV.CI_COMMIT_AUTHOR + } + + if (ENV.CI_OPEN_MERGE_REQUESTS) { + const pr_number = ENV.CI_OPEN_MERGE_REQUESTS.split("!")[1]; + gitlab.pull_request_name = "#" + pr_number; + gitlab.pull_request_url = ENV.CI_PROJECT_URL + "/-/merge_requests/" + pr_number; + } + + return gitlab +} + +module.exports = { + info +} diff --git a/src/helpers/ci/index.js b/src/helpers/ci/index.js new file mode 100644 index 0000000..925ba41 --- /dev/null +++ b/src/helpers/ci/index.js @@ -0,0 +1,60 @@ +const os = require('os'); +const github = require('./github'); +const gitlab = require('./gitlab'); +const jenkins = require('./jenkins'); +const azure_devops = require('./azure-devops'); +const system = require('./system'); + +const ENV = process.env; + +/** + * @returns {import('../../extensions/extensions').ICIInfo} + */ +function getCIInformation() { + const ci_info = getBaseCIInfo(); + const system_info = system.info(); + return { + ...ci_info, + ...system_info + } +} + +function getBaseCIInfo() { + if (ENV.GITHUB_ACTIONS) { + return github.info(); + } + if (ENV.GITLAB_CI) { + return gitlab.info(); + } + if (ENV.JENKINS_URL) { + return jenkins.info(); + } + if (ENV.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI) { + return azure_devops.info(); + } + return getDefaultInformation(); +} + +function getDefaultInformation() { + return { + ci: ENV.TEST_BEATS_CI_NAME, + git: ENV.TEST_BEATS_CI_GIT, + repository_url: ENV.TEST_BEATS_CI_REPOSITORY_URL, + repository_name: ENV.TEST_BEATS_CI_REPOSITORY_NAME, + repository_ref: ENV.TEST_BEATS_CI_REPOSITORY_REF, + repository_commit_sha: ENV.TEST_BEATS_CI_REPOSITORY_COMMIT_SHA, + branch_url: ENV.TEST_BEATS_BRANCH_URL, + branch_name: ENV.TEST_BEATS_BRANCH_NAME, + pull_request_url: ENV.TEST_BEATS_PULL_REQUEST_URL, + pull_request_name: ENV.TEST_BEATS_PULL_REQUEST_NAME, + build_url: ENV.TEST_BEATS_CI_BUILD_URL, + build_number: ENV.TEST_BEATS_CI_BUILD_NUMBER, + build_name: ENV.TEST_BEATS_CI_BUILD_NAME, + build_reason: ENV.TEST_BEATS_CI_BUILD_REASON, + user: ENV.TEST_BEATS_CI_USER || os.userInfo().username + } +} + +module.exports = { + getCIInformation +} \ No newline at end of file diff --git a/src/helpers/ci/jenkins.js b/src/helpers/ci/jenkins.js new file mode 100644 index 0000000..aaf5a9c --- /dev/null +++ b/src/helpers/ci/jenkins.js @@ -0,0 +1,38 @@ +const ENV = process.env; + +/** + * @returns {import('../../extensions/extensions').ICIInfo} + */ +function info() { + const jenkins = { + ci: 'JENKINS', + git: '', + repository_url: ENV.GIT_URL || ENV.GITHUB_URL || ENV.BITBUCKET_URL, + repository_name: ENV.JOB_NAME, + repository_ref: ENV.BRANCH || ENV.BRANCH_NAME, + repository_commit_sha: ENV.GIT_COMMIT || ENV.GIT_COMMIT_SHA || ENV.GITHUB_SHA || ENV.BITBUCKET_COMMIT, + branch_url: '', + branch_name: '', + pull_request_url:'', + pull_request_name: '', + build_url: ENV.BUILD_URL, + build_number: ENV.BUILD_NUMBER, + build_name: ENV.JOB_NAME, + build_reason: ENV.BUILD_CAUSE, + user: ENV.USER || ENV.USERNAME + } + + jenkins.branch_url = jenkins.repository_url + jenkins.repository_ref.replace('refs/heads/', '/tree/'); + jenkins.branch_name = jenkins.repository_ref.replace('refs/heads/', ''); + + if (jenkins.repository_ref.includes('refs/pull')) { + jenkins.pull_request_url = jenkins.repository_url + jenkins.repository_ref.replace('refs/pull/', '/pull/'); + jenkins.pull_request_name = jenkins.repository_ref.replace('refs/pull/', '').replace('/merge', ''); + } + + return jenkins_info; +} + +module.exports = { + info +} diff --git a/src/helpers/ci/system.js b/src/helpers/ci/system.js new file mode 100644 index 0000000..c5ed804 --- /dev/null +++ b/src/helpers/ci/system.js @@ -0,0 +1,30 @@ +const os = require('os'); +const pkg = require('../../../package.json'); + +function info() { + function getRuntimeInfo() { + if (typeof process !== 'undefined' && process.versions && process.versions.node) { + return { name: 'node', version: process.versions.node }; + } else if (typeof Deno !== 'undefined') { + return { name: 'deno', version: Deno.version.deno }; + } else if (typeof Bun !== 'undefined') { + return { name: 'bun', version: Bun.version }; + } else { + return { name: 'unknown', version: 'unknown' }; + } + } + + const runtime = getRuntimeInfo(); + + return { + runtime: runtime.name, + runtime_version: runtime.version, + os: os.platform(), + os_version: os.release(), + testbeats_version: pkg.version + } +} + +module.exports = { + info +} \ No newline at end of file diff --git a/test/ext-ci-info.spec.js b/test/ext-ci-info.spec.js index 493b2ba..cb7823c 100644 --- a/test/ext-ci-info.spec.js +++ b/test/ext-ci-info.spec.js @@ -14,6 +14,17 @@ describe('extensions - ci-info', () => { process.env.GITHUB_RUN_NUMBER = ''; process.env.GITHUB_WORKFLOW = ''; + process.env.GITLAB_CI='' + process.env.CI_PROJECT_URL = '' + process.env.CI_PROJECT_NAME = '' + process.env.CI_COMMIT_REF_NAME = '' + process.env.CI_COMMIT_SHA = '' + process.env.CI_JOB_URL = '' + process.env.CI_JOB_ID = '' + process.env.CI_JOB_NAME = '' + process.env.CI_PIPELINE_SOURCE = '' + process.env.GITLAB_USER_LOGIN = ''; + process.env.SYSTEM_TEAMFOUNDATIONCOLLECTIONURI = ''; process.env.BUILD_REPOSITORY_URI = ''; process.env.BUILD_REPOSITORY_NAME = ''; @@ -170,6 +181,89 @@ describe('extensions - ci-info', () => { assert.equal(mock.getInteraction(id).exercised, true); }); + it('should send test-summary with gitlab ci information to slack', async () => { + process.env.GITLAB_CI=true + process.env.CI_PROJECT_URL = 'https://gitlab.com/testbeats/demo' + process.env.CI_PROJECT_NAME = 'demo' + process.env.CI_COMMIT_REF_NAME = 'branch' + process.env.CI_COMMIT_SHA = 'sha' + process.env.CI_JOB_URL = 'https://gitlab.com/testbeats/demo/-/jobs/id-123' + process.env.CI_JOB_ID = 'id-123' + process.env.CI_JOB_NAME = 'Test' + process.env.CI_PIPELINE_SOURCE = 'push' + process.env.GITLAB_USER_LOGIN = 'dummy-user'; + + const id = mock.addInteraction('post test-summary with gitlab ci-info to slack'); + await publish({ + config: { + targets: [ + { + name: 'slack', + inputs: { + url: 'http://localhost:9393/message' + }, + extensions: [ + { + name: 'ci-info' + } + ] + } + ], + results: [ + { + type: 'testng', + files: [ + 'test/data/testng/single-suite.xml' + ] + } + ] + } + }); + assert.equal(mock.getInteraction(id).exercised, true); + }); + + it('should send test-summary with gitlab ci information to slack - with PR', async () => { + process.env.GITLAB_CI=true + process.env.CI_OPEN_MERGE_REQUESTS='testbeats/demo!1' + process.env.CI_PROJECT_URL = 'https://gitlab.com/testbeats/demo' + process.env.CI_PROJECT_NAME = 'demo' + process.env.CI_COMMIT_REF_NAME = 'branch' + process.env.CI_COMMIT_SHA = 'sha' + process.env.CI_JOB_URL = 'https://gitlab.com/testbeats/demo/-/jobs/id-123' + process.env.CI_JOB_ID = 'id-123' + process.env.CI_JOB_NAME = 'Test' + process.env.CI_PIPELINE_SOURCE = 'push' + process.env.GITLAB_USER_LOGIN = 'dummy-user'; + + const id = mock.addInteraction('post test-summary with gitlab ci-info with PR to slack'); + await publish({ + config: { + targets: [ + { + name: 'slack', + inputs: { + url: 'http://localhost:9393/message' + }, + extensions: [ + { + name: 'ci-info' + } + ] + } + ], + results: [ + { + type: 'testng', + files: [ + 'test/data/testng/single-suite.xml' + ] + } + ] + } + }); + assert.equal(mock.getInteraction(id).exercised, true); + }); + it('should send test-summary with azure devops ci information to chat and extra data', async () => { process.env.GITHUB_ACTIONS = 'GITHUB_ACTIONS'; process.env.GITHUB_SERVER_URL = 'https://github.com'; diff --git a/test/mocks/slack.mock.js b/test/mocks/slack.mock.js index 7a7f33e..24b194b 100644 --- a/test/mocks/slack.mock.js +++ b/test/mocks/slack.mock.js @@ -729,6 +729,70 @@ addInteractionHandler('post test-summary with ci-info to slack', () => { } }); +addInteractionHandler('post test-summary with gitlab ci-info to slack', () => { + return { + request: { + method: 'POST', + path: '/message', + body: { + "attachments": [ + { + "color": "#36A64F", + "blocks": [ + { + "@DATA:TEMPLATE@": "SLACK_ROOT_SINGLE_SUITE" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Repository:* | *Branch:* \n*Build:* " + } + } + ], + "fallback": "Default suite\nResults: 4 / 4 Passed (100%)" + } + ] + } + }, + response: { + status: 200 + } + } +}); + +addInteractionHandler('post test-summary with gitlab ci-info with PR to slack', () => { + return { + request: { + method: 'POST', + path: '/message', + body: { + "attachments": [ + { + "color": "#36A64F", + "blocks": [ + { + "@DATA:TEMPLATE@": "SLACK_ROOT_SINGLE_SUITE" + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*Repository:* | *Pull Request:* \n*Build:* " + } + } + ], + "fallback": "Default suite\nResults: 4 / 4 Passed (100%)" + } + ] + } + }, + response: { + status: 200 + } + } +}); + addInteractionHandler('post test-summary with multiple suites and ci-info to to slack', () => { return { request: {