-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: refactor into libraries. add join flag. support multiple pr jobs
BREAKING CHANGE: altered the signature for getWorkflow, getNextJobs
- Loading branch information
Showing
10 changed files
with
359 additions
and
245 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,121 +1,6 @@ | ||
'use strict'; | ||
|
||
/** | ||
* Get the list of nodes for the graph | ||
* @method calculateNodes | ||
* @param {Object} jobs Hash of job configs | ||
* @return {Array} List of nodes (jobs) | ||
*/ | ||
const calculateNodes = (jobs) => { | ||
const nodes = []; | ||
|
||
Object.keys(jobs).forEach((j) => { | ||
nodes.push({ name: j }); | ||
}); | ||
|
||
return nodes; | ||
}; | ||
|
||
/** | ||
* Get all the edges of the directed graph from a legacy workflow config | ||
* @method calculateLegacyEdges | ||
* @param {Array} workflow List of all jobs in the workflow excluding "main" | ||
* @return {Array} List of edge objects { src, dest } | ||
*/ | ||
const calculateLegacyEdges = (workflow) => { | ||
// In legacy-mode "main" is always required to exist, and be the target of commit and pr | ||
const edges = [ | ||
{ src: '~pr', dest: 'main' }, | ||
{ src: '~commit', dest: 'main' } | ||
]; | ||
|
||
// Legacy-mode workflows are always linear, starting with main | ||
let src = 'main'; | ||
|
||
workflow.forEach((dest) => { | ||
edges.push({ src, dest }); | ||
src = dest; | ||
}); | ||
|
||
return edges; | ||
}; | ||
|
||
/** | ||
* Calculate edges of directed graph based on "requires" property of jobs | ||
* @method calculateEdges | ||
* @param {Object} jobs Hash of job configurations | ||
* @return {Array} List of graph edges { src, dest } | ||
*/ | ||
const calculateEdges = (jobs) => { | ||
const edges = []; | ||
|
||
Object.keys(jobs).forEach((j) => { | ||
const job = jobs[j]; | ||
const dest = j; | ||
|
||
if (Array.isArray(job.requires)) { | ||
job.requires.forEach((src) => { | ||
edges.push({ src, dest }); | ||
}); | ||
} | ||
}); | ||
|
||
return edges; | ||
}; | ||
|
||
/** | ||
* Given a pipeline config, return a directed graph configuration that describes the workflow | ||
* @method getWorkflow | ||
* @param {Object} obj | ||
* @param {Object} obj.config A pipeline config | ||
* @param {Object} obj.config.jobs Hash of job configs | ||
* @param {Array} [obj.config.workflow] Legacy workflow config | ||
* @param {Boolean} [obj.useLegacy] Flag to process legacy workflows | ||
* @return {Object} List of nodes and edges { nodes, edges } | ||
*/ | ||
const getWorkflow = ({ config: pipelineConfig, useLegacy = false }) => { | ||
const jobConfig = pipelineConfig.jobs; | ||
let edges = []; | ||
|
||
if (!jobConfig) { | ||
throw new Error('No Job config provided'); | ||
} | ||
|
||
const hasRequiresConfig = Object.keys(jobConfig) | ||
.some(j => Array.isArray(jobConfig[j].requires)); | ||
|
||
if (useLegacy && !hasRequiresConfig) { | ||
// Work out whether there is a user defined workflow, | ||
// or if we use the order of jobs defined in jobConfig | ||
const workflow = Array.isArray(pipelineConfig.workflow) ? | ||
pipelineConfig.workflow : | ||
Object.keys(jobConfig).filter(j => j !== 'main'); // remove main since that is a hard dependency | ||
|
||
edges = calculateLegacyEdges(workflow); | ||
} else { | ||
edges = calculateEdges(jobConfig); | ||
} | ||
|
||
return { nodes: calculateNodes(jobConfig), edges }; | ||
}; | ||
|
||
/** | ||
* Calculate the next jobs to execute, given a workflow and a trigger job | ||
* @method getNextJobs | ||
* @param {Object} workflow Directed graph representation of workflow | ||
* @param {String} trigger Name of event that triggers jobs (~pr, ~commit, JobName) | ||
* @return {Array} List of job names | ||
*/ | ||
const getNextJobs = (workflow, trigger) => { | ||
const jobs = new Set(); | ||
|
||
workflow.edges.forEach((edge) => { | ||
if (edge.src === trigger) { | ||
jobs.add(edge.dest); | ||
} | ||
}); | ||
|
||
return Array.from(jobs); | ||
}; | ||
const getWorkflow = require('./lib/getWorkflow'); | ||
const getNextJobs = require('./lib/getNextJobs'); | ||
|
||
module.exports = { getWorkflow, getNextJobs }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
'use strict'; | ||
|
||
/** | ||
* Calculate the next jobs to execute, given a workflow and a trigger job | ||
* @method getNextJobs | ||
* @param {Object} workflowGraph Directed graph representation of workflow | ||
* @param {Object} config | ||
* @param {String} config.trigger The triggering event (~pr, ~commit, jobName) | ||
* @param {String} [config.prNum] The PR number (required when ~pr trigger) | ||
* @return {Array} List of job names | ||
*/ | ||
const getNextJobs = (workflowGraph, config) => { | ||
const jobs = new Set(); | ||
|
||
if (!config || !config.trigger) { | ||
throw new Error('Must provide a trigger'); | ||
} | ||
|
||
if (config.trigger === '~pr' && !config.prNum) { | ||
throw new Error('Must provide a PR number with "~pr" trigger'); | ||
} | ||
|
||
workflowGraph.edges.forEach((edge) => { | ||
if (edge.src === config.trigger) { | ||
// Make PR jobs PR-$num:$cloneJob (not sure how to better handle multiple PR jobs) | ||
jobs.add(config.trigger === '~pr' ? `PR-${config.prNum}:${edge.dest}` : edge.dest); | ||
} | ||
}); | ||
|
||
return Array.from(jobs); | ||
}; | ||
|
||
module.exports = getNextJobs; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
'use strict'; | ||
|
||
/** | ||
* Get the list of nodes for the graph | ||
* @method calculateNodes | ||
* @param {Object} jobs Hash of job configs | ||
* @return {Array} List of nodes (jobs) | ||
*/ | ||
const calculateNodes = (jobs) => { | ||
const nodes = [ | ||
{ name: '~pr' }, | ||
{ name: '~commit' } | ||
]; | ||
|
||
Object.keys(jobs).forEach((name) => { | ||
nodes.push({ name }); | ||
}); | ||
|
||
return nodes; | ||
}; | ||
|
||
/** | ||
* Get all the edges of the directed graph from a legacy workflow config | ||
* @method calculateLegacyEdges | ||
* @param {Array} workflow List of all jobs in the workflow excluding "main" | ||
* @return {Array} List of edge objects { src, dest } | ||
*/ | ||
const calculateLegacyEdges = (workflow) => { | ||
// In legacy-mode "main" is always required to exist, and be the target of commit and pr | ||
const edges = [ | ||
{ src: '~pr', dest: 'main' }, | ||
{ src: '~commit', dest: 'main' } | ||
]; | ||
|
||
// Legacy-mode workflows are always linear, starting with main | ||
let src = 'main'; | ||
|
||
workflow.forEach((dest) => { | ||
edges.push({ src, dest }); | ||
src = dest; | ||
}); | ||
|
||
return edges; | ||
}; | ||
|
||
/** | ||
* Calculate edges of directed graph based on "requires" property of jobs | ||
* @method calculateEdges | ||
* @param {Object} jobs Hash of job configurations | ||
* @return {Array} List of graph edges { src, dest } | ||
*/ | ||
const calculateEdges = (jobs) => { | ||
const edges = []; | ||
|
||
Object.keys(jobs).forEach((j) => { | ||
const job = jobs[j]; | ||
const dest = j; | ||
|
||
if (Array.isArray(job.requires)) { | ||
const specialTriggers = job.requires.filter(name => name.charAt(0) === '~'); | ||
const normalTriggers = job.requires.filter(name => name.charAt(0) !== '~'); | ||
const isJoin = normalTriggers.length > 1; | ||
|
||
specialTriggers.forEach((src) => { | ||
edges.push({ src, dest }); | ||
}); | ||
|
||
normalTriggers.forEach((src) => { | ||
const obj = { src, dest }; | ||
|
||
if (isJoin) { | ||
obj.join = true; | ||
} | ||
|
||
edges.push(obj); | ||
}); | ||
} | ||
}); | ||
|
||
return edges; | ||
}; | ||
|
||
/** | ||
* Given a pipeline config, return a directed graph configuration that describes the workflow | ||
* @method getWorkflow | ||
* @param {Object} pipelineConfig A Pipeline Config | ||
* @param {Object} pipelineConfig.jobs Hash of job configs | ||
* @param {Array} [pipelineConfig.workflow] Legacy workflow config | ||
* @param {Object} [config] configuration object | ||
* @param {Boolean} [config.useLegacy] Flag to process legacy workflows | ||
* @return {Object} List of nodes and edges { nodes, edges } | ||
*/ | ||
const getWorkflow = (pipelineConfig, config = { useLegacy: false }) => { | ||
const jobConfig = pipelineConfig.jobs; | ||
let edges = []; | ||
|
||
if (!jobConfig) { | ||
throw new Error('No Job config provided'); | ||
} | ||
|
||
const hasRequiresConfig = Object.keys(jobConfig) | ||
.some(j => Array.isArray(jobConfig[j].requires)); | ||
|
||
if (config.useLegacy && !hasRequiresConfig) { | ||
// Work out whether there is a user defined workflow, | ||
// or if we use the order of jobs defined in jobConfig | ||
let workflow = Array.isArray(pipelineConfig.workflow) ? | ||
pipelineConfig.workflow : | ||
Object.keys(jobConfig); | ||
|
||
// remove main since that is a hard dependency in legacy workflows | ||
// main is already accounted for in calculateLegacyEdges | ||
workflow = workflow.filter(j => j !== 'main'); | ||
|
||
edges = calculateLegacyEdges(workflow); | ||
} else { | ||
edges = calculateEdges(jobConfig); | ||
} | ||
|
||
return { nodes: calculateNodes(jobConfig), edges }; | ||
}; | ||
|
||
module.exports = getWorkflow; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"nodes": [ | ||
{ "name": "~pr" }, | ||
{ "name": "~commit" }, | ||
{ "name": "main" }, | ||
{ "name": "foo" }, | ||
{ "name": "bar" } | ||
], | ||
"edges": [ | ||
{ "src": "~pr", "dest": "main" }, | ||
{ "src": "~commit", "dest": "main" }, | ||
{ "src": "main", "dest": "foo" }, | ||
{ "src": "foo", "dest": "bar" } | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"jobs": { | ||
"main": {}, | ||
"foo": {}, | ||
"bar": {} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"jobs": { | ||
"main": { "requires": ["~pr", "~commit"] }, | ||
"foo": { "requires": ["main"] }, | ||
"bar": { "requires": ["foo"] } | ||
} | ||
} |
Oops, something went wrong.