├── .backstage └── database.json ├── .gitignore ├── .pyrustic └── buildver │ └── build_report ├── LICENSE ├── MANIFEST.in ├── README.md ├── VERSION ├── backstage.tasks ├── backstage ├── __init__.py ├── __main__.py ├── cli │ └── __init__.py ├── constant │ ├── .private │ └── __init__.py ├── core │ ├── .private │ └── __init__.py ├── error │ └── __init__.py ├── namespace │ ├── .private │ └── __init__.py ├── pattern │ ├── .private │ └── __init__.py ├── runner │ ├── .private │ └── __init__.py ├── text │ ├── .private │ └── __init__.py ├── usage │ ├── .private │ └── __init__.py └── util │ ├── .private │ └── __init__.py ├── docs └── modules │ ├── README.md │ └── content │ ├── backstage.cli │ ├── README.md │ └── content │ │ ├── classes │ │ └── Cli.md │ │ └── functions.md │ ├── backstage.error │ ├── README.md │ └── content │ │ ├── classes │ │ ├── Break.md │ │ ├── Continue.md │ │ ├── Error.md │ │ ├── Exit.md │ │ ├── Fail.md │ │ ├── FailedAssertion.md │ │ ├── IndentError.md │ │ ├── InterpretationError.md │ │ ├── Return.md │ │ ├── SubprocessError.md │ │ └── VariableError.md │ │ └── functions.md │ └── backstage │ ├── README.md │ └── content │ ├── classes │ └── Backstage.md │ └── functions.md ├── pyproject.toml ├── setup.cfg ├── setup.py └── tests ├── __init__.py └── __main__.py /.backstage/database.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # File generated by Setupinit 2 | 3 | # Python 4 | __pycache__/ 5 | *.py[cod] 6 | build/ 7 | dist/ 8 | *.egg-info/ 9 | 10 | # Environments 11 | venv/ 12 | env/ 13 | 14 | # PyCharm 15 | .idea/ 16 | 17 | # Databases 18 | *.db 19 | 20 | # Pyrustic local data 21 | .pyrustic/ 22 | 23 | # Backstage local data 24 | .backstage/ 25 | -------------------------------------------------------------------------------- /.pyrustic/buildver/build_report: -------------------------------------------------------------------------------- 1 | 0.0.22 1686305391 2 | 0.0.21 1684428727 3 | 0.0.20 1677288961 4 | 0.0.19 1663938957 5 | 0.0.18 1663938876 6 | 0.0.17 1663365719 7 | 0.0.16 1663360194 8 | 0.0.15 1663358090 9 | 0.0.14 1663356741 10 | 0.0.13 1663346473 11 | 0.0.12 1663341908 12 | 0.0.11 1663284563 13 | 0.0.10 1662592046 14 | 0.0.9 1647894236 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021, 2022, 2023 Pyrustic Evangelist 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include backstage * 2 | global-exclude *.py[cod] 3 | include VERSION 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 | Demo 4 |

5 | By © Jorge Royan / http://www.royan.com.ar, CC BY-SA 3.0, Link 6 |

