Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Format the task runner #27256

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ compile: install

# Compile all assets in development.
compile-dev: install
@NODE_ENV=development ./tools/task-runner/runner compile --dev
@NODE_ENV=development ./tools/task-runner/runner compile/index.dev

# Compile atom-specific JS
compile-atoms: install
Expand All @@ -58,7 +58,7 @@ compile-javascript: install # PRIVATE
@./tools/task-runner/runner compile/javascript

compile-javascript-dev: install # PRIVATE
@./tools/task-runner/runner compile/javascript --dev
@./tools/task-runner/runner compile/javascript/index.dev

compile-css: install # PRIVATE
@./tools/task-runner/runner compile/css
Expand Down
2 changes: 0 additions & 2 deletions tools/__tasks__/compile/javascript/index.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ const inlineSVGs = require('../inline-svgs/index.js');
const clean = require('./clean.js');
const copy = require('./copy.js');
const webpack = require('./webpack.dev');
const webpackDCR = require('./webpack-dcr.dev');
const bundlePolyfills = require('./bundle-polyfills');

const task = {
Expand All @@ -13,7 +12,6 @@ const task = {
clean,
copy,
webpack,
webpackDCR,
bundlePolyfills,
],
};
Expand Down
2 changes: 1 addition & 1 deletion tools/task-runner/run-task-verbose-formater.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@ class VerboseRenderer {
}
}

module.exports = VerboseRenderer;
module.exports = { VerboseRenderer };
319 changes: 179 additions & 140 deletions tools/task-runner/runner
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,61 @@ const path = require('path');
const os = require('os');

const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');
const { Listr } = require('listr2');
const execa = require('execa');
const chalk = require('chalk');
const figures = require('figures');
const uniq = require('lodash.uniq');
const { VerboseRenderer } = require('./run-task-verbose-formater.js');

// name of the tasks directory
const tasksDirectory = '__tasks__';

// use yargs to get a useful CLI
const { dev: IS_DEV, debug: IS_DEBUG, verbose: IS_VERBOSE, stdout: IS_STDOUT, _: TASKS } = yargs
.option('dev', {
demand: false,
describe: 'Prefer the dev version of the task, if it exits.',
type: 'boolean',
nargs: 0,
})
.option('debug', {
demand: false,
describe: 'Log everything there is to log.',
type: 'boolean',
nargs: 0,
})
.option('verbose', {
demand: false,
describe: 'Log everything there is to log with a simple format for the output.',
type: 'boolean',
nargs: 0,
})
.option('stdout', {
demand: false,
describe: 'Log all stdout once the tasks are finished (errors are logged by default).',
type: 'boolean',
nargs: 0,
})
.usage('Usage: $0 <task> [<task>] [--dev]')
.command('task', `Run a task defined in '${tasksDirectory}'.`)
.example('$0 copy', 'Run all the copy tasks.')
.example('$0 javascript/copy', 'Run the javascript copy task.')
.example('$0 javascript/copy --dev', 'Run the javascript copy task, and prefer the development version, if it exists.')
.example('$0 javascript/copy css/copy --dev', 'Run the javascript and css copy tasks, and prefer the development versions, if they exist.')
.demand(1)
.help().alias('h', 'help') // eslint-disable-line newline-per-chained-call
.version().alias('v', 'version') // eslint-disable-line newline-per-chained-call
.argv;
const {
debug: IS_DEBUG,
verbose: IS_VERBOSE,
stdout: IS_STDOUT,
_: TASKS,
} = yargs(hideBin(process.argv))
.option('debug', {
demand: false,
describe: 'Log everything there is to log.',
type: 'boolean',
nargs: 0,
})
.option('verbose', {
demand: false,
describe:
'Log everything there is to log with a simple format for the output.',
type: 'boolean',
nargs: 0,
})
.option('stdout', {
demand: false,
describe:
'Log all stdout once the tasks are finished (errors are logged by default).',
type: 'boolean',
nargs: 0,
})
.usage('Usage: $0 <task> [<task>] [--dev]')
.command('task', `Run a task defined in '${tasksDirectory}'.`)
.example('$0 copy', 'Run all the copy tasks.')
.example('$0 javascript/copy', 'Run the javascript copy task.')
.example(
'$0 javascript/copy --dev',
'Run the javascript copy task, and prefer the development version, if it exists.',
)
.example(
'$0 javascript/copy css/copy --dev',
'Run the javascript and css copy tasks, and prefer the development versions, if they exist.',
)
.demand(1)
.help()
.alias('h', 'help') // eslint-disable-line newline-per-chained-call
.version()
.alias('v', 'version').argv; // eslint-disable-line newline-per-chained-call
Comment on lines +63 to +65
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can these comments go now?

