artefacts-cli 0.8.0__py3-none-any.whl → 0.9.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
artefacts/cli/app.py CHANGED
@@ -1,13 +1,12 @@
1
- import configparser
2
1
  import getpass
3
2
  import json
4
3
  import os
5
4
  import platform
6
5
  import random
7
- import subprocess
8
6
  import tarfile
9
7
  import tempfile
10
8
  import time
9
+ from typing import Optional
11
10
  from urllib.parse import urlparse
12
11
  import webbrowser
13
12
 
@@ -17,94 +16,29 @@ import requests
17
16
  from pathlib import Path
18
17
  from gitignore_parser import parse_gitignore
19
18
 
20
- from artefacts.cli import init_job, generate_scenarios, AuthenticationError, __version__
19
+ from artefacts.cli import (
20
+ init_job,
21
+ generate_scenarios,
22
+ localise,
23
+ AuthenticationError,
24
+ __version__,
25
+ )
21
26
  from artefacts.cli import app_containers as containers
22
- from artefacts.cli.constants import DEPRECATED_FRAMEWORKS, SUPPORTED_FRAMEWORKS
27
+ from artefacts.cli.config import APIConf
28
+ from artefacts.cli.constants import (
29
+ DEPRECATED_FRAMEWORKS,
30
+ SUPPORTED_FRAMEWORKS,
31
+ CONFIG_PATH,
32
+ )
33
+ from artefacts.cli.helpers import (
34
+ add_key_to_conf,
35
+ get_conf_from_file,
36
+ get_artefacts_api_url,
37
+ get_git_revision_branch,
38
+ get_git_revision_hash,
39
+ )
23
40
  from artefacts.cli.utils import add_output_from_default, config_validation, read_config
24
41
 
25
- HOME = os.path.expanduser("~")
26
- CONFIG_DIR = f"{HOME}/.artefacts"
27
- CONFIG_PATH = f"{CONFIG_DIR}/config"
28
-
29
-
30
- def get_git_revision_hash() -> str:
31
- try:
32
- return (
33
- subprocess.check_output(["git", "rev-parse", "HEAD"])
34
- .decode("ascii")
35
- .strip()
36
- )
37
- except subprocess.CalledProcessError:
38
- return ""
39
-
40
-
41
- def get_git_revision_branch() -> str:
42
- try:
43
- return (
44
- subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])
45
- .decode("ascii")
46
- .strip()
47
- )
48
- except subprocess.CalledProcessError:
49
- return ""
50
-
51
-
52
- def get_conf_from_file():
53
- config = configparser.ConfigParser()
54
- if not os.path.isfile(CONFIG_PATH):
55
- os.makedirs(CONFIG_DIR, exist_ok=True)
56
- config["DEFAULT"] = {}
57
- with open(CONFIG_PATH, "w") as f:
58
- config.write(f)
59
- config.read(CONFIG_PATH)
60
- return config
61
-
62
-
63
- def get_artefacts_api_url(project_profile):
64
- return os.environ.get(
65
- "ARTEFACTS_API_URL",
66
- project_profile.get(
67
- "ApiUrl",
68
- "https://app.artefacts.com/api",
69
- ),
70
- )
71
-
72
-
73
- class APIConf:
74
- def __init__(self, project_name: str, job_name: str = None) -> None:
75
- config = get_conf_from_file()
76
- if project_name in config:
77
- profile = config[project_name]
78
- else:
79
- profile = {}
80
- self.api_url = get_artefacts_api_url(profile)
81
- self.api_key = os.environ.get("ARTEFACTS_KEY", profile.get("ApiKey", None))
82
- if self.api_key is None:
83
- batch_id = os.environ.get("AWS_BATCH_JOB_ID", None)
84
- job_id = os.environ.get("ARTEFACTS_JOB_ID", None)
85
- if batch_id is None or job_id is None:
86
- raise click.ClickException(
87
- f"No API KEY set. Please run 'artefacts config add {project_name}'"
88
- )
89
- auth_type = "Internal"
90
- # Batch id for array jobs contains array index
91
- batch_id = batch_id.split(":")[0]
92
- self.headers = {"Authorization": f"{auth_type} {job_id}:{batch_id}"}
93
- else:
94
- auth_type = "ApiKey"
95
- self.headers = {"Authorization": f"{auth_type} {self.api_key}"}
96
- self.headers["User-Agent"] = (
97
- f"ArtefactsClient/{__version__} ({platform.platform()}/{platform.python_version()})"
98
- )
99
- if job_name:
100
- click.echo(f"[{job_name}] Connecting to {self.api_url} using {auth_type}")
101
- else:
102
- click.echo(f"Connecting to {self.api_url} using {auth_type}")
103
-
104
-
105
- def validate_artefacts_config(config_file: str) -> dict:
106
- pass
107
-
108
42
 
