# -*- coding: utf-8 -*- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. from __future__ import absolute_import, print_function, unicode_literals import pipes import yaml import re import os import slugid def run_decision_task(job, params): arguments = [] if 'triggered-by' in job: arguments.append('--triggered-by={}'.format(job['triggered-by'])) if 'target-tasks-method' in job: arguments.append('--target-tasks-method={}'.format(job['target-tasks-method'])) return [ make_decision_task( params, symbol=job['treeherder-symbol'], arguments=arguments), ] def make_decision_task(params, symbol, arguments=[], head_rev=None): """Generate a basic decision task, based on the root .taskcluster.yml""" with open('.taskcluster.yml') as f: taskcluster_yml = f.read() if not head_rev: head_rev = params['head_rev'] # do a cheap and dirty job of the template substitution that mozilla-taskcluster # does when it reads .taskcluster.yml comment = '"no push -- cron task \'{job_name}\'"'.format(**params), replacements = { '\'{{{?now}}}?\'': "{'relative-datestamp': '0 seconds'}", '{{{?owner}}}?': 'nobody@mozilla.org', '{{#shellquote}}{{{comment}}}{{/shellquote}}': comment, '{{{?source}}}?': params['head_repository'], '{{{?url}}}?': params['head_repository'], '{{{?project}}}?': params['project'], '{{{?level}}}?': params['level'], '{{{?revision}}}?': head_rev, '\'{{#from_now}}([^{]*){{/from_now}}\'': "{'relative-datestamp': '\\1'}", '{{{?pushdate}}}?': '0', # treeherder ignores pushlog_id, so set it to -1 '{{{?pushlog_id}}}?': '-1', # omitted as unnecessary # {{#as_slugid}}..{{/as_slugid}} } for pattern, replacement in replacements.iteritems(): taskcluster_yml = re.sub(pattern, replacement, taskcluster_yml) task = yaml.load(taskcluster_yml)['tasks'][0]['task'] # set some metadata task['metadata']['name'] = 'Decision task for cron job ' + params['job_name'] cron_task_id = os.environ.get('TASK_ID', '') descr_md = 'Created by a [cron task](https://tools.taskcluster.net/task-inspector/#{}/)' task['metadata']['description'] = descr_md.format(cron_task_id) th = task['extra']['treeherder'] th['groupSymbol'] = 'cron' th['symbol'] = symbol # add a scope based on the repository, with a cron: suffix match = re.match(r'https://(hg.mozilla.org)/(.*?)/?$', params['head_repository']) if not match: raise Exception('Unrecognized head_repository') repo_scope = 'assume:repo:{}/{}:cron:{}'.format( match.group(1), match.group(2), params['job_name']) task.setdefault('scopes', []).append(repo_scope) # append arguments, quoted, to the decision task command shellcmd = task['payload']['command'] shellcmd[-1] = shellcmd[-1].rstrip('\n') # strip yaml artifact for arg in arguments: shellcmd[-1] += ' ' + pipes.quote(arg) task_id = slugid.nice() # set taskGroupid = taskId, as expected of decision tasks by other systems. # This creates a new taskGroup for this graph. task['taskGroupId'] = task_id # set the schedulerId based on the level task['schedulerId'] = 'gecko-level-{}'.format(params['level']) return (task_id, task)