Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2@file 

3@brief Parse a file *.yml* and convert it into a set of actions. 

4""" 

5import os 

6import re 

7from ..texthelper.templating import apply_template 

8from ..filehelper import read_content_ufs 

9from .yaml_helper_yaml import yaml_load 

10from .jenkins_helper import get_platform 

11 

12 

13_jenkins_split = "JENKINS_SPLIT" 

14 

15 

16def pickname(*args): 

17 """ 

18 Picks the first string non null in the list. 

19 

20 @param l list of string 

21 @return string 

22 """ 

23 for s in args: 

24 s = s.strip() 

25 if s: 

26 return s 

27 raise ValueError( # pragma: no cover 

28 "Unable to find a non empty string in {0}".format(args)) 

29 

30 

31def load_yaml(file_or_buffer, context=None, engine="jinja2", platform=None): 

32 """ 

33 Loads a :epkg:`yml` file (*.yml*). 

34 

35 @param file_or_buffer string or physical file or url 

36 @param context variables to replace in the configuration 

37 @param engine see @see fn apply_template 

38 @param platform to join path differently based on the OS 

39 @return see `PyYAML <http://pyyaml.org/wiki/PyYAMLDocumentation>`_ 

40 """ 

41 def replace(val, rep, into): 

42 if val is None: 

43 return val 

44 return val.replace(rep, into) 

45 content, source = read_content_ufs(file_or_buffer, add_source=True) 

46 

47 def ospathjoinp(*args, **kwargs): 

48 p = kwargs.get('platform', platform) 

49 return ospathjoin(*args, platform=p) 

50 

51 if context is None: 

52 context = dict(replace=replace, ospathjoin=ospathjoinp, 

53 pickname=pickname) 

54 else: 

55 fs = [("replace", replace), ("ospathjoin", ospathjoinp), 

56 ("pickname", pickname)] 

57 if any(_[0] not in context for _ in fs): 

58 context = context.copy() 

59 for k, f in fs: 

60 if k not in context: 

61 context[k] = f 

62 if not isinstance(context, dict): 

63 raise TypeError( # pragma: no cover 

64 "context must be a dictionary not {}.".format(type(context))) 

65 if "project_name" not in context: 

66 project_name = infer_project_name(file_or_buffer, source) 

67 else: 

68 project_name = context["project_name"] 

69 if project_name.endswith("__"): 

70 raise ValueError( # pragma: no cover 

71 "project_name is wrong, it cannot end by '__': '{0}'" 

72 "".format(project_name)) 

73 if "project_name" not in context and project_name is not None: 

74 context["project_name"] = project_name 

75 

76 if ("root_path" not in context or 

77 not context["root_path"].endswith(project_name)): 

78 context = context.copy() 

79 context["root_path"] = ospathjoin( 

80 context.get("root_path", ""), project_name, platform=platform) 

81 

82 if "root_path" in context: 

83 if platform is None: 

84 platform = get_platform(platform) 

85 if platform.startswith("win"): 

86 addition = "set current={0}\\%NAME_JENKINS%".format( 

87 context["root_path"]) 

88 else: 

89 addition = "export current={0}/$NAME_JENKINS".format( 

90 context["root_path"]) 

91 content = "automatedsetup:\n - {0}\n{1}".format(addition, content) 

92 

93 content = apply_template(content, context, engine) 

94 try: 

95 return yaml_load(content), project_name 

96 except Exception as e: # pragma: no cover 

97 raise SyntaxError( 

98 "Unable to parse content\n{0}".format(content)) from e 

99 

100 

101def evaluate_condition(cond, variables=None): 

102 """ 

103 Evaluates a condition inserted in a :epkg:`yml` file. 

104 

105 @param cond (str) condition 

106 @param variables (dict|None) dictionary 

107 @return boolean 

108 

109 Example of a condition:: 

110 

111 [ ${PYTHON} == "C:\\Python370_x64" ] 

112 """ 

113 if variables is not None: 

114 for k, v in variables.items(): 

115 rep = "${%s}" % k 

116 vv = '"%s"' % v 

117 cond = cond.replace(rep, vv) 

118 cond = cond.replace(rep.upper(), vv) 

119 cond = cond.strip() 

120 if cond.startswith("[") and cond.endswith("]"): 

121 e = eval(cond) 

122 return all(e) 

123 try: 

124 return eval(cond) 

125 except SyntaxError as e: 

126 raise SyntaxError( 

127 "Unable to interpret '{0}'\nvariables: {1}".format(cond, variables)) from e 

128 

129 

130def interpret_instruction(inst, variables=None): 

131 """ 

