@ckeditor/ckeditor5-dev-ci 38.0.4 → 38.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
5
+ * For licensing, see LICENSE.md.
6
+ */
7
+
8
+ /* eslint-env node */
9
+
10
+ const formatMessage = require( '../lib/format-message' );
11
+ const slackNotify = require( 'slack-notify' );
12
+
13
+ // This script assumes that is being executed on Circle CI.
14
+ // Step it is used on should have set value: `when: on_fail`, since it does not
15
+ // check whether the build failed when determining whether the notification should be sent.
16
+ // Described environment variables starting with "CKE5" should be added by the integrator.
17
+
18
+ const {
19
+ /**
20
+ * Required. Token to a Github account with the scope: "repos". It is required for obtaining an author of
21
+ * the commit if the build failed. The repository can be private and we can't use the public API.
22
+ */
23
+ CKE5_GITHUB_TOKEN,
24
+
25
+ /**
26
+ * Required. CircleCI API token used for obtaining data about the build from API.
27
+ */
28
+ CKE5_CIRCLE_TOKEN,
29
+
30
+ /**
31
+ * Required. Webhook URL of the Slack channel where the notification should be sent.
32
+ */
33
+ CKE5_SLACK_WEBHOOK_URL,
34
+
35
+ /**
36
+ * Optional. If both are defined, the script will use the URL as the commit URL.
37
+ * Otherwise, URL will be constructed using current repository data.
38
+ */
39
+ CKE5_TRIGGER_REPOSITORY_SLUG,
40
+ CKE5_TRIGGER_COMMIT_HASH,
41
+
42
+ /**
43
+ * Optional. If set to "true", commit author will be hidden.
44
+ * See: https://github.com/ckeditor/ckeditor5/issues/9252.
45
+ */
46
+ CKE5_SLACK_NOTIFY_HIDE_AUTHOR,
47
+
48
+ // Variables that are available by default in Circle environment.
49
+ CIRCLE_BRANCH,
50
+ CIRCLE_BUILD_NUM,
51
+ CIRCLE_BUILD_URL,
52
+ CIRCLE_PROJECT_REPONAME,
53
+ CIRCLE_PROJECT_USERNAME,
54
+ CIRCLE_SHA1
55
+ } = process.env;
56
+
57
+ notifyCircleStatus();
58
+
59
+ async function notifyCircleStatus() {
60
+ if ( !CKE5_GITHUB_TOKEN ) {
61
+ throw new Error( 'Missing environment variable: CKE5_GITHUB_TOKEN' );
62
+ }
63
+
64
+ if ( !CKE5_SLACK_WEBHOOK_URL ) {
65
+ throw new Error( 'Missing environment variable: CKE5_SLACK_WEBHOOK_URL' );
66
+ }
67
+
68
+ const jobData = await getJobData();
69
+
70
+ const message = await formatMessage( {
71
+ slackMessageUsername: 'Circle CI',
72
+ iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Circleci-icon-logo.svg/475px-Circleci-icon-logo.svg.png',
73
+ repositoryOwner: CIRCLE_PROJECT_USERNAME,
74
+ repositoryName: CIRCLE_PROJECT_REPONAME,
75
+ branch: CIRCLE_BRANCH,
76
+ jobUrl: CIRCLE_BUILD_URL,
77
+ jobId: CIRCLE_BUILD_NUM,
78
+ githubToken: CKE5_GITHUB_TOKEN,
79
+ triggeringCommitUrl: getTriggeringCommitUrl(),
80
+ startTime: Math.ceil( ( new Date( jobData.started_at ) ).getTime() / 1000 ),
81
+ endTime: Math.ceil( ( new Date() ) / 1000 ),
82
+ shouldHideAuthor: CKE5_SLACK_NOTIFY_HIDE_AUTHOR === 'true'
83
+ } );
84
+
85
+ return slackNotify( CKE5_SLACK_WEBHOOK_URL )
86
+ .send( message )
87
+ .catch( err => console.log( 'API error occurred:', err ) );
88
+ }
89
+
90
+ async function getJobData() {
91
+ const fetchUrl = [
92
+ 'https://circleci.com/api/v2/project/gh',
93
+ CIRCLE_PROJECT_USERNAME,
94
+ CIRCLE_PROJECT_REPONAME,
95
+ 'job',
96
+ CIRCLE_BUILD_NUM
97
+ ].join( '/' );
98
+
99
+ const fetchOptions = {
100
+ method: 'GET',
101
+ headers: { 'Circle-Token': CKE5_CIRCLE_TOKEN }
102
+ };
103
+
104
+ const rawResponse = await fetch( fetchUrl, fetchOptions );
105
+ return rawResponse.json();
106
+ }
107
+
108
+ function getTriggeringCommitUrl() {
109
+ let repoSlug, hash;
110
+
111
+ if ( CKE5_TRIGGER_REPOSITORY_SLUG && CKE5_TRIGGER_COMMIT_HASH ) {
112
+ repoSlug = CKE5_TRIGGER_REPOSITORY_SLUG;
113
+ hash = CKE5_TRIGGER_COMMIT_HASH;
114
+ } else {
115
+ repoSlug = [ CIRCLE_PROJECT_USERNAME, CIRCLE_PROJECT_REPONAME ].join( '/' );
116
+ hash = CIRCLE_SHA1;
117
+ }
118
+
119
+ return [ 'https://github.com', repoSlug, 'commit', hash ].join( '/' );
120
+ }
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
5
+ * For licensing, see LICENSE.md.
6
+ */
7
+
8
+ /* eslint-env node */
9
+
10
+ const formatMessage = require( '../lib/format-message' );
11
+ const slackNotify = require( 'slack-notify' );
12
+
13
+ const ALLOWED_BRANCHES = [
14
+ 'stable',
15
+ 'master'
16
+ ];
17
+
18
+ const ALLOWED_EVENTS = [
19
+ 'push',
20
+ 'cron',
21
+ 'api'
22
+ ];
23
+
24
+ // This script assumes that is being executed on Travis CI.
25
+ // Described environment variables should be added by the integrator.
26
+
27
+ const {
28
+ /**
29
+ * POSIX timestamps created when the script has begun and ended the job.
30
+ * Timestamps should be in seconds instead of milliseconds.
31
+ */
32
+ START_TIME,
33
+ END_TIME,
34
+
35
+ /**
36
+ * Token to a Github account with the scope: "repos". It is required for obtaining an author of
37
+ * the commit if the build failed. The repository can be private and we can't use the public API.
38
+ */
39
+ GITHUB_TOKEN,
40
+
41
+ /**
42
+ * Required. Webhook URL of the Slack channel where the notification should be sent.
43
+ */
44
+ SLACK_WEBHOOK_URL,
45
+
46
+ /**
47
+ * Optional. If defined, the script will use the URL as the commit URL.
48
+ * Otherwise, URL will be constructed using current repository data.
49
+ */
50
+ SLACK_NOTIFY_COMMIT_URL,
51
+
52
+ /**
53
+ * Optional. If set to "true", commit author will be hidden.
54
+ * See: https://github.com/ckeditor/ckeditor5/issues/9252.
55
+ */
56
+ SLACK_NOTIFY_HIDE_AUTHOR,
57
+
58
+ // Variables that are available by default in Travis environment.
59
+ TRAVIS_BRANCH,
60
+ TRAVIS_COMMIT,
61
+ TRAVIS_EVENT_TYPE,
62
+ TRAVIS_JOB_NUMBER,
63
+ TRAVIS_JOB_WEB_URL,
64
+ TRAVIS_REPO_SLUG,
65
+ TRAVIS_TEST_RESULT
66
+ } = process.env;
67
+
68
+ notifyTravisStatus();
69
+
70
+ async function notifyTravisStatus() {
71
+ // Send a notification only for main branches...
72
+ if ( !ALLOWED_BRANCHES.includes( TRAVIS_BRANCH ) ) {
73
+ console.log( `Aborting slack notification due to an invalid branch (${ TRAVIS_BRANCH }).` );
74
+
75
+ process.exit();
76
+ }
77
+
78
+ // ...and an event that triggered the build is correct...
79
+ if ( !ALLOWED_EVENTS.includes( TRAVIS_EVENT_TYPE ) ) {
80
+ console.log( `Aborting slack notification due to an invalid event type (${ TRAVIS_EVENT_TYPE }).` );
81
+
82
+ process.exit();
83
+ }
84
+
85
+ // ...and for builds that failed.
86
+ if ( TRAVIS_TEST_RESULT == 0 ) {
87
+ console.log( 'The build did not fail. The notification will not be sent.' );
88
+
89
+ process.exit();
90
+ }
91
+
92
+ const [ repositoryOwner, repositoryName ] = TRAVIS_REPO_SLUG.split( '/' );
93
+
94
+ const message = await formatMessage( {
95
+ slackMessageUsername: 'Travis CI',
96
+ iconUrl: 'https://a.slack-edge.com/66f9/img/services/travis_36.png',
97
+ repositoryOwner,
98
+ repositoryName,
99
+ branch: TRAVIS_BRANCH,
100
+ jobUrl: TRAVIS_JOB_WEB_URL,
101
+ jobId: TRAVIS_JOB_NUMBER,
102
+ githubToken: GITHUB_TOKEN,
103
+ triggeringCommitUrl: SLACK_NOTIFY_COMMIT_URL || `https://github.com/${ TRAVIS_REPO_SLUG }/commit/${ TRAVIS_COMMIT }`,
104
+ startTime: Number( START_TIME ),
105
+ endTime: Number( END_TIME ),
106
+ shouldHideAuthor: SLACK_NOTIFY_HIDE_AUTHOR === 'true'
107
+ } );
108
+
109
+ return slackNotify( SLACK_WEBHOOK_URL )
110
+ .send( message )
111
+ .catch( err => console.log( 'API error occurred:', err ) );
112
+ }
@@ -0,0 +1,3 @@
1
+ [
2
+ "CKTravisBot"
3
+ ]
@@ -17,9 +17,8 @@
17
17
  "ldrobnik": "U04DJ7B7ZCJ",
