Skip to content

Commit

Permalink
✨ Add dependencies to Stages
Browse files Browse the repository at this point in the history
  • Loading branch information
skerit committed Jan 18, 2024
1 parent bd125f8 commit c2acef0
Showing 1 changed file with 213 additions and 52 deletions.
265 changes: 213 additions & 52 deletions lib/core/stage.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ const Stage = Function.inherits('Alchemy.Base', 'Alchemy.Stages', function Stage
// The main pledge
this[MAIN_PLEDGE] = new Pledge.Swift();

// The dependencies of this stage
this.depends_on = null;

// Pre-tasks
this.pre_tasks = new Map();

Expand All @@ -55,6 +58,17 @@ const Stage = Function.inherits('Alchemy.Base', 'Alchemy.Stages', function Stage
this.ended = null;
});

/**
* Is this the root stage?
*
* @author Jelle De Loecker <[email protected]>
* @since 1.4.0
* @version 1.4.0
*/
Stage.setProperty(function is_root() {
return this.root_stage === this;
});

/**
* Get the main pledge
*
Expand All @@ -66,6 +80,50 @@ Stage.setProperty(function pledge() {
return this[MAIN_PLEDGE];
});

/**
* Add a dependency to this stage
*
* @author Jelle De Loecker <[email protected]>
* @since 1.4.0
* @version 1.4.0
*
* @param {string[]} stage_ids The id of the stage it depends on.
*
* @return {Alchemy.Stages.Stage}
*/
Stage.setMethod(function dependsOn(stage_ids) {

if (!this.depends_on) {
this.depends_on = [];
}

stage_ids = Array.cast(stage_ids);

for (let i = 0; i < stage_ids.length; i++) {
let id = stage_ids[i];

if (typeof id != 'string') {
throw new Error('Stage id should be a string');
}

if (!this.is_root && !id.startsWith(this.root_stage.name)) {
id = this.root_stage.name + '.' + id;
}

stage_ids[i] = id;

}

this.depends_on.push(...stage_ids);

// Push these dependencies to the already existing children
for (let [name, stage] of this.child_stages) {
stage.dependsOn(stage_ids);
}

return this;
});

/**
* Add a new child stage
*
Expand All @@ -86,6 +144,10 @@ Stage.setMethod(function createStage(name, fnc) {

let stage = new Stage(name, this);

if (this.depends_on?.length) {
stage.dependsOn(this.depends_on);
}

this.child_stages.set(name, stage);

if (fnc) {
Expand Down Expand Up @@ -177,7 +239,7 @@ Stage.setMethod(function _doTasks(type) {
} else if (Pledge.isThenable(value)) {
tasks.push(value);

value.done((err, result) => {
Pledge.cast(value).done((err, result) => {
task_map.set(fnc, result || err || true);
});
}
Expand Down Expand Up @@ -293,7 +355,7 @@ Stage.setMethod(function getStage(id) {

let parts = id.split('.');

if (parts[0] == this.name) {
if (this.is_root && parts[0] == this.name) {
parts.shift();
}

Expand Down Expand Up @@ -348,6 +410,102 @@ Stage.setMethod(function afterStages(stages, callback) {
return Function.series(tasks, callback);
});

/**
* Recursively get all the child-stages (including this one)
*
* @author Jelle De Loecker <[email protected]>
* @since 1.4.0
* @version 1.4.0
*
* @param {string[]} filter
*
* @return {Alchemy.Stage.Stage[]}
*/
Stage.setMethod(function getFlattenedStages(filter) {

let result = [];

result.push(this);

// If the filter is given, split each entry up into parts
if (filter) {
filter = Array.cast(filter);

for (let i = 0; i < filter.length; i++) {
let parts = filter[i].split('.');

if (this.is_root && parts[0] == this.name) {
parts.shift();
}

filter[i] = parts;
}
}

for (let [name, stage] of this.child_stages) {

let sub_filter;

if (filter) {
let matches_filter = false;

sub_filter = [];

for (let i = 0; i < filter.length; i++) {
let parts = filter[i];

if (parts[0] == name) {
matches_filter = true;

// Create a clone of the parts array
parts = parts.slice(0);

// Remove the first part
parts.shift();

if (parts.length) {
// And add it to the sub filter
sub_filter.push(parts);
}
}
}

if (!matches_filter) {
continue;
}

if (!sub_filter.length) {
sub_filter = null;
}
}

result.push(...stage.getFlattenedStages(sub_filter));
}

return result;
});