132 Interprets an instruction with if statement. 

133 

134 @param inst (str) instruction 

135 @param variables (dict|None) 

136 @return (str|None) 

137 

138 Example of a statement:: 

139 

140 - if [ ${PYTHON} == "C:\\\\Python391_x64" ] then python setup.py build_sphinx fi 

141 

142 Another example:: 

143 

144 - if [ ${VERSION} == "3.9" and ${DIST} == "std" ] 

145 then 

146 --CMD=$PYINT -u scikit-learn/bench_plot_polynomial_features_partial_fit.py;; 

147 --NAME=SKL_POLYF_PF;; 

148 fi 

149 

150 In this second syntax, lines must end with ``;;``. 

151 If an instruction cannot be interpreted, it is left 

152 left unchanged as the function assumes it can only be solved 

153 in a bash script. 

154 

155 Switch to ``;;`` instead of ``;`` as a instruction separator 

156 for conditional instructions. 

157 """ 

158 if isinstance(inst, list): 

159 res = [interpret_instruction(_, variables) for _ in inst] 

160 if any(res): 

161 return [_ for _ in res if _ is not None] 

162 return None 

163 if isinstance(inst, tuple): 

164 if len(inst) != 2 or inst[1] is None: 

165 raise ValueError( # pragma: no cover 

166 "Unable to interpret '{}'.".format(inst)) 

167 return (inst[0], interpret_instruction(inst[1], variables)) 

168 if isinstance(inst, dict): 

169 return inst 

170 if isinstance(inst, (int, float)): 

171 return inst 

172 

173 inst = inst.replace("\n", " ") 

174 exp = re.compile("^ *if +(.*) +then +(.*)( +else +(.*))? +fi *$") 

175 find = exp.search(inst) 

176 if find: 

177 gr = find.groups() 

178 try: 

179 e = evaluate_condition(gr[0], variables) 

180 except SyntaxError: 

181 # We assume the condition is a linux condition. 

182 return inst 

183 g = gr[1] if e else gr[3] 

184 return None if g is None else interpret_instruction(g, variables) 

185 

186 if inst.startswith('--'): 

187 # one format like --CMD=...; --NAME==...; 

188 exp = re.compile("--([a-zA-Z]+?)=(.+?);;") 

189 find = exp.findall(inst) 

190 if find: 

191 inst = {k.strip(): v.strip() for k, v in find} 

192 inst = {k: (None if not v or len(v) == 0 else v) 

193 for k, v in inst.items()} 

194 return inst 

195 return inst 

196 return inst 

197 

198 

199def enumerate_convert_yaml_into_instructions(obj, variables=None, add_environ=True): 

200 """ 

201 Converts a :epkg:`yml` file into sequences of instructions, 

202 conditions are interpreted. 

203 

204 @param obj yaml objects (@see fn load_yaml) 

205 @param variables additional variables to be used 

206 @param add_environ add environment variables available, does not 

207 overwrite existing variables 

208 when the job is generated 

209 @return list of tuple(instructions, variables) 

210 

211 The function expects the following list 

212 of steps in this order: 

213 

214 * *automatedsetup*: added by this module 

215 * *language*: should be python 

216 * *python*: list of interpreters (multiplies jobs) 

217 * *virtualenv*: name of the virtual environment 

218 * *install*: list of installation steps in the virtual environment 

219 * *before_script*: list of steps to run 

220 * *script*: list of script to run (multiplies jobs) 

221 * *after_script*: list of steps to run 

222 * *documentation*: documentation to run after the 

223 

224 Each step *multiplies jobs* creates a sequence of jobs and a :epkg:`Jenkins` job. 