109
43
  @click.group()
110
44
  def config():
@@ -119,15 +53,8 @@ def path():
119
53
  click.echo(CONFIG_PATH)
120
54
 
121
55
 
122
- def add_key_to_conf(project_name, api_key):
123
- config = get_conf_from_file()
124
- config[project_name] = {"ApiKey": api_key}
125
- with open(CONFIG_PATH, "w") as f:
126
- config.write(f)
127
-
128
-
129
56
  @config.command()
130
- @click.argument("project_name")
57
+ @click.argument("project_name", metavar=localise("PROJECT_NAME"))
131
58
  def add(project_name):
132
59
  """
133
60
  Set configuration for PROJECT_NAME
@@ -145,16 +72,26 @@ def add(project_name):
145
72
  os.system(f'cmd.exe /C start "" {settings_page_url} 2>/dev/null')
146
73
  else:
147
74
  webbrowser.open(settings_page_url)
148
- click.echo(f"Opening the project settings page: {settings_page_url}")
75
+ click.echo(
76
+ localise("Opening the project settings page: {url}").format(
77
+ url=settings_page_url
78
+ )
79
+ )
149
80
  api_key = click.prompt(
150
- f"Please enter your API KEY for {project_name}", type=str, hide_input=True
81
+ localise("Please enter your API KEY for {project}").format(
82
+ project=project_name
83
+ ),
84
+ type=str,
85
+ hide_input=True,
151
86
  )
152
87
  add_key_to_conf(project_name, api_key)
153
- click.echo(f"API KEY saved for {project_name}")
88
+ click.echo(localise("API KEY saved for {project}").format(project=project_name))
154
89
  if click.confirm(
155
- "Would you like to download the generated artefacts.yaml file? This will overwrite any existing config file in the current directory."
90
+ localise(
91
+ "Would you like to download the generated artefacts.yaml file? This will overwrite any existing config file in the current directory."
92
+ )
156
93
  ):
157
- api_conf = APIConf(project_name)
94
+ api_conf = APIConf(project_name, __version__)
158
95
  config_file_name = "artefacts.yaml"
159
96
  config_file_url = f"{api_url}/{project_name}/{config_file_name}"
160
97
  r = requests.get(config_file_url, headers=api_conf.headers)
@@ -164,7 +101,7 @@ def add(project_name):
164
101
 
165
102
 
166
103
  @config.command()
167
- @click.argument("project_name")
104
+ @click.argument("project_name", metavar=localise("PROJECT_NAME"))
168
105
  def delete(project_name):
169
106
  """
170
107
  Delete configuration for PROJECT_NAME
@@ -173,14 +110,14 @@ def delete(project_name):
173
110
  config.remove_section(project_name)
174
111
  with open(CONFIG_PATH, "w") as f:
175
112
  config.write(f)
176
- click.echo(f"{project_name} config removed")
113
+ click.echo(localise("{project} config removed").format(project=project_name))
177
114
 
178
115
 
179
116
  @click.command()
180
- @click.argument("project_name")
117
+ @click.argument("project_name", metavar=localise("PROJECT_NAME"))
181
118
  def hello(project_name):
