#! /usr/bin/env node /** Entrypoint for the task graph extension / decisions for gaia tests. */ var decorate = require('../lib/decorate_task'); var yaml = require('js-yaml'); var fs = require('fs'); var fsPath = require('path'); var taskcluster = require('taskcluster-client'); var util = require('util'); var template = require('json-templater-fix/object'); // Cache for task definitions... var TASK_DEFINITIONS = {}; function loadTask(name, parameters) { if (!TASK_DEFINITIONS[name]) { var path = fsPath.resolve(__dirname + '/../tasks/' + name + '.yml'); TASK_DEFINITIONS[name] = yaml.safeLoad(fs.readFileSync(path)); } return decorate(TASK_DEFINITIONS[name], parameters); } function params() { return Array.prototype.slice.call(arguments).reduce(function(target, props) { for (var key in props) target[key] = props[key]; return target; }, {}); } // List of all the possible definitions... var config = yaml.safeLoad(fs.readFileSync(__dirname + '/../tasks.yml')); // Map of task ids to task names. var taskIdMap = {}; // Final task graph definition place holder... var graph = { tasks: [] }; // XXX: When extending the task graph the scopes and metadata fields are invalid // according to the schema so for local testing we need the --full option so the // task graph can be submitted. if (process.argv[2] == '--full') { graph.scopes = [ 'docker-worker:cache:resources', util.format( 'queue:define-task:%s/%s', config.params.provisionerId, config.params.workerType ), util.format( 'queue:create-task:%s/%s', config.params.provisionerId, config.params.workerType ), ]; graph.metadata = { source: process.env.GITHUB_HEAD_GIT + '/blob/tests/taskcluster/bin/graph', owner: 'jlal@mozilla.com', // TODO: Obviously change this... description: 'Generated task graph for gaia', name: 'Gaia' }; } var nextYear = new Date(Date.now()); nextYear.setFullYear(nextYear.getFullYear() + 1); nextYear = nextYear.toJSON(); var currentTaskId = process.env.TASK_ID; var currentRunId = process.env.RUN_ID; ////////////////////// // Boostrap the graph ////////////////////// taskIdMap['bootstrap'] = taskcluster.slugid(); var packageUrl = 'https://queue.taskcluster.net/v1/task/' + currentTaskId + '/runs/' + currentRunId + '/artifacts/public/package.json'; var bootstrapTask = loadTask('../bootstrap', params(config.params, { process: process, nextYear: nextYear, packageUrl: packageUrl, taskId: taskIdMap['bootstrap'] })); bootstrapTask.taskId = taskIdMap['bootstrap']; graph.tasks.push(bootstrapTask); /////////////////////// // Add dependent tasks /////////////////////// // Expand all tasks based on the `tasks.yml` file... Object.keys(config.definitions).forEach(function(name) { var definition = config.definitions[name]; // If we are not using chunks don't loop. if (!definition || !definition.chunks) { taskIdMap[name] = taskcluster.slugid(); var task = loadTask(name, params(config.params, { // Useful for environment variables. process: process, // For dependencies. nextYear: nextYear })); task.taskId = taskIdMap[name]; task.requires = [taskIdMap['bootstrap']]; graph.tasks.push(task); return; } // Handle all chunking options... for (var chunk = 1; chunk <= definition.chunks; chunk++) { // In practice relying on chunks in the graph is probably a bad idea unless // doing it explicitly with some sort of specific logic but here for // consistency. var taskId = taskIdMap[name + '-' + chunk] = taskcluster.slugid(); var task = loadTask(name, params(config.params, { // Allow process to be used in variables useful for process.env. process: process, nextYear: nextYear, // Chunk parameters chunk: chunk, totalChunks: definition.chunks })); task.requires = [taskIdMap['bootstrap']]; graph.tasks.push(task); } }); graph.tasks = graph.tasks.map(function(task) { return template(task, { tasks: taskIdMap }); }); // Output the entire task graph for extension.... process.stdout.write(JSON.stringify(graph, null, 2));