225 """ 

226 if variables is None: 

227 def_variables = {} 

228 else: 

229 def_variables = variables.copy() 

230 if 'Python37' in def_variables and 'Python38' not in def_variables: 

231 raise RuntimeError( # pragma: no cover 

232 "Key 'Python38' is missing in {}.".format(def_variables)) 

233 if add_environ: 

234 for k, v in os.environ.items(): 

235 if k not in def_variables: 

236 def_variables[k] = v 

237 sequences = [] 

238 count = {} 

239 steps = ["automatedsetup", "language", "python", "virtualenv", "install", 

240 "before_script", "script", "after_script", 

241 "documentation"] 

242 for key in steps: 

243 value = obj.get(key, None) 

244 if key == "language": 

245 if value != "python": 

246 raise NotImplementedError( # pragma: no cover 

247 "language must be python") 

248 continue # pragma: no cover 

249 if value is not None: 

250 if key in {'python', 'script'} and not isinstance(value, list): 

251 value = [value] 

252 count[key] = len(value) 

253 sequences.append((key, value)) 

254 

255 for k in obj: 

256 if k not in steps: 

257 raise ValueError( 

258 "Unexpected key '{0}' found in yaml file. Expect:\n{1}".format(k, "\n".join(steps))) 

259 

260 # multiplications 

261 i_python = 0 

262 i_script = 0 

263 notstop = True 

264 while notstop: 

265 seq = [] 

266 add = True 

267 variables = def_variables.copy() 

268 for key, value in sequences: 

269 if key == "python": 

270 value = value[i_python] 

271 if isinstance(value, dict): 

272 if 'PATH' not in value: 

273 raise KeyError( # pragma: no cover 

274 "The dictionary should include key 'path': {0}" 

275 "".format(value)) 

276 for k, v in sorted(value.items()): 

277 if k != 'PATH': 

278 variables[k] = v 

279 seq.append(('INFO', (k, v))) 

280 value = value["PATH"] 

281 elif key == "script": 

282 value = interpret_instruction(value[i_script], variables) 

283 if isinstance(value, dict): 

284 for k, v in sorted(value.items()): 

285 if k not in ('CMD', 'CMDPY'): 

286 seq.append(('INFO', (k, v))) 

287 variables[k] = v 

288 

289 i_script += 1 

290 if i_script >= count['script']: 

291 i_script = 0 

292 i_python += 1 

293 if i_python >= count['python']: 

294 notstop = False 

295 if value is not None and value != 'None': 

296 seq.append((key, value)) 

297 variables[key] = value 

298 else: 

299 add = False 

300 if add: 

301 r = interpret_instruction(seq, variables) 

302 if r is not None: 

303 yield r, variables 

304 

305 

306def ospathjoin(*args, **kwargs): 

307 """ 

308 Simple ``o.path.join`` for a specific platform. 

309 

310 @param args list of paths 

311 @param kwargs additional parameters, among them, 

312 *platform* (win32 or ...) 

313 @return path 

314 """ 

315 def build_value(*args, **kwargs): 

316 platform = kwargs.get('platform', None) 

317 if platform is None: 

318 return os.path.join(*args) 

319 elif platform.startswith("win"): 

320 return "\\".join(args) 

321 return "/".join(args) 

322 

323 value = build_value(*args, **kwargs) 

324 if value == "/$PYINT": 

325 raise RuntimeError( # pragma: no cover 

326 "Impossible values {} - {}.".format(args, kwargs)) 

327 return value 

328 

329 

330def ospathdirname(lp, platform=None): 

331 """ 

332 Simple ``o.path.dirname`` for a specific platform. 

333 

334 @param lp path 

335 @param platform platform 

336 @return path 

337 """ 

338 if platform is None: 

339 return os.path.dirname(lp) 

340 elif platform.startswith("win"): 

341 return "\\".join(lp.replace("/", "\\").split("\\")[:-1]) 

342 return "/".join(lp.replace("\\", "/").split("/")[:-1]) 

343 

344 

345def convert_sequence_into_batch_file(seq, variables=None, platform=None): 

346 """ 

347 Converts a sequence of instructions into a batch file. 

348 

349 @param seq sequence of instructions 

350 @param variables list of variables 

351 @param platform ``get_platform(platform)`` if None 

352 @return (str) batch file or a list of batch file if the constant ``JENKINS_SPLIT`` 