182
119
  """Show message to confirm credentials allow access to PROJECT_NAME"""
183
- api_conf = APIConf(project_name)
120
+ api_conf = APIConf(project_name, __version__)
184
121
  response = requests.get(
185
122
  f"{api_conf.api_url}/{project_name}/info",
186
123
  headers=api_conf.headers,
@@ -192,81 +129,106 @@ def hello(project_name):
192
129
  )
193
130
  else:
194
131
  result = response.json()
195
- raise click.ClickException(f"Error getting project info: {result['message']}")
132
+ raise click.ClickException(
133
+ localise("Error getting project info: {message}").format(
134
+ message=result["message"]
135
+ )
136
+ )
196
137
 
197
138
 
198
- @click.command()
139
+ @click.command(
140
+ context_settings=dict(
141
+ ignore_unknown_options=True,
142
+ )
143
+ )
144
+ @click.argument("jobname", metavar=localise("JOBNAME"))
199
145
  @click.option(
200
146
  "--config",
201
147
  callback=config_validation,
202
148
  default="artefacts.yaml",
203
- help="Artefacts config file.",
149
+ help=localise("Artefacts configuration file."),
204
150
  )
205
151
  @click.option(
206
152
  "--dryrun",
207
153
  is_flag=True,
208
154
  default=False,
209
- help="Dryrun: no tracking or test execution",
155
+ help=localise("Run with no tracking nor test execution"),
210
156
  )
211
157
  @click.option(
212
158
  "--nosim",
213
159
  is_flag=True,
214
160
  default=False,
215
- help="nosim: no simulator resource provided by Artefacts",
161
+ help=localise("Skip configuring a simulator resource provided by Artefacts"),
216
162
  )
217
163
  @click.option(
218
164
  "--noupload",
219
165
  is_flag=True,
220
166
  default=False,
221
- help="noupload: rosbags are not uploaded to cloud",
167
+ help=localise(
168
+ "Do not upload to Artefacts files generated during a run (e.g. rosbags)"
169
+ ),
222
170
  )
223
171
  @click.option(
224
172
  "--noisolation",
225
173
  is_flag=True,
226
174
  default=False,
227
- help="noisolation: for debugging, break the 'middleware network' isolation between the test suite and the host (in ROS1: --reuse-master flag / in ROS2: --disable-isolation flag)",
175
+ help=localise(
176
+ "Break the 'middleware network' isolation between the test suite and the host (in ROS1: --reuse-master flag; in ROS2: --disable-isolation flag). Primarily for debugging"
177
+ ),
228
178
  )
229
179
  @click.option(
230
180
  "--description",
231
181
  default=None,
232
- help="Optional description for this run",
182
+ help=localise("Optional description for this run"),
233
183
  )
234
184
  @click.option(
235
185
  "--skip-validation",
236
186
  is_flag=True,
237
187
  default=False,
238
188
  is_eager=True, # Necessary for callbacks to see it.
239
- help="Skip configuration validation, so that unsupported settings can be tried out, e.g. non-ROS settings or simulators like SAPIEN.",
189
+ help=localise(
190
+ "Skip configuration validation, so that unsupported settings can be tried out, e.g. non-ROS settings or simulators like SAPIEN."
191
+ ),
240
192
  )
241
193
  @click.option(
242
194
  "--in-container",
243
195
  is_flag=True,
244
196
  default=False,
245
- help='[Experimental] Run the job inside a package container. The container image is build if it does not exist yet, with default name as "artefacts" (please use --with-image to override the image name). This option overrides (for now) --dryrun, --nosim, --noisolation and --description.',
197
+ help=localise(
198
+ '[Experimental] Run the job inside a package container. The container image is build if it does not exist yet, with default name as "artefacts" (please use --with-image to override the image name). This option overrides (for now) --dryrun, --nosim, --noisolation and --description.'
199
+ ),
246
200
  )
