outerbounds 0.3.68__py3-none-any.whl → 0.3.104__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. outerbounds/_vendor/PyYAML.LICENSE +20 -0
  2. outerbounds/_vendor/__init__.py +0 -0
  3. outerbounds/_vendor/_yaml/__init__.py +34 -0
  4. outerbounds/_vendor/click/__init__.py +73 -0
  5. outerbounds/_vendor/click/_compat.py +626 -0
  6. outerbounds/_vendor/click/_termui_impl.py +717 -0
  7. outerbounds/_vendor/click/_textwrap.py +49 -0
  8. outerbounds/_vendor/click/_winconsole.py +279 -0
  9. outerbounds/_vendor/click/core.py +2998 -0
  10. outerbounds/_vendor/click/decorators.py +497 -0
  11. outerbounds/_vendor/click/exceptions.py +287 -0
  12. outerbounds/_vendor/click/formatting.py +301 -0
  13. outerbounds/_vendor/click/globals.py +68 -0
  14. outerbounds/_vendor/click/parser.py +529 -0
  15. outerbounds/_vendor/click/py.typed +0 -0
  16. outerbounds/_vendor/click/shell_completion.py +580 -0
  17. outerbounds/_vendor/click/termui.py +787 -0
  18. outerbounds/_vendor/click/testing.py +479 -0
  19. outerbounds/_vendor/click/types.py +1073 -0
  20. outerbounds/_vendor/click/utils.py +580 -0
  21. outerbounds/_vendor/click.LICENSE +28 -0
  22. outerbounds/_vendor/vendor_any.txt +2 -0
  23. outerbounds/_vendor/yaml/__init__.py +471 -0
  24. outerbounds/_vendor/yaml/_yaml.cpython-311-darwin.so +0 -0
  25. outerbounds/_vendor/yaml/composer.py +146 -0
  26. outerbounds/_vendor/yaml/constructor.py +862 -0
  27. outerbounds/_vendor/yaml/cyaml.py +177 -0
  28. outerbounds/_vendor/yaml/dumper.py +138 -0
  29. outerbounds/_vendor/yaml/emitter.py +1239 -0
  30. outerbounds/_vendor/yaml/error.py +94 -0
  31. outerbounds/_vendor/yaml/events.py +104 -0
  32. outerbounds/_vendor/yaml/loader.py +62 -0
  33. outerbounds/_vendor/yaml/nodes.py +51 -0
  34. outerbounds/_vendor/yaml/parser.py +629 -0
  35. outerbounds/_vendor/yaml/reader.py +208 -0
  36. outerbounds/_vendor/yaml/representer.py +378 -0
  37. outerbounds/_vendor/yaml/resolver.py +245 -0
  38. outerbounds/_vendor/yaml/scanner.py +1555 -0
  39. outerbounds/_vendor/yaml/serializer.py +127 -0
  40. outerbounds/_vendor/yaml/tokens.py +129 -0
  41. outerbounds/command_groups/apps_cli.py +586 -0
  42. outerbounds/command_groups/cli.py +9 -5
  43. outerbounds/command_groups/local_setup_cli.py +1 -5
  44. outerbounds/command_groups/perimeters_cli.py +198 -25
  45. outerbounds/command_groups/tutorials_cli.py +111 -0
  46. outerbounds/command_groups/workstations_cli.py +2 -2
  47. outerbounds/utils/kubeconfig.py +2 -2
  48. outerbounds/utils/metaflowconfig.py +68 -9
  49. outerbounds/utils/schema.py +2 -2
  50. outerbounds/utils/utils.py +19 -0
  51. outerbounds/vendor.py +159 -0
  52. {outerbounds-0.3.68.dist-info → outerbounds-0.3.104.dist-info}/METADATA +14 -7
  53. outerbounds-0.3.104.dist-info/RECORD +59 -0
  54. {outerbounds-0.3.68.dist-info → outerbounds-0.3.104.dist-info}/WHEEL +1 -1
  55. outerbounds-0.3.68.dist-info/RECORD +0 -15
  56. {outerbounds-0.3.68.dist-info → outerbounds-0.3.104.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,586 @@
1
+ import json
2
+ import os
3
+ from os import path
4
+ from outerbounds._vendor import click
5
+ import requests
6
+
7
+ from ..utils import metaflowconfig
8
+ from ..utils.schema import (
9
+ CommandStatus,
10
+ OuterboundsCommandResponse,
11
+ OuterboundsCommandStatus,
12
+ )
13
+
14
+
15
+ @click.group()
16
+ def cli(**kwargs):
17
+ pass
18
+
19
+
20
+ @click.group(help="Manage apps")
21
+ def app(**kwargs):
22
+ pass
23
+
24
+
25
+ @app.command(help="Start an app using a port and a name")
26
+ @click.option(
27
+ "-d",
28
+ "--config-dir",
29
+ default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
30
+ help="Path to Metaflow configuration directory",
31
+ show_default=True,
32
+ )
33
+ @click.option(
34
+ "-p",
35
+ "--profile",
36
+ default=os.environ.get("METAFLOW_PROFILE", ""),
37
+ help="The named metaflow profile in which your workstation exists",
38
+ )
39
+ @click.option(
40
+ "--port",
41
+ required=True,
42
+ help="Port number where you want to start your app",
43
+ type=int,
44
+ )
45
+ @click.option(
46
+ "--name",
47
+ required=True,
48
+ help="Name of your app",
49
+ type=str,
50
+ )
51
+ @click.option(
52
+ "-o",
53
+ "--output",
54
+ default="",
55
+ help="Show output in the specified format.",
56
+ type=click.Choice(["json", ""]),
57
+ )
58
+ def start(config_dir=None, profile=None, port=-1, name="", output=""):
59
+ if len(name) == 0 or len(name) >= 20:
60
+ click.secho(
61
+ "App name should not be more than 20 characters long.",
62
+ fg="red",
63
+ err=True,
64
+ )
65
+ return
66
+ elif not name.isalnum() or not name.islower():
67
+ click.secho(
68
+ "App name can only contain lowercase alphanumeric characters.",
69
+ fg="red",
70
+ err=True,
71
+ )
72
+ return
73
+
74
+ start_app_response = OuterboundsCommandResponse()
75
+
76
+ validate_workstation_step = CommandStatus(
77
+ "ValidateRunningOnWorkstation",
78
+ OuterboundsCommandStatus.OK,
79
+ "Command is being run on a workstation.",
80
+ )
81
+
82
+ list_workstations_step = CommandStatus(
83
+ "ListWorkstations",
84
+ OuterboundsCommandStatus.OK,
85
+ "List of workstations fetched.",
86
+ )
87
+
88
+ validate_request = CommandStatus(
89
+ "ValidateRequest",
90
+ OuterboundsCommandStatus.OK,
91
+ "Start app request is valid.",
92
+ )
93
+
94
+ start_app_step = CommandStatus(
95
+ "StartApp",
96
+ OuterboundsCommandStatus.OK,
97
+ f"App {name} started on port {port}!",
98
+ )
99
+
100
+ if "WORKSTATION_ID" not in os.environ:
101
+ validate_workstation_step.update(
102
+ OuterboundsCommandStatus.FAIL,
103
+ "All outerbounds app commands can only be run from a workstation.",
104
+ "",
105
+ )
106
+ start_app_response.add_step(validate_workstation_step)
107
+ click.secho(
108
+ "All outerbounds app commands can only be run from a workstation.",
109
+ fg="red",
110
+ err=True,
111
+ )
112
+
113
+ if output == "json":
114
+ click.echo(json.dumps(start_app_response.as_dict(), indent=4))
115
+ return
116
+
117
+ try:
118
+ try:
119
+ metaflow_token = metaflowconfig.get_metaflow_token_from_config(
120
+ config_dir, profile
121
+ )
122
+ api_url = metaflowconfig.get_sanitized_url_from_config(
123
+ config_dir, profile, "OBP_API_SERVER"
124
+ )
125
+
126
+ workstations_response = requests.get(
127
+ f"{api_url}/v1/workstations", headers={"x-api-key": metaflow_token}
128
+ )
129
+ workstations_response.raise_for_status()
130
+ start_app_response.add_step(list_workstations_step)
131
+ except:
132
+ click.secho("Failed to list workstations!", fg="red", err=True)
133
+ list_workstations_step.update(
134
+ OuterboundsCommandStatus.FAIL, "Failed to list workstations!", ""
135
+ )
136
+ start_app_response.add_step(list_workstations_step)
137
+ if output == "json":
138
+ click.echo(json.dumps(start_app_response.as_dict(), indent=4))
139
+ return
140
+
141
+ workstations_json = workstations_response.json()["workstations"]
142
+ for workstation in workstations_json:
143
+ if workstation["instance_id"] == os.environ["WORKSTATION_ID"]:
144
+ if "named_ports" in workstation["spec"]:
145
+ try:
146
+ ensure_app_start_request_is_valid(
147
+ workstation["spec"]["named_ports"], port, name
148
+ )
149
+ except ValueError as e:
150
+ click.secho(str(e), fg="red", err=True)
151
+ validate_request.update(
152
+ OuterboundsCommandStatus.FAIL,
153
+ str(e),
154
+ "",
155
+ )
156
+ start_app_response.add_step(validate_request)
157
+ if output == "json":
158
+ click.echo(
159
+ json.dumps(start_app_response.as_dict(), indent=4)
160
+ )
161
+ return
162
+
163
+ start_app_response.add_step(validate_request)
164
+
165
+ for named_port in workstation["spec"]["named_ports"]:
166
+ if int(named_port["port"]) == port:
167
+ if named_port["enabled"] and named_port["name"] == name:
168
+ click.secho(
169
+ f"App {name} already running on port {port}!",
170
+ fg="green",
171
+ err=True,
172
+ )
173
+ click.secho(
174
+ f"App URL: {api_url.replace('api', 'ui')}/apps/{os.environ['WORKSTATION_ID']}/{name}/",
175
+ fg="green",
176
+ err=True,
177
+ )
178
+ start_app_response.add_step(start_app_step)
179
+ if output == "json":
180
+ click.echo(
181
+ json.dumps(
182
+ start_app_response.as_dict(), indent=4
183
+ )
184
+ )
185
+ return
186
+ else:
187
+ try:
188
+ response = requests.put(
189
+ f"{api_url}/v1/workstations/update/{os.environ['WORKSTATION_ID']}/namedports",
190
+ headers={"x-api-key": metaflow_token},
191
+ json={
192
+ "port": port,
193
+ "name": name,
194
+ "enabled": True,
195
+ },
196
+ )
197
+
198
+ response.raise_for_status()
199
+ click.secho(
200
+ f"App {name} started on port {port}!",
201
+ fg="green",
202
+ err=True,
203
+ )
204
+ click.secho(
205
+ f"App URL: {api_url.replace('api', 'ui')}/apps/{os.environ['WORKSTATION_ID']}/{name}/",
206
+ fg="green",
207
+ err=True,
208
+ )
209
+ except Exception:
210
+ click.secho(
211
+ f"Failed to start app {name} on port {port}!",
212
+ fg="red",
213
+ err=True,
214
+ )
215
+ start_app_step.update(
216
+ OuterboundsCommandStatus.FAIL,
217
+ f"Failed to start app {name} on port {port}!",
218
+ "",
219
+ )
220
+
221
+ start_app_response.add_step(start_app_step)
222
+ if output == "json":
223
+ click.echo(
224
+ json.dumps(
225
+ start_app_response.as_dict(), indent=4
226
+ )
227
+ )
228
+ return
229
+ except Exception as e:
230
+ click.secho(f"Failed to start app {name} on port {port}!", fg="red", err=True)
231
+ start_app_step.update(
232
+ OuterboundsCommandStatus.FAIL,
233
+ f"Failed to start app {name} on port {port}!",
234
+ "",
235
+ )
236
+ start_app_response.add_step(start_app_step)
237
+ if output == "json":
238
+ click.secho(json.dumps(start_app_response.as_dict(), indent=4))
239
+
240
+
241
+ @app.command(help="Stop an app using its port number")
242
+ @click.option(
243
+ "-d",
244
+ "--config-dir",
245
+ default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
246
+ help="Path to Metaflow configuration directory",
247
+ show_default=True,
248
+ )
249
+ @click.option(
250
+ "-p",
251
+ "--profile",
252
+ default=os.environ.get("METAFLOW_PROFILE", ""),
253
+ help="The named metaflow profile in which your workstation exists",
254
+ )
255
+ @click.option(
256
+ "--port",
257
+ required=False,
258
+ default=-1,
259
+ help="Port number where you want to start your app.",
260
+ type=int,
261
+ )
262
+ @click.option(
263
+ "--name",
264
+ required=False,
265
+ help="Name of your app",
266
+ default="",
267
+ type=str,
268
+ )
269
+ @click.option(
270
+ "-o",
271
+ "--output",
272
+ default="",
273
+ help="Show output in the specified format.",
274
+ type=click.Choice(["json", ""]),
275
+ )
276
+ def stop(config_dir=None, profile=None, port=-1, name="", output=""):
277
+ if port == -1 and not name:
278
+ click.secho(
279
+ "Please provide either a port number or a name to stop the app.",
280
+ fg="red",
281
+ err=True,
282
+ )
283
+ return
284
+ elif port > 0 and name:
285
+ click.secho(
286
+ "Please provide either a port number or a name to stop the app, not both.",
287
+ fg="red",
288
+ err=True,
289
+ )
290
+ return
291
+
292
+ stop_app_response = OuterboundsCommandResponse()
293
+
294
+ validate_workstation_step = CommandStatus(
295
+ "ValidateRunningOnWorkstation",
296
+ OuterboundsCommandStatus.OK,
297
+ "Command is being run on a workstation.",
298
+ )
299
+
300
+ list_workstations_step = CommandStatus(
301
+ "ListWorkstations",
302
+ OuterboundsCommandStatus.OK,
303
+ "List of workstations fetched.",
304
+ )
305
+
306
+ validate_port_exists = CommandStatus(
307
+ "ValidatePortExists",
308
+ OuterboundsCommandStatus.OK,
309
+ "Port exists on workstation",
310
+ )
311
+
312
+ stop_app_step = CommandStatus(
313
+ "StopApp",
314
+ OuterboundsCommandStatus.OK,
315
+ f"App stopped on port {port}!",
316
+ )
317
+
318
+ if "WORKSTATION_ID" not in os.environ:
319
+ validate_workstation_step.update(
320
+ OuterboundsCommandStatus.FAIL,
321
+ "All outerbounds app commands can only be run from a workstation.",
322
+ "",
323
+ )
324
+ stop_app_response.add_step(validate_workstation_step)
325
+ click.secho(
326
+ "All outerbounds app commands can only be run from a workstation.",
327
+ fg="red",
328
+ err=True,
329
+ )
330
+
331
+ if output == "json":
332
+ click.echo(json.dumps(stop_app_response.as_dict(), indent=4))
333
+ return
334
+
335
+ try:
336
+ try:
337
+ metaflow_token = metaflowconfig.get_metaflow_token_from_config(
338
+ config_dir, profile
339
+ )
340
+ api_url = metaflowconfig.get_sanitized_url_from_config(
341
+ config_dir, profile, "OBP_API_SERVER"
342
+ )
343
+
344
+ workstations_response = requests.get(
345
+ f"{api_url}/v1/workstations", headers={"x-api-key": metaflow_token}
346
+ )
347
+ workstations_response.raise_for_status()
348
+ stop_app_response.add_step(list_workstations_step)
349
+ except:
350
+ click.secho("Failed to list workstations!", fg="red", err=True)
351
+ list_workstations_step.update(
352
+ OuterboundsCommandStatus.FAIL, "Failed to list workstations!", ""
353
+ )
354
+ stop_app_response.add_step(list_workstations_step)
355
+ if output == "json":
356
+ click.echo(json.dumps(stop_app_response.as_dict(), indent=4))
357
+ return
358
+
359
+ app_found = False
360
+ workstations_json = workstations_response.json()["workstations"]
361
+ for workstation in workstations_json:
362
+ if workstation["instance_id"] == os.environ["WORKSTATION_ID"]:
363
+ if "named_ports" in workstation["spec"]:
364
+ for named_port in workstation["spec"]["named_ports"]:
365
+ if (
366
+ int(named_port["port"]) == port
367
+ or named_port["name"] == name
368
+ ):
369
+ app_found = True
370
+ stop_app_response.add_step(validate_port_exists)
371
+ if named_port["enabled"]:
372
+ try:
373
+ response = requests.put(
374
+ f"{api_url}/v1/workstations/update/{os.environ['WORKSTATION_ID']}/namedports",
375
+ headers={"x-api-key": metaflow_token},
376
+ json={
377
+ "port": named_port["port"],
378
+ "name": named_port["name"],
379
+ "enabled": False,
380
+ },
381
+ )
382
+ response.raise_for_status()
383
+ click.secho(
384
+ f"App {named_port['name']} stopped on port {named_port['port']}!",
385
+ fg="green",
386
+ err=True,
387
+ )
388
+ except Exception as e:
389
+ click.secho(
390
+ f"Failed to stop app {named_port['name']} on port {named_port['port']}!",
391
+ fg="red",
392
+ err=True,
393
+ )
394
+ stop_app_step.update(
395
+ OuterboundsCommandStatus.FAIL,
396
+ f"Failed to stop app {named_port['name']} on port {named_port['port']}!",
397
+ "",
398
+ )
399
+
400
+ stop_app_response.add_step(stop_app_step)
401
+ if output == "json":
402
+ click.echo(
403
+ json.dumps(
404
+ stop_app_response.as_dict(), indent=4
405
+ )
406
+ )
407
+ return
408
+
409
+ if app_found:
410
+ already_stopped_message = (
411
+ f"No deployed app named {name} found."
412
+ if name
413
+ else f"There is no app deployed on port {port}"
414
+ )
415
+ click.secho(
416
+ already_stopped_message,
417
+ fg="green",
418
+ err=True,
419
+ )
420
+ stop_app_response.add_step(stop_app_step)
421
+ if output == "json":
422
+ click.echo(json.dumps(stop_app_response.as_dict(), indent=4))
423
+ return
424
+
425
+ err_message = (
426
+ (f"Port {port} not found on workstation {os.environ['WORKSTATION_ID']}")
427
+ if port != -1
428
+ else f"App {name} not found on workstation {os.environ['WORKSTATION_ID']}"
429
+ )
430
+
431
+ click.secho(
432
+ err_message,
433
+ fg="red",
434
+ err=True,
435
+ )
436
+
437
+ validate_port_exists.update(
438
+ OuterboundsCommandStatus.FAIL,
439
+ err_message,
440
+ "",
441
+ )
442
+ stop_app_response.add_step(validate_port_exists)
443
+ if output == "json":
444
+ click.echo(json.dumps(stop_app_response.as_dict(), indent=4))
445
+ except Exception as e:
446
+ click.secho(f"Failed to stop app on port {port}!", fg="red", err=True)
447
+ stop_app_step.update(
448
+ OuterboundsCommandStatus.FAIL, f"Failed to stop on port {port}!", ""
449
+ )
450
+ stop_app_response.add_step(stop_app_step)
451
+ if output == "json":
452
+ click.echo(json.dumps(stop_app_response.as_dict(), indent=4))
453
+
454
+
455
+ @app.command(help="Stop an app using its port number")
456
+ @click.option(
457
+ "-d",
458
+ "--config-dir",
459
+ default=path.expanduser(os.environ.get("METAFLOW_HOME", "~/.metaflowconfig")),
460
+ help="Path to Metaflow configuration directory",
461
+ show_default=True,
462
+ )
463
+ @click.option(
464
+ "-p",
465
+ "--profile",
466
+ default=os.environ.get("METAFLOW_PROFILE", ""),
467
+ help="The named metaflow profile in which your workstation exists",
468
+ )
469
+ @click.option(
470
+ "-o",
471
+ "--output",
472
+ default="",
473
+ help="Show output in the specified format.",
474
+ type=click.Choice(["json", ""]),
475
+ )
476
+ def list(config_dir=None, profile=None, output=""):
477
+ list_app_response = OuterboundsCommandResponse()
478
+
479
+ validate_workstation_step = CommandStatus(
480
+ "ValidateRunningOnWorkstation",
481
+ OuterboundsCommandStatus.OK,
482
+ "Command is being run on a workstation.",
483
+ )
484
+
485
+ list_workstations_step = CommandStatus(
486
+ "ListWorkstations",
487
+ OuterboundsCommandStatus.OK,
488
+ "List of workstations fetched.",
489
+ )
490
+
491
+ if "WORKSTATION_ID" not in os.environ:
492
+ validate_workstation_step.update(
493
+ OuterboundsCommandStatus.FAIL,
494
+ "All outerbounds app commands can only be run from a workstation.",
495
+ "",
496
+ )
497
+ list_app_response.add_step(validate_workstation_step)
498
+ click.secho(
499
+ "All outerbounds app commands can only be run from a workstation.",
500
+ fg="red",
501
+ err=True,
502
+ )
503
+
504
+ if output == "json":
505
+ click.echo(json.dumps(list_app_response.as_dict(), indent=4))
506
+ return
507
+
508
+ try:
509
+ try:
510
+ metaflow_token = metaflowconfig.get_metaflow_token_from_config(
511
+ config_dir, profile
512
+ )
513
+ api_url = metaflowconfig.get_sanitized_url_from_config(
514
+ config_dir, profile, "OBP_API_SERVER"
515
+ )
516
+
517
+ workstations_response = requests.get(
518
+ f"{api_url}/v1/workstations", headers={"x-api-key": metaflow_token}
519
+ )
520
+ workstations_response.raise_for_status()
521
+ list_app_response.add_step(list_workstations_step)
522
+ except:
523
+ click.secho("Failed to list workstations!", fg="red", err=True)
524
+ list_workstations_step.update(
525
+ OuterboundsCommandStatus.FAIL, "Failed to list workstations!", ""
526
+ )
527
+ list_app_response.add_step(list_workstations_step)
528
+ if output == "json":
529
+ click.echo(json.dumps(list_app_response.as_dict(), indent=4))
530
+ return
531
+
532
+ workstations_json = workstations_response.json()["workstations"]
533
+ for workstation in workstations_json:
534
+ if workstation["instance_id"] == os.environ["WORKSTATION_ID"]:
535
+ if "named_ports" in workstation["spec"]:
536
+ for named_port in workstation["spec"]["named_ports"]:
537
+ if named_port["enabled"]:
538
+ click.secho(
539
+ f"App Name: {named_port['name']}", fg="green", err=True
540
+ )
541
+ click.secho(
542
+ f"App Port on Workstation: {named_port['port']}",
543
+ fg="green",
544
+ err=True,
545
+ )
546
+ click.secho(f"App Status: Deployed", fg="green", err=True)
547
+ click.secho(
548
+ f"App URL: {api_url.replace('api', 'ui')}/apps/{os.environ['WORKSTATION_ID']}/{named_port['name']}/",
549
+ fg="green",
550
+ err=True,
551
+ )
552
+ else:
553
+ click.secho(
554
+ f"App Port on Workstation: {named_port['port']}",
555
+ fg="yellow",
556
+ err=True,
557
+ )
558
+ click.secho(
559
+ f"App Status: Not Deployed", fg="yellow", err=True
560
+ )
561
+
562
+ click.echo("\n", err=True)
563
+ except Exception as e:
564
+ click.secho(f"Failed to list apps!", fg="red", err=True)
565
+ if output == "json":
566
+ click.echo(json.dumps(list_app_response.as_dict(), indent=4))
567
+
568
+
569
+ def ensure_app_start_request_is_valid(existing_named_ports, port: int, name: str):
570
+ existing_apps_by_port = {np["port"]: np for np in existing_named_ports}
571
+
572
+ if port not in existing_apps_by_port:
573
+ raise ValueError(f"Port {port} not found on workstation")
574
+
575
+ for existing_named_port in existing_named_ports:
576
+ if (
577
+ name == existing_named_port["name"]
578
+ and existing_named_port["port"] != port
579
+ and existing_named_port["enabled"]
580
+ ):
581
+ raise ValueError(
582
+ f"App with name '{name}' is already deployed on port {existing_named_port['port']}"
583
+ )
584
+
585
+
586
+ cli.add_command(app, name="app")
@@ -1,12 +1,16 @@
1
- import click
2
- from . import local_setup_cli
3
- from . import workstations_cli
4
- from . import perimeters_cli
1
+ from outerbounds._vendor import click
2
+ from . import local_setup_cli, workstations_cli, perimeters_cli, apps_cli, tutorials_cli
5
3
 
6
4
 
7
5
  @click.command(
8
6
  cls=click.CommandCollection,
9
- sources=[local_setup_cli.cli, workstations_cli.cli, perimeters_cli.cli],
7
+ sources=[
8
+ local_setup_cli.cli,
9
+ workstations_cli.cli,
10
+ perimeters_cli.cli,
11
+ apps_cli.cli,
12
+ tutorials_cli.cli,
13
+ ],
10
14
  )
11
15
  def cli(**kwargs):
12
16
  pass
@@ -11,8 +11,7 @@ from importlib.machinery import PathFinder
11
11
  from os import path
12
12
  from pathlib import Path
13
13
  from typing import Any, Callable, Dict, List
14
-
15
- import click
14
+ from outerbounds._vendor import click
16
15
  import requests
17
16
  from requests.exceptions import HTTPError
18
17
 
@@ -641,7 +640,6 @@ class ConfigurationWriter:
641
640
  if config_type == "inline":
642
641
  if "OBP_PERIMETER" in self.decoded_config:
643
642
  self.selected_perimeter = self.decoded_config["OBP_PERIMETER"]
644
-
645
643
  if "OBP_METAFLOW_CONFIG_URL" in self.decoded_config:
646
644
  self.decoded_config = {
647
645
  "OBP_METAFLOW_CONFIG_URL": self.decoded_config[
@@ -702,8 +700,6 @@ class ConfigurationWriter:
702
700
  with open(config_path, "w") as fd:
703
701
  json.dump(self.existing, fd, indent=4)
704
702
 
705
- # Every time a config is initialized, we should also reset the corresponding ob_config[_profile].json
706
- remote_config = metaflowconfig.init_config(self.out_dir, self.profile)
707
703
  if self.selected_perimeter and "OBP_METAFLOW_CONFIG_URL" in self.decoded_config:
708
704
  with open(self.ob_config_path, "w") as fd:
709
705
  ob_config_dict = {