/**
* Recursively get all the child-stages (including this one)
* sorted in launch order.
*
* @author Jelle De Loecker <[email protected]>
* @since 1.4.0
* @version 1.4.0
*
* @param {string[]} filter
*
* @return {Alchemy.Stage.Stage[]}
*/
Stage.setMethod(function getSortedStages(filter) {

let stages = this.getFlattenedStages(filter);

stages.sortTopological('id', 'depends_on');

return stages;
});

/**
* Launch this stage and all the given child stages.
*
Expand All @@ -357,23 +515,21 @@ Stage.setMethod(function afterStages(stages, callback) {
*
* @param {string[]} child_stages The child stages to launch
*/
Stage.setMethod(function launch(child_stages) {
Stage.setMethod(async function launch(child_stages) {

if (child_stages == null) {
throw new Error('Unable to launch a stage without allowed child stages');
}

if (!this.started) {
this.started = Date.now();
if (child_stages === true) {
child_stages = undefined;
}

this.emit('launching', this);
let stages = this.getSortedStages(child_stages);

if (this !== this.root_stage) {
this.root_stage.emit('launching', this);
}
for (let stage of stages) {
await stage._launch();
}

return this._launch(child_stages);
});

/**
Expand All @@ -387,6 +543,16 @@ Stage.setMethod(function launch(child_stages) {
*/
Stage.setMethod(async function _launch(child_stages) {

if (!this.started) {
this.started = Date.now();

this.emit('launching', this);

if (!this.is_root) {
this.root_stage.emit('launching', this);
}
}

if (!this[STATUS]) {
this[STATUS] = PRE_STATUS;
}
Expand All @@ -399,68 +565,37 @@ Stage.setMethod(async function _launch(child_stages) {

await this._doTasks('main_tasks');

if (child_stages === true) {
child_stages = [];

for (let [name, stage] of this.child_stages) {
child_stages.push(name);
}
} else if (typeof child_stages == 'string') {
child_stages = [child_stages];
}

if (child_stages.length) {
await this.pre_tasks[STATUS_PLEDGE];
await this.main_tasks[STATUS_PLEDGE];

if (this[STATUS] != POST_STATUS) {
this[STATUS] = CHILD_STATUS;
}

let stage_tasks = [];

for (let name of child_stages) {
let stage = this.child_stages.get(name);

if (!stage) {
throw new Error('Child stage "' + name + '" not found');
}

stage_tasks.push(async (next) => {
await stage.launch(true);
next();
});
}
await this.pre_tasks[STATUS_PLEDGE];
await this.main_tasks[STATUS_PLEDGE];

await Function.series(stage_tasks);
if (this[STATUS] != POST_STATUS) {
this[STATUS] = CHILD_STATUS;
}

this[STATUS] = POST_STATUS;

if (this.hasFinishedAllChildStages()) {
await this._doTasks('post_tasks');
this.refreshStatus();
}
await this.refreshStatus();
});


/**
* Check if everything is finished
*
* @author Jelle De Loecker <[email protected]>
* @since 1.4.0
* @version 1.4.0
*/
Stage.setMethod(function refreshStatus() {
Stage.setMethod(async function refreshStatus() {

if (!this.hasFinishedAllChildStages()) {
return;
}

await this._doTasks('post_tasks');

if (!this.ended) {
this.ended = Date.now();
}

this[STATUS] = POST_STATUS;

this.pledge.resolve();

if (this.parent) {
Expand All @@ -483,6 +618,32 @@ Stage.setMethod(function createSputnikShim(mapping) {
return new SputnikShim(this, mapping);
});

/**
* Custom Janeway representation (left side)
*
* @author Jelle De Loecker <[email protected]>
* @since 1.4.0
* @version 1.4.0
*
* @return {string}
*/
Stage.setMethod(Symbol.for('janeway_arg_left'), function janewayClassIdentifier() {
return 'A.S.' + this.constructor.name;
});

/**
* Custom Janeway representation (right side)
*
* @author Jelle De Loecker <[email protected]>
* @since 1.4.0
* @version 1.4.0
*
* @return {String}
*/
Stage.setMethod(Symbol.for('janeway_arg_right'), function janewayInstanceInfo() {
return this.id;
});

/**
* The SputnikShim class
*
Expand Down

0 comments on commit c2acef0

Please sign in to comment.