Overview
pls is a simple, general-purpose task runner that aims at making common tasks easier to manage and execute. It was inspired by some of the functionalities provided by the nifty package manager, only without the package manager part.
Main Features
pls can be used to:
- Define a catalog of actions to perform on things, which will result in commands to be executed, each with the same simple syntax.
- Define a catalog of things, representing virtually anything that can be the object of a shell command or referred within other things.
- Define a set of dependencies among commands, in the form of commands.
- Manage aliases to commonly-used strings (properties) to use within other sections of the configuration.
Hello, World!
Here’s minimal but quite comprehensive example of how everything works with pls. Given the following pls.yml file (placed in $HOME or in %USERPROFILE% on Windows):
things:
home:
value: /home/h3rald
bin:
value: {{home.value}}/bin
self:
value: {{home.value}}/dev/pls
exe: pls
nimble: true
actions:
build:
nimble+value: cd {{value}} && nimble build -d:release
publish:
exe+value: cd {{value}} && $(cp "{{exe}}" "{{bin.value}}") &
deps:
publish self:
- build self
It will be possible to run the following command to build the pls program itself and copy it to the /home/h3rald/bin:
pls publish self
Key Concepts
pls is based on a few intuitive abstractions that are used for its configuration, as described in the following sections.
Shell Command
A shell command is simply a string that can be passed to the underlying system shell to be executed.
Command
A command is an instruction given to pls to execute a shell command. The syntax of a command is always the following:
action-identifier thing-identifier-1 [… thing-identifier-n]
pls Commands vs. Shell Commands
The word command identifies a pls command, while a command executed by the underlying operating system shell is referred to as a shell command.
Action
An action identifies something that can be done with one or more things. Depending on the thing specified, the action can be configured to execute a different shell command.
Thing
A thing identifies something that can be referenced by an action. There is virtually no restriction on what a thing may represent: it can be a folder, a file, the name of a running process or service, and so on.
Property
A property identifies a trait of a thing. It can be a simple flag or an attribute defining something about a thing that can be used as part of the identifier of an action definition or referenced via a placeholder in action definitions and other property values.
Action Definition
An action definition is defined by an identifier composed by plus-separated properties (e.g.: git+folder+value), and determines what shell command to execute when a command is run.
Dependency
A dependency identifies a command that must be executed before another command. If the shell command executed as a dependency fails, no more dependencies will be executed and neither will the command with the dependencies.
Placeholder
A placeholder is a reference to a property wrapped in double curly brackets that can be used in property values or shell commands.
Getting Started
Downloading Pre-built Binaries
The easiest way to get pls is by downloading one of the prebuilt binaries from the Github Releases Page:
Building from Source
You can also build pls from source, if there is no pre-built binary for your platform.
To do so, after installing the Nim programming language, you can:
- Clone the pls repository.
- Navigate to the pls repository local folder.
- Run nimble build -d:release
Using pls
The first time you run pls, a pls.yml configuration file will be created in your $HOME (%USERPROFILE% on Windows) folder. This YAML configuration file will teach pls everything it needs to know to execute commands and it will be parsed and processed every time pls is executed.
Configuring pls
The pls.yml file contains three sections:
- actions, where each action is defined.
- things, where each thing is defined.
- deps, where each dependency is defined.
Consider the following sample pls.yml file:
things:
home:
value: /home/h3rald
dev:
value: {{home.value}}/dev
bin:
value: {{home.value}}/bin
h3:
value: {{home.dev}}/h3
npm: true
self:
value: {{home.dev}}/{{exe}}
conf: {{home.value}}/{{exe}}.yml
exe: pls
nimble: true
actions:
config:
conf: vim "{{conf}}"
edit:
value: vim "{{value}}"
publish:
exe+value: cd "{{value}}" && $(cp "{{exe}}" "{{bin.value}}") &
build:
npm+value: cd "{{value}}" && npm run build
nimble+value: cd "{{value}}" && nimble build -d:release
deps:
publish self:
- build self
This configuration file should give you an idea of how to configure pls to execute your own custom commands. In this case, it is possible to execute commands like:
- pls publish self
- pls config self
- pls edit h3
- pls build self h3
…and so on. Let’s see how to configure each section more in detail.
Configuring things
Keep in mind that a thing is going to be used as the object or target of your action, so it typically should represent a file, a folder, a URL, a service name, and so on. Also, it makes sense to define new things if they are going to be used often in actions or referenced in other things. In relation to the configuration example, note how the home thing is re-used in dev and bin.
A thing is defined by one or more arbitrary properties. There are virtually no restrictions on what these properties can represent, but they must fit on one line and they will always be parsed as strings, no matter what the highlighting of your pls.yml says. Typically, it makes sense to define a value property for the property that most characterizes the thing, but this is merely a convention, nothing more.
Any property of any thing can be reused via a placeholder:
- in actions
- in other things
Configuring actions
While property identifiers are just straightforward names, actions are identified by a combination of plus-separated property identifiers. Each action can have one or more action definitions, each specifying a different set of property identifiers.
When an action is executed on one or more things, pls will try to match the appropriate action definition based on the properties specified in the action definition. Consider the build action in the configuration example; it is possible to run the following commands:
pls build self
which will result in cd /home/h3rald/dev/pls && nimble build -d:release being executed, and:
pls build h3
which will result in cd /home/h3rald/dev/h3 && npm run build.
Note how a different action definition is triggered depending on the properties of the specified thing. If however you try running the following command:
pls build home
nothing will happen, as there is no matching action definition for home, which only contains a value property.
Tip
If several action definitions match for the specified thing, the one with the most matching properties will be used.
Configuring dependencies
You can use the dependencies section to configure dependencies among commands. Essentially, for each command (comprised of an action followed by one or more thing), you can specify a list comprised of one or more commands that will be executed beforehand.
In the configuration example, executing:
pls publish self
will cause the build self command to be executed beforehand.
Command execution and dependencies
If dependencies are specified for a command:
- all its dependencies are executed sequentially before the command is executed
- if one dependent command fails, no more dependencies will be executed, and the command will not be executed
Executing commands
The previous sections already contain a few examples on how to run commands with pls. Essentially, the syntax is always the same:
pls action thing-1 […thing-n]
So, for example:
pls config self
will execute the config action on the self thing, and:
pls build self h3
will execute execute the build action on the self thing, and then on the h3 thing.
There are no built-in commands, so the first argument specified after pls is interpreted as an action, and the following ones as things. If no things are specified, an error will be printed.
Inspecting commands
If you want to see what shell commands a particular command will execute, or how certain thing properties will be matched or used, you can specify the -i (--inspect) option to print some diagnostic messages and the resulting shell commands, without executing them:
pls build self -i . Command: build self . Thing: self . -> Matching Definition: nimble+value . -> Command Definition: cd {{value}} && nimble build . Resolving placeholder: {{home.value}} -> /home/h3rald . Resolving placeholder: {{dev.value}} -> /home/h3rald/dev . Resolving placeholder: {{self.value}} -> /home/h3rald/dev/pls . -> Resolved Command: cd /home/h3rald/dev/pls && nimble build -d:release
Displaying actions, things, and things
If you want to quickly display the actions, things or dependencies that are available without opening the pls.yml file, you can use the -a (--actions), -t (--things), and -d (--deps) respectively. Unless the -f (--full) option is specified as well, a simply list of actions, things, or dependencies will be displayed.
If you specify the -f (--full) option as well:
- action definitions will be displayed for each action
- properties will be displayed for each thing
- dependent commands will be displayed for each dependency
The pls Configuration YAML Schema
The following schema is based on the YAML Schema extension of JSON Schema Draft 4 and describes the configuration of pls.yml files.
%YAML 1.1
---
$schema: https://h3rald.com/pls/yaml-schema/v1.0.0
id: https://h3rald.com/schemas/pls/metadata-1.0.0
tag: tag:h3rald.com:pls/metadata-1.0.0
title: pls Configuration File
type: object
properties:
things:
type: object
patternProperties:
^[a-z0-9][a-zA-Z0-9_-]+$:
$ref: #/$defs/thing
actions:
type: object
patternProperties:
^[a-z0-9][a-zA-Z0-9_-]+$:
$ref: #/$defs/action
deps:
type: object
patternProperties:
^[a-z0-9][a-zA-Z0-9_-]+)( [a-z0-9][a-zA-Z0-9_-]+)+$:
$ref: #/$defs/dependencies
required: [things, actions]
additionalProperties: false
$defs:
thing:
type: object
patternProperties:
^[a-z0-9][a-zA-Z0-9_-]+$: string
action:
type: object
patternProperties:
^[a-z0-9][a-zA-Z0-9_-]+(\+[a-z0-9][a-zA-Z0-9_-]+)*$: string
dependencies:
type: array
items:
type:
$ref: #/$defs/command
command:
type: string
pattern: |
^[a-z0-9][a-zA-Z0-9_-]+( [a-z0-9][a-zA-Z0-9_-]+)+$