18
18
  "LukaszGudel": "U01FRHAT8T1",
19
19
  "mabryl": "U045033D401",
20
- "Mgsy": "U5A38S2PN",
21
20
  "martnpaneq": "U045B560E00",
22
- "mabryl": "U045033D401",
21
+ "Mgsy": "U5A38S2PN",
23
22
  "mlewand": "U03UQQ1N3",
24
23
  "mmotyczynska": "U02NSTQCTB8",
25
24
  "mremiszewski": "U04ACUBMDGT",
@@ -31,6 +30,5 @@
31
30
  "pszczesniak": "U04167FKV88",
32
31
  "Reinmar": "U03UQRP0C",
33
32
  "scofalik": "U05611ZMM",
34
- "wwalc": "U03UQQ8PY",
35
- "CKTravisBot": "CKTravisBot"
33
+ "wwalc": "U03UQQ8PY"
36
34
  }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md.
4
+ */
5
+
6
+ /* eslint-env node */
7
+
8
+ 'use strict';
9
+
10
+ const fetch = require( 'node-fetch' );
11
+
12
+ const bots = require( './data/bots.json' );
13
+ const members = require( './data/members.json' );
14
+
15
+ const REPOSITORY_REGEXP = /github\.com\/([^/]+)\/([^/]+)/;
16
+
17
+ /**
18
+ * @param {String} slackMessageUsername
19
+ * @param {String} iconUrl
20
+ * @param {String} repositoryOwner
21
+ * @param {String} repositoryName
22
+ * @param {String} branch
23
+ * @param {String} jobUrl
24
+ * @param {String} jobId
25
+ * @param {String} githubToken
26
+ * @param {String} triggeringCommitUrl
27
+ * @param {Number} startTime
28
+ * @param {Number} endTime
29
+ * @param {Boolean} shouldHideAuthor
30
+ */
31
+ module.exports = async function formatMessage( options ) {
32
+ const commitDetails = await getCommitDetails( options.triggeringCommitUrl, options.githubToken );
33
+ const repoUrl = `https://github.com/${ options.repositoryOwner }/${ options.repositoryName }`;
34
+
35
+ return {
36
+ username: options.slackMessageUsername,
37
+ icon_url: options.iconUrl,
38
+ unfurl_links: 1,
39
+ text: getNotifierMessage( { ...options, ...commitDetails } ),
40
+ attachments: [ {
41
+ color: 'danger',
42
+ fields: [ {
43
+ title: 'Repository (branch)',
44
+ value: `<${ repoUrl }|${ options.repositoryName }> (<${ repoUrl }/tree/${ options.branch }|${ options.branch }>)`,
45
+ short: true
46
+ }, {
47
+ title: 'Build number',
48
+ value: `<${ options.jobUrl }|#${ options.jobId }>`,
49
+ short: true
50
+ }, {
51
+ title: 'Commit',
52
+ value: `<${ options.triggeringCommitUrl }|${ commitDetails.hash.substring( 0, 7 ) }>`,
53
+ short: true
54
+ }, {
55
+ title: 'Build time',
56
+ value: getExecutionTime( options.startTime, options.endTime ),
57
+ short: true
58
+ }, {
59
+ title: 'Commit message',
60
+ value: getFormattedMessage( commitDetails.commitMessage, options.triggeringCommitUrl ),
61
+ short: false
62
+ } ]
63
+ } ]
64
+ };
65
+ };
66
+
67
+ /**
68
+ * Returns the additional message that will be added to the notifier post.
69
+ *
70
+ * @param {Object} options
71
+ * @param {Boolean} options.shouldHideAuthor
72
+ * @param {String|null} options.githubAccount
73
+ * @param {String} options.commitAuthor
74
+ * @returns {String}
75
+ */
76
+ function getNotifierMessage( options ) {
77
+ const slackAccount = members[ options.githubAccount ] || null;
78
+
79
+ if ( options.shouldHideAuthor ) {
80
+ return '_The author of the commit was hidden. <https://github.com/ckeditor/ckeditor5/issues/9252|Read more about why.>_';
81
+ }
82
+
83
+ // If the author of the commit could not be obtained, let's ping the entire team.
84
+ if ( !slackAccount ) {
85
+ return `@channel (${ options.commitAuthor }), could you take a look?`;
86
+ }
87
+
88
+ if ( bots.includes( options.githubAccount ) ) {
89
+ return '_This commit is a result of merging a branch into another branch._';
90
+ }
91
+
92
+ return `<@${ slackAccount }>, could you take a look?`;
93
+ }
94
+
95
+ /**
96
+ * Returns string representing amount of time passed between two timestamps.
97
+ * Timestamps should be in seconds instead of milliseconds.
98
+ *
99
+ * @param {Number} startTime
100
+ * @param {Number} endTime
101
+ * @returns {String}
102
+ */
103
+ function getExecutionTime( startTime, endTime ) {
104
+ if ( !startTime || !endTime ) {
105
+ return 'Unavailable.';
106
+ }
107
+
108
+ const totalMs = ( endTime - startTime ) * 1000;
109
+ const date = new Date( totalMs );
110
+ const hours = date.getUTCHours();
111
+ const minutes = date.getUTCMinutes();
112
+ const seconds = date.getUTCSeconds();
113
+
114
+ const stringParts = [];
115
+
116
+ if ( hours ) {
117
+ stringParts.push( `${ hours } hr.` );
118
+ }
119
+
120
+ if ( minutes ) {
121
+ stringParts.push( `${ minutes } min.` );
122
+ }
123
+
124
+ if ( seconds ) {
125
+ stringParts.push( `${ seconds } sec.` );
126
+ }
127
+
128
+ if ( !stringParts.length ) {
129
+ return '0 sec.';
130
+ }
131
+
132
+ return stringParts.join( ' ' );
133
+ }
134
+
135
+ /**
136
+ * Replaces `#Id` and `Repo/Owner#Id` with URls to Github Issues.
137
+ *
138
+ * @param {String} commitMessage
139
+ * @param {String} triggeringCommitUrl
140
+ * @returns {string}
141
+ */
142
+ function getFormattedMessage( commitMessage, triggeringCommitUrl ) {
143
+ if ( !commitMessage ) {
144
+ return 'Unavailable';
145
+ }
146
+
147
+ const [ , repoOwner, repoName ] = triggeringCommitUrl.match( REPOSITORY_REGEXP );
148
+
149
+ return commitMessage
150
+ .replace( / #(\d+)/g, ( _, issueId ) => {
151
+ return ` <https://github.com/${ repoOwner }/${ repoName }/issues/${ issueId }|#${ issueId }>`;
152
+ } )
153
+ .replace( /([\w-]+\/[\w-]+)#(\d+)/g, ( _, repoSlug, issueId ) => {
154
+ return `<https://github.com/${ repoSlug }/issues/${ issueId }|${ repoSlug }#${ issueId }>`;
155
+ } );
156
+ }
157
+
158
+ /**
159
+ * Returns a promise that resolves the commit details (author and message) based on the specified GitHub URL.
160
+ *
161
+ * @param {String} triggeringCommitUrl The URL to the commit on GitHub.
162
+ * @param {String} githubToken Github token used for authorization a request,
163
+ * @returns {Promise.<Object>}
164
+ */
165
+ function getCommitDetails( triggeringCommitUrl, githubToken ) {
166
+ const apiGithubUrlCommit = getGithubApiUrl( triggeringCommitUrl );
167
+ const options = {
168
+ method: 'GET',
169
+ credentials: 'include',
170
+ headers: {
171
+ authorization: `token ${ githubToken }`
172
+ }
173
+ };
174
+
175
+ return fetch( apiGithubUrlCommit, options )
176
+ .then( response => response.json() )
177
+ .then( json => ( {
178
+ githubAccount: json.author ? json.author.login : null,
179
+ commitAuthor: json.commit.author.name,
180
+ commitMessage: json.commit.message,
181
+ hash: json.sha
182
+ } ) );
183
+ }
184
+
185
+ /**
186
+ * Returns a URL to GitHub API which returns details of the commit that caused the CI to fail its job.
187
+ *
188
+ * @param {String} triggeringCommitUrl The URL to the commit on GitHub.
189
+ * @returns {String}
190
+ */
191
+ function getGithubApiUrl( triggeringCommitUrl ) {
192
+ return triggeringCommitUrl.replace( 'github.com/', 'api.github.com/repos/' ).replace( '/commit/', '/commits/' );
193
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-dev-ci",
3
- "version": "38.0.4",
3
+ "version": "38.1.0",
4
4
  "description": "Utils for CKEditor5 CI builds.",
5
5
  "keywords": [],
6
6
  "dependencies": {
@@ -16,7 +16,8 @@
16
16
  "lib"
17
17
  ],
18
18
  "bin": {
19
- "ckeditor5-dev-ci-notify-travis-status": "bin/notifytravisstatus.js"
19
+ "ckeditor5-dev-ci-notify-travis-status": "bin/notify-travis-status.js",
20
+ "ckeditor5-dev-ci-notify-circle-status": "bin/notify-circle-status.js"
20
21
  },
21
22
  "author": "CKSource (http://cksource.com/)",
22
23
  "license": "GPL-2.0-or-later",
@@ -1,88 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
5
- * For licensing, see LICENSE.md.
6
- */
7
-
8
- /* eslint-env node */
9
-
10
- // This script assumes that is being executed on Travis CI. It requires following environment variables:
11
- //
12
- // - SLACK_WEBHOOK_URL - a URL where the notification should be sent
13
- // - START_TIME - POSIX time (when the script has begun the job)
14
- // - END_TIME - POSIX time (when the script has finished the job)
15
- // - GITHUB_TOKEN - token to a Github account with the scope: "repos". It is requires for obtaining an author of
16
- // the commit if the build failed. The repository can be private and we can't use the public API.
17
- //
18
- // If the `SLACK_NOTIFY_COMMIT_URL` environment variable is defined, the script use the URL as the commit URL.
19
- // Otherwise, a marge of variables `TRAVIS_REPO_SLUG` and `TRAVIS_COMMIT` will be used.
20
- //
21
- // Pinging an author of the commit can be disabled by defining the `SLACK_NOTIFY_HIDE_AUTHOR` environment variable
22
- // to `"true"` (`SLACK_NOTIFY_HIDE_AUTHOR="true"`). See: https://github.com/ckeditor/ckeditor5/issues/9252.
23
- //
24
- // In order to enable the debug mode, set the `DEBUG=true` as the environment variable.
25
-
26
- const buildBranch = process.env.TRAVIS_BRANCH;
27
-
28
- const ALLOWED_BRANCHES = [
29
- 'stable',
30
- 'master'
31
- ];
32
-
33
- const ALLOWED_EVENTS = [
34
- 'push',
35
- 'cron',
36
- 'api'
37
- ];
38
-
39
- // Send a notification only for main branches...
40
- if ( !ALLOWED_BRANCHES.includes( buildBranch ) ) {
41
- printLog( `Aborting due to an invalid branch (${ buildBranch }).` );
42
-
43
- process.exit();
44
- }
45
-
46
- // ...and an event that triggered the build is correct...
47
- if ( !ALLOWED_EVENTS.includes( process.env.TRAVIS_EVENT_TYPE ) ) {
48
- printLog( `Aborting due to an invalid event type (${ process.env.TRAVIS_EVENT_TYPE }).` );
49
-
50
- process.exit();
51
- }
52
-
53
- // ...and for builds that failed.
54
- if ( process.env.TRAVIS_TEST_RESULT == 0 ) {
55
- printLog( 'The build did not fail. The notification will not be sent.' );
56
-
57
- process.exit();
58
- }
59
-
60
- const notifyTravisStatus = require( '../lib/notifytravisstatus' );
61
-
62
- const options = {
63
- repositorySlug: process.env.TRAVIS_REPO_SLUG,
64
- startTime: parseInt( process.env.START_TIME ),
65
- endTime: parseInt( process.env.END_TIME ),
66
- githubToken: process.env.GITHUB_TOKEN,
67
- slackWebhookUrl: process.env.SLACK_WEBHOOK_URL,
68
- branch: buildBranch,
69
- commit: process.env.TRAVIS_COMMIT,
70
- notifyCommitUrl: process.env.SLACK_NOTIFY_COMMIT_URL,
71
- shouldHideAuthor: process.env.SLACK_NOTIFY_HIDE_AUTHOR === 'true',
72
- jobUrl: process.env.TRAVIS_JOB_WEB_URL,
73
- jobId: process.env.TRAVIS_JOB_NUMBER
74
- };
75
-
76
- notifyTravisStatus( options )
77
- .catch( error => {
78
- console.error( error );
79
-
80
- throw error;
81
- } );
82
-
83
- /**
84
- * @param {String} message
85
- */
86
- function printLog( message ) {
87
- console.log( '[Slack Notification]', message );
88
- }
package/lib/index.js DELETED
@@ -1,12 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md.
4
- */
5
-
6
- 'use strict';
7
-
8
- const notifyTravisStatus = require( './notifytravisstatus' );
9
-
10
- module.exports = {
11
- notifyTravisStatus
12
- };
@@ -1,224 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md.
4
- */
5
-
6
- 'use strict';
7
-
8
- // A map that translates GitHub accounts to Slack ids.
9
- const members = require( './members.json' );
10
-
11
- const REPOSITORY_REGEXP = /github\.com\/([^/]+)\/([^/]+)/;
12
-
13
- const fetch = require( 'node-fetch' );
14
- const slackNotify = require( 'slack-notify' );
15
-
16
- /**
17
- * @param {Object} options
18
- * @param {String} options.repositorySlug
19
- * @param {Number} options.startTime
20
- * @param {Number} options.endTime
21
- * @param {String} options.githubToken
22
- * @param {String} options.slackWebhookUrl
23
- * @param {String} options.branch
24
- * @param {String} options.commit
25
- * @param {String} options.notifyCommitUrl
26
- * @param {Boolean} options.shouldHideAuthor
27
- * @param {String} options.jobUrl
28
- * @param {String} options.jobId
29
- * @returns {Promise}
30
- */
31
- module.exports = async function notifyTravisStatus( options ) {
32
- const commitUrl = getCommitUrl( {
33
- commit: options.commit,
34
- notifyCommitUrl: options.notifyCommitUrl,
35
- repositorySlug: options.repositorySlug
36
- } );
37
- const { githubAccount, commitAuthor, commitMessage } = await getCommitDetails( commitUrl, options.githubToken );
38
-
39
- const [ buildRepoOwner, buildRepoName ] = options.repositorySlug.split( '/' );
40
- const slackAccount = members[ githubAccount ] || null;
41
- const shortCommit = commitUrl.split( '/' ).pop().substring( 0, 7 );
42
- const repoMatch = commitUrl.match( REPOSITORY_REGEXP );
43
-
44
- const messageData = {
45
- username: 'Travis CI',
46
- text: getNotifierMessage( {
47
- slackAccount,
48
- githubAccount,
49
- commitAuthor,
50
- shouldHideAuthor: options.shouldHideAuthor
51
- } ),
52
- icon_url: 'https://a.slack-edge.com/66f9/img/services/travis_36.png',
53
- unfurl_links: 1,
54
- attachments: [
55
- {
56
- color: 'danger',
57
- fields: [
58
- {
59
- title: 'Repository (branch)',
60
- value: [
61
- `<https://github.com/${ buildRepoOwner }/${ buildRepoName }|${ buildRepoName }>`,
62
- `(<https://github.com/${ buildRepoOwner }/${ buildRepoName }/tree/${ options.branch }|${ options.branch }>)`
63
- ].join( ' ' ),
64
- short: true
65
- },
66
- {
67
- title: 'Commit',
68
- value: `<${ commitUrl }|${ shortCommit }>`,
69
- short: true
70
- },
71
- {
72
- title: 'Build number',
73
- value: `<${ options.jobUrl }|#${ options.jobId }>`,
74
- short: true
75
- },
76
- {
77
- title: 'Build time',
78
- value: getExecutionTime( options.endTime, options.startTime ),
79
- short: true
80
- },
81
- {
82
- title: 'Commit message',
83
- value: getFormattedMessage( commitMessage, repoMatch[ 1 ], repoMatch[ 2 ] ),
84
- short: false
85
- }
86
- ]
87
- }
88
- ]
89
- };
90
-
91
- return slackNotify( options.slackWebhookUrl )
92
- .send( messageData )
93
- .catch( err => console.log( 'API error occurred:', err ) );
94
- };
95
-
96
- /**
97
- * Returns string representing amount of time passed between two timestamps.
98
- *
99
- * @param {Number} endTime
100
- * @param {Number} startTime
101
- * @returns {String}
102
- */
103
- function getExecutionTime( endTime, startTime ) {
104
- const totalMs = ( endTime - startTime ) * 1000;
105
- const date = new Date( totalMs );
106
- const hours = date.getUTCHours();
107
- const minutes = date.getUTCMinutes();
108
- const seconds = date.getUTCSeconds();
109
-
110
- const stringParts = [];
111
-
112
- if ( hours ) {
113
- stringParts.push( `${ hours } hr.` );
114
- }
115
-
116
- if ( minutes ) {
117
- stringParts.push( `${ minutes } min.` );
118
- }
119
-
120
- if ( seconds ) {
121
- stringParts.push( `${ seconds } sec.` );
122
- }
123
-
124
- return stringParts.join( ' ' );
125
- }
126
-
127
- /**
128
- * Replaces `#Id` and `Repo/Owner#Id` with URls to Github Issues.
129
- *
130
- * @param {String} message
131
- * @param {String} repoOwner
132
- * @param {String} repoName
133
- * @returns {string}
134
- */
135
- function getFormattedMessage( message, repoOwner, repoName ) {
136
- return message
137
- .replace( / #(\d+)/g, ( _, issueId ) => {
138
- return ` <https://github.com/${ repoOwner }/${ repoName }/issues/${ issueId }|#${ issueId }>`;
139
- } )
140
- .replace( /([\w-]+\/[\w-]+)#(\d+)/g, ( _, repoSlug, issueId ) => {
141
- return `<https://github.com/${ repoSlug }/issues/${ issueId }|${ repoSlug }#${ issueId }>`;
142
- } );
143
- }
144
-
145
- /**
146
- * Returns a URL to the commit details on GitHub.
147
- *
148
- * @param {Object} options
149
- * @param {String} options.notifyCommitUrl
150
- * @param {String} options.repositorySlug
151
- * @param {String} options.commit
152
- * @returns {String}
153
- */
154
- function getCommitUrl( options ) {
155
- if ( options.notifyCommitUrl ) {
156
- return options.notifyCommitUrl;
157
- }
158
-
159
- return `https://github.com/${ options.repositorySlug }/commit/${ options.commit }`;
160
- }
161
-
162
- /**
163
- * Returns a URL to GitHub API which returns details of the commit that caused the CI to fail its job.
164
- *
165
- * @param {String} commitUrl The URL to the commit on GitHub.
166
- * @returns {String}
167
- */
168
- function getGithubApiUrl( commitUrl ) {
169
- return commitUrl.replace( 'github.com/', 'api.github.com/repos/' ).replace( '/commit/', '/commits/' );
170
- }
171
-
172
- /**
173
- * Returns a promise that resolves the commit details (author and message) based on the specified GitHub URL.
174
- *
175
- * @param {String} commitUrl The URL to the commit on GitHub.
176
- * @param {String} githubToken Github token used for authorization a request,
177
- * @returns {Promise.<Object>}
178
- */
179
- function getCommitDetails( commitUrl, githubToken ) {
180
- const apiGithubUrlCommit = getGithubApiUrl( commitUrl );
181
- const options = {
182
- method: 'GET',
183
- credentials: 'include',
184
- headers: {
185
- authorization: `token ${ githubToken }`
186
- }
187
- };
188
-
189
- return fetch( apiGithubUrlCommit, options )
190
- .then( response => response.json() )
191
- .then( json => ( {
192
- githubAccount: json.author ? json.author.login : null,
193
- commitAuthor: json.commit.author.name,
194
- commitMessage: json.commit.message
195
- } ) );
196
- }
197
-
198
- /**
199
- * Returns the additional message that will be added to the notifier post.
200
- *
201
- * @param {Object} options
202
- * @param {Boolean} options.shouldHideAuthor
203
- * @param {String|null} options.slackAccount
204
- * @param {String|null} options.githubAccount
205
- * @param {String} options.commitAuthor
206
- * @returns {String}
207
- */
208
- function getNotifierMessage( options ) {
209
- if ( options.shouldHideAuthor ) {
210
- return '_The author of the commit was hidden. <https://github.com/ckeditor/ckeditor5/issues/9252|Read more about why.>_';
211
- }
212
-
213
- // If the author of the commit could not be obtained, let's ping the entire team.
214
- if ( !options.slackAccount ) {
215
- return `@channel (${ options.commitAuthor }), could you take a look?`;
216
- }
217
-
218
- // Slack and GitHub names for bots are equal.
219
- if ( options.slackAccount === options.githubAccount ) {
220
- return '_This commit is a result of merging a branch into another branch._';
221
- }
222
-
223
- return `<@${ options.slackAccount }>, could you take a look?`;
224
- }