Nifty_UserGuide.md
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 |
% Nifty User Guide % Fabio Cevasco % - ## Overview {{n -> *nifty*}} is a simple, self-contained program that can be used as a bare-bones, decentralized (pseudo) package manager and script runner. It was born out of the necessity of building {{nim -> [Nim](https://nim-lang.org)}} programs with several dependencies like [min](https://h3rald.com/min) or [hastysite](https://h3rald.com/hastysite) on machines with low memory (i.e. a VPS running x86 Linux with 500MB of RAM). The main problem was that on such low-end machine it [may not even be possible](https://github.com/nim-lang/nimble/issues/278) to compile the [Nimble](https://github.com/nim-lang/nimble) package manager, because apparently it requires more RAM to compile than Nim itself. Nimble offers a lot of features that _proper_ package managers do, like dependency management, package creation and publishing, support for semantic versioning, etc. while {{n}} does not. Hence {{n}} is only a \_pseudo-\_package manager and script runner, but it could be useful in certain situations nonetheless. ### Main features In a nutshell, {{n}} is a program that executes user-defined scripts on a set of folders or files within a user-define package folder. It doesn't do (almost) anything by itself, it just relies on other programs and utilities that are typically already available on your system like [git](https://git-scm.com) and [curl](https://curl.haxx.se) to do all the heavy-lifting. #### Run side-by-side your existing package manager {{n}} doesn't claim to replace your existing package manager, therefore it tries not to get too much in the way of your existing project structure. All it needs to work resides in a humble {{nj -> `nifty.json`}} file that is used to: - keep track of what packages are part of the current project - provide the full definition of all the available commands and how to execute them on specific packages The folder where packages will be stored is by default set to a [packages](class:kwd) subfolder within the current project directory, but even this can be configured in the {{nj}} file. #### Define your own packages For {{n}}, a package can be a folder containing files, or even a single files. Through the {{nj}} file, you can define: - The _source_ of a package (typically a git repository or event just a URL). - Whether the package supports _git_, _curl_ or any other command that will be used to retrieve its contents. {{example-package -> > %sidebar% > Example package > > ``` > "niftylogger.nim": { > "name": "niftylogger.nim", > "src": "https://github.com/h3rald/nifty/blob/master/lib/niftylogger.nim", > "curl": true > } > ``` > > }} > %warning% > Important > > {{n}} does not support nor understand versioning of any kind. It will not attempt to figure out what version of software you need unless you tell it. This is by design, to keep things simple. #### Define your own commands You can use your {{nj}} to teach {{n}} new tricks, i.e. how to execute new commands on packages. Your commands look like... well, CLI commands, except that you can use placeholders like `{\{name}\}` and `{\{src}\}` in them for your package name, source, etc. {{example-command -> > %sidebar% > Example command > > ``` > "install": { > "_syntax": "install [<package>]", > "_description": "Installs the specified package (or all mapped packages) to the storage directory.", > "git+src": { > "cmd": "git clone {\{src}\} --depth 1" > }, > "git+src+tag": { > "cmd": "git clone --branch \{\{tag\}\} \{\{src\}\} --depth 1" > }, > "curl+src+name": { > "cmd": "curl \{\{src\}\} -o \{\{name\}\}" > } > } > ``` > > }} #### Run on many different platforms and regardless of the type of project {{n}} is a self-contained executable program written in {{nim -> [Nim](https://nim-lang.org)}} and runs on all platforms where Nim compiles. Also, unlike other package managers that are typically used within the context of one specific programming language (like [NPM](https://www.npmjs.com) for Javascript or [RubyGems](https://rubygems.org) for Ruby), {{n}} can be used in virtually any project, regardless of the programming language used. ## Getting started ### Downloading Pre-built Binaries {# release -> [nifty for $1]({{release}}/dowload/{{$version}}/nifty_v{{$version}}_$2.zip)#} The easiest way to get {{n}} is by downloading one of the prebuilt binaries from the [Github Releases Page]({{release -> https://github.com/h3rald/nifty/releases}}): - {#release||Mac OS X (x64)||macosx_x64#} - {#release||Windows (x64)||windows_x64#} - {#release||Linux (x64)||linux_x64#} ### Building from Source You can also build {{n}} from source, if there is no pre-built binary for your platform. To do so, after installing the {{nim}} programming language, you can: 3. Clone the nifty [repository](https://github.com/h3rald/nifty). 4. Navigate to the [nifty](class:dir) repository local folder. 5. Run **nim c -d:release nifty.nim** ## Using nifty To initialize a new project, run the following command within a folder (it doesn't have to be empty): > %terminal% > > nifty init > Project initialized using 'packages' as storage directory. {{n}} will create a file like the following: ``` { "storage": "packages", "commands": { "install": { "_syntax": "install [<package>]", "_description": "Installs the specified package (or all mapped packages) to the storage directory.", "git+src": { "cmd": "git clone {\{src}\} --depth 1" }, "git+src+tag": { "cmd": "git clone --branch \{\{tag\}\} \{\{src\}\} --depth 1" }, "curl+src+name": { "cmd": "curl \{\{src\}\} -o \{\{name\}\}" } }, "upgrade": { "_syntax": "upgrade [<package>]", "_description": "Upgrades the specified previously-installed package (or all packages).", "git+name": { "cmd": "git pull", "pwd": "\{\{name\}\}" }, "curl+src+name": { "cmd": "curl \{\{src\}\} -o \{\{name\}\}" } } }, "packages": {} } ``` ### Managing packages After initializing a project, you'd probably want to add some packages your project depends on. First, you must teach {{n}} where and how to retrieve your packages; this is done by executing the [map](#map) command, which is used essentially to add or modify a package to the {{nj}} file. Suppose you want to include "NiftyLogger" in your project. NiftyLogger is a simple logger module written in Nim, and it is constituted by a single file, available within the nifty repository itself: <https://github.com/h3rald/nifty/blob/master/lib/niftylogger.nim> You can create a **niftylogger.nim** package by running `nifty map niftylogger.nim` and specifying some properties that will help {{n}} manage your package: > %terminal% > nifty map niftylogger > Mapping new package: niftylogger > (!) Specify properties for package 'niftylogger': > -> Name: src > -> Value: "https://github.com/h3rald/nifty/blob/master/lib/niftylogger.nim" > (!) OK? [y/n]: y > (!) Do you want to add/remove more properties? [y/n]: y > -> Name: curl > -> Value: true > (!) OK? [y/n]: y > (!) Do you want to add/remove more properties? [y/n]: n > Adding package mapping 'niftylogger'... > src: "https://github.com/h3rald/nifty/blob/master/lib/niftylogger.nim" > curl: true > Package mapping 'niftylogger' saved. The resulting package definition within the {{nj}} file is the following: #example_package# {{example-package}} You can now: - Install the package using the [install](#install) command. - Remove the package mapping using the [unmap](#unmap) ### Managing commands By default, when you [initialize a project](#Using-nifty) the generated {{nj}} file contains two default custom command configurations: - [install](#install) - [upgrade](#upgrade) These commands can be used to respectively install and upgrade packages using git or curl. Consider for example the definition of the **install** command: #example_command# {{example-command}} Apart for the **\_syntax** and the **\_description** properties that are used internally by the [help](#help) command, the other properties specify different command-line commands to execute on projects, depending on the properties that have been defined for them. ### Executing commands Considering for example the [example of package definition](#example_package) for niftylogger, executing `nifty install niftylogger` will effectively execute the following command: `curl https://github.com/h3rald/nifty/blob/master/lib/niftylogger.nim -o niftylogger.nim` Essentially, when a command is executed, {{n}} will: 1. Retrieve the specified package definition within the {{nj}} file. 2. Retrieve the specified command within the {{nj}} file. 3. find a suitable definition for the specified package. Essentially, {{n}} will try to look for the command definition related to the specified command that most closely matches the properties defined for the specified package. In this case, given the [example install command](#example_command) and the [example niftylogger package](#example_package): 1. The package definition for **niftylogger** is retrieved. 2. The command configuration for **install** is retrieved. 3. The **git+src** command definition is retrieved, because: > %unstyled% > > - [](class:square) The definition **git+src+tag** cannot be used, because niftylogger does not contain a **git** or a **tag** property. > - [](class:square) The definition **git+src** cannot be used, because niftylogger does not contain a **git** property. > - [](class:check) The definition **curl+src+name** is used, because niftylogger has a **name**, a **src**, and a **curl** property. > %sidebar% > Command execution rules > > - If a package is specified when executing a command, the command will be executed only on the specified package. > - If the specified package is also a valid {{n}} project (i.e. it contains a valid {{nj}} file), the same command will be executed on all the packages specified in the {{nj}} file, and so on, recursively. > - If no packages are specified when executing a command, that command will be executed on _all packages_ specified in the {{nj}} file, recursively. In a similar way, you can modify the [commands](#commands) section of the {{nj}} file and create your own command configurations. Each command configuration can have one or more command definitions identified by the property placeholders used. > %tip% > Tip > > {{n}} will always try to match the most specific command definition within any given configuration, i.e. the one with the most matching placeholders. ### Executing task lists [Task Lists](#tasklists) are simple lists of commands to be executed on specific packages. To execute a tasklist, simply specify the name of the tasklist prepended with a `$`, like this: `nifty $start-all` In this case, the commands defined in the **start-all** task list will be executed. ## The {{nj}} file format The {{nj}} file contains information on the current _project_ (for {{n}}, a project is simply a folder with a {{nj}} in it), organized into three main sections: - storage - commands - packages The following is an example of {{nj}} file taken from the [min](https://github.com/h3rald/min) repository: ``` { "storage": "packages", "commands": { "install": { "git+src": { "cmd": "git clone {\{src}\} --depth 1" }, "git+src+tag": { "cmd": "git clone --branch {\{tag}\} {\{src}\} --depth 1" }, "curl+src+name": { "cmd": "curl {\{src}\} -o {\{name}\}" }, "_syntax": "install [<package>]", "_description": "Installs the specified package (or all mapped packages) to the storage directory." }, "upgrade": { "_syntax": "upgrade [<package>]", "_description": "Upgrades the specified previously-installed package (or all packages).", "git+name": { "cmd": "git pull", "pwd": "{\{name}\}" }, "curl+src+name": { "cmd": "curl {\{src}\} -o {\{name}\}" } } }, "packages": { "nim-sgregex": { "name": "nim-sgregex", "src": "https://github.com/h3rald/nim-sgregex.git", "git": true }, "nim-miniz": { "name": "nim-miniz", "src": "https://github.com/h3rald/nim-miniz.git", "git": true }, "nimSHA2": { "name": "nimSHA2", "src": "https://github.com/jangko/nimSHA2.git", "git": true }, "sha1": { "name": "sha1", "src": "https://github.com/onionhammer/sha1.git", "git": true }, "nimline": { "name": "nimline", "src": "https://github.com/h3rald/nimline.git", "git": true }, "niftylogger.nim": { "name": "niftylogger.nim", "src": "https://raw.githubusercontent.com/h3rald/nifty/master/lib/niftylogger.nim", "curl": true } } } ``` ### storage This property is used to specify which directory within the current directory contains (or will contain) packages. By default, it is set to **packages**. ### commands Custom command configurations are placed within the `commands` object. Each command typically contains the following system properties: \_syntax : The syntax of the command, displayed when the [help](#help) command is executed. \_description : A brief description of the command, displayed when the [help](#help) command is executed. And one or more command definitions, each identified by the placeholders used in it. Command definition contain the following properties: cmd : A command to execute on a specified, containing placeholders for package properties. By default, the **name** property is always available for all packages, and corresponds to the package identifier. pwd _(optional)_ : The directory where the command will be executed (relative to the [storage](#storage) directory specified in the {{nj}} file). > %note% > Notes > > - Command definition identifiers must be composed by plus-separated property names, e.g. **git+src**, or **wget+src+name**. > - You can use any of the property names specified in the command definition identifier as placeholders within its definition. #### Command configuration example Consider the following command configuration for the default [upgrade](#upgrade) custom command: ``` "upgrade": { "_syntax": "upgrade [<package>]", "_description": "Upgrades the specified previously-installed package (or all packages).", "git+name": { "cmd": "git pull", "pwd": "\{\{name\}\}" }, "curl+src+name": { "cmd": "curl \{\{src\}\} -o \{\{name\}\}" } } ``` In this case, there are two command definitions: - git+name - curl+src+name ### packages Packages definitions are placed in a `packages` object. Each package must be identified uniquely by a name, and can contain arbitrary properties that will be used when [executing commands](Executing-commands). Typically, you should create properties identifying: - Where to get the package from, i.e. an URL to a file to download, a git repository, or whatever can be fetched. Typically a property called **src** is used for this purpose. - How to get the package, i.e. the name of the actual command to run. Typically, you can just define boolean properties named **git**, **curl**, **fossil**, etc. Optionally, you can also specify a **dir** property that will be used by default as the working directory for all the commands executed on the package. #### Package definition example Consider the following definition for a package called **nimSHA2**: ``` "nimSHA2": { "name": "nimSHA2", "src": "https://github.com/jangko/nimSHA2.git", "git": true } ``` In this case, the package can be fetched from a git repository. ### tasklists Tasklist definitions are optional, but they can be used to execute several commands on specific packages in sequence. #### Task list definition example Considering the following tasklist definition to execute a custom **start** command on several packages, one after the other: ``` "start-all": [ "start mydbserver", "start myapi", "start myuiserver" ] ``` {#command -> ### $1 #### Syntax ``` nifty $2 ``` #### Description $3 #} ## Default system commands The following sections describe the default system commands. Unlike [custom commands](#Default-custom-commands), system commands _cannot_ be customized and do not require external programs to run. {#command||help||help [<command>]|| Display help on the specified command (or all commands). #} {#command||info||info <package>|| Displays information on <package> (essentially all its properties).#} {#command||init||init [<storage-dir>]|| Initializes a project in the current directory (using <storage-dir> as storage directory).#} {#command||list||list|| Lists all dependencies (recursively) of the current project.#} {#command||map||map <package>|| Configures a new or existing package <package>. The configuration of the package properly is interactive: {{n}} will prompt whether you want to add or modify properties.#} {#command||purge||purge|| Deletes the storage directory and all its contents.#} {#command||remove||remove [<package>]|| Physically deletes the specified package (or all packages) from the storage directory. This command asks for confirmation before deleting each package.#} {#command||unmap||unmap <package>|| Unmaps the previously-mapped package <package>, removing all its properties from the {{nj}} file.#} {#command||update||update|| Updates the command definitions for the current project and migrate nifty.json file (if necessary). This program effectively updates your {{nj}} file adding/updating commands and settings, but it does not attempt to remove any custom command configurations or definitions that you may have added.#} ## Default custom commands {#command||install||install [<package>]|| Installs the specified package (or all mapped packages) to the storage directory, using the matching command definition. By default, the following definitions are available for this command: ``` "git+src": { "cmd": "git clone {\{src}\} --depth 1" }, "git+src+tag": { "cmd": "git clone --branch {\{tag}\} {\{src}\} --depth 1" }, "curl+src+name": { "cmd": "curl {\{src}\} -o {\{name}\}" } ```#} {#command||upgrade||upgrade [<package>]|| Upgrades the specified previously-installed package (or all packages). By default, the following definitions are available for this command: ``` "git+name": { "cmd": "git pull", "pwd": "{\{name}\}" }, "curl+src+name": { "cmd": "curl {\{src}\} -o {\{name}\}" } ```#} |