@ckeditor/ckeditor5-dev-ci 38.2.2 → 38.3.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.
@@ -0,0 +1,109 @@
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
+ 'use strict';
11
+
12
+ const { execSync } = require( 'child_process' );
13
+ const fetch = require( 'node-fetch' );
14
+ const minimist = require( 'minimist' );
15
+ const processJobStatuses = require( '../lib/process-job-statuses' );
16
+
17
+ // This script allows the creation of a new job within a workflow that will be executed
18
+ // in the end, when all other jobs will be finished or errored.
19
+ //
20
+ // Below, you can find an example workflow.
21
+ //
22
+ // ┌─────┐ ┌─────┐
23
+ // │Job A├────►│Job B├──────────┐
24
+ // └─────┘ └─────┘ ▼
25
+ // ┌────────┐
26
+ // │Notifier│
27
+ // └────────┘
28
+ // ┌─────┐ ▲
29
+ // │Job C├──────────────────────┘
30
+ // └─────┘
31
+ //
32
+ // Job A triggers Job B, and Job C has no dependencies. When all jobs are done, we would like to execute Notifier.
33
+ //
34
+ // The Notifier job should also be executed when Job A ends with an error. In such a case, Job B is still blocked.
35
+ // Hence, we need to iterate over all jobs and verify if their dependencies ended with an error to unlock
36
+ // executing the final part of the workflow.
37
+
38
+ const {
39
+ /**
40
+ * Required. CircleCI API token used for obtaining data about the build from API.
41
+ */
42
+ CKE5_CIRCLE_TOKEN,
43
+
44
+ // Variables that are available by default in Circle environment.
45
+ CIRCLE_WORKFLOW_ID,
46
+ CIRCLE_JOB
47
+ } = process.env;
48
+
49
+ const { task } = parseArguments( process.argv.slice( 2 ) );
50
+
51
+ waitForOtherJobsAndSendNotification()
52
+ .catch( err => {
53
+ console.error( err );
54
+
55
+ process.exit( 1 );
56
+ } );
57
+
58
+ async function waitForOtherJobsAndSendNotification() {
59
+ const jobs = processJobStatuses(
60
+ await getOtherJobsData()
61
+ );
62
+
63
+ const workflowFinished = jobs.every( job => [ 'success', 'failed', 'failed_parent' ].includes( job.status ) );
64
+ const anyJobsFailed = jobs.some( job => job.status === 'failed' );
65
+
66
+ if ( !workflowFinished ) {
67
+ await new Promise( r => setTimeout( r, 30 * 1000 ) );
68
+
69
+ return waitForOtherJobsAndSendNotification();
70
+ }
71
+
72
+ if ( anyJobsFailed ) {
73
+ return execSync( task, { stdio: 'inherit' } );
74
+ }
75
+
76
+ console.log( 'All jobs were successful.' );
77
+ }
78
+
79
+ /**
80
+ * Fetches and returns data of all jobs except the one where this script runs.
81
+ */
82
+ async function getOtherJobsData() {
83
+ const url = `https://circleci.com/api/v2/workflow/${ CIRCLE_WORKFLOW_ID }/job`;
84
+ const options = { headers: { 'Circle-Token': CKE5_CIRCLE_TOKEN } };
85
+
86
+ const response = await fetch( url, options );
87
+ const data = await response.json();
88
+
89
+ return data.items.filter( job => job.name !== CIRCLE_JOB );
90
+ }
91
+
92
+ /**
93
+ * @param {Array.<String>} args
94
+ * @returns {Object} result
95
+ * @returns {String} result.task
96
+ */
97
+ function parseArguments( args ) {
98
+ const config = {
99
+ string: [
100
+ 'task'
101
+ ],
102
+
103
+ default: {
104
+ task: 'yarn ckeditor5-dev-ci-notify-circle-status'
105
+ }
106
+ };
107
+
108
+ return minimist( args, config );
109
+ }
@@ -1,4 +1,5 @@
1
1
  [
2
2
  "CKTravisBot",
3
+ "CKEditorBot",
3
4
  "CSBot"
4
5
  ]
@@ -82,7 +82,7 @@ function getNotifierMessage( options ) {
82
82
  }
83
83
 
