@ckeditor/ckeditor5-dev-ci 38.0.5 → 38.1.1
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.
- package/bin/notify-circle-status.js +134 -0
- package/bin/notify-travis-status.js +113 -0
- package/lib/data/bots.json +3 -0
- package/lib/{members.json → data/members.json} +2 -4
- package/lib/format-message.js +194 -0
- package/package.json +3 -2
- package/bin/notifytravisstatus.js +0 -88
- package/lib/index.js +0 -12
- package/lib/notifytravisstatus.js +0 -224
|
@@ -0,0 +1,134 @@
|
|
|
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. Value of Circle's `pipeline.number` variable.
|
|
21
|
+
*/
|
|
22
|
+
CKE5_PIPELINE_NUMBER,
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Required. Token to a Github account with the scope: "repos". It is required for obtaining an author of
|
|
26
|
+
* the commit if the build failed. The repository can be private and we can't use the public API.
|
|
27
|
+
*/
|
|
28
|
+
CKE5_GITHUB_TOKEN,
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Required. CircleCI API token used for obtaining data about the build from API.
|
|
32
|
+
*/
|
|
33
|
+
CKE5_CIRCLE_TOKEN,
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Required. Webhook URL of the Slack channel where the notification should be sent.
|
|
37
|
+
*/
|
|
38
|
+
CKE5_SLACK_WEBHOOK_URL,
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Optional. If both are defined, the script will use the URL as the commit URL.
|
|
42
|
+
* Otherwise, URL will be constructed using current repository data.
|
|
43
|
+
*/
|
|
44
|
+
CKE5_TRIGGER_REPOSITORY_SLUG,
|
|
45
|
+
CKE5_TRIGGER_COMMIT_HASH,
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Optional. If set to "true", commit author will be hidden.
|
|
49
|
+
* See: https://github.com/ckeditor/ckeditor5/issues/9252.
|
|
50
|
+
*/
|
|
51
|
+
CKE5_SLACK_NOTIFY_HIDE_AUTHOR,
|
|
52
|
+
|
|
53
|
+
// Variables that are available by default in Circle environment.
|
|
54
|
+
CIRCLE_BRANCH,
|
|
55
|
+
CIRCLE_BUILD_NUM,
|
|
56
|
+
CIRCLE_PROJECT_REPONAME,
|
|
57
|
+
CIRCLE_PROJECT_USERNAME,
|
|
58
|
+
CIRCLE_SHA1,
|
|
59
|
+
CIRCLE_WORKFLOW_ID
|
|
60
|
+
} = process.env;
|
|
61
|
+
|
|
62
|
+
notifyCircleStatus();
|
|
63
|
+
|
|
64
|
+
async function notifyCircleStatus() {
|
|
65
|
+
if ( !CKE5_GITHUB_TOKEN ) {
|
|
66
|
+
throw new Error( 'Missing environment variable: CKE5_GITHUB_TOKEN' );
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if ( !CKE5_SLACK_WEBHOOK_URL ) {
|
|
70
|
+
throw new Error( 'Missing environment variable: CKE5_SLACK_WEBHOOK_URL' );
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const jobData = await getJobData();
|
|
74
|
+
const buildUrl = [
|
|
75
|
+
'https://app.circleci.com/pipelines/github',
|
|
76
|
+
CIRCLE_PROJECT_USERNAME,
|
|
77
|
+
CIRCLE_PROJECT_REPONAME,
|
|
78
|
+
CKE5_PIPELINE_NUMBER,
|
|
79
|
+
'workflows',
|
|
80
|
+
CIRCLE_WORKFLOW_ID
|
|
81
|
+
].join( '/' );
|
|
82
|
+
|
|
83
|
+
const message = await formatMessage( {
|
|
84
|
+
slackMessageUsername: 'Circle CI',
|
|
85
|
+
iconUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Circleci-icon-logo.svg/475px-Circleci-icon-logo.svg.png',
|
|
86
|
+
repositoryOwner: CIRCLE_PROJECT_USERNAME,
|
|
87
|
+
repositoryName: CIRCLE_PROJECT_REPONAME,
|
|
88
|
+
branch: CIRCLE_BRANCH,
|
|
89
|
+
buildTitle: 'Workflow ID',
|
|
90
|
+
buildUrl,
|
|
91
|
+
buildId: CIRCLE_WORKFLOW_ID.split( '-' )[ 0 ] + '-...',
|
|
92
|
+
githubToken: CKE5_GITHUB_TOKEN,
|
|
93
|
+
triggeringCommitUrl: getTriggeringCommitUrl(),
|
|
94
|
+
startTime: Math.ceil( ( new Date( jobData.started_at ) ).getTime() / 1000 ),
|
|
95
|
+
endTime: Math.ceil( ( new Date() ) / 1000 ),
|
|
96
|
+
shouldHideAuthor: CKE5_SLACK_NOTIFY_HIDE_AUTHOR === 'true'
|
|
97
|
+
} );
|
|
98
|
+
|
|
99
|
+
return slackNotify( CKE5_SLACK_WEBHOOK_URL )
|
|
100
|
+
.send( message )
|
|
101
|
+
.catch( err => console.log( 'API error occurred:', err ) );
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function getJobData() {
|
|
105
|
+
const fetchUrl = [
|
|
106
|
+
'https://circleci.com/api/v2/project/gh',
|
|
107
|
+
CIRCLE_PROJECT_USERNAME,
|
|
108
|
+
CIRCLE_PROJECT_REPONAME,
|
|
109
|
+
'job',
|
|
110
|
+
CIRCLE_BUILD_NUM
|
|
111
|
+
].join( '/' );
|
|
112
|
+
|
|
113
|
+
const fetchOptions = {
|
|
114
|
+
method: 'GET',
|
|
115
|
+
headers: { 'Circle-Token': CKE5_CIRCLE_TOKEN }
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const rawResponse = await fetch( fetchUrl, fetchOptions );
|
|
119
|
+
return rawResponse.json();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getTriggeringCommitUrl() {
|
|
123
|
+
let repoSlug, hash;
|
|
124
|
+
|
|
125
|
+
if ( CKE5_TRIGGER_REPOSITORY_SLUG && CKE5_TRIGGER_COMMIT_HASH ) {
|
|
126
|
+
repoSlug = CKE5_TRIGGER_REPOSITORY_SLUG;
|
|
127
|
+
hash = CKE5_TRIGGER_COMMIT_HASH;
|
|
128
|
+
} else {
|
|
129
|
+
repoSlug = [ CIRCLE_PROJECT_USERNAME, CIRCLE_PROJECT_REPONAME ].join( '/' );
|
|
130
|
+
hash = CIRCLE_SHA1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return [ 'https://github.com', repoSlug, 'commit', hash ].join( '/' );
|
|
134
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
buildTitle: 'Job number',
|
|
101
|
+
buildUrl: TRAVIS_JOB_WEB_URL,
|
|
102
|
+
buildId: '#' + TRAVIS_JOB_NUMBER,
|
|
103
|
+
githubToken: GITHUB_TOKEN,
|
|
104
|
+
triggeringCommitUrl: SLACK_NOTIFY_COMMIT_URL || `https://github.com/${ TRAVIS_REPO_SLUG }/commit/${ TRAVIS_COMMIT }`,
|
|
105
|
+
startTime: Number( START_TIME ),
|
|
106
|
+
endTime: Number( END_TIME ),
|
|
107
|
+
shouldHideAuthor: SLACK_NOTIFY_HIDE_AUTHOR === 'true'
|
|
108
|
+
} );
|
|
109
|
+
|
|
110
|
+
return slackNotify( SLACK_WEBHOOK_URL )
|
|
111
|
+
.send( message )
|
|
112
|
+
.catch( err => console.log( 'API error occurred:', err ) );
|
|
113
|
+
}
|
|
@@ -17,9 +17,8 @@
|
|
|
17
17
|
"ldrobnik": "U04DJ7B7ZCJ",
|
|
18
18
|
"LukaszGudel": "U01FRHAT8T1",
|
|
19
19
|
"mabryl": "U045033D401",
|
|
20
|
-
"Mgsy": "U5A38S2PN",
|
|
21
20
|
"martnpaneq": "U045B560E00",
|
|
22
|
-
"
|
|
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,194 @@
|
|
|
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} buildTitle
|
|
24
|
+
* @param {String} buildUrl
|
|
25
|
+
* @param {String} buildId
|
|
26
|
+
* @param {String} githubToken
|
|
27
|
+
* @param {String} triggeringCommitUrl
|
|
28
|
+
* @param {Number} startTime
|
|
29
|
+
* @param {Number} endTime
|
|
30
|
+
* @param {Boolean} shouldHideAuthor
|
|
31
|
+
*/
|
|
32
|
+
module.exports = async function formatMessage( options ) {
|
|
33
|
+
const commitDetails = await getCommitDetails( options.triggeringCommitUrl, options.githubToken );
|
|
34
|
+
const repoUrl = `https://github.com/${ options.repositoryOwner }/${ options.repositoryName }`;
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
username: options.slackMessageUsername,
|
|
38
|
+
icon_url: options.iconUrl,
|
|
39
|
+
unfurl_links: 1,
|
|
40
|
+
text: getNotifierMessage( { ...options, ...commitDetails } ),
|
|
41
|
+
attachments: [ {
|
|
42
|
+
color: 'danger',
|
|
43
|
+
fields: [ {
|
|
44
|
+
title: 'Repository (branch)',
|
|
45
|
+
value: `<${ repoUrl }|${ options.repositoryName }> (<${ repoUrl }/tree/${ options.branch }|${ options.branch }>)`,
|
|
46
|
+
short: true
|
|
47
|
+
}, {
|
|
48
|
+
title: options.buildTitle,
|
|
49
|
+
value: `<${ options.buildUrl }|${ options.buildId }>`,
|
|
50
|
+
short: true
|
|
51
|
+
}, {
|
|
52
|
+
title: 'Commit',
|
|
53
|
+
value: `<${ options.triggeringCommitUrl }|${ commitDetails.hash.substring( 0, 7 ) }>`,
|
|
54
|
+
short: true
|
|
55
|
+
}, {
|
|
56
|
+
title: 'Build time',
|
|
57
|
+
value: getExecutionTime( options.startTime, options.endTime ),
|
|
58
|
+
short: true
|
|
59
|
+
}, {
|
|
60
|
+
title: 'Commit message',
|
|
61
|
+
value: getFormattedMessage( commitDetails.commitMessage, options.triggeringCommitUrl ),
|
|
62
|
+
short: false
|
|
63
|
+
} ]
|
|
64
|
+
} ]
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Returns the additional message that will be added to the notifier post.
|
|
70
|
+
*
|
|
71
|
+
* @param {Object} options
|
|
72
|
+
* @param {Boolean} options.shouldHideAuthor
|
|
73
|
+
* @param {String|null} options.githubAccount
|
|
74
|
+
* @param {String} options.commitAuthor
|
|
75
|
+
* @returns {String}
|
|
76
|
+
*/
|
|
77
|
+
function getNotifierMessage( options ) {
|
|
78
|
+
const slackAccount = members[ options.githubAccount ] || null;
|
|
79
|
+
|
|
80
|
+
if ( options.shouldHideAuthor ) {
|
|
81
|
+
return '_The author of the commit was hidden. <https://github.com/ckeditor/ckeditor5/issues/9252|Read more about why.>_';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// If the author of the commit could not be obtained, let's ping the entire team.
|
|
85
|
+
if ( !slackAccount ) {
|
|
86
|
+
return `@channel (${ options.commitAuthor }), could you take a look?`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if ( bots.includes( options.githubAccount ) ) {
|
|
90
|
+
return '_This commit is a result of merging a branch into another branch._';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return `<@${ slackAccount }>, could you take a look?`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Returns string representing amount of time passed between two timestamps.
|
|
98
|
+
* Timestamps should be in seconds instead of milliseconds.
|
|
99
|
+
*
|
|
100
|
+
* @param {Number} startTime
|
|
101
|
+
* @param {Number} endTime
|
|
102
|
+
* @returns {String}
|
|
103
|
+
*/
|
|
104
|
+
function getExecutionTime( startTime, endTime ) {
|
|
105
|
+
if ( !startTime || !endTime ) {
|
|
106
|
+
return 'Unavailable.';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const totalMs = ( endTime - startTime ) * 1000;
|
|
110
|
+
const date = new Date( totalMs );
|
|
111
|
+
const hours = date.getUTCHours();
|
|
112
|
+
const minutes = date.getUTCMinutes();
|
|
113
|
+
const seconds = date.getUTCSeconds();
|
|
114
|
+
|
|
115
|
+
const stringParts = [];
|
|
116
|
+
|
|
117
|
+
if ( hours ) {
|
|
118
|
+
stringParts.push( `${ hours } hr.` );
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if ( minutes ) {
|
|
122
|
+
stringParts.push( `${ minutes } min.` );
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if ( seconds ) {
|
|
126
|
+
stringParts.push( `${ seconds } sec.` );
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if ( !stringParts.length ) {
|
|
130
|
+
return '0 sec.';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return stringParts.join( ' ' );
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Replaces `#Id` and `Repo/Owner#Id` with URls to Github Issues.
|
|
138
|
+
*
|
|
139
|
+
* @param {String} commitMessage
|
|
140
|
+
* @param {String} triggeringCommitUrl
|
|
141
|
+
* @returns {string}
|
|
142
|
+
*/
|
|
143
|
+
function getFormattedMessage( commitMessage, triggeringCommitUrl ) {
|
|
144
|
+
if ( !commitMessage ) {
|
|
145
|
+
return 'Unavailable';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const [ , repoOwner, repoName ] = triggeringCommitUrl.match( REPOSITORY_REGEXP );
|
|
149
|
+
|
|
150
|
+
return commitMessage
|
|
151
|
+
.replace( / #(\d+)/g, ( _, issueId ) => {
|
|
152
|
+
return ` <https://github.com/${ repoOwner }/${ repoName }/issues/${ issueId }|#${ issueId }>`;
|
|
153
|
+
} )
|
|
154
|
+
.replace( /([\w-]+\/[\w-]+)#(\d+)/g, ( _, repoSlug, issueId ) => {
|
|
155
|
+
return `<https://github.com/${ repoSlug }/issues/${ issueId }|${ repoSlug }#${ issueId }>`;
|
|
156
|
+
} );
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Returns a promise that resolves the commit details (author and message) based on the specified GitHub URL.
|
|
161
|
+
*
|
|
162
|
+
* @param {String} triggeringCommitUrl The URL to the commit on GitHub.
|
|
163
|
+
* @param {String} githubToken Github token used for authorization a request,
|
|
164
|
+
* @returns {Promise.<Object>}
|
|
165
|
+
*/
|
|
166
|
+
function getCommitDetails( triggeringCommitUrl, githubToken ) {
|
|
167
|
+
const apiGithubUrlCommit = getGithubApiUrl( triggeringCommitUrl );
|
|
168
|
+
const options = {
|
|
169
|
+
method: 'GET',
|
|
170
|
+
credentials: 'include',
|
|
171
|
+
headers: {
|
|
172
|
+
authorization: `token ${ githubToken }`
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
return fetch( apiGithubUrlCommit, options )
|
|
177
|
+
.then( response => response.json() )
|
|
178
|
+
.then( json => ( {
|
|
179
|
+
githubAccount: json.author ? json.author.login : null,
|
|
180
|
+
commitAuthor: json.commit.author.name,
|
|
181
|
+
commitMessage: json.commit.message,
|
|
182
|
+
hash: json.sha
|
|
183
|
+
} ) );
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Returns a URL to GitHub API which returns details of the commit that caused the CI to fail its job.
|
|
188
|
+
*
|
|
189
|
+
* @param {String} triggeringCommitUrl The URL to the commit on GitHub.
|
|
190
|
+
* @returns {String}
|
|
191
|
+
*/
|
|
192
|
+
function getGithubApiUrl( triggeringCommitUrl ) {
|
|
193
|
+
return triggeringCommitUrl.replace( 'github.com/', 'api.github.com/repos/' ).replace( '/commit/', '/commits/' );
|
|
194
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-dev-ci",
|
|
3
|
-
"version": "38.
|
|
3
|
+
"version": "38.1.1",
|
|
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/
|
|
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
|
-
}
|