7 |
8 | 9 | 10 | 11 | 12 | # Pyrustic Backstage 13 | Three-speed scripting language and task automation tool 14 | 15 | This project is part of the [Pyrustic Open Ecosystem](https://pyrustic.github.io). 16 | > [Installation](#installation)     [Demo](#demo)     [Latest](https://github.com/pyrustic/backstage/tags)     [Modules](https://github.com/pyrustic/backstage/tree/master/docs/modules#readme) 17 | 18 | 19 | ## Table of contents 20 | - [Overview](#overview) 21 | - [Structure of the script file](#structure-of-the-script-file) 22 | - [Spawning processes and branching subtasks](#spawning-processes-and-branching-subtasks) 23 | - [Data types and control flow](#data-types-and-control-flow) 24 | - [Namespaces and persistence](#namespaces-and-persistence) 25 | - [Variable interpolation and escaping](#variable-interpolation-and-escaping) 26 | - [Environment variables and language syntax](#environment-variables-and-language-syntax) 27 | - [File and directory manipulation](#file-and-directory-manipulation) 28 | - [Exception handling and tests](#exception-handling-and-tests) 29 | - [Interfacing with Python](#interfacing-with-python) 30 | - [Exception handling and tests](#exception-handling-and-tests) 31 | - [Command line interface and developer experience](#command-line-interface-and-developer-experience) 32 | - [Miscellaneous](#miscellaneous) 33 | - [Demo](#demo) 34 | - [Installation](#installation) 35 | 36 | # Overview 37 | **Backstage** is a cross-platform **automation tool** that looks for a `backstage.tasks` file in the current working directory to run a specific task defined in that file on demand. A task can be a sequence or pipeline of processes to be spawned, instructions for performing file and directory manipulation, or something more sophisticated. 38 | 39 | The `backstage.tasks` file uses the [Jesth](https://github.com/pyrustic/jesth) (**J**ust **E**xtract **S**ections **T**hen **H**ack) file format that acts like a broken [INI file](https://en.wikipedia.org/wiki/INI_file) parser that only extract sections each made of a header and a body which is just a list of lines. 40 | 41 | Using an eponymous **three-speed scripting language** designed for the automation tool, the programmer can, inside the `backstage.tasks` file, define, coordinate and use the various resources at his disposal to automate things. 42 | 43 | The three-speed scripting language concept is inspired from the three forward gear ratios of early automobiles [transmission system](https://en.wikipedia.org/wiki/Manual_transmission). In the following subsections, we will explore each metaphorical gear of the **Backstage** scripting language, then we will briefly expose the automation tool itself. 44 | 45 | ## First gear 46 | In first gear, a `backstage.tasks` file is intended to store a list of tasks related to a specific project, each task exposing a list of commands or subtasks to be executed. Here, a command represents a process or pipeline of processes to be spawned. [Environment variables](#environment-variables-and-language-syntax) can be used in commands via [variable interpolation](https://en.wikipedia.org/wiki/String_interpolation). No other logic is involved. 47 | 48 | ### Example 49 | ``` 50 | [task1] 51 | # three commands to run sequentially 52 | $ git commit -m 'Update' 53 | $ python -m my.package.module 54 | $ program1 arg1 | program2 {HOME} 55 | --- 56 | # run the subtask 'task2' 57 | & task2 58 | --- 59 | # run the subtask 'task3' in a new thread 60 | ~ task3 61 | 62 | [task2] 63 | $ program val1 {CWD} 64 | $ git push origin master 65 | 66 | [task3] 67 | # some heavy computation 68 | $ engine -x 5000 69 | $ engine --cleanup 70 | 71 | [_task4] 72 | # This is a private task (with an underscore as prefix) 73 | $ clean dir 74 | ``` 75 | 76 | ## Second gear 77 | In second gear, the `backstage.tasks` file not only stores the tasks like in first gear, but here logic intervenes, variables are defined, control flow is used, built-in commands are called, et cetera. Basically, in second gear, **Backstage** unleashes its power and allows the programmer to anticipate problems, make sophisticated combinations of subtasks, in short to write a real **script to automate things**. 78 | 79 | ### Example 80 | ``` 81 | [task1] 82 | # commit changes 83 | $ git commit -m 'Update' 84 | --- 85 | # tell user if 'Commit' has been success 86 | if R == 0 87 | # print 'Success !' 88 | : Success ! 89 | else 90 | : Failed to commit changes 91 | --- 92 | # say hello ten times 93 | set age = 42 94 | set name = `John Doe` 95 | from 1 to 10 96 | $ python -m say.hello {name} {age} 97 | --- 98 | # create a file in user home 99 | set pathname = {HOME}/iliad.txt 100 | create file pathname 101 | --- 102 | # append some data to a file 103 | set data = `\nHello World !` 104 | append data to pathname 105 | --- 106 | # browse current working directory 107 | browse files and dirs in CWD 108 | : Directory -> {R} 109 | : Files -> 110 | for item in files 111 | : - {item} 112 | : Dirs -> 113 | for item in dirs 114 | : - {item} 115 | ``` 116 | 117 | ## Third gear 118 | In third gear, in addition with whatever can be done with previous gears, the programmer can directly from the `backstage.tasks` file, call [Python](https://www.python.org/about/) functions with arguments and get the return ! Thanks to this third gear, any too complex or overly verbose calculation can be written in **Python** and called from **Backstage**. This [functionality](#interfacing-with-python) alone proves that **Backstage** is all about making the programmer's life better, not pretending to replace existing mature solutions that actually work. 119 | 120 | ### Example 121 | ``` 122 | [task1] 123 | interface with package.coffeemaker alias coffeebro 124 | 125 | set sugar_cubes (int) = 1 + 1 + 1 126 | set extra = `milk` 127 | 128 | call coffeebro.make(sugar_cubes, extra, 42) 129 | 130 | if R == 1 131 | : Coffee successfully made ! 132 | else 133 | : Oops, failed to make coffee... 134 | : Exception -> {EXCEPTION} 135 | : Traceback -> {TRACEBACK} 136 | ``` 137 | 138 | ## Automation tool 139 | The scripting language help to define tasks in the `backstage.tasks` file that is intended to be consumed by the automation tool. As an automation tool, **Backstage** exposes a [command line interface](#command-line-interface-and-developer-experience) that allows the user to **discover** available tasks, **run** a task with arguments, read a task [documentation](#embedded-documentation-and-tests), use a [glob](https://en.wikipedia.org/wiki/Glob_(programming))-like syntax to **search** for a task by its name or by a keyword (case-insensitive mode) that is part of the documentation of the task, et cetera. 140 | 141 | ### Example 1 142 | Let's assume that there is a `backstage.tasks` file in the directory `/home/alex/project`. This `backstage.tasks` file contains three public tasks and one private tasks (prefixed with an underscore). 143 | 144 | This example shows how one could use the automation tool to run a task defined in the `backstage.tasks` file: 145 | 146 | ```bash 147 | $ cd /home/alex/project 148 | 149 | $ backstage -c 150 | Available tasks (3): 151 | make_coffee task1 task2 152 | 153 | $ backstage make_coffee 154 | Making coffee... 155 | 156 | $ backstage mak* 157 | Making coffee... 158 | 159 | $ backstage make_coffee sugar=3 160 | Making coffee with 3 sugar cubes... 161 | ``` 162 | 163 | ### Example 2 164 | This is the contents of a `backstage.tasks` file located at `/home/alex/project`: 165 | 166 | ``` 167 | [task] 168 | # define 'x' as a variable with an 'int' assignment tag 169 | set x (int) = 1 + 1 + 1 170 | 171 | # default name (by default, the assignment tag is 'str') 172 | set default_name = `John Doe` 173 | 174 | # print some text 175 | : Hi and Welcome ! 176 | : I can print your name {x} times in a row ! 177 | 178 | # take user input (always a 'str') 179 | > name : `What is your name ? ` 180 | 181 | # control flow 182 | if name == EMPTY 183 | set name = {default_name} 184 | 185 | # iteration 186 | from 1 to x 187 | : {R} - Ave {name} ! 188 | 189 | # branch subtask '_task2' and pass it an argument 190 | & _task2 {name} 191 | 192 | 193 | [_task2] 194 | # define the variable 'name' (first argument passed to this task) 195 | set name = {ARGS[0]} 196 | 197 | # Just say Goodbye ! 198 | : Goodbye {name} ! 199 | 200 | ``` 201 | 202 | Let's run the task named `task` from the command line: 203 | 204 | ```bash 205 | $ cd /home/alex/project 206 | 207 | $ backstage task 208 | Hi and Welcome ! 209 | I can print your name 3 times in a row ! 210 | What is your name ? Alex 211 | 1 - Ave Alex ! 212 | 2 - Ave Alex ! 213 | 3 - Ave Alex ! 214 | Goodbye Alex ! 215 | ``` 216 | 217 | > **Note:** You can reproduce this example as it. Just [install](#installation) **Backstage**, copy-paste the script in a `backstage.tasks` file, then run `backstage task` in the command line. 218 | 219 | ## And more 220 | There is more to talk about **Backstage**, like the ability to embed documentation and tests in the `backstage.tasks` file and access them from the command line. Backstage is enough versatile to do the job of a trivial task runner or to automate things with its scripting language that has a built-in bridge to the powerful Python ecosystem. 221 | 222 | In the following sections, we will explore this project in depth. You can also jump to the [demo](#demo) to start playing with **Backstage** ! 223 | 224 | # Structure of the script file 225 | As stated in the [Overview](#overview) section, a `backstage.tasks` file is basically a [JesthFile](https://github.com/pyrustic/jesth). 226 | 227 | In a `backstage.tasks` file, a section represents a task. The section title is the name of the task and the section body is made of commands to run and the constructs of the **Backstage** scripting language. A valid task name is an alphanumeric string that can contains an underscore. 228 | 229 | ``` 230 | You can write here at the top of the script, 231 | a description of the script, 232 | the date of its creation, 233 | or any useful information. 234 | 235 | [task1] 236 | # body of task1 237 | ... 238 | 239 | [task2] 240 | # body of task2 241 | ... 242 | 243 | [_private] 244 | # prefix a task name with an underscore 245 | # to turn it into a private task that 246 | # won't appear in the list of available tasks 247 | # when you will type 'backstage --check' in the command line 248 | ``` 249 | 250 | As you can guess, a line starting with `#` is a comment. But this is only true inside a task body, because in fact not all sections are tasks as described before: a section can also be an embedded test or documentation. 251 | 252 | 253 | ## Embedded documentation 254 | You can embed documentation inside the `backstage.tasks` file. To create a documentation for a task, create a section which name is postfixed with `.help`: 255 | 256 | ``` 257 | [task1] 258 | pass 259 | 260 | 261 | [task1.help] 262 | This is the description line. 263 | 264 | Usage: 265 | backstage task