247
201
  @click.option(
248
202
  "--dockerfile",
249
203
  default="Dockerfile",
250
- help="[Experimental] Path to a custom Dockerfile. Defaults to Dockerfile in the run directory. This flag is only used together with `--in-container`",
204
+ help=localise(
205
+ "[Experimental] Path to a custom Dockerfile. Defaults to Dockerfile in the run directory. This flag is only used together with `--in-container`"
206
+ ),
251
207
  )
252
208
  @click.option(
253
209
  "--with-image",
254
210
  default=None,
255
- help="[Deprecated and unused from 0.8.0; Image names are now internally managed] Run the job using the image name passed here. Only used when running with --in-container set.",
211
+ help=localise(
212
+ "[Deprecated and unused from 0.8.0; Image names are now internally managed] Run the job using the image name passed here. Only used when running with --in-container set."
213
+ ),
256
214
  )
257
215
  @click.option(
258
216
  "--no-rebuild",
259
217
  is_flag=True,
260
218
  default=False,
261
- help="[Experimental] Override the default behaviour to always rebuild the container image (as we assume incremental testing).",
219
+ help=localise(
220
+ "[Experimental] Override the default behaviour to always rebuild the container image (as we assume incremental testing)."
221
+ ),
262
222
  )
263
223
  @click.option(
264
224
  "--with-gui",
265
225
  is_flag=True,
266
226
  default=False,
267
- help="Show any GUI if any is created by the test runs. By default, UI elements are run but hidden---only test logs are returned. Please note GUI often assume X11 (e.g. ROS), typically with Qt, so this may not work without a appropriate environment.",
227
+ help=localise(
228
+ "Show any GUI if any is created by the test runs. By default, UI elements are run but hidden---only test logs are returned. Please note GUI often assume X11 (e.g. ROS), typically with Qt, so this may not work without a appropriate environment."
229
+ ),
268
230
  )
269
- @click.argument("jobname")
231
+ @click.argument("container-engine-args", nargs=-1, type=click.UNPROCESSED)
270
232
  @click.pass_context