84
84
  if ( bots.includes( options.githubAccount ) ) {
85
- return '_This commit is a result of merging a branch into another branch._';
85
+ return '_Automated stuff happened on one of the branches. Got time to have a look at it, anyone?_';
86
86
  }
87
87
 
88
88
  // If the author of the commit could not be obtained, let's ping the entire team.
@@ -0,0 +1,93 @@
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
+ /**
9
+ * The function aims to determine a proper build status for children's jobs based on their parent's statuses.
10
+ *
11
+ * For example, in the following workflow:
12
+ *
13
+ * ┌────┐ ┌────┐
14
+ * │Id 1├────►│Id 2│
15
+ * └────┘ └────┘
16
+ *
17
+ * If the "Id 1" job is marked as failed, the "Id 2" job should be kept, too (it will never start due to an error in the parent task).
18
+ *
19
+ * @param {Array.<WorkflowJob>} jobs
20
+ * @returns {Array.<WorkflowJob>}
21
+ */
22
+ module.exports = function processJobStatuses( jobs ) {
23
+ // To avoid modifying the original object, let's clone.
24
+ const jobsClone = clone( jobs );
25
+
26
+ // Find jobs to mark as "failed" based on their relationship.
27
+ const jobsToProcess = jobsClone
28
+ .filter( job => {
29
+ // Ignore job with no dependencies.
30
+ if ( !job.dependencies.length ) {
31
+ return false;
32
+ }
33
+
34
+ // If the job is already marked as failed, ignore it, too.
35
+ if ( isJobFailed( job ) ) {
36
+ return false;
37
+ }
38
+
39
+ // Verify if the job is a descant child of any failed job.
40
+ const dependencies = job.dependencies
41
+ .map( parentJobId => jobsClone.find( job => job.id === parentJobId ) )
42
+ .filter( parentJob => isJobFailed( parentJob ) );
43
+
44
+ // If so, save the job to process.
45
+ return dependencies.length;
46
+ } );
47
+
48
+ for ( const job of jobsToProcess ) {
49
+ job.status = 'failed_parent';
50
+ }
51
+
52
+ // Whenever a job has been changed, we need to iterate over all jobs again to verify the remaining jobs.
53
+ if ( jobsToProcess.length ) {
54
+ return processJobStatuses( jobsClone );
55
+ }
56
+
57
+ return jobsClone;
58
+ };
59
+
60
+ /**
61
+ * @param {WorkflowJob} job
62
+ * @returns {Boolean}
63
+ */
64
+ function isJobFailed( job ) {
65
+ if ( job.status === 'failed' ) {
66
+ return true;
67
+ }
68
+
69
+ if ( job.status === 'failed_parent' ) {
70
+ return true;
71
+ }
72
+
73
+ return false;
74
+ }
75
+
76
+ /**
77
+ * @template T
78
+ * @param {T} obj
79
+ * @returns {T}
80
+ */
81
+ function clone( obj ) {
82
+ return JSON.parse( JSON.stringify( obj ) );
83
+ }
84
+
85
+ /**
86
+ * @typedef {Object} WorkflowJob
87
+ *
88
+ * @property {String} id
89
+ *
90
+ * @property {'blocked'|'running'|'failed'|'failed_parent'|'success'} status
91
+ *
92
+ * @property {Array.<String>} dependencies
93
+ */
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-dev-ci",
3
- "version": "38.2.2",
4
- "description": "Utils for CKEditor5 CI builds.",
3
+ "version": "38.3.1",
4
+ "description": "Utils used on various Continuous Integration services.",
5
5
  "keywords": [],
6
6
  "dependencies": {
7
+ "minimist": "^1.2.5",
7
8
  "node-fetch": "^2.6.7",
8
9
  "slack-notify": "^2.0.6"
9
10
  },
@@ -17,7 +18,8 @@
17
18
  ],
18
19
  "bin": {
19
20
  "ckeditor5-dev-ci-notify-travis-status": "bin/notify-travis-status.js",
20
- "ckeditor5-dev-ci-notify-circle-status": "bin/notify-circle-status.js"
21
+ "ckeditor5-dev-ci-notify-circle-status": "bin/notify-circle-status.js",
22
+ "ckeditor5-dev-ci-circle-workflow-notifier": "bin/circle-workflow-notifier.js"
21
23
  },
22
24
  "author": "CKSource (http://cksource.com/)",
23
25
  "license": "GPL-2.0-or-later",