You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/modules/pkl-cli/pages/index.adoc
+316-2Lines changed: 316 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -489,7 +489,9 @@ If these are the only failures, the command exits with exit code 10.
489
489
Otherwise, failures result in exit code 1.
490
490
491
491
<modules>::
492
-
The absolute or relative URIs of the modules to test. Relative URIs are resolved against the working directory.
492
+
The absolute or relative URIs of the modules to test.
493
+
The module must extend `pkl:test`.
494
+
Relative URIs are resolved against the working directory.
493
495
494
496
==== Options
495
497
@@ -546,6 +548,23 @@ Use `--no-power-assertions` to disable this feature if you prefer simpler output
546
548
547
549
This command also takes <<common-options, common options>>.
548
550
551
+
[[command-run]]
552
+
=== `pkl run`
553
+
554
+
*Synopsis:* `pkl run [<options>] [<module>] [<command options>]`
555
+
556
+
Evaluate a <<cli-tools,CLI command>> defined by `<module>`.
557
+
558
+
<module>::
559
+
The absolute or relative URIs of the command module to run.
560
+
The module must extend `pkl:Command`.
561
+
Relative URIs are resolved against the working directory.
562
+
563
+
<command options>::
564
+
Additional CLI options and arguments defined by `<module>`.
565
+
566
+
This command also takes <<common-options, common options>>, but they must be specified before `<module>`.
567
+
549
568
[[command-repl]]
550
569
=== `pkl repl`
551
570
@@ -800,7 +819,7 @@ Write the path of files with formatting violations to stdout.
800
819
[[common-options]]
801
820
=== Common options
802
821
803
-
The <<command-eval>>, <<command-test>>, <<command-repl>>, <<command-project-resolve>>, <<command-project-package>>, <<command-download-package>>, and <<command-analyze-imports>> commands support the following common options:
822
+
The <<command-eval>>, <<command-test>>, <<command-run>>, <<command-repl>>, <<command-project-resolve>>, <<command-project-package>>, <<command-download-package>>, and <<command-analyze-imports>> commands support the following common options:
@@ -901,6 +920,301 @@ If multiple module outputs are written to the same file, or to standard output,
901
920
By default, module outputs are separated with `---`, as in a YAML stream.
902
921
The separator can be customized using the `--module-output-separator` option.
903
922
923
+
[[cli-tools]]
924
+
== Implementing CLI Tools
925
+
926
+
CLI tools can be implemented in Pkl by modules extending the `pkl:Command` module.
927
+
With `pkl:Command`, you can define a script in Pkl that is executed by your shell, providing a better CLI experience.
928
+
929
+
Regular evaluation requires use of xref:language-reference:index.adoc#resources[resources] like properties and evironment variables to provide parameters:
Pkl commands have a few properties that distinguish them from standard module evaluation:
943
+
944
+
* Users provide input to commands using familiar command line idioms, providing a better experience than deriving inputs from xref:language-reference:index.adoc#resources[resources] like external properties or environment variables.
945
+
* Commands can dynamically import modules when they are specified as command line options.
946
+
* Commands may write to standard output (via `output.text` or `output.bytes`) and the filesystem (via `output.files`) in the same evaluation.
947
+
* Command file output may write to any absolute path (not only relative to the `--multiple-file-output-path` option).
948
+
** Relative output paths are written relative to the current working directory (or `--working-dir`, if specified).
949
+
** Paths of output file are printed to the command's standard error.
950
+
951
+
IMPORTANT: Users of `pkl run` must be aware of the security implications of this behavior.
952
+
Using `pkl eval` prevents accidental overwrites by not allowing absolute paths, but `pkl run` does not offer this protection.
953
+
Commands may write to any path the invoking user has permissions to modify.
954
+
955
+
Commands are implemented as regular modules and declare their supported command line flags and positional arguments using a class with annotated properties.
956
+
957
+
=== Defining Commands
958
+
959
+
Commands are defined by creating a module that extends `pkl:Command`:
960
+
961
+
[source,pkl%tested]
962
+
.my-tool.pkl
963
+
----
964
+
/// This doc comment becomes part of the command's CLI help!
965
+
/// Markdown formatting is **allowed!**
966
+
extends "pkl:Command"
967
+
968
+
options: Options // <1>
969
+
970
+
class Options {
971
+
// Define CLI flags/arguments...
972
+
}
973
+
974
+
// Regular module code...
975
+
----
976
+
<1> Re-declaration of the `options` property's type.
977
+
978
+
Like `pkl eval`, when a command completes without an evaluation error the process exits successfully (exit code 0).
979
+
Commands can return a failure using `throw` (exit code 1), but otherwise may not control the exit code.
980
+
981
+
Other than the differences listed above, commands behave like any other Pkl module.
982
+
For example, there is no way to execute other programs or make arbitrary HTTP requests.
983
+
If additional functionality is desired, xref:language-reference:index.adoc#external-readers[external readers] may be used to extends Pkl's capabilities.
984
+
985
+
=== Command Options
986
+
987
+
Each property of a command's options class becomes a command line option.
988
+
Properties with the `local`, `hidden`, `fixed`, and/or `const` modifiers are not parsed as options
989
+
A property's doc comment, if present, becomes the corresponding option's CLI help description.
990
+
Doc comments are interpreted as Markdown text and formatted nicely when displayed to users.
991
+
Properties must have xref:language-reference:index.adoc#type-annotations[type annotations] to determine how they are parsed.
992
+
993
+
Properties may be xref:language-reference:index.adoc#annotations[annotated] to influence how they behave:
994
+
995
+
* Properties annotated with link:{uri-stdlib-Command-Flag}[`@Flag`] become CLI flags named `--<property name>` that accept a value.
996
+
* `Boolean` properties annotated with link:{uri-stdlib-Command-BooleanFlag}[`@BooleanFlag`] become CLI flags named `--<property name>` and `--no-<property name>` that result in `true` and `false` values, respectively.
997
+
* `Int` (and type aliases of `Int`) properties annotated with link:{uri-stdlib-Command-CountedFlag}[`@CountedFlag`] become CLI flags named `--<property name>` that produce a value equal to the number of times they are present on the command line.
998
+
* Properties annotated with link:{uri-stdlib-Command-Argument}[`@Argument`] become positional CLI arguments and are parsed in the order they appear in the class.
999
+
* Properties with no annotation are treated the same as `@Flag` with no further customization.
1000
+
1001
+
Flag options may set a `shortName` property to define a single-character abbreviation (`-<short name>`).
1002
+
Flag abbreviations may be combined (e.g. `-a -b -v -v -f some-value` is equivalent to `-abvvf some-value`).
1003
+
1004
+
A `@Flag` or `@Argument` property's type annotation determines how it is converted from the raw string value:
1005
+
1006
+
|===
1007
+
|Type |Behavior
1008
+
1009
+
|`String`
1010
+
|Value is used verbatim.
1011
+
1012
+
|`Char`
1013
+
|Value is used verbatim but must be exactly one character.
1014
+
1015
+
|`Boolean`
1016
+
|True values: `true`, `t`, `1`, `yes`, `y`, `on`
1017
+
1018
+
False values: `false`, `f`, `0`, `no`, `n`, `off`
1019
+
1020
+
|`Number`
1021
+
|Value is parsed as an `Int` if possible, otherwise parsed as `Float`.
|Each occurrence of the option becomes an element of the final value.
1037
+
1038
+
`Element` values are parsed based on the above primitive types.
1039
+
1040
+
|`Map<Key, Value>`, `Mapping<Key, Value>`
1041
+
|Each occurrence of the option becomes an entry of the final value.
1042
+
1043
+
Values are split on the first `"="` character; the first part is parsed as `Key` and the second as `Value`, both based on the above primitive types.
1044
+
1045
+
|`Pair<First, Second>`
1046
+
|Value is split on the first `"="` character; the first part is parsed as `First` and the second as `Second`, both based on the above primitive types.
1047
+
1048
+
|===
1049
+
1050
+
If a flag that accepts only a single value is provided multiple times, the last occurrence becomes the final value.
1051
+
1052
+
Only a single positional argument accepting multiple values is permitted per command.
1053
+
1054
+
A property with a xref:language-reference:index.adoc#nullable-types[nullable type] is optional and, if not specified on the command line, will have value `null`.
1055
+
Properties with default values are also optional.
1056
+
Type constraints are evaluated when the command is executed, so additional restrictions on option values are enforced at runtime.
1057
+
1058
+
==== Custom Option Conversion and Aggregation
1059
+
1060
+
A property may be annotated with any type if its `@Flag` or `@Argument` annotation sets the `convert` or `transformAll` properties.
1061
+
The `convert` property is a xref:language-reference:index.adoc#anonymous-functions[function] that overrides how _each_ raw option value is interpreted.
1062
+
The `transformAll` property is a function that overrides how _all_ parsed option values become the final property value.
1063
+
1064
+
The `convert` function may return an link:{uri-stdlib-Command-Import}[`Import`] value that is replaced during option parsing with the actual value of the module specified by its `uri` property.
1065
+
If `glob` is `true`, the replacement value is a `Mapping`; its keys are the _absolute_ URIs of the matched modules and its values are the actual module values.
1066
+
When specifying glob import options on the command line, it is often necessary to quote the value to avoid it being interpreted by the shell.
1067
+
If the return value of `convert` is a `List`, `Set`, `Map`, or `Pair`, each contained value (elements and entry keys/values) that are `Import` values are also replaced.
1068
+
1069
+
[IMPORTANT]
1070
+
====
1071
+
If an option has type `Mapping<String, «some module type»>` and should accept a single glob pattern value, the option's annotation must also set `multiple = false` to override the default behavior of `Mapping` options accepting multiple values.
1072
+
Example:
1073
+
[source%parsed,{pkl}]
1074
+
----
1075
+
@Flag {
1076
+
convert = (it) -> new Import { uri = it; glob = true }
1077
+
multiple = false
1078
+
}
1079
+
birds: Mapping<String, Bird>
1080
+
----
1081
+
1082
+
If multiple glob patterns values should be accepted and merged, `transformAll` may be used to merge every glob-imported `Mapping`:
1083
+
[source%parsed,{pkl}]
1084
+
----
1085
+
@Flag {
1086
+
convert = (it) -> new Import { uri = it; glob = true }
Like many other command line libraries, `pkl:Command` allows building commands into a hierarchy with a root command and subcommands:
1099
+
1100
+
[source,pkl%tested]
1101
+
.my-tool.pkl
1102
+
----
1103
+
extends "pkl:Command"
1104
+
1105
+
command {
1106
+
subcommands {
1107
+
import("subcommand1.pkl")
1108
+
import("subcommand2.pkl")
1109
+
for (_, subcommand in import*("./subcommands/*.pkl")) {
1110
+
subcommand
1111
+
}
1112
+
}
1113
+
}
1114
+
----
1115
+
1116
+
[source,pkl%tested]
1117
+
.subcommand1.pkl
1118
+
----
1119
+
extends "pkl:Command"
1120
+
1121
+
import "my-tool.pkl"
1122
+
1123
+
parent: `my-tool` // <1>
1124
+
1125
+
// Regular module code...
1126
+
----
1127
+
<1> Optional; asserts that this is a subcommand of `my-tool` and simplifies accessing properties and options of the parent command
1128
+
1129
+
Each element of `subcommands` must have a unique value for `command.name`.
1130
+
1131
+
=== Testing Commands
1132
+
1133
+
Command modules are normal Pkl modules, so they may be imported and used like any other module.
1134
+
This is particularly helpful when testing commands, as the command's `options` and `parent` properties can be populated by test code.
1135
+
1136
+
Testing the above command and subcommand might look like this:
1137
+
1138
+
[source,pkl%tested]
1139
+
----
1140
+
amends "pkl:test"
1141
+
1142
+
import "my-tool.pkl"
1143
+
import "subcommand1.pkl"
1144
+
1145
+
examples {
1146
+
["Test my-tool"] {
1147
+
(`my-tool`) {
1148
+
options {
1149
+
// Set my-tool options here...
1150
+
}
1151
+
}.output.text
1152
+
}
1153
+
["Test subcommand1"] {
1154
+
(subcommand1) {
1155
+
parent { // this amends `my-tool`
1156
+
options {
1157
+
// Set my-tool options here...
1158
+
}
1159
+
}
1160
+
options {
1161
+
// Set subcommand options here...
1162
+
}
1163
+
}.output.text
1164
+
}
1165
+
}
1166
+
----
1167
+
1168
+
[[commands-as-standalone-scripts]]
1169
+
=== Commands as standalone scripts
1170
+
1171
+
On *nix platforms, Pkl commands can be configured to run as standalone tools that can be invoked without the `pkl run` command.
1172
+
To achieve this, the command file must be marked executable (i.e. `chmod +x my-tool.pkl`) and a link:https://en.wikipedia.org/wiki/Shebang_(Unix)[shebang comment] must be added on the first line of the file:
1173
+
1174
+
[source,pkl%parsed]
1175
+
----
1176
+
#!/usr/bin/env -S pkl run
1177
+
----
1178
+
1179
+
NOTE: The `-S` flag for `env` is required on Linux systems due to a limitation of shebang handling in the Linux kernel.
1180
+
While not required on other *nix platforms like macOS, but it should be included for compatibility.
1181
+
1182
+
==== Shell Completion
1183
+
1184
+
Like with Pkl's own CLI, <<command-shell-completion, shell completions>> can be generated for standalone scripts.
1185
+
1186
+
[source,shell]
1187
+
----
1188
+
# Generate shell completion script for bash
1189
+
./my-tool.pkl shell-completion bash
1190
+
1191
+
# Generate shell completion script for zsh
1192
+
./my-tool.pkl shell-completion zsh
1193
+
----
1194
+
1195
+
==== Customizing Completion Candidates
1196
+
1197
+
`@Flag` and `@Argument` annotations may specify the `completionCandidates` to improve generated shell completions.
1198
+
1199
+
Valid values include:
1200
+
1201
+
* A `Listing<String>` of literal string values to offer for completion.
1202
+
* The literal string `"path"`, which offers local file paths for completion.
1203
+
1204
+
Options with a string literal union type implicitly offer the members of the union as completion candidates.
1205
+
1206
+
=== Flag name ambiguities
1207
+
1208
+
It is possible for commands to define flags with names or short names that collide with Pkl's own command line options.
1209
+
To avoid ambiguity in parsing these options, all flags for Pkl itself (e.g. `--root-dir`) must be placed before the root command module's URI.
1210
+
Command authors are encouraged to avoid overlapping with Pkl's built-in flags, but this may not always be feasible, especially for single-character abbreviated names.
1211
+
1212
+
This imposes a limitation around <<commands-as-standalone-scripts,standalone commands>> that prevents users from customizing Pkl evaluator options when they are invoked.
1213
+
There are two recommended workarounds for this limitation:
1214
+
1215
+
* Use a `PklProject` to define evaluator settings instead of doing so on the command line.
1216
+
* If the command line must be used, switch to invoking via `pkl run [<flags>] [<root command module>]`.
0 commit comments