271
233
  def run(
272
234
  ctx: click.Context,
@@ -283,6 +245,7 @@ def run(
283
245
  with_image: str = "artefacts",
284
246
  no_rebuild: bool = False,
285
247
  with_gui: bool = False,
248
+ container_engine_args: Optional[tuple] = None,
286
249
  ):
287
250
  """
288
251
  Run JOBNAME locally
@@ -296,6 +259,14 @@ def run(
296
259
  * Images are rebuilt at each run (relatively fast when no change).
297
260
  * `dockerfile` allows to specify an alternative Dockerfile.
298
261
  """
262
+ # Workaround for job names coming after engine arguments
263
+ # Idea: Job names do not start with hyphens.
264
+ if jobname.startswith("-") and container_engine_args is not None:
265
+ _fix = list(container_engine_args)
266
+ _fix.insert(0, jobname)
267
+ jobname = _fix.pop()
268
+ container_engine_args = tuple(_fix)
269
+
299
270
  warpconfig = read_config(config)
300
271
  project_id = warpconfig["project"]
301
272
 
@@ -303,7 +274,7 @@ def run(
303
274
  click.echo("#" * 80)
304
275
  click.echo(f"# Job {jobname}".ljust(79, " ") + "#")
305
276
  click.echo("#" * 80)
306
- click.echo(f"[{jobname}] Checking container image")
277
+ click.echo(f"[{jobname}] " + localise("Checking container image"))
307
278
  if not no_rebuild:
308
279
  ctx.invoke(
309
280
  containers.build,
@@ -311,24 +282,32 @@ def run(
311
282
  dockerfile=dockerfile,
312
283
  only=[jobname],
313
284
  )
314
- click.echo(f"[{jobname}] Container image ready")
315
- click.echo(f"[{jobname}] Run in container")
285
+ click.echo(f"[{jobname}] " + localise("Container image ready"))
286
+ click.echo(f"[{jobname}] " + localise("Run in container"))
316
287
  return ctx.invoke(
317
288
  containers.run,
318
289
  jobname=jobname,
319
290
  config=config,
320
291
  with_gui=with_gui,
292
+ engine_args=container_engine_args,
321
293
  )
322
294
 
323
- api_conf = APIConf(project_id, jobname)
324
- click.echo(f"[{jobname}] Starting tests")
295
+ api_conf = APIConf(project_id, __version__, jobname)
296
+ click.echo(f"[{jobname}] " + localise("Starting tests"))
325
297
  if jobname not in warpconfig["jobs"]:
326
- click.secho(f"[{jobname}] Error: Job name not defined", err=True, bold=True)
298
+ click.secho(
299
+ f"[{jobname}] " + localise("Error: Job name not defined"),
300
+ err=True,
301
+ bold=True,
302
+ )
327
303
  raise click.Abort()
328
304
  jobconf = warpconfig["jobs"][jobname]
329
305
  job_type = jobconf.get("type", "test")
330
306
  if job_type not in ["test"]:
331
- click.echo(f"[{jobname}] Job type not supported: {job_type}")
307
+ click.echo(
308
+ f"[{jobname}] "
309
+ + localise("Job type not supported: {jt}").format(jt=job_type)
310
+ )
332
311
  return
333
312
 
334
313
  framework = jobconf["runtime"].get("framework", None)
@@ -337,19 +316,30 @@ def run(
337
316
  if framework in DEPRECATED_FRAMEWORKS.keys():
338
317
  migrated_framework = DEPRECATED_FRAMEWORKS[framework]
339
318
  click.echo(
340
- f"[{jobname}] The selected framework '{framework}' is deprecated. Using '{migrated_framework}' instead."
319
+ f"[{jobname}] "
320
+ + localise(
321
+ "The selected framework '{framework}' is deprecated. Using '{alt}' instead."
322
+ ).format(framework=framework, alt=migrated_framework)
341
323
  )
342
324
  framework = migrated_framework
343
325
 
344
326
  if framework not in SUPPORTED_FRAMEWORKS:
345
327
  click.echo(
346
- f"[{jobname}] WARNING: framework: '{framework}' is not officially supported. Attempting run."
328
+ f"[{jobname}] "
329
+ + localise(
330
+ "WARNING: framework: '{framework}' is not officially supported. Attempting run."
331
+ ).format(framework=framework)
347
332
  )
348
333
 
349
334
  batch_index = os.environ.get("AWS_BATCH_JOB_ARRAY_INDEX", None)
350
335
  if batch_index is not None:
351
336
  batch_index = int(batch_index)
352
- click.echo(f"[{jobname}] AWS BATCH ARRAY DETECTED, batch_index={batch_index}")
337
+ click.echo(
338
+ f"[{jobname}] "
339
+ + localise("AWS BATCH ARRAY DETECTED, batch_index={index}").format(
340
+ index=batch_index
341
+ )
342
+ )
353
343
  scenarios, first = generate_scenarios(jobconf, batch_index)
354
344
  context = None
355
345
  execution_context = getpass.getuser() + "@" + platform.node()
@@ -373,7 +363,10 @@ def run(
373
363
  )
374
364
  except AuthenticationError:
375
365
  click.secho(
376
- f"[{jobname}] Unable to authenticate (Stage: Job initialisation), please check your project name and API key",
366
+ f"[{jobname}] "
367
+ + localise(
368
+ "Unable to authenticate (Stage: Job initialisation), please check your project name and API key"
369
+ ),
377
370
  err=True,
378
371
  bold=True,
379
372
  )
@@ -382,13 +375,19 @@ def run(
382
375
  job_success = True
383
376
  for scenario_n, scenario in enumerate(scenarios):
384
377
  click.echo(
385
- f"[{jobname}] Starting scenario {scenario_n + 1}/{len(scenarios)}: {scenario['name']}"
378
+ f"[{jobname}] "
379
+ + localise("Starting scenario {sid}/{num}: {name}").format(
380
+ sid=scenario_n + 1, num=len(scenarios), name=scenario["name"]
381
+ )
386
382
  )
387
383
  try:
388
384
  run = warpjob.new_run(scenario)
389
385
  except AuthenticationError:
390
386
  click.secho(
391
- f"[{jobname}] Unable to authenticate (Stage: Job run), please check your project name and API key",
387
+ f"[{jobname}] "
388
+ + localise(
389
+ "Unable to authenticate (Stage: Job run), please check your project name and API key"
390
+ ),
392
391
  err=True,
393
392
  bold=True,
394
393
  )
@@ -399,19 +398,22 @@ def run(
399
398
 
400
399
  if "ros_testfile" not in run.params:
401
400
  click.secho(
402
- f"[{jobname}] Test launch file not specified for ros2 project",
401
+ f"[{jobname}] "
402
+ + localise("Test launch file not specified for ros2 project"),
403
403
  err=True,
404
404
  bold=True,
405
405
  )
406
406
  result = get_TestSuite_error_result(
407
407
  scenario["name"],
408
- "launch_test file not specified error",
409
- f"Please specify a `ros_testfile` in the artefacts.yaml scenario configuration.",
408
+ localise("launch_test file not specified error"),
409
+ localise(
410
+ "Please specify a `ros_testfile` in the artefacts.yaml scenario configuration."
411
+ ),
410
412
  )
411
413
  run.log_tests_results([result], False)
412
414
  run.stop()
413
415
  if dryrun:
414
- click.echo(f"[{jobname}] Performing dry run")
416
+ click.echo(f"[{jobname}] " + localise("Performing dry run"))
415
417
  results, success = {}, True
416
418
  else:
417
419
  try:
@@ -421,7 +423,8 @@ def run(
421
423
  warpjob.log_tests_result(False)
422
424
  click.secho(e, bold=True, err=True)
423
425
  click.secho(
424
- f"[{jobname}] artefacts failed to execute the tests",
426
+ f"[{jobname}] "
427
+ + localise("Artefacts failed to execute the tests"),
425
428
  err=True,
426
429
  bold=True,
427
430
  )
@@ -431,7 +434,10 @@ def run(
431
434
  warpjob.stop()
432
435
  warpjob.log_tests_result(job_success)
433
436
  click.secho(
434
- f"[{jobname}] Not able to execute tests. Make sure that ROS2 is sourced and that your launch file syntax is correct.",
437
+ f"[{jobname}] "
438
+ + localise(
439
+ "Not able to execute tests. Make sure that ROS2 is sourced and that your launch file syntax is correct."
440
+ ),
435
441
  err=True,
436
442
  bold=True,
437
443
  )
@@ -443,13 +449,14 @@ def run(
443
449
 
444
450
  if "ros_testfile" not in run.params:
445
451
  click.secho(
446
- f"[{jobname}] Test launch file not specified for ros1 project",
452
+ f"[{jobname}] "
453
+ + localise("Test launch file not specified for ros1 project"),
447
454
  err=True,
448
455
  bold=True,
449
456
  )
450
457
  raise click.Abort()
451
458
  if dryrun:
452
- click.echo(f"[{jobname}] Performing dry run")
459
+ click.echo(f"[{jobname}] " + localise("Performing dry run"))
453
460
  results, success = {}, True
454
461
  else:
455
462
  results, success = run_ros1_tests(run)
@@ -460,13 +467,14 @@ def run(
460
467
 
461
468
  if "run" not in run.params:
462
469
  click.secho(
463
- f"[{jobname}] run command not specified for scenario",
470
+ f"[{jobname}] "
471
+ + localise("run command not specified for scenario"),
464
472
  err=True,
465
473
  bold=True,
466
474
  )
467
475
  raise click.Abort()
468
476
  if dryrun:
469
- click.echo(f"[{jobname}] Performing dry run")
477
+ click.echo(f"[{jobname}] " + localise("Performing dry run"))
470
478
  results, success = {}, True
471
479
  else:
472
480
  results, success = run_other_tests(run)
@@ -480,7 +488,7 @@ def run(
480
488
 
481
489
  run.stop()
482
490
  warpjob.log_tests_result(job_success)
483
- click.echo(f"[{jobname}] Done")
491
+ click.echo(f"[{jobname}] " + localise("Done"))
484
492
  time.sleep(random.random() * 1)
485
493
 
486
494
  warpjob.stop()
@@ -491,21 +499,23 @@ def run(
491
499
  "--config",
492
500
  callback=config_validation,
493
501
  default="artefacts.yaml",
494
- help="Artefacts config file.",
502
+ help=localise("Artefacts configuration file."),
495
503
  )
496
504
  @click.option(
497
505
  "--description",
498
506
  default=None,
499
- help="Optional description for this run",
507
+ help=localise("Optional description for this run"),
500
508
  )
501
509
  @click.option(
502
510
  "--skip-validation",
503
511
  is_flag=True,
504
512
  default=False,
505
513
  is_eager=True, # Necessary for callbacks to see it.
506
- help="Skip configuration validation, so that unsupported settings can be tried out, e.g. non-ROS settings or simulators like SAPIEN.",
514
+ help=localise(
515
+ "Skip configuration validation, so that unsupported settings can be tried out, e.g. non-ROS settings or simulators like SAPIEN."
516
+ ),
507
517
  )
508
- @click.argument("jobname")
518
+ @click.argument("jobname", metavar=localise("JOBNAME"))
509
519
  def run_remote(config, description, jobname, skip_validation=False):
510
520
  """
511
521
  Run JOBNAME in the cloud by packaging local sources.
@@ -516,9 +526,11 @@ def run_remote(config, description, jobname, skip_validation=False):
516
526
  try:
517
527
  warpconfig = read_config(config)
518
528
  except FileNotFoundError:
519
- raise click.ClickException(f"Project config file not found: {config}")
529
+ raise click.ClickException(
530
+ localise("Project config file not found: {config}").format(config=config)
531
+ )
520
532
  project_id = warpconfig["project"]
521
- api_conf = APIConf(project_id)
533
+ api_conf = APIConf(project_id, __version__)
522
534
  project_folder = os.path.dirname(os.path.abspath(config))
523
535
  dashboard_url = urlparse(api_conf.api_url)
524
536
  dashboard_url = f"{dashboard_url.scheme}://{dashboard_url.netloc}/{project_id}"
@@ -527,7 +539,9 @@ def run_remote(config, description, jobname, skip_validation=False):
527
539
  warpconfig["jobs"][jobname]
528
540
  except KeyError:
529
541
  raise click.ClickException(
530
- f"Can't find a job named '{jobname}' in config '{config}'"
542
+ localise("Can't find a job named '{jobname}' in config '{config}'").format(
543
+ jobname=jobname, config=config
544
+ )
531
545
  )
532
546
 
533
547
  # Mutate job and then keep only the selected job in the config
@@ -545,7 +559,7 @@ def run_remote(config, description, jobname, skip_validation=False):
545
559
  if "on" in run_config:
546
560
  del run_config["on"]
547
561
 
548
- click.echo("Packaging source...")
562
+ click.echo(localise("Packaging source..."))
549
563
 
550
564
  with tempfile.NamedTemporaryFile(
551
565
  prefix=project_id.split("/")[-1], suffix=".tgz", delete=True
@@ -590,7 +604,13 @@ def run_remote(config, description, jobname, skip_validation=False):
590
604
  result = upload_urls_response.json()
591
605
  except requests.exceptions.JSONDecodeError:
592
606
  raise click.ClickException(
593
- f"Apologies, problem in interacting with the Artefacts backend: {upload_urls_response.status_code} {upload_urls_response.reason}. Response text: {upload_urls_response.text}."
607
+ localise(
608
+ "Apologies, problem in interacting with the Artefacts backend: {status_code} {reason}. Response text: {detail}."
609
+ ).format(
610
+ status_code=upload_urls_response.status_code,
611
+ reason=upload_urls_response.reason,
612
+ detail=upload_urls_response.text,
613
+ )
594
614
  )
595
615
 
596
616
  if (
@@ -598,25 +618,39 @@ def run_remote(config, description, jobname, skip_validation=False):
598
618
  and result["message"] == "Not allowed"
599
619
  ):
600
620
  raise click.ClickException(
601
- f"Missing access! Please make sure your API key is added at {dashboard_url}/settings"
621
+ localise(
622
+ "Missing access! Please make sure your API key is added at {url}/settings"
623
+ ).format(url=dashboard_url)
602
624
  )
603
625
 
604
626
  if upload_urls_response.status_code == 402:
605
627
  raise click.ClickException(
606
- f"Billing issue, please go to {dashboard_url}/settings to correct: {result['error']}"
628
+ localise(
629
+ "Billing issue, please go to {url}/settings to correct: {error}"
630
+ ).format(url=dashboard_url, error=result["error"])
607
631
  )
608
632
 
609
633
  if "message" in result:
610
634
  raise click.ClickException(
611
- f"Error getting project info: {result['message']}"
635
+ localise("Error getting project info: {message}").format(
636
+ message=result["message"]
637
+ )
612
638
  )
613
639
  elif "error" in result:
614
640
  raise click.ClickException(
615
- f"Error getting project info: {result['error']}"
641
+ localise("Error getting project info: {message}").format(
642
+ message=result["error"]
643
+ )
616
644
  )
617
645
  else:
618
646
  raise click.ClickException(
619
- f"Error getting project info: {upload_urls_response.status_code} {upload_urls_response.reason}. Response text: {upload_urls_response.text}."
647
+ localise(
648
+ "Error getting project info: {status_code} {reason}. Response text: {detail}."
649
+ ).format(
650
+ status_code=upload_urls_response.status_code,
651
+ reason=upload_urls_response.reason,
652
+ detail=upload_urls_response.text,
653
+ )
620
654
  )
621
655
 
622
656
  upload_urls = upload_urls_response.json()["upload_urls"]
@@ -627,7 +661,7 @@ def run_remote(config, description, jobname, skip_validation=False):
627
661
  description = os.environ.get("GITHUB_WORKFLOW")
628
662
  url = f"{os.environ.get('GITHUB_SERVER_URL')}/{os.environ.get('GITHUB_REPOSITORY')}/actions/runs/{os.environ.get('GITHUB_RUN_ID')}"
629
663
  else:
630
- description = "Testing local source"
664
+ description = localise("Testing local source")
631
665
  # Mock the necessary parts of the GitHub event
632
666
  execution_context = getpass.getuser() + "@" + platform.node()
633
667
  integration_payload = {
@@ -675,7 +709,9 @@ def run_remote(config, description, jobname, skip_validation=False):
675
709
  )
676
710
 
677
711
  click.echo(
678
- f"Uploading complete! The new job will show up shortly at {dashboard_url}"
712
+ localise(
713
+ "Uploading complete! The new job will show up shortly at {url}"
714
+ ).format(url=dashboard_url)
679
715
  )
680
716
 
681
717
 
@@ -683,14 +719,7 @@ def run_remote(config, description, jobname, skip_validation=False):
683
719
  @click.version_option(version=__version__)
684
720
  def artefacts():
685
721
  """A command line tool to interface with ARTEFACTS"""
686
- compute_env = os.getenv("AWS_BATCH_CE_NAME", "")
687
- if compute_env != "":
688
- click.echo(f"running version {__version__}")
689
- if (
690
- "development" in compute_env
691
- and os.getenv("ARTEFACTS_API_URL", None) is None
692
- ):
693
- os.environ["ARTEFACTS_API_URL"] = "https://ui.artefacts.com/api"
722
+ pass
694
723
 
695
724
 
696
725
  artefacts.add_command(config)