353 was found in section install (this tweak is needed when the job has to be split 

354 for :epkg:`Jenkins`. 

355 """ 

356 global _jenkins_split 

357 if platform is None: 

358 platform = get_platform(platform) 

359 

360 iswin = platform.startswith("win") 

361 

362 if iswin: 

363 error_level = "if %errorlevel% neq 0 exit /b %errorlevel%" 

364 else: 

365 error_level = "if [ $? -ne 0 ]; then exit $?; fi" 

366 

367 interpreter = None 

368 venv_interpreter = None 

369 root_project = None 

370 anaconda = False 

371 conda = None 

372 echo = "@echo" if iswin else "echo" 

373 

374 rowsset = [] 

375 if iswin: 

376 rowsset.append("@echo off") 

377 rowsset.append("set PATH0=%PATH%") 

378 

379 def add_path_win(rows, interpreter, platform, root_project): 

380 path_inter = ospathdirname(interpreter, platform) 

381 if len(path_inter) == 0: 

382 raise ValueError( # pragma: no cover 

383 "Unable to guess interpreter path from '{0}', platform={1}" 

384 "".format(interpreter, platform)) 

385 if iswin: 

386 rows.append("set PATH={0};%PATH%".format(path_inter)) 

387 else: 

388 rows.append("export PATH={0}:$PATH".format(path_inter)) 

389 if root_project is not None: 

390 if iswin: 

391 rows.append("set ROOTPROJECT={0}".format(root_project)) 

392 else: 

393 rows.append("export ROOTPROJECT={0}".format(root_project)) 

394 

395 rows = [] 

396 splits = [rows] 

397 typstr = str 

398 

399 for key, value in seq: 

400 if key == "automatedsetup": 

401 rows.append("") 

402 rows.append(echo + " AUTOMATEDSETUP") 

403 rows.append("\n".join(value)) 

404 rows.append("") 

405 elif key == "python": 

406 variables["YMLPYTHON"] = value 

407 if variables.get('DIST', None) == "conda": 

408 rows.append(echo + " conda") 

409 anaconda = True 

410 interpreter = ospathjoin( 

411 value, "python", platform=platform) 

412 venv_interpreter = value 

413 if platform.startswith("win"): 

414 conda = ospathjoin( 

415 value, "Scripts", "conda", platform=platform) 

416 else: 

417 conda = ospathjoin( 

418 value, "bin", "conda", platform=platform) 

419 else: 

420 if iswin: 

421 interpreter = ospathjoin( 

422 value, "python", platform=platform) 

423 else: 

424 interpreter = ospathjoin( 

425 value, "$PYINT", platform=platform) 

426 venv_interpreter = value 

427 rows.append(echo + " interpreter=" + interpreter) 

428 

429 elif key == "virtualenv": 

430 if isinstance(value, list): 

431 if len(value) > 2: 

432 raise ValueError( # pragma: no cover 

433 "Expecting one or two values for the path of the virtual environment" 

434 ":\n{0}".format(value)) 

435 d = value[0].copy() 

436 for i in range(1, len(value)): 

437 d.update(value[i]) 

438 value = d 

439 p = value["path"] if isinstance(value, dict) else value 

440 rows.append("") 

441 rows.append(echo + " CREATE VIRTUAL ENVIRONMENT in %s" % p) 

442 if not anaconda: 

443 if iswin: 

444 rows.append('if not exist "{0}" mkdir "{0}"'.format(p)) 

445 else: 

446 rows.append('if [-f {0}]; then mkdir "{0}"; fi'.format(p)) 

447 if anaconda: 

448 pinter = ospathdirname(interpreter, platform=platform) 

449 rows.append( 

450 '"{0}" create -y -v -p "{1}" --clone "{2}" --offline --no-update-deps'.format(conda, p, pinter)) 

451 interpreter = ospathjoin( 

452 p, "python", platform=platform) 

453 else: 

454 if iswin: 

455 rows.append("set KEEPPATH=%PATH%") 

456 rows.append("set PATH={0};%PATH%".format(venv_interpreter)) 

457 else: 

458 rows.append("export KEEPPATH=$PATH") 

459 rows.append( 

460 "export PATH={0}:$PATH".format(venv_interpreter)) 

461 pat = '"{0}" -m virtualenv {1}' 

462 if isinstance(value, dict): 

463 system_site_packages = value.get( 

464 'system_site_packages', True) 

465 else: 

466 system_site_packages = True 

467 if system_site_packages: 

468 pat += " --system-site-packages" 

469 rows.append(pat.format(interpreter, p)) 

470 if iswin: 

471 rows.append("set PATH=%KEEPPATH%") 

472 interpreter = ospathjoin( 

473 p, "Scripts", "python", platform=platform) 

474 else: 

475 rows.append("export PATH=$KEEPPATH") 

476 interpreter = ospathjoin( 

477 p, "bin", "python", platform=platform) 

478 rows.append(error_level) 

479 

480 elif key in {"install", "before_script", "script", "after_script", "documentation"}: 

481 if value is not None: 

482 if isinstance(value, dict): 

483 if "CMD" not in value and "CMDPY" not in value: 

484 raise KeyError( # pragma: no cover 

485 "A script defined by a dictionary must contain key " 

486 "'{0}' or '{1}' in \n{2}".format("CMD", 'CMDPY', value)) 

487 if "NAME" in value: 

488 if iswin: 

489 rows.append("set JOB_NAME=%s" % value["NAME"]) 

490 else: 

491 rows.append("export JOB_NAME=%s" % value["NAME"]) 

492 if "CMD" in value: 

493 value = value["CMD"] 

494 else: 

495 value = evaluate_condition( 

496 value["CMDPY"], variables=variables) 

497 elif isinstance(value, list): 

498 starter = list(rows) 

499 elif isinstance(value, typstr): 

500 pass 

501 else: 

502 raise TypeError( # pragma: no cover 

503 "value must of type list, dict, not '{0}'\n{1}" 

504 "".format(type(value), value)) 

505 

506 rows.append("") 

507 rows.append(echo + " " + key.upper()) 

508 add_path_win(rows, interpreter, platform, root_project) 

509 if not isinstance(value, list): 

510 value = [value, error_level] 

511 else: 

512 keep = value 

513 value = [] 

514 for v in keep: 

515 if v.startswith(_jenkins_split): 

516 if "-" in v: 

517 nbrem = v.split("-")[-1] 

518 try: 

519 nbrem = int(nbrem) 

520 except ValueError: # pragma: no cover 

521 raise ValueError( 

522 "Unable to interpret '{0}'".format(v)) 

523 else: 

524 nbrem = 0 

525 rows.extend(value) 

526 value = [] 

527 st = list(starter) 

528 if nbrem > 0: 

529 st = st[:-nbrem] 

530 splits.append(st) 

531 rows = splits[-1] 

532 add_path_win(rows, interpreter, 

533 platform, root_project) 

534 else: 

535 value.append(v) 

536 value.append(error_level) 

537 rows.extend(value) 

538 elif key == 'INFO': 

539 vs = '"{0}"'.format(value[1]) if isinstance( 

540 value[1], str) and " " in value[1] else value[1] 

541 if iswin: 

542 rowsset.append("SET {0}={1}".format(value[0], vs)) 

543 else: 

544 rowsset.append("export {0}={1}".format(value[0], vs)) 

545 else: 

546 raise ValueError( # pragma: no cover 

547 "unexpected key '{0}'".format(key)) 

548 

549 splits = [rowsset + _ for _ in splits] 

550 allres = [] 

551 for rows in splits: 

552 try: 

553 res = "\n".join(rows) 

554 except TypeError as e: # pragma: no cover 

555 raise TypeError("Unexpected type\n{0}".format( 

556 "\n".join([str((type(_), _)) for _ in rows]))) from e 

557 if _jenkins_split in res: 

558 raise ValueError( # pragma: no cover 

559 "Constant '{0}' is present in the generated script. " 

560 "It can only be added to the install section." 

561 "".format(_jenkins_split)) 

562 allres.append(res) 

563 return allres if len(allres) > 1 else allres[0] 

564 

565 

566def infer_project_name(file_or_buffer, source): 

567 """ 

568 Infers a project name based on :epkg:`yml` file. 

569 

570 @param file_or_buffer file name 

571 @param source second output of @see fn read_content_ufs 

572 @return name 

573 

574 The function can infer a name for *source* in ``{'r', 'u'}``. 

575 For *source* equal to ``'s'``, it returns ``'unknown_string'``. 

576 """ 

577 if source == "r": 

578 fold = os.path.dirname(file_or_buffer) 

579 last = os.path.split(fold)[-1] 

580 elif source == "u": 

581 spl = file_or_buffer.split('/') 

582 pos = -2 

583 name = None 

584 while len(spl) > -pos: 

585 name = spl[pos] 

586 if name in {'master'}: 

587 pos -= 1 

588 elif 'github' in name: 

589 break 

590 else: 

591 break 

592 if name is None: 

593 raise ValueError( # pragma: no cover 

594 "Unable to infer project name for '{0}'".format( 

595 file_or_buffer)) 

596 return name 

597 elif source == "s": 

598 return "unknown_string" 

599 else: 

600 raise ValueError( # pragma: no cover 

601 "Unexpected value for add_source: '{0}' for '{1}'".format( 

602 source, file_or_buffer)) 

603 return last 

604 

605 

606def enumerate_processed_yml(file_or_buffer, context=None, engine="jinja2", platform=None, 

607 server=None, git_repo=None, add_environ=True, overwrite=False, 

608 build_location=None, **kwargs): 

609 """ 

610 Submits or enumerates jobs based on the content of a :epkg:`yml` file. 

611 

612 @param file_or_buffer filename or string 

613 @param context variables to replace in the configuration 

614 @param engine see @see fn apply_template 

615 @param server see @see cl JenkinsExt 

616 @param platform plaform where the job will be executed 

617 @param git_repo git repository (if *server* is not None) 

618 @param add_environ add environment variable before interpreting the job 

619 @param overwrite overwrite the job if it already exists in Jenkins 

620 @param build_location location for the build 

621 @param kwargs see @see me create_job_template 

622 @return enumerator for *(job, name, variables)* 

623 

624 Example of a :epkg:`yml` file 

625 `.local.jenkins.win.yml <https://github.com/sdpython/pyquickhelper/blob/master/.local.jenkins.win.yml>`_. 

626 A subfolder was added to the project location. 

627 A scheduler can be defined as well by adding ``SCHEDULER:'* * * * *'``. 

628 """ 

629 typstr = str 

630 fLOG = kwargs.get('fLOG', None) 

631 project_name = None if context is None else context.get( 

632 "project_name", None) 

633 obj, project_name = load_yaml( 

634 file_or_buffer, context=context, platform=platform) 

635 platform_set = platform or get_platform(platform) 

636 for seq, var in enumerate_convert_yaml_into_instructions(obj, variables=context, add_environ=add_environ): 

637 conv = convert_sequence_into_batch_file( 

638 seq, variables=var, platform=platform) 

639 

640 # we extract a suffix from the command line 

641 if server is not None: 

642 name = "_".join([project_name, var.get('NAME', ''), 

643 typstr(var.get("VERSION", '')).replace(".", ""), 

644 var.get('DIST', '')]) 

645 

646 if platform_set.startswith("win"): 

647 if isinstance(conv, list): 

648 conv = ["SET NAME_JENKINS=" + 

649 name + "\n" + _ for _ in conv] 

650 else: 

651 conv = "SET NAME_JENKINS=" + name + "\n" + conv 

652 else: 

653 if isinstance(conv, list): 

654 conv = ["export NAME_JENKINS=" + 

655 name + "\n" + _ for _ in conv] 

656 conv.append("export $(cat ~/.profile)") 

657 else: 

658 conv = ("export NAME_JENKINS=" + name + 

659 "\nexport $(cat ~/.profile)\n" + conv) 

660 

661 import jenkins 

662 try: 

663 j = server.get_job_config(name) if not server._mock else None 

664 except jenkins.NotFoundException: # pragma: no cover 

665 j = None 

666 except jenkins.JenkinsException as e: # pragma: no cover 

667 from .jenkins_exceptions import JenkinsExtException 

668 raise JenkinsExtException( 

669 "Unable to retrieve job config for name='{0}'.".format(name)) from e 

670 

671 update_job = False 

672 if j is not None: 

673 if kwargs.get('update', True): 

674 update_job = True 

675 else: 

676 if fLOG is not None: # pragma: no cover 

677 fLOG("[jenkins] delete job", name) 

678 server.delete_job(name) 

679 

680 if git_repo is not None and project_name not in git_repo: 

681 git_repo += project_name 

682 

683 # set up location 

684 if build_location is None: 

685 loc = None 

686 else: 

687 loc = ospathjoin(build_location, project_name, 

688 name, platform=platform) 

689 

690 if overwrite or j is None: 

691 timeout = var.get("TIMEOUT", None) 

692 scheduler = var.get("SCHEDULER", None) 

693 clean_repo = var.get("CLEAN", True) in { 

694 True, 1, "True", "true", "1"} 

695 if timeout is not None: 

696 kwargs["timeout"] = timeout 

697 if scheduler is not None: 

698 if "FIXED" in scheduler: 

699 scheduler = scheduler.replace("FIXED", "").strip() 

700 adjuster_scheduler = False 

701 elif "STARTUP" in scheduler: 

702 adjuster_scheduler = False 

703 elif 'fixed' in scheduler.lower(): 

704 raise ValueError( # pragma: no cover 

705 "Scheduler should contain 'FIXED' in upper case.") 

706 elif 'startup' in scheduler.lower(): 

707 raise ValueError( # pragma: no cover 

708 "Scheduler should contain 'STARTUP' in upper case.") 

709 else: 

710 adjuster_scheduler = True 

711 kwargs["scheduler"] = scheduler 

712 kwargs["adjuster_scheduler"] = adjuster_scheduler 

713 yield server.create_job_template(name, script=conv, git_repo=git_repo, 

714 update=update_job, location=loc, 

715 clean_repo=clean_repo, **kwargs), name, var 

716 else: 

717 yield conv, None, var