Copy link
Contributor Author

@mxdvl mxdvl Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They probably can, but easier to do once we’ve moved to ESM in the next PR. Here I’ve mostly just run prettier tools/task-runner/runner --write and updated IS_DEV part of getTasksFromModule


// if this is true, we log as much as we can
const VERBOSE = IS_VERBOSE || IS_DEBUG;
Expand All @@ -66,124 +76,153 @@ const cache = [];

// use exaca to run simple terminal commands
const exec = (task, onError, ctx) => {
const [cmd, ...args] = task.trim().split(' ');

return execa(cmd, args)
.then((result) => {
// store any stdout incase we need it later
if (result.stdout) ctx.stdouts.push(result.stdout);
})
.catch((e) => {
// if the task supplies an `onError` function, run it
if (typeof onError === 'function') onError(ctx);
// continue with a fake rejected promise
return Promise.reject(e);
});
const [cmd, ...args] = task.trim().split(' ');

return execa(cmd, args)
.then((result) => {
// store any stdout incase we need it later
if (result.stdout) ctx.stdouts.push(result.stdout);
})
.catch((e) => {
// if the task supplies an `onError` function, run it
if (typeof onError === 'function') onError(ctx);
// continue with a fake rejected promise
return Promise.reject(e);
});
};

const getCpuCount = () => os.cpus().length;

// turn a list of our tasks into objects listr can use
function listrify(steps, { concurrent = false } = {}) {
const listrTasks = steps
.map((step) => {
const { description: title, task, concurrent: isConcurrent, onError } = step;

// if another task has included this one, don't run it again
const skip = cache.indexOf(step) !== -1 ? () => 'Skipping: already run by another task' : false;
cache.push(step);

// if the task is a set of subtasks, prepare them
if (Array.isArray(task)) {
return {
title,
task: () => listrify(task.map((_task) => {
if (_task.task) return _task;
if (typeof _task === 'string') return { title, task: ctx => exec(_task, onError, ctx), skip };
return { title, task: _task, skip };
}), { concurrent: VERBOSE ? false : (isConcurrent ? getCpuCount() : false) }),
skip,
};
}

// treat tasks that are strings as terminal commands
if (typeof task === 'string') return { title, task: ctx => exec(task, onError, ctx), skip };

// assume the task is a function
// if it's not, listr will blow up anyway, which is fine
return {
title,
task: ctx => new Promise((resolve, reject) => {
try {
resolve(task(ctx));
} catch (e) {
if (typeof onError === 'function') onError(ctx);
if (VERBOSE) console.log(e);
reject(e);
}
}),
skip,
};
});

let renderer = 'default';
if (IS_VERBOSE) renderer = require('./run-task-verbose-formater');

return new Listr(listrTasks, {
concurrent: concurrent ? getCpuCount() : false,
collapse: true,
renderer,
});
const listrTasks = steps.map((step) => {
const {
description: title,
task,
concurrent: isConcurrent,
onError,
} = step;

// if another task has included this one, don't run it again
const skip =
cache.indexOf(step) !== -1
? () => 'Skipping: already run by another task'
: false;
cache.push(step);

// if the task is a set of subtasks, prepare them
if (Array.isArray(task)) {
return {
title,
task: () =>
listrify(
task.map((_task) => {
if (_task.task) return _task;
if (typeof _task === 'string')
return {
title,
task: (ctx) => exec(_task, onError, ctx),
skip,
};
return { title, task: _task, skip };
}),
{
concurrent: VERBOSE
? false
: isConcurrent
? getCpuCount()
: false,
},
),
skip,
};
}

// treat tasks that are strings as terminal commands
if (typeof task === 'string')
return { title, task: (ctx) => exec(task, onError, ctx), skip };

// assume the task is a function
// if it's not, listr will blow up anyway, which is fine
return {
title,
task: (ctx) =>
new Promise((resolve, reject) => {
try {
resolve(task(ctx));
} catch (e) {
if (typeof onError === 'function') onError(ctx);
if (VERBOSE) console.log(e);
reject(e);
}
}),
skip,
};
});

const renderer = IS_VERBOSE ? VerboseRenderer : 'default';

return new Listr(listrTasks, {
concurrent: concurrent ? getCpuCount() : false,
collapse: true,
renderer,
});
}

// resolve the tasks from yargs to actual files
const getTasksFromModule = (taskName) => {
try {
const modulePath = path.resolve(taskSrc, taskName);
if (IS_DEV) {
try {
return require(`${modulePath}.dev`);
} catch (e) { /* do nothing */ }
try {
return require(`${modulePath}/index.dev`);
} catch (e) { /* do nothing */ }
}
return require(modulePath);
} catch (e) {
// we can't find any modules, or something else has gone wrong in resolving it
// so output an erroring task
return {
description: `${chalk.red(taskName)} failed:`,
task: () => Promise.reject(e),
};
}
try {
const modulePath = path.resolve(taskSrc, taskName);
return require(modulePath);
} catch (e) {
// we can't find any modules, or something else has gone wrong in resolving it
// so output an erroring task
return {
description: `${chalk.red(taskName)} failed:`,
task: () => Promise.reject(e),
};
}
};

// get a list of the tasks we're going to run
const taskModules = TASKS.map(getTasksFromModule);

// run them!
listrify(taskModules).run({
// we're adding these to the [listr context](https://github.com/SamVerschueren/listr#context)
messages: [],
stdouts: [],
}).catch((e) => {
// something went wrong, so log whatever we have
if (!e.stderr && !e.stdout) console.log(e);
if (e.stderr) console.error(`\n${e.stderr.trim()}`);
if (e.stdout) console.log(`\n${e.stdout.trim()}`);
return Object.assign(e.context, { error: true });
}).then((ctx) => {
if (IS_STDOUT) ctx.stdouts.forEach(stdout => console.log(stdout.toString().trim()));

if (ctx.messages.length) {
console.log('');
uniq(ctx.messages).forEach(message => console.log(chalk.dim(`${figures.arrowRight} ${message.split('\n').join('\n ')}`)));
}

if (ctx.error) {
console.log('');
// if something's gone wrong, fail hard
process.exit(1);
}
});
listrify(taskModules)
.run({
// we're adding these to the [listr context](https://github.com/SamVerschueren/listr#context)
messages: [],
stdouts: [],
})
.catch((e) => {
// something went wrong, so log whatever we have
if (!e.stderr && !e.stdout) console.log(e);
if (e.stderr) console.error(`\n${e.stderr.trim()}`);
if (e.stdout) console.log(`\n${e.stdout.trim()}`);
return Object.assign(e.context, { error: true });
})
.then((ctx) => {
if (IS_STDOUT)
ctx.stdouts.forEach((stdout) =>
console.log(stdout.toString().trim()),
);

if (ctx.messages.length) {
console.log('');
uniq(ctx.messages).forEach((message) =>
console.log(
chalk.dim(
`${figures.arrowRight} ${message
.split('\n')
.join('\n ')}`,
),
),
);
}

if (ctx.error) {
console.log('');
// if something's gone wrong, fail hard
process.exit(1);
}
});
Loading