vortex-cli 4.19.2__tar.gz → 4.20.0__tar.gz
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.
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/PKG-INFO +42 -2
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/README.md +41 -1
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/pyproject.toml +1 -1
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/cli.py +29 -8
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/copy.py +4 -2
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/delete.py +3 -1
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/log.py +2 -2
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/new.py +173 -5
- vortex_cli-4.20.0/vortex/commands/use.py +28 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/main.py +13 -2
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/models.py +4 -4
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/soap.py +2 -2
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex_cli.egg-info/PKG-INFO +42 -2
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex_cli.egg-info/SOURCES.txt +1 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/LICENSE +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/setup.cfg +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/__init__.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/__main__.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/colour.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/__init__.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/clean.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/clone.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/code.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/config.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/db.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/docs.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/execute.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/export.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/find.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/grep.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/import_.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/list.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/commands/watch.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/constants.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/docs/Blackbook.pdf +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/lib/puakma-6.0.40.jar +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/logging.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/spinner.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/util.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex/workspace.py +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex_cli.egg-info/dependency_links.txt +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex_cli.egg-info/entry_points.txt +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex_cli.egg-info/requires.txt +0 -0
- {vortex_cli-4.19.2 → vortex_cli-4.20.0}/vortex_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vortex_cli
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.20.0
|
|
4
4
|
Summary: Vortex CLI
|
|
5
5
|
Author-email: Jordan Amos <jordan.amos@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -109,6 +109,7 @@ For a full list of commands see `--help`.
|
|
|
109
109
|
### Command Overview
|
|
110
110
|
|
|
111
111
|
- `code`: Open the workspace in Visual Studio Code.
|
|
112
|
+
- `use`: Set the default server so you don't need to pass `--server` on every command. e.g. `vortex use production`
|
|
112
113
|
- `list` (or `ls`): List Puakma Applications on the server or cloned locally. (`ls` is an alias for `vortex list --local`)
|
|
113
114
|
- `clone`: Clone Puakma Applications and their design objects into the workspace or in the .pmx format.
|
|
114
115
|
- `watch`: Watch the workspace for changes to Design Objects and automatically upload them to the server.
|
|
@@ -117,9 +118,48 @@ For a full list of commands see `--help`.
|
|
|
117
118
|
- `log`: View the server log.
|
|
118
119
|
- `find`: Find Design Objects of cloned applications by name.
|
|
119
120
|
- `grep`: Search the contents of cloned Design Objects using a Regular Expression.
|
|
120
|
-
- `new`: Create new Design Objects, Applications, or Keywords. Use
|
|
121
|
+
- `new`: Create new Design Objects, Applications, or Keywords. Use `--update <ID>` to update instead. Run without flags to launch an interactive wizard.
|
|
121
122
|
- `copy`: Copy a Design Object from one application to another.
|
|
122
123
|
- `delete`: Delete Design Objects by ID.
|
|
123
124
|
- `db`: Interact with Database Connections.
|
|
124
125
|
- `docs`: Open the Tornado Server Blackbook.
|
|
125
126
|
- `execute`: Execute a command on the server.
|
|
127
|
+
|
|
128
|
+
### Switching Servers
|
|
129
|
+
|
|
130
|
+
If you work with multiple servers, use `vortex use <server>` to set the default instead of passing `--server` on every command:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
vortex use production
|
|
134
|
+
vortex list
|
|
135
|
+
vortex watch
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Interactive Wizards
|
|
139
|
+
|
|
140
|
+
Run `vortex new object` or `vortex new app` without any flags to launch a step-by-step wizard:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
vortex new object
|
|
144
|
+
# → pick app from locally cloned list
|
|
145
|
+
# → pick design type
|
|
146
|
+
# → enter name
|
|
147
|
+
# → enter content type (resource only)
|
|
148
|
+
# → enter parent page, open action, save action (page only)
|
|
149
|
+
# → preview and confirm
|
|
150
|
+
|
|
151
|
+
vortex new app
|
|
152
|
+
# → enter name
|
|
153
|
+
# → pick from existing groups or enter a new one
|
|
154
|
+
# → enter description (optional)
|
|
155
|
+
# → preview and confirm
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Skipping Confirmation Prompts
|
|
159
|
+
|
|
160
|
+
Pass `--yes` (or `-y`) to skip Y/y confirmation prompts, useful for scripting:
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
vortex --yes delete 123 456
|
|
164
|
+
vortex -y new object --name MyAction --app-id 10 --type action
|
|
165
|
+
```
|
|
@@ -67,6 +67,7 @@ For a full list of commands see `--help`.
|
|
|
67
67
|
### Command Overview
|
|
68
68
|
|
|
69
69
|
- `code`: Open the workspace in Visual Studio Code.
|
|
70
|
+
- `use`: Set the default server so you don't need to pass `--server` on every command. e.g. `vortex use production`
|
|
70
71
|
- `list` (or `ls`): List Puakma Applications on the server or cloned locally. (`ls` is an alias for `vortex list --local`)
|
|
71
72
|
- `clone`: Clone Puakma Applications and their design objects into the workspace or in the .pmx format.
|
|
72
73
|
- `watch`: Watch the workspace for changes to Design Objects and automatically upload them to the server.
|
|
@@ -75,9 +76,48 @@ For a full list of commands see `--help`.
|
|
|
75
76
|
- `log`: View the server log.
|
|
76
77
|
- `find`: Find Design Objects of cloned applications by name.
|
|
77
78
|
- `grep`: Search the contents of cloned Design Objects using a Regular Expression.
|
|
78
|
-
- `new`: Create new Design Objects, Applications, or Keywords. Use
|
|
79
|
+
- `new`: Create new Design Objects, Applications, or Keywords. Use `--update <ID>` to update instead. Run without flags to launch an interactive wizard.
|
|
79
80
|
- `copy`: Copy a Design Object from one application to another.
|
|
80
81
|
- `delete`: Delete Design Objects by ID.
|
|
81
82
|
- `db`: Interact with Database Connections.
|
|
82
83
|
- `docs`: Open the Tornado Server Blackbook.
|
|
83
84
|
- `execute`: Execute a command on the server.
|
|
85
|
+
|
|
86
|
+
### Switching Servers
|
|
87
|
+
|
|
88
|
+
If you work with multiple servers, use `vortex use <server>` to set the default instead of passing `--server` on every command:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
vortex use production
|
|
92
|
+
vortex list
|
|
93
|
+
vortex watch
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Interactive Wizards
|
|
97
|
+
|
|
98
|
+
Run `vortex new object` or `vortex new app` without any flags to launch a step-by-step wizard:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
vortex new object
|
|
102
|
+
# → pick app from locally cloned list
|
|
103
|
+
# → pick design type
|
|
104
|
+
# → enter name
|
|
105
|
+
# → enter content type (resource only)
|
|
106
|
+
# → enter parent page, open action, save action (page only)
|
|
107
|
+
# → preview and confirm
|
|
108
|
+
|
|
109
|
+
vortex new app
|
|
110
|
+
# → enter name
|
|
111
|
+
# → pick from existing groups or enter a new one
|
|
112
|
+
# → enter description (optional)
|
|
113
|
+
# → preview and confirm
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Skipping Confirmation Prompts
|
|
117
|
+
|
|
118
|
+
Pass `--yes` (or `-y`) to skip Y/y confirmation prompts, useful for scripting:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
vortex --yes delete 123 456
|
|
122
|
+
vortex -y new object --name MyAction --app-id 10 --type action
|
|
123
|
+
```
|
|
@@ -90,11 +90,14 @@ def validate_args(
|
|
|
90
90
|
) -> None:
|
|
91
91
|
if args.command == "new":
|
|
92
92
|
msg = None
|
|
93
|
-
is_update_mode = bool(args
|
|
93
|
+
is_update_mode = bool(getattr(args, "update_ids", None))
|
|
94
94
|
if args.subcommand == "object":
|
|
95
95
|
missing_required_fields = not is_update_mode and not (
|
|
96
96
|
args.name and args.app_id and args.design_type
|
|
97
97
|
)
|
|
98
|
+
# Allow wizard mode when all required fields are missing
|
|
99
|
+
has_any_required = args.name or args.app_id or args.design_type
|
|
100
|
+
wizard_mode = missing_required_fields and not has_any_required
|
|
98
101
|
missing_content_type = (
|
|
99
102
|
args.design_type in (DesignType.RESOURCE, DesignType.DOCUMENTATION)
|
|
100
103
|
and not args.content_type
|
|
@@ -111,7 +114,7 @@ def validate_args(
|
|
|
111
114
|
or args.parent_page
|
|
112
115
|
or args.content_type
|
|
113
116
|
)
|
|
114
|
-
if missing_required_fields:
|
|
117
|
+
if missing_required_fields and not wizard_mode:
|
|
115
118
|
msg = "--name, --app-id, and --type are required unless using --update"
|
|
116
119
|
elif missing_content_type:
|
|
117
120
|
msg = (
|
|
@@ -123,13 +126,19 @@ def validate_args(
|
|
|
123
126
|
elif update_is_missing_args:
|
|
124
127
|
msg = "Please specify an option to update"
|
|
125
128
|
elif args.subcommand == "app":
|
|
126
|
-
|
|
129
|
+
has_any_app_required = args.name or args.group
|
|
130
|
+
app_wizard_mode = not is_update_mode and not has_any_app_required
|
|
131
|
+
if (
|
|
132
|
+
not is_update_mode
|
|
133
|
+
and not (args.name and args.group)
|
|
134
|
+
and not app_wizard_mode
|
|
135
|
+
):
|
|
127
136
|
msg = "--name and --group are required when creating new applications."
|
|
128
137
|
if msg:
|
|
129
138
|
new_parser.error(msg)
|
|
130
139
|
|
|
131
140
|
elif args.command == "clone" and not (args.app_ids or args.reclone):
|
|
132
|
-
clone_parser.error("Please
|
|
141
|
+
clone_parser.error("Please specify the APP_ID[s] to clone or use '--reclone'")
|
|
133
142
|
elif args.command == "db" and args.sql is not None:
|
|
134
143
|
try:
|
|
135
144
|
args.database = int(args.database)
|
|
@@ -250,7 +259,7 @@ def add_clone_parser(
|
|
|
250
259
|
)
|
|
251
260
|
clone_parser.add_argument(
|
|
252
261
|
"--reclone",
|
|
253
|
-
help="Reclone locally cloned
|
|
262
|
+
help="Reclone locally cloned applications",
|
|
254
263
|
action="store_true",
|
|
255
264
|
)
|
|
256
265
|
clone_parser.add_argument(
|
|
@@ -416,7 +425,7 @@ def add_log_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
416
425
|
type=_positive_int,
|
|
417
426
|
help=(
|
|
418
427
|
"Used only with --keep-alive. "
|
|
419
|
-
"The number of seconds
|
|
428
|
+
"The number of seconds between requests for new logs. Default is %(default)s"
|
|
420
429
|
),
|
|
421
430
|
default=5,
|
|
422
431
|
metavar="SECONDS",
|
|
@@ -659,13 +668,13 @@ def add_db_parser(command_parser: _SubParsersAction[ArgumentParser]) -> Argument
|
|
|
659
668
|
"--update",
|
|
660
669
|
"-u",
|
|
661
670
|
action="store_true",
|
|
662
|
-
help="Set this flag when running
|
|
671
|
+
help="Set this flag when running queries for altering/updating the database",
|
|
663
672
|
)
|
|
664
673
|
return db_parser
|
|
665
674
|
|
|
666
675
|
|
|
667
676
|
def add_docs_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
|
|
668
|
-
command_parser.add_parser("docs", help="Open the
|
|
677
|
+
command_parser.add_parser("docs", help="Open the Tornado Server Blackbook")
|
|
669
678
|
|
|
670
679
|
|
|
671
680
|
def add_execute_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
|
|
@@ -710,6 +719,18 @@ def add_execute_parser(command_parser: _SubParsersAction[ArgumentParser]) -> Non
|
|
|
710
719
|
_add_server_option(execute_parser)
|
|
711
720
|
|
|
712
721
|
|
|
722
|
+
def add_use_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
|
|
723
|
+
use_parser = command_parser.add_parser(
|
|
724
|
+
"use",
|
|
725
|
+
help="Set the default server definition to use",
|
|
726
|
+
)
|
|
727
|
+
use_parser.add_argument(
|
|
728
|
+
"server_name",
|
|
729
|
+
metavar="SERVER_NAME",
|
|
730
|
+
help="The name of the server definition in the config file",
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
|
|
713
734
|
def add_import_parser(
|
|
714
735
|
command_parser: _SubParsersAction[ArgumentParser],
|
|
715
736
|
) -> ArgumentParser:
|
|
@@ -40,7 +40,7 @@ async def _acopy_obj(
|
|
|
40
40
|
to_app: PuakmaApplication,
|
|
41
41
|
copy_params: bool,
|
|
42
42
|
) -> int:
|
|
43
|
-
_err_msg = f"Failed to copy {obj} to {
|
|
43
|
+
_err_msg = f"Failed to copy {obj} to {to_app.id}"
|
|
44
44
|
try:
|
|
45
45
|
# Copying to the same application, need a new name in that case
|
|
46
46
|
if obj.app == to_app:
|
|
@@ -151,6 +151,8 @@ def copy_(
|
|
|
151
151
|
obj_ids: list[int],
|
|
152
152
|
to_app_id: int,
|
|
153
153
|
copy_params: bool,
|
|
154
|
+
*,
|
|
155
|
+
yes: bool = False,
|
|
154
156
|
) -> int:
|
|
155
157
|
to_app = workspace.lookup_app(server, to_app_id)
|
|
156
158
|
objs = []
|
|
@@ -171,7 +173,7 @@ def copy_(
|
|
|
171
173
|
|
|
172
174
|
print(f"The following Objects will be copied to {to_app}:\n")
|
|
173
175
|
render_objects(objs, show_params=copy_params)
|
|
174
|
-
if input("\n[Y/y] to continue:") not in ["Y", "y"]:
|
|
176
|
+
if not yes and input("\n[Y/y] to continue:") not in ["Y", "y"]:
|
|
175
177
|
return 1
|
|
176
178
|
|
|
177
179
|
with (
|
|
@@ -38,6 +38,8 @@ def delete(
|
|
|
38
38
|
workspace: Workspace,
|
|
39
39
|
server: PuakmaServer,
|
|
40
40
|
obj_ids: list[int],
|
|
41
|
+
*,
|
|
42
|
+
yes: bool = False,
|
|
41
43
|
) -> int:
|
|
42
44
|
app_objs: dict[PuakmaApplication, list[DesignObject]] = collections.defaultdict(
|
|
43
45
|
list
|
|
@@ -54,7 +56,7 @@ def delete(
|
|
|
54
56
|
_deleted = Colour.colour("DELETED", Colour.RED)
|
|
55
57
|
print(f"The following Design Objects will be {_deleted}:\n")
|
|
56
58
|
render_objects(all_objs)
|
|
57
|
-
if input("\n[Y/y] to continue:") not in ["Y", "y"]:
|
|
59
|
+
if not yes and input("\n[Y/y] to continue:") not in ["Y", "y"]:
|
|
58
60
|
logger.error("Operation Cancelled")
|
|
59
61
|
return 1
|
|
60
62
|
|
|
@@ -23,7 +23,7 @@ def log(
|
|
|
23
23
|
server: PuakmaServer,
|
|
24
24
|
limit: int,
|
|
25
25
|
source_filter: str | None,
|
|
26
|
-
|
|
26
|
+
message_filter: str | None,
|
|
27
27
|
errors_only: bool,
|
|
28
28
|
info_only: bool,
|
|
29
29
|
debug_only: bool,
|
|
@@ -35,7 +35,7 @@ def log(
|
|
|
35
35
|
s.get_last_log_items,
|
|
36
36
|
limit,
|
|
37
37
|
source_filter,
|
|
38
|
-
|
|
38
|
+
message_filter,
|
|
39
39
|
errors_only,
|
|
40
40
|
info_only,
|
|
41
41
|
debug_only,
|
|
@@ -177,6 +177,8 @@ def _validate_update_objs(
|
|
|
177
177
|
workspace: Workspace,
|
|
178
178
|
server: PuakmaServer,
|
|
179
179
|
design_obj_ids: list[int],
|
|
180
|
+
*,
|
|
181
|
+
yes: bool = False,
|
|
180
182
|
**kwargs: Any,
|
|
181
183
|
) -> tuple[tuple[DesignObject, ...], bool]:
|
|
182
184
|
_universal_keys = ("name", "design_type", "comment", "inherit_from")
|
|
@@ -213,7 +215,7 @@ def _validate_update_objs(
|
|
|
213
215
|
_updated = Colour.colour("UPDATED", Colour.GREEN)
|
|
214
216
|
print(f"The following Design Object(s) will be {_updated}:\n")
|
|
215
217
|
print(tabulate.tabulate(rows, headers=row_headers))
|
|
216
|
-
if input("\n[Y/y] to continue:") not in ["Y", "y"]:
|
|
218
|
+
if not yes and input("\n[Y/y] to continue:") not in ["Y", "y"]:
|
|
217
219
|
exit(1)
|
|
218
220
|
|
|
219
221
|
return tuple(objs), recreate_params
|
|
@@ -223,11 +225,13 @@ def _update_objects(
|
|
|
223
225
|
workspace: Workspace,
|
|
224
226
|
server: PuakmaServer,
|
|
225
227
|
design_obj_ids: list[int],
|
|
228
|
+
*,
|
|
229
|
+
yes: bool = False,
|
|
226
230
|
**kwargs: Any,
|
|
227
231
|
) -> int:
|
|
228
232
|
try:
|
|
229
233
|
updated_objs, recreate_params = _validate_update_objs(
|
|
230
|
-
workspace, server, design_obj_ids, **kwargs
|
|
234
|
+
workspace, server, design_obj_ids, yes=yes, **kwargs
|
|
231
235
|
)
|
|
232
236
|
except (DesignObjectNotFound, DesignObjectAmbiguousError) as e:
|
|
233
237
|
logger.error(e)
|
|
@@ -259,6 +263,8 @@ def _new_object(
|
|
|
259
263
|
parent_page: str | None = None,
|
|
260
264
|
open_action: str | None = None,
|
|
261
265
|
save_action: str | None = None,
|
|
266
|
+
*,
|
|
267
|
+
yes: bool = False,
|
|
262
268
|
) -> int:
|
|
263
269
|
if not _validate_args_for_type(
|
|
264
270
|
design_type,
|
|
@@ -288,7 +294,7 @@ def _new_object(
|
|
|
288
294
|
_created = Colour.colour("CREATED", Colour.GREEN)
|
|
289
295
|
print(f"The following Design Object(s) will be {_created}:\n")
|
|
290
296
|
render_objects(objs, show_params=_show_params)
|
|
291
|
-
if input("\n[Y/y] to continue:") not in ["Y", "y"]:
|
|
297
|
+
if not yes and input("\n[Y/y] to continue:") not in ["Y", "y"]:
|
|
292
298
|
return 1
|
|
293
299
|
|
|
294
300
|
recreate_params = True if parent_page or open_action or save_action else False
|
|
@@ -308,11 +314,13 @@ def _new_app(
|
|
|
308
314
|
inherit_from: str | None,
|
|
309
315
|
template_name: str | None,
|
|
310
316
|
description: str | None,
|
|
317
|
+
*,
|
|
318
|
+
yes: bool = False,
|
|
311
319
|
) -> int:
|
|
312
320
|
app = PuakmaApplication(-1, name, group, inherit_from, template_name, server.host)
|
|
313
321
|
print("The following Application will be created:\n")
|
|
314
322
|
render_apps([app], show_inherited=True)
|
|
315
|
-
if input("\n[Y/y] to continue:") not in ["Y", "y"]:
|
|
323
|
+
if not yes and input("\n[Y/y] to continue:") not in ["Y", "y"]:
|
|
316
324
|
return 1
|
|
317
325
|
|
|
318
326
|
with server as s:
|
|
@@ -342,13 +350,167 @@ def _new_keyword(
|
|
|
342
350
|
return 0
|
|
343
351
|
|
|
344
352
|
|
|
353
|
+
def _pick_from_list(prompt: str, options: list[str]) -> str:
|
|
354
|
+
print(f"\n{prompt}")
|
|
355
|
+
for i, option in enumerate(options, 1):
|
|
356
|
+
print(f" {i}. {option}")
|
|
357
|
+
while True:
|
|
358
|
+
choice = input("Enter number: ").strip()
|
|
359
|
+
try:
|
|
360
|
+
idx = int(choice)
|
|
361
|
+
if 1 <= idx <= len(options):
|
|
362
|
+
return options[idx - 1]
|
|
363
|
+
except ValueError:
|
|
364
|
+
pass
|
|
365
|
+
print(f"Invalid choice. Please enter a number between 1 and {len(options)}.")
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _new_object_wizard(
|
|
369
|
+
workspace: Workspace,
|
|
370
|
+
server: PuakmaServer,
|
|
371
|
+
*,
|
|
372
|
+
yes: bool = False,
|
|
373
|
+
) -> int:
|
|
374
|
+
# Step 1: List locally cloned apps for user to pick from
|
|
375
|
+
print("Creating a new Design Object...\n")
|
|
376
|
+
apps = workspace.listapps(server)
|
|
377
|
+
if not apps:
|
|
378
|
+
logger.error(
|
|
379
|
+
"No locally cloned applications found. Run 'vortex clone <app_id>' first."
|
|
380
|
+
)
|
|
381
|
+
return 1
|
|
382
|
+
|
|
383
|
+
app_labels = [f"[{app.id}] {app}" for app in apps]
|
|
384
|
+
chosen_label = _pick_from_list("Select an application:", app_labels)
|
|
385
|
+
chosen_idx = app_labels.index(chosen_label)
|
|
386
|
+
chosen_app = apps[chosen_idx]
|
|
387
|
+
|
|
388
|
+
# Reload the app from local workspace to get design objects
|
|
389
|
+
app = workspace.lookup_app(server, chosen_app.id)
|
|
390
|
+
|
|
391
|
+
# Step 2: Pick design type
|
|
392
|
+
design_type_names = [t.name.lower() for t in DesignType if t != DesignType.ERROR]
|
|
393
|
+
chosen_type_name = _pick_from_list("Select a design type:", design_type_names)
|
|
394
|
+
design_type = DesignType.from_name(chosen_type_name)
|
|
395
|
+
|
|
396
|
+
# Step 3: Ask for name
|
|
397
|
+
name = ""
|
|
398
|
+
while not name.strip():
|
|
399
|
+
name = input("\nEnter object name: ").strip()
|
|
400
|
+
if not name:
|
|
401
|
+
print("Name cannot be empty.")
|
|
402
|
+
|
|
403
|
+
# Step 4: content_type — RESOURCE only (PAGE auto-sets text/html, others don't support it)
|
|
404
|
+
content_type = None
|
|
405
|
+
if design_type == DesignType.RESOURCE:
|
|
406
|
+
content_type = input(
|
|
407
|
+
"Enter content type (e.g. text/javascript, text/css): "
|
|
408
|
+
).strip()
|
|
409
|
+
if not content_type:
|
|
410
|
+
logger.error("Content type is required for resource type.")
|
|
411
|
+
return 1
|
|
412
|
+
|
|
413
|
+
# Step 5: parent_page, open_action, save_action — PAGE only
|
|
414
|
+
parent_page = None
|
|
415
|
+
open_action = None
|
|
416
|
+
save_action = None
|
|
417
|
+
if design_type == DesignType.PAGE:
|
|
418
|
+
parent_page = (
|
|
419
|
+
input("Enter parent page (optional, press Enter to skip): ").strip() or None
|
|
420
|
+
)
|
|
421
|
+
open_action = (
|
|
422
|
+
input("Enter open action (optional, press Enter to skip): ").strip() or None
|
|
423
|
+
)
|
|
424
|
+
save_action = (
|
|
425
|
+
input("Enter save action (optional, press Enter to skip): ").strip() or None
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
return _new_object(
|
|
429
|
+
workspace,
|
|
430
|
+
server,
|
|
431
|
+
name=name,
|
|
432
|
+
app_id=app.id,
|
|
433
|
+
design_type=design_type,
|
|
434
|
+
content_type=content_type,
|
|
435
|
+
parent_page=parent_page,
|
|
436
|
+
open_action=open_action,
|
|
437
|
+
save_action=save_action,
|
|
438
|
+
yes=yes,
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _new_app_wizard(
|
|
443
|
+
server: PuakmaServer,
|
|
444
|
+
*,
|
|
445
|
+
yes: bool = False,
|
|
446
|
+
) -> int:
|
|
447
|
+
print("Creating a new Application...\n")
|
|
448
|
+
|
|
449
|
+
# Step 1: Ask for app name
|
|
450
|
+
name = ""
|
|
451
|
+
while not name.strip():
|
|
452
|
+
name = input("Enter application name: ").strip()
|
|
453
|
+
if not name:
|
|
454
|
+
print("Name cannot be empty.")
|
|
455
|
+
|
|
456
|
+
# Step 2: Ask for group (show existing groups if available)
|
|
457
|
+
# Note: Do NOT use 'with server as s:' here — the context manager closes the
|
|
458
|
+
# underlying httpx client on exit, which would break the subsequent _new_app() call.
|
|
459
|
+
try:
|
|
460
|
+
apps = server.fetch_all_apps([], [], [], False, False, False)
|
|
461
|
+
existing_groups = sorted({app.group for app in apps if app.group})
|
|
462
|
+
if existing_groups:
|
|
463
|
+
print("\nExisting groups:")
|
|
464
|
+
for i, group in enumerate(existing_groups, 1):
|
|
465
|
+
print(f" {i}. {group}")
|
|
466
|
+
print(f" {len(existing_groups) + 1}. (enter a new group)")
|
|
467
|
+
while True:
|
|
468
|
+
choice = input("Enter number or group name: ").strip()
|
|
469
|
+
try:
|
|
470
|
+
idx = int(choice)
|
|
471
|
+
if 1 <= idx <= len(existing_groups):
|
|
472
|
+
group = existing_groups[idx - 1]
|
|
473
|
+
break
|
|
474
|
+
elif idx == len(existing_groups) + 1:
|
|
475
|
+
group = input("Enter new group name: ").strip()
|
|
476
|
+
break
|
|
477
|
+
except ValueError:
|
|
478
|
+
group = choice
|
|
479
|
+
break
|
|
480
|
+
print(
|
|
481
|
+
f"Invalid choice. Please enter a number between "
|
|
482
|
+
f"1 and {len(existing_groups) + 1}, or a group name."
|
|
483
|
+
)
|
|
484
|
+
else:
|
|
485
|
+
group = input("Enter application group: ").strip()
|
|
486
|
+
except Exception:
|
|
487
|
+
group = input("Enter application group: ").strip()
|
|
488
|
+
|
|
489
|
+
# Step 3: Ask for optional description
|
|
490
|
+
description = (
|
|
491
|
+
input("Enter description (optional, press Enter to skip): ").strip() or None
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
return _new_app(
|
|
495
|
+
server,
|
|
496
|
+
group,
|
|
497
|
+
name,
|
|
498
|
+
inherit_from=None,
|
|
499
|
+
template_name=None,
|
|
500
|
+
description=description,
|
|
501
|
+
yes=yes,
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
|
|
345
505
|
def new(workspace: Workspace, server: PuakmaServer, args: argparse.Namespace) -> int:
|
|
506
|
+
yes = getattr(args, "yes", False)
|
|
346
507
|
if args.subcommand in ("obj", "object"):
|
|
347
|
-
if args
|
|
508
|
+
if getattr(args, "update_ids", None):
|
|
348
509
|
return _update_objects(
|
|
349
510
|
workspace,
|
|
350
511
|
server,
|
|
351
512
|
args.update_ids,
|
|
513
|
+
yes=yes,
|
|
352
514
|
name=args.name,
|
|
353
515
|
design_type=args.design_type,
|
|
354
516
|
content_type=args.content_type,
|
|
@@ -358,6 +520,8 @@ def new(workspace: Workspace, server: PuakmaServer, args: argparse.Namespace) ->
|
|
|
358
520
|
open_action=args.open_action,
|
|
359
521
|
save_action=args.save_action,
|
|
360
522
|
)
|
|
523
|
+
elif not (args.name and args.app_id and args.design_type):
|
|
524
|
+
return _new_object_wizard(workspace, server, yes=yes)
|
|
361
525
|
else:
|
|
362
526
|
return _new_object(
|
|
363
527
|
workspace,
|
|
@@ -371,8 +535,11 @@ def new(workspace: Workspace, server: PuakmaServer, args: argparse.Namespace) ->
|
|
|
371
535
|
parent_page=args.parent_page,
|
|
372
536
|
open_action=args.open_action,
|
|
373
537
|
save_action=args.save_action,
|
|
538
|
+
yes=yes,
|
|
374
539
|
)
|
|
375
540
|
elif args.subcommand == "app":
|
|
541
|
+
if not (args.name and args.group):
|
|
542
|
+
return _new_app_wizard(server, yes=yes)
|
|
376
543
|
return _new_app(
|
|
377
544
|
server,
|
|
378
545
|
args.group,
|
|
@@ -380,6 +547,7 @@ def new(workspace: Workspace, server: PuakmaServer, args: argparse.Namespace) ->
|
|
|
380
547
|
args.inherit_from,
|
|
381
548
|
args.template,
|
|
382
549
|
args.description,
|
|
550
|
+
yes=yes,
|
|
383
551
|
)
|
|
384
552
|
elif args.subcommand in ("kw", "keyword"):
|
|
385
553
|
return _new_keyword(
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import configparser
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from vortex.workspace import Workspace
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger("vortex")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def use(workspace: Workspace, server_name: str) -> int:
|
|
12
|
+
config = configparser.ConfigParser()
|
|
13
|
+
config.read(workspace.server_config_file)
|
|
14
|
+
|
|
15
|
+
available_servers = config.sections()
|
|
16
|
+
if server_name not in available_servers:
|
|
17
|
+
logger.error(
|
|
18
|
+
f"Server '{server_name}' not found in config. "
|
|
19
|
+
f"Available servers: {available_servers}"
|
|
20
|
+
)
|
|
21
|
+
return 1
|
|
22
|
+
|
|
23
|
+
config.set(config.default_section, "default", server_name)
|
|
24
|
+
with open(workspace.server_config_file, "w") as f:
|
|
25
|
+
config.write(f)
|
|
26
|
+
|
|
27
|
+
logger.info(f"Default server set to '{server_name}'")
|
|
28
|
+
return 0
|
|
@@ -28,6 +28,7 @@ from vortex.commands.import_ import import_
|
|
|
28
28
|
from vortex.commands.list import list_
|
|
29
29
|
from vortex.commands.log import log
|
|
30
30
|
from vortex.commands.new import new
|
|
31
|
+
from vortex.commands.use import use
|
|
31
32
|
from vortex.commands.watch import watch
|
|
32
33
|
from vortex.logging import LoggingHandler
|
|
33
34
|
from vortex.logging import logging_handler
|
|
@@ -47,7 +48,7 @@ def error_handler() -> Generator[None]:
|
|
|
47
48
|
except KeyboardInterrupt:
|
|
48
49
|
raise SystemExit(130) from None
|
|
49
50
|
except Exception as e:
|
|
50
|
-
msg = f"An unexpected error
|
|
51
|
+
msg = f"An unexpected error occurred. {e}".rstrip()
|
|
51
52
|
logger.critical(msg)
|
|
52
53
|
LoggingHandler.log_to_file_and_exit(e, 3)
|
|
53
54
|
|
|
@@ -82,6 +83,12 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
82
83
|
action="store_true",
|
|
83
84
|
help="Initialise the workspace and config files, if they don't already exist",
|
|
84
85
|
)
|
|
86
|
+
parser.add_argument(
|
|
87
|
+
"--yes",
|
|
88
|
+
"-y",
|
|
89
|
+
action="store_true",
|
|
90
|
+
help="Skip confirmation prompts and auto-confirm",
|
|
91
|
+
)
|
|
85
92
|
|
|
86
93
|
command_parser = parser.add_subparsers(dest="command")
|
|
87
94
|
cli.add_list_parser(command_parser)
|
|
@@ -97,6 +104,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
97
104
|
cli.add_execute_parser(command_parser)
|
|
98
105
|
cli.add_export_parser(command_parser)
|
|
99
106
|
cli.add_import_parser(command_parser)
|
|
107
|
+
cli.add_use_parser(command_parser)
|
|
100
108
|
|
|
101
109
|
clone_parser = cli.add_clone_parser(command_parser)
|
|
102
110
|
code_parser = cli.add_code_parser(command_parser)
|
|
@@ -135,6 +143,8 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
135
143
|
return code(workspace, remaining_args)
|
|
136
144
|
elif args.command == "docs":
|
|
137
145
|
return docs()
|
|
146
|
+
elif args.command == "use":
|
|
147
|
+
return use(workspace, args.server_name)
|
|
138
148
|
|
|
139
149
|
server = workspace.read_server_from_config(server_name)
|
|
140
150
|
logger.debug(f"Using server {server}")
|
|
@@ -238,7 +248,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
238
248
|
)
|
|
239
249
|
return new(workspace, server, args)
|
|
240
250
|
elif args.command == "delete":
|
|
241
|
-
return delete(workspace, server, args.obj_ids)
|
|
251
|
+
return delete(workspace, server, args.obj_ids, yes=args.yes)
|
|
242
252
|
elif args.command == "copy":
|
|
243
253
|
return copy_(
|
|
244
254
|
workspace,
|
|
@@ -246,6 +256,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
246
256
|
args.ids,
|
|
247
257
|
to_app_id=args.app_id,
|
|
248
258
|
copy_params=args.copy_params,
|
|
259
|
+
yes=args.yes,
|
|
249
260
|
)
|
|
250
261
|
elif args.command == "db":
|
|
251
262
|
return db(
|
|
@@ -269,7 +269,7 @@ class PuakmaServer:
|
|
|
269
269
|
self,
|
|
270
270
|
limit_items: int,
|
|
271
271
|
source_filter: str | None,
|
|
272
|
-
|
|
272
|
+
message_filter: str | None,
|
|
273
273
|
errors_only: bool,
|
|
274
274
|
info_only: bool,
|
|
275
275
|
debug_only: bool,
|
|
@@ -282,8 +282,8 @@ class PuakmaServer:
|
|
|
282
282
|
where.write("WHERE 1=1")
|
|
283
283
|
if source_filter:
|
|
284
284
|
_and("source", source_filter)
|
|
285
|
-
if
|
|
286
|
-
_and("logstring",
|
|
285
|
+
if message_filter:
|
|
286
|
+
_and("logstring", message_filter)
|
|
287
287
|
if last_log_item_id is not None and last_log_item_id > 0:
|
|
288
288
|
where.write(f" AND logid > {last_log_item_id}")
|
|
289
289
|
if errors_only:
|
|
@@ -329,7 +329,7 @@ class PuakmaApplication:
|
|
|
329
329
|
self.id = id_
|
|
330
330
|
self.name = name
|
|
331
331
|
self.group = group
|
|
332
|
-
# Some apps dont have a group. Adding a
|
|
332
|
+
# Some apps dont have a group. Adding a placeholder will
|
|
333
333
|
# allow for consistent paths locally
|
|
334
334
|
self.group_safe = group or "~"
|
|
335
335
|
self.inherit_from = inherit_from
|
|
@@ -310,8 +310,8 @@ class ServerDesigner(_PuakmaSOAPService):
|
|
|
310
310
|
name = "ServerDesigner"
|
|
311
311
|
|
|
312
312
|
async def ainitiate_connection(self) -> str:
|
|
313
|
-
|
|
314
|
-
resp = await self._apost(
|
|
313
|
+
operation = "initiateConnection"
|
|
314
|
+
resp = await self._apost(operation)
|
|
315
315
|
logger.info(f"Connected to {self.server.host}...")
|
|
316
316
|
return str(resp.text)
|
|
317
317
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vortex_cli
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.20.0
|
|
4
4
|
Summary: Vortex CLI
|
|
5
5
|
Author-email: Jordan Amos <jordan.amos@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -109,6 +109,7 @@ For a full list of commands see `--help`.
|
|
|
109
109
|
### Command Overview
|
|
110
110
|
|
|
111
111
|
- `code`: Open the workspace in Visual Studio Code.
|
|
112
|
+
- `use`: Set the default server so you don't need to pass `--server` on every command. e.g. `vortex use production`
|
|
112
113
|
- `list` (or `ls`): List Puakma Applications on the server or cloned locally. (`ls` is an alias for `vortex list --local`)
|
|
113
114
|
- `clone`: Clone Puakma Applications and their design objects into the workspace or in the .pmx format.
|
|
114
115
|
- `watch`: Watch the workspace for changes to Design Objects and automatically upload them to the server.
|
|
@@ -117,9 +118,48 @@ For a full list of commands see `--help`.
|
|
|
117
118
|
- `log`: View the server log.
|
|
118
119
|
- `find`: Find Design Objects of cloned applications by name.
|
|
119
120
|
- `grep`: Search the contents of cloned Design Objects using a Regular Expression.
|
|
120
|
-
- `new`: Create new Design Objects, Applications, or Keywords. Use
|
|
121
|
+
- `new`: Create new Design Objects, Applications, or Keywords. Use `--update <ID>` to update instead. Run without flags to launch an interactive wizard.
|
|
121
122
|
- `copy`: Copy a Design Object from one application to another.
|
|
122
123
|
- `delete`: Delete Design Objects by ID.
|
|
123
124
|
- `db`: Interact with Database Connections.
|
|
124
125
|
- `docs`: Open the Tornado Server Blackbook.
|
|
125
126
|
- `execute`: Execute a command on the server.
|
|
127
|
+
|
|
128
|
+
### Switching Servers
|
|
129
|
+
|
|
130
|
+
If you work with multiple servers, use `vortex use <server>` to set the default instead of passing `--server` on every command:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
vortex use production
|
|
134
|
+
vortex list
|
|
135
|
+
vortex watch
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Interactive Wizards
|
|
139
|
+
|
|
140
|
+
Run `vortex new object` or `vortex new app` without any flags to launch a step-by-step wizard:
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
vortex new object
|
|
144
|
+
# → pick app from locally cloned list
|
|
145
|
+
# → pick design type
|
|
146
|
+
# → enter name
|
|
147
|
+
# → enter content type (resource only)
|
|
148
|
+
# → enter parent page, open action, save action (page only)
|
|
149
|
+
# → preview and confirm
|
|
150
|
+
|
|
151
|
+
vortex new app
|
|
152
|
+
# → enter name
|
|
153
|
+
# → pick from existing groups or enter a new one
|
|
154
|
+
# → enter description (optional)
|
|
155
|
+
# → preview and confirm
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Skipping Confirmation Prompts
|
|
159
|
+
|
|
160
|
+
Pass `--yes` (or `-y`) to skip Y/y confirmation prompts, useful for scripting:
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
vortex --yes delete 123 456
|
|
164
|
+
vortex -y new object --name MyAction --app-id 10 --type action
|
|
165
|
+
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|