commit a65cbaf581cfcdb336dd9f40c4575d28497cf704 Author: Charles N Wyble Date: Mon Dec 9 12:44:28 2024 -0600 Squashed 'vendor/git.knownelement.com/ExternalVendorCode/bash3boilerplate/' content from commit 2e878ec git-subtree-dir: vendor/git.knownelement.com/ExternalVendorCode/bash3boilerplate git-subtree-split: 2e878ec30f0572ad80f34743b6136536cf7c202f diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..57a9617 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,7 @@ +Thanks for contributing to b3bp! As part of your PR, have you: + +- [ ] Added an item in [CHANGELOG.md](https://github.com/kvz/bash3boilerplate/blob/HEAD/CHANGELOG.md) with attribution? +- [ ] Added your name to the [README.md](https://github.com/kvz/bash3boilerplate/blob/HEAD/README.md#authors) +- [ ] Linted your code? (`make test` should do the trick) + +If so, great! Feel free to replace this message with a description of your work and hit submit! diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2d2b2b8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,5 @@ +groups: + production-dependencies: + dependency-type: 'production' + development-dependencies: + dependency-type: 'development' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a660a17 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +name: b3bp CI +on: + push: + branches: + - main + pull_request: + types: + - opened + - synchronize +jobs: + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 1 + - uses: actions/setup-node@v1 + with: + node-version: 18.x + - name: Install + run: | + corepack yarn + - name: Lint + env: + SHELLCHECK_SEVERITY: warning + run: | + corepack yarn lint + - name: Test + run: | + corepack yarn test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2213a68 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.yarn +assets/build +env.sh +node_modules +npm-debug.log diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 0000000..2ebe56a --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,3 @@ +external-sources=true +shell=bash +color=always diff --git a/.vscode/bash3boilerplate.code-workspace b/.vscode/bash3boilerplate.code-workspace new file mode 100644 index 0000000..64bfb87 --- /dev/null +++ b/.vscode/bash3boilerplate.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": ".." + } + ], + "settings": {} +} diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..12afb68 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,175 @@ +# Changelog + +Here's is a combined todo/done list. You can see what todos are planned for the upcoming release, as well as ideas that may/may not make into a release in `Ideas`. + +## Ideas + +Unplanned. + +- [ ] Better style guide checking (#84) + +## main + +Released: TBA. +[Diff](https://github.com/kvz/bash3boilerplate/compare/2.7.2...main). + +- [ ] + +## 2.7.2 + +Released: 2023-08-29 +[Diff](https://github.com/kvz/bash3boilerplate/compare/v2.4.1...2.7.2). + +- [x] Upgrade and cleanup node dependencies +- [x] Remove lanyon-based website in favor of simple redirect to github for bash3boilerplate.sh +- [x] Make tests pass again +- [x] Make linting and style checking separate actions +- [x] Add feature to edit/update comments in ini file (#132, @rfuehrer) +- [x] Upgrade to `lanyon@0.1.16` +- [x] Capture correct error_code in err_report (#124, @eval) +- [x] Enhanced ini file handling: create new file, create new sections, handle default section, read key from given section (@rfuehrer) + +## v2.4.2 + +Released: 2019-11-07. +[Diff](https://github.com/kvz/bash3boilerplate/compare/v2.4.1...v2.4.2). + +- [x] Upgrade to `lanyon@0.1.16` +- [x] Capture correct error_code in err_report (#124, @eval) +- [x] Enhanced ini file handling: create new file, create new sections, handle default section, read key from given section (@rfuehrer) + +## v2.4.1 + +Released: 2019-11-07. +[Diff](https://github.com/kvz/bash3boilerplate/compare/v2.3.0...v2.4.1). + +- [x] Upgrade to `lanyon@0.1.7` +- [x] Allow counting how many times an argument is used (@genesiscloud) +- [x] Fix typos in megamount (thanks @gsaponaro) +- [x] Enable color in screen or tmux (#92, @gmasse) +- [x] Change `egrep` to `grep -E` in test and lib scripts to comply with ShellCheck (#92, @gmasse) +- [x] Fix typo in FAQ (#92, @gmasse) +- [x] Fix Travis CI failure on src/templater.sh (@gmasse) +- [x] Add magic variable which contains full command invocation +- [x] More contrasted alert and emergency colors (#111 @gmeral) +- [x] Add support for repeatable arguments (@genesiscloud) +- [x] Fix remaining warnings with shellcheck v0.7.0 (#107, @genesiscloud) + +## v2.4.0 + +Released: 2016-12-21. +[Diff](https://github.com/kvz/bash3boilerplate/compare/v2.3.0...v2.4.0). + +- [x] Upgrade to `lanyon@0.0.143` + +## v2.3.0 + +Released: 2016-12-21. +[Diff](https://github.com/kvz/bash3boilerplate/compare/v2.2.0...v2.3.0). + +- [x] Add magic variable `__i_am_main_script` to distinguish if b3bp is being sourced or called directly (#45, @zbeekman) +- [x] Add style checks for tab characters and trailing whitespace (@zbeekman) +- [x] Add backtracing to help localize errors (#44, @zbeekman) +- [x] Additional FAQ entries (#47, suggested by @gdevenyi, implemented by @zbeekman) +- [x] Ensure that shifting over `--` doesn't throw an errexit error (#21, @zbeekman) +- [x] Add Pull Request template (#83) + +## v2.2.0 + +Released: 2016-12-21. +[Diff](https://github.com/kvz/bash3boilerplate/compare/v2.1.0...v2.2.0). + +- [x] README and FAQ improvements (#66, @mstreuhofer) +- [x] Add support for sourcing b3bp (#61, @mstreuhofer) +- [x] Upgrade all Node.js dependencies for development (#78) +- [x] Switch to http://lanyon.io for static site building, add a new logo +- [x] Cleanup environment variables (#58, @mstreuhofer) +- [x] Support multi-line logs (#57, @mstreuhofer) +- [x] Run shellcheck as part of the acceptance test (#79, @mstreuhofer) +- [x] Brace all variables, used `[[` instead of `[` (#33, #76, @mstreuhofer) +- [x] Add automatic usage validation for required args (#22, #65, @mstreuhofer) +- [x] Remove all usage of eval (@mstreuhofer) +- [x] Get rid of awk, sed & egrep usage (#71, @mstreuhofer) +- [x] Fix auto-color-off code (#69, #70, @mstreuhofer) +- [x] Use shellcheck to find and fix unclean code (#68, #80, @mstreuhofer) +- [x] Allow for multiline opt description in `__usage` (#7, @mstreuhofer) +- [x] Allow `__usage` and `__helptext` to be defined before sourcing `main.sh` thus makeing b3bp behave like a library (@mstreuhofer) +- [x] Add the same License text to each script header (@mstreuhofer) + +## v2.1.0 + +Released: 2016-11-08. +[Diff](https://github.com/kvz/bash3boilerplate/compare/v2.0.0...v2.1.0). + +- [x] Cleanup b3bp variables (adds prefixes across the board) (thanks @mstreuhofer) +- [x] Add multi-line logging support (thanks @mstreuhofer) +- [x] Mangle long-option names to allow dashes (thanks @zbeekman) +- [x] Remove OS detection altogether (#38, thx @zbeekman) +- [x] Offer the main template for download as http://bash3boilerplate.sh/main.sh +- [x] Better OS detection (#38, thx @moviuro) +- [x] Improve README copy (#34, thx galaktos) +- [x] Fix unquoted variable access within (#34 thx galaktos) +- [x] For delete-key-friendliness, bundle the commandline definition block along with its parser +- [x] Less verbose header comments +- [x] For delete-key-friendliness, don't crash on undeclared help vars +- [x] Introduce `errtrace`, which is on by default (BREAKING) +- [x] Add a configurable `helptext` that is left alone by the parses and allows you to have a richer help +- [x] Add a simple documentation website +- [x] Add best practice of using `__double_underscore_prefixed_vars` to indicate global variables that are solely controlled inside your script +- [x] Make license more permissive by not requiring distribution of the LICENSE file if the copyright & attribution comments are left intact +- [x] Respect `--no-color` by setting the `NO_COLOR` flag in `main.sh` (#25, thx @gdevenyi) +- [x] Split out changelog into separate file +- [x] Added a [FAQ](./FAQ.md) (#15, #14, thanks @rouson) +- [x] Fix Travis OSX testing (before, it would silently pass failures) (#10) +- [x] Enable dashes in long, GNU style options, as well as numbers (thanks @zbeekman) + +## v2.0.0 + +Released: 2016-02-17. +[Diff](https://github.com/kvz/bash3boilerplate/compare/v1.2.1...v2.0.0). + +- [x] Add tests for `templater` and follow Library export best practices +- [x] Add tests for `ini_val` and follow Library export best practices +- [x] Add tests for `parse_url` and follow Library export best practices +- [x] Add tests for `megamount` and follow Library export best practices +- [x] Remove `bump` from `src` (BREAKING) +- [x] Remove `semver` from `src` (BREAKING) + +## v1.2.1 + +Released: 2016-02-17. +[Diff](https://github.com/kvz/bash3boilerplate/compare/v1.2.0...v1.2.1). + +- [x] Add Travis CI automated testing for OSX (thanks @zbeekman) + +## v1.2.0 + +Released: 2016-02-16. +[Diff](https://github.com/kvz/bash3boilerplate/compare/v1.1.0...v1.2.0). + +- [x] Allow disabling colors via `NO_COLOR` environment variable +- [x] Enable `errexit`, `nounset` and `pipefail` options at the top of the script already +- [x] More refined colors (thanks @arathai) +- [x] Add a changelog to the README +- [x] Add `__os` magic var (limited to discovering OSX and defaulting to Linux for now) +- [x] Add `__base` magic var (`main`, if the source script is `main.sh`) +- [x] Enable long, GNU style options (thanks @zbeekman) +- [x] Add Travis CI automated testing for Linux + +## v1.1.0 + +Released: 2015-06-29. +[Diff](https://github.com/kvz/bash3boilerplate/compare/v1.0.3...v1.1.0). + +- [x] Add `ALLOW_REMAINDERS` configuration to templater +- [x] Fix typo: 'debugmdoe' to 'debugmode' (thanks @jokajak) +- [x] Use `${BASH_SOURCE[0]}` for `__file` instead of `${0}` + +## v1.0.3 + +Released: 2014-11-02. +[Diff](https://github.com/kvz/bash3boilerplate/compare/5db569125319a89b9561b434db84e4d91faefb63...v1.0.3). + +- [x] Add `ini_val`, `megamount`, `parse_url` +- [x] Add re-usable libraries in `./src` +- [x] Use npm as an additional distribution channel diff --git a/FAQ.md b/FAQ.md new file mode 100644 index 0000000..fb38dc2 --- /dev/null +++ b/FAQ.md @@ -0,0 +1,171 @@ +[This document is formatted with GitHub-Flavored Markdown. ]: # +[For better viewing, including hyperlinks, read it online at ]: # +[https://github.com/kvz/bash3boilerplate/blob/HEAD/FAQ.md ]: # + +## Contents + +- [What is a CLI](#what-is-a-cli)? +- [How do I incorporate BASH3 Boilerplate into my own project](#how-do-i-incorporate-bash3-boilerplate-into-my-own-project)? +- [How do I add a command-line flag](#how-do-i-add-a-command-line-flag)? +- [How do I access the value of a command-line argument](#how-do-i-access-the-value-of-a-command-line-argument)? +- [What is a magic variable](#what-is-a-magic-variable)? +- [How do I submit an issue report](#how-do-i-submit-an-issue-report)? +- [How can I contribute to this project](#how-can-i-contribute-to-this-project)? +- [Why are you typing BASH in all caps](#why-are-you-typing-bash-in-all-caps)? +- [You are saying you are portable, but why won't b3bp code run in dash / busybox / posh / ksh / mksh / zsh](#you-are-saying-you-are-portable-but-why-wont-b3bp-code-run-in-dash--busybox--posh--ksh--mksh--zsh)? +- [How do I do Operating System detection](#how-do-i-do-operating-system-detection)? +- [How do I access a potentially unset (environment) variable](#how-do-i-access-a-potentially-unset-environment-variable)? +- [How can I detect or trap CTRL-C and other signals](#how-can-i-detect-or-trap-ctrl-c-and-other-signals)? +- [How can I get the PID of my running script](how-can-i-get-the-pid-of-my-running-script)? + + + +# Frequently Asked Questions + +## What is a CLI? + +A "CLI" is a [command-line interface](https://en.wikipedia.org/wiki/Command-line_interface). + +## How do I incorporate BASH3 Boilerplate into my own project? + +You can incorporate BASH3 Boilerplate into your project in one of two ways: + +1. Copy the desired portions of [`main.sh`](http://bash3boilerplate.sh/main.sh) into your own script. +1. Download [`main.sh`](http://bash3boilerplate.sh/main.sh) and start pressing the delete-key to remove unwanted things + +Once the `main.sh` has been tailor-made for your project, you can either append your own script in the same file, or source it in the following ways: + +1. Copy [`main.sh`](http://bash3boilerplate.sh/main.sh) into the same directory as your script and then edit and embed it into your script using Bash's `source` include feature, e.g.: + +```bash +#!/usr/bin/env bash +source main.sh +``` + +1. Source [`main.sh`](http://bash3boilerplate.sh/main.sh) in your script or at the command line: + +```bash +#!/usr/bin/env bash +source main.sh +``` + +## How do I add a command-line flag? + +1. Copy the line from the `main.sh` [read block](https://github.com/kvz/bash3boilerplate/blob/v2.1.0/main.sh#L109-L115) that most resembles the desired behavior and paste the line into the same block. +1. Edit the single-character (e.g., `-d`) and, if present, the multi-character (e.g., `--debug`) versions of the flag in the copied line. +1. Omit the `[arg]` text in the copied line, if the desired flag takes no arguments. +1. Omit or edit the text after `Default=` to set or not set default values, respectively. +1. Omit the `Required.` text, if the flag is optional. + +## How do I access the value of a command-line argument? + +To find out the value of an argument, append the corresponding single-character flag to the text `$arg_`. For example, if the [read block] +contains the line + +```bash + -t --temp [arg] Location of tempfile. Default="/tmp/bar" +``` + +then you can evaluate the corresponding argument and assign it to a variable as follows: + +```bash +__temp_file_name="${arg_t}" +``` + +## What is a magic variable? + +The [magic variables](https://github.com/kvz/bash3boilerplate/blob/v2.1.0/main.sh#L26-L28) in `main.sh` are special in that they have a different value, depending on your environment. You can use `${__file}` to get a reference to your current script, and `${__dir}` to get a reference to the directory it lives in. This is not to be confused with the location of the calling script that might be sourcing the `${__file}`, which is accessible via `${0}`, or the current directory of the administrator running the script, accessible via `$(pwd)`. + +## How do I submit an issue report? + +Please visit our [Issues](https://github.com/kvz/bash3boilerplate/issues) page. + +## How can I contribute to this project? + +Please fork this repository. After that, create a branch containing your suggested changes and submit a pull request based on the main branch +of . We are always more than happy to accept your contributions! + +## Why are you typing BASH in all caps? + +As an acronym, Bash stands for Bourne-again shell, and is usually written with one uppercase. +This project's name, however, is "BASH3 Boilerplate". It is a reference to +"[HTML5 Boilerplate](https://html5boilerplate.com/)", which was founded to serve a similar purpose, +only for crafting webpages. +Somewhat inconsistent – but true to Unix ancestry – the abbreviation for our project is "b3bp". + +## You are saying you are portable, but why won't b3bp code run in dash / busybox / posh / ksh / mksh / zsh? + +When we say _portable_, we mean across Bash versions. Bash is widespread and most systems +offer at least version 3 of it. Make sure you have that available and b3bp will work for you. + +We run automated tests to make sure that it will. Here is some proof for the following platforms: + +- [Linux](https://travis-ci.org/kvz/bash3boilerplate/jobs/109804166#L91-L94) `GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu)` +- [OSX](https://travis-ci.org/kvz/bash3boilerplate/jobs/109804167#L2453-L2455) `GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)` + +This portability, however, does not mean that we try to be compatible with +KornShell, Zsh, posh, yash, dash, or other shells. We allow syntax that would explode if +you pasted it in anything but Bash 3 and up. + +## How do I do Operating System detection? + +We used to offer a magic `__os` variable, but we quickly [discovered](https://github.com/kvz/bash3boilerplate/issues/38) that it would be hard +to create a satisfactory abstraction that is not only correct, but also covers enough use-cases, +while still having a relatively small footprint in `main.sh`. + +For simple OS detection, we recommend using the `${OSTYPE}` variable available in Bash as +is demoed in [this stackoverflow post](http://stackoverflow.com/a/8597411/151666): + +```bash +if [[ "${OSTYPE}" = "linux-gnu" ]]; then + echo "GNU Linux" +elif [[ "${OSTYPE}" = "darwin"* ]]; then + echo "Mac OSX" +elif [[ "${OSTYPE}" = "cygwin" ]]; then + echo "POSIX compatibility layer and Linux environment emulation for Windows" +elif [[ "${OSTYPE}" = "msys" ]]; then + echo "Lightweight shell and GNU utilities compiled for Windows (part of MinGW)" +elif [[ "${OSTYPE}" = "win32" ]]; then + echo "I'm not sure this can happen." +elif [[ "${OSTYPE}" = "freebsd"* ]]; then + echo "..." +else + echo "Unknown." +fi +``` + +## How do I access a potentially unset (environment) variable? + +The set -o nounset line in `main.sh` causes error termination when an unset environment variables is detected as unbound. There are multiple ways to avoid this. + +Some code to illustrate: + +```bash +# method 1 +echo ${NAME1:-Damian} # echos Damian, $NAME1 is still unset +# method 2 +echo ${NAME2:=Damian} # echos Damian, $NAME2 is set to Damian +# method 3 +NAME3=${NAME3:-Damian}; echo ${NAME3} # echos Damian, $NAME3 is set to Damian +``` + +This subject is briefly touched on as well in the [Safety and Portability section under point 5](README.md#safety-and-portability). b3bp currently uses [method 1](https://github.com/kvz/bash3boilerplate/blob/v2.1.0/main.sh#L252) when we want to access a variable that could be undeclared, and [method 3](https://github.com/kvz/bash3boilerplate/blob/v2.1.0/main.sh#L31) when we also want to set a default to an undeclared variable, because we feel it is more readable than method 2. We feel `:=` is easily overlooked, and not very beginner friendly. Method 3 seems more explicit in that regard in our humble opinion. + +## How can I detect or trap Ctrl-C and other signals? + +You can trap [Unix signals](https://en.wikipedia.org/wiki/Unix_signal) like [Ctrl-C](https://en.wikipedia.org/wiki/Control-C) with code similar to: + +```bash +# trap ctrl-c and call ctrl_c() +trap ctrl_c INT + +function ctrl_c() { + echo "** Trapped CTRL-C" +} +``` + +See http://mywiki.wooledge.org/SignalTrap for a list of signals, examples, and an in depth discussion. + +## How can I get the PID of my running script? + +The PID of a running script is contained in the `${$}` variable. This is _not_ the pid of any subshells. With Bash 4 you can get the PID of your subshell with `${BASHPID}`. For a comprehensive list of Bash built in variables see, e.g., http://www.tldp.org/LDP/abs/html/internalvariables.html diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a9fd3b8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Kevin van Zonneveld and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7a2a446 --- /dev/null +++ b/Makefile @@ -0,0 +1,59 @@ +# Licensed under MIT. +# Copyright (2016) by Kevin van Zonneveld https://twitter.com/kvz +# +# https://www.npmjs.com/package/fakefile +# +# Please do not edit this file directly, but propose changed upstream instead: +# https://github.com/kvz/fakefile/blob/main/Makefile +# +# This Makefile offers convience shortcuts into any Node.js project that utilizes npm scripts. +# It functions as a wrapper around the actual listed in `package.json` +# So instead of typing: +# +# $ npm script build:assets +# +# you could also type: +# +# $ make build-assets +# +# Notice that colons (:) are replaced by dashes for Makefile compatibility. +# +# The benefits of this wrapper are: +# +# - You get to keep the the scripts package.json, which is more portable +# (Makefiles & Windows are harder to mix) +# - Offer a polite way into the project for developers coming from different +# languages (npm scripts is obviously very Node centric) +# - Profit from better autocomplete (make ) than npm currently offers. +# OSX users will have to install bash-completion +# (http://davidalger.com/development/bash-completion-on-os-x-with-brew/) + +ifeq ($(shell test -e ./yarn.lock && echo -n yes),yes) + RUNNER=yarn + INSTALLER=yarn install +else + RUNNER=npm run + INSTALLER=npm install +endif + +define npm_script_targets +TARGETS := $(shell \ + node -e 'for (var k in require("./package.json").scripts) {console.log(k.replace(/:/g, "-"));}' + | grep -v -E "^install$$" +) +$$(TARGETS): + $(RUNNER) $(shell \ + node -e 'for (var k in require("./package.json").scripts) {console.log(k.replace(/:/g, "-"), k);}' + | grep -E "^$(MAKECMDGOALS)\s" + | head -n1 + | awk '{print $$2}' + ) + +.PHONY: $$(TARGETS) +endef + +$(eval $(call npm_script_targets)) + +# These npm run scripts are available, without needing to be mentioned in `package.json` +install: + $(INSTALLER) diff --git a/README.md b/README.md new file mode 100644 index 0000000..bd96bac --- /dev/null +++ b/README.md @@ -0,0 +1,175 @@ +[This document is formatted with GitHub-Flavored Markdown. ]: # +[For better viewing, including hyperlinks, read it online at ]: # +[https://github.com/kvz/bash3boilerplate/blob/HEAD/README.md]: # + +- [Overview](#overview) +- [Goals](#goals) +- [Features](#features) +- [Installation](#installation) +- [Changelog](#changelog) +- [Frequently Asked Questions](#frequently-asked-questions) +- [Best Practices](#best-practices) +- [Who uses b3bp](#who-uses-b3bp) +- [Authors](#authors) +- [License](#license) + +## Overview + + + +When hacking up Bash scripts, there are often things such as logging or command-line argument parsing that: + +- You need every time +- Come with a number of pitfalls you want to avoid +- Keep you from your actual work + +Here's an attempt to bundle those things in a generalized way so that +they are reusable as-is in most scripts. + +We call it "BASH3 Boilerplate" or b3bp for short. + +## Goals + +Delete-Key-**Friendly**. Instead of introducing packages, includes, compilers, etc., we propose using [`main.sh`](https://bash3boilerplate.sh/main.sh) as a base and removing the parts you don't need. +While this may feel a bit archaic at first, it is exactly the strength of Bash scripts that we should want to embrace. + +**Portable**. We are targeting Bash 3 (OSX still ships +with 3, for instance). If you are going to ask people to install +Bash 4 first, you might as well pick a more advanced language as a +dependency. + +## Features + +- Conventions that will make sure that all your scripts follow the same, battle-tested structure +- Safe by default (break on error, pipefail, etc.) +- Configuration by environment variables +- Simple command-line argument parsing that requires no external dependencies. Definitions are parsed from help info, ensuring there will be no duplication +- Helpful magic variables like `__file` and `__dir` +- Logging that supports colors and is compatible with [Syslog Severity levels](https://en.wikipedia.org/wiki/Syslog#Severity_levels), as well as the [twelve-factor](https://12factor.net/) guidelines + +## Installation + +There are three different ways to install b3bp: + +### Option 1: Download the main template + +Use curl or wget to download the source and save it as your script. Then you can start deleting the unwanted bits, and adding your own logic. + +```bash +wget https://bash3boilerplate.sh/main.sh +vim main.sh +``` + +### Option 2: Clone the entire project + +Besides `main.sh`, this will also get you the entire b3bp repository. This includes a few extra functions that we keep in the `./src` directory. + +```bash +git clone git@github.com:kvz/bash3boilerplate.git +``` + +### Option 3: Require via npm + +As of `v1.0.3`, b3bp can also be installed as a Node module, meaning you can define it as a dependency in `package.json` via: + +```bash +npm init +npm install --save --save-exact bash3boilerplate +``` + +Even though this option introduces a Node.js dependency, it does allow for easy version pinning and distribution in environments that already have this prerequisite. This is, however, entirely optional and nothing prevents you from ignoring this possibility. + +## Changelog + +Please see the [CHANGELOG.md](./CHANGELOG.md) file. + +## Frequently Asked Questions + +Please see the [FAQ.md](./FAQ.md) file. + +## Best practices + +As of `v1.0.3`, b3bp offers some nice re-usable libraries in `./src`. In order to make the snippets in `./src` more useful, we recommend the following guidelines. + +### Function packaging + +It is nice to have a Bash package that can not only be used in the terminal, but also invoked as a command line function. In order to achieve this, the exporting of your functionality _should_ follow this pattern: + +```bash +if [[ "${BASH_SOURCE[0]}" = "${0}" ]]; then + my_script "${@}" + exit $? +fi +export -f my_script +``` + +This allows a user to `source` your script or invoke it as a script. + +```bash +# Running as a script +$ ./my_script.sh some args --blah +# Sourcing the script +$ source my_script.sh +$ my_script some more args --blah +``` + +(taken from the [bpkg](https://raw.githubusercontent.com/bpkg/bpkg/HEAD/README.md) project) + +### Scoping + +1. In functions, use `local` before every variable declaration. +1. Use `UPPERCASE_VARS` to indicate environment variables that can be controlled outside your script. +1. Use `__double_underscore_prefixed_vars` to indicate global variables that are solely controlled inside your script, with the exception of arguments that are already prefixed with `arg_`, as well as functions, over which b3bp poses no restrictions. + +### Coding style + +1. Use two spaces for tabs, do not use tab characters. +1. Do not introduce whitespace at the end of lines or on blank lines as they obfuscate version control diffs. +1. Use long options (`logger --priority` vs `logger -p`). If you are on the CLI, abbreviations make sense for efficiency. Nevertheless, when you are writing reusable scripts, a few extra keystrokes will pay off in readability and avoid ventures into man pages in the future, either by you or your collaborators. Similarly, we prefer `set -o nounset` over `set -u`. +1. Use a single equal sign when checking `if [[ "${NAME}" = "Kevin" ]]`; double or triple signs are not needed. +1. Use the new bash builtin test operator (`[[ ... ]]`) rather than the old single square bracket test operator or explicit call to `test`. + +### Safety and Portability + +1. Use `{}` to enclose your variables. Otherwise, Bash will try to access the `$ENVIRONMENT_app` variable in `/srv/$ENVIRONMENT_app`, whereas you probably intended `/srv/${ENVIRONMENT}_app`. Since it is easy to miss cases like this, we recommend that you make enclosing a habit. +1. Use `set`, rather than relying on a shebang like `#!/usr/bin/env bash -e`, since that is neutralized when someone runs your script as `bash yourscript.sh`. +1. Use `#!/usr/bin/env bash`, as it is more portable than `#!/bin/bash`. +1. Use `${BASH_SOURCE[0]}` if you refer to current file, even if it is sourced by a parent script. In other cases, use `${0}`. +1. Use `:-` if you want to test variables that could be undeclared. For instance, with `if [[ "${NAME:-}" = "Kevin" ]]`, `$NAME` will evaluate to `Kevin` if the variable is empty. The variable itself will remain unchanged. The syntax to assign a default value is `${NAME:=Kevin}`. + +## Who uses b3bp? + +- [Transloadit](https://transloadit.com) +- [OpenCoarrays](https://www.opencoarrays.org) +- [Sourcery Institute](https://www.sourceryinstitute.org) +- [Computational Brain Anatomy Laboratory](https://cobralab.ca/) +- [Genesis Cloud](https://genesiscloud.com/) + +We are looking for endorsements! Are you also using b3bp? [Let us know](https://github.com/kvz/bash3boilerplate/issues/new?title=I%20use%20b3bp) and get listed. + +## Authors + +- [Kevin van Zonneveld](https://kvz.io) +- [Izaak Beekman](https://izaakbeekman.com/) +- [Manuel Streuhofer](https://github.com/mstreuhofer) +- [Alexander Rathai](mailto:Alexander.Rathai@gmail.com) +- [Dr. Damian Rouson](https://www.sourceryinstitute.org/) (documentation, feedback) +- [@jokajak](https://github.com/jokajak) (documentation) +- [Gabriel A. Devenyi](https://staticwave.ca/) (feedback) +- [@bravo-kernel](https://github.com/bravo-kernel) (feedback) +- [@skanga](https://github.com/skanga) (feedback) +- [galaktos](https://www.reddit.com/user/galaktos) (feedback) +- [@moviuro](https://github.com/moviuro) (feedback) +- [Giovanni Saponaro](https://github.com/gsaponaro) (feedback) +- [Germain Masse](https://github.com/gmasse) +- [A. G. Madi](https://github.com/warpengineer) +- [Lukas Stockner](mailto:oss@genesiscloud.com) +- [Gert Goet](https://github.com/eval) +- [@rfuehrer](https://github.com/rfuehrer) + +## License + +Copyright (c) 2013 Kevin van Zonneveld and [contributors](https://github.com/kvz/bash3boilerplate#authors). +Licensed under [MIT](https://raw.githubusercontent.com/kvz/bash3boilerplate/HEAD/LICENSE). +You are not obligated to bundle the LICENSE file with your b3bp projects as long +as you leave these references intact in the header comments of your source files. diff --git a/docs/CNAME b/docs/CNAME new file mode 100644 index 0000000..6aa6526 --- /dev/null +++ b/docs/CNAME @@ -0,0 +1 @@ +bash3boilerplate.sh \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..8ad180c --- /dev/null +++ b/docs/index.html @@ -0,0 +1,15 @@ + + + + + + Redirecting... + + + If you are not redirected, + click here. + + diff --git a/example.sh b/example.sh new file mode 100755 index 0000000..009afaf --- /dev/null +++ b/example.sh @@ -0,0 +1,147 @@ +#!/usr/bin/env bash +# This file: +# +# - Demos BASH3 Boilerplate (change this for your script) +# +# Usage: +# +# LOG_LEVEL=7 ./example.sh -f /tmp/x -d (change this for your script) +# +# Based on a template by BASH3 Boilerplate v2.3.0 +# http://bash3boilerplate.sh/#authors +# +# The MIT License (MIT) +# Copyright (c) 2013 Kevin van Zonneveld and contributors +# You are not obligated to bundle the LICENSE file with your b3bp projects as long +# as you leave these references intact in the header comments of your source files. + + +### BASH3 Boilerplate (b3bp) Header +############################################################################## + +# Commandline options. This defines the usage page, and is used to parse cli +# opts & defaults from. The parsing is unforgiving so be precise in your syntax +# - A short option must be preset for every long option; but every short option +# need not have a long option +# - `--` is respected as the separator between options and arguments +# - We do not bash-expand defaults, so setting '~/app' as a default will not resolve to ${HOME}. +# you can use bash variables to work around this (so use ${HOME} instead) + +# shellcheck disable=SC2034 +read -r -d '' __usage <<-'EOF' || true # exits non-zero when EOF encountered + -f --file [arg] Filename to process. Required. + -t --temp [arg] Location of tempfile. Default="/tmp/bar" + -v Enable verbose mode, print script as it is executed + -d --debug Enables debug mode + -h --help This page + -n --no-color Disable color output + -1 --one Do just one thing + -i --input [arg] File to process. Can be repeated. + -x Specify a flag. Can be repeated. +EOF + +# shellcheck disable=SC2034 +read -r -d '' __helptext <<-'EOF' || true # exits non-zero when EOF encountered + This is Bash3 Boilerplate's help text. Feel free to add any description of your + program or elaborate more on command-line arguments. This section is not + parsed and will be added as-is to the help. +EOF + +# shellcheck source=main.sh +source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/main.sh" + + +### Signal trapping and backtracing +############################################################################## + +function __b3bp_cleanup_before_exit () { + info "Cleaning up. Done" +} +trap __b3bp_cleanup_before_exit EXIT + +# requires `set -o errtrace` +__b3bp_err_report() { + local error_code=${?} + # shellcheck disable=SC2154 + error "Error in ${__file} in function ${1} on line ${2}" + exit ${error_code} +} +# Uncomment the following line for always providing an error backtrace +# trap '__b3bp_err_report "${FUNCNAME:-.}" ${LINENO}' ERR + + +### Command-line argument switches (like -d for debugmode, -h for showing helppage) +############################################################################## + +# debug mode +if [[ "${arg_d:?}" = "1" ]]; then + set -o xtrace + PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' + LOG_LEVEL="7" + # Enable error backtracing + trap '__b3bp_err_report "${FUNCNAME:-.}" ${LINENO}' ERR +fi + +# verbose mode +if [[ "${arg_v:?}" = "1" ]]; then + set -o verbose +fi + +# no color mode +if [[ "${arg_n:?}" = "1" ]]; then + NO_COLOR="true" +fi + +# help mode +if [[ "${arg_h:?}" = "1" ]]; then + # Help exists with code 1 + help "Help using ${0}" +fi + + +### Validation. Error out if the things required for your script are not present +############################################################################## + +[[ "${arg_f:-}" ]] || help "Setting a filename with -f or --file is required" +[[ "${LOG_LEVEL:-}" ]] || emergency "Cannot continue without LOG_LEVEL. " + + +### Runtime +############################################################################## + +# shellcheck disable=SC2154 +info "__i_am_main_script: ${__i_am_main_script}" +# shellcheck disable=SC2154 +info "__file: ${__file}" +# shellcheck disable=SC2154 +info "__dir: ${__dir}" +# shellcheck disable=SC2154 +info "__base: ${__base}" +info "OSTYPE: ${OSTYPE}" + +info "arg_f: ${arg_f}" +info "arg_d: ${arg_d}" +info "arg_v: ${arg_v}" +info "arg_h: ${arg_h}" +if [[ -n "${arg_i:-}" ]]; then + info "arg_i: ${#arg_i[@]}" + for input_file in "${arg_i[@]}"; do + info " - ${input_file}" + done +else + info "arg_i: 0" +fi +# shellcheck disable=SC2015 +[[ -n "${arg_x:-}" ]] && info "arg_x: ${#arg_x[@]}" || info "arg_x: 0" + +info "$(echo -e "multiple lines example - line #1\\nmultiple lines example - line #2\\nimagine logging the output of 'ls -al /path/'")" + +# All of these go to STDERR, so you can use STDOUT for piping machine readable information to other software +debug "Info useful to developers for debugging the application, not useful during operations." +info "Normal operational messages - may be harvested for reporting, measuring throughput, etc. - no action required." +notice "Events that are unusual but not error conditions - might be summarized in an email to developers or admins to spot potential problems - no immediate action required." +warning "Warning messages, not an error, but indication that an error will occur if action is not taken, e.g. file system 85% full - each item must be resolved within a given time." +error "Non-urgent failures, these should be relayed to developers or admins; each item must be resolved within a given time." +critical "Should be corrected immediately, but indicates failure in a primary system, an example is a loss of a backup ISP connection." +alert "Should be corrected immediately, therefore notify staff who can fix the problem. An example would be the loss of a primary ISP connection." +emergency "A \"panic\" condition usually affecting multiple apps/servers/sites. At this level it would usually notify all tech staff on call." diff --git a/main.sh b/main.sh new file mode 100755 index 0000000..4fb4dfd --- /dev/null +++ b/main.sh @@ -0,0 +1,453 @@ +#!/usr/bin/env bash +# This file: +# +# - Demos BASH3 Boilerplate (change this for your script) +# +# Usage: +# +# LOG_LEVEL=7 ./main.sh -f /tmp/x -d (change this for your script) +# +# Based on a template by BASH3 Boilerplate vv2.7.2 +# http://bash3boilerplate.sh/#authors +# +# The MIT License (MIT) +# Copyright (c) 2013 Kevin van Zonneveld and contributors +# You are not obligated to bundle the LICENSE file with your b3bp projects as long +# as you leave these references intact in the header comments of your source files. + +# Exit on error. Append "|| true" if you expect an error. +set -o errexit +# Exit on error inside any functions or subshells. +set -o errtrace +# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR +set -o nounset +# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip` +set -o pipefail +# Turn on traces, useful while debugging but commented out by default +# set -o xtrace + +if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then + __i_am_main_script="0" # false + + if [[ "${__usage+x}" ]]; then + if [[ "${BASH_SOURCE[1]}" = "${0}" ]]; then + __i_am_main_script="1" # true + fi + + __b3bp_external_usage="true" + __b3bp_tmp_source_idx=1 + fi +else + __i_am_main_script="1" # true + [[ "${__usage+x}" ]] && unset -v __usage + [[ "${__helptext+x}" ]] && unset -v __helptext +fi + +# Set magic variables for current file, directory, os, etc. +__dir="$(cd "$(dirname "${BASH_SOURCE[${__b3bp_tmp_source_idx:-0}]}")" && pwd)" +__file="${__dir}/$(basename "${BASH_SOURCE[${__b3bp_tmp_source_idx:-0}]}")" +__base="$(basename "${__file}" .sh)" +# shellcheck disable=SC2034,SC2015 +__invocation="$(printf %q "${__file}")$( (($#)) && printf ' %q' "$@" || true)" + +# Define the environment variables (and their defaults) that this script depends on +LOG_LEVEL="${LOG_LEVEL:-6}" # 7 = debug -> 0 = emergency +NO_COLOR="${NO_COLOR:-}" # true = disable color. otherwise autodetected + + +### Functions +############################################################################## + +function __b3bp_log () { + local log_level="${1}" + shift + + # shellcheck disable=SC2034 + local color_debug="\\x1b[35m" + # shellcheck disable=SC2034 + local color_info="\\x1b[32m" + # shellcheck disable=SC2034 + local color_notice="\\x1b[34m" + # shellcheck disable=SC2034 + local color_warning="\\x1b[33m" + # shellcheck disable=SC2034 + local color_error="\\x1b[31m" + # shellcheck disable=SC2034 + local color_critical="\\x1b[1;31m" + # shellcheck disable=SC2034 + local color_alert="\\x1b[1;37;41m" + # shellcheck disable=SC2034 + local color_emergency="\\x1b[1;4;5;37;41m" + + local colorvar="color_${log_level}" + + local color="${!colorvar:-${color_error}}" + local color_reset="\\x1b[0m" + + if [[ "${NO_COLOR:-}" = "true" ]] || { [[ "${TERM:-}" != "xterm"* ]] && [[ "${TERM:-}" != "screen"* ]]; } || [[ ! -t 2 ]]; then + if [[ "${NO_COLOR:-}" != "false" ]]; then + # Don't use colors on pipes or non-recognized terminals + color=""; color_reset="" + fi + fi + + # all remaining arguments are to be printed + local log_line="" + + while IFS=$'\n' read -r log_line; do + echo -e "$(date -u +"%Y-%m-%d %H:%M:%S UTC") ${color}$(printf "[%9s]" "${log_level}")${color_reset} ${log_line}" 1>&2 + done <<< "${@:-}" +} + +function emergency () { __b3bp_log emergency "${@}"; exit 1; } +function alert () { [[ "${LOG_LEVEL:-0}" -ge 1 ]] && __b3bp_log alert "${@}"; true; } +function critical () { [[ "${LOG_LEVEL:-0}" -ge 2 ]] && __b3bp_log critical "${@}"; true; } +function error () { [[ "${LOG_LEVEL:-0}" -ge 3 ]] && __b3bp_log error "${@}"; true; } +function warning () { [[ "${LOG_LEVEL:-0}" -ge 4 ]] && __b3bp_log warning "${@}"; true; } +function notice () { [[ "${LOG_LEVEL:-0}" -ge 5 ]] && __b3bp_log notice "${@}"; true; } +function info () { [[ "${LOG_LEVEL:-0}" -ge 6 ]] && __b3bp_log info "${@}"; true; } +function debug () { [[ "${LOG_LEVEL:-0}" -ge 7 ]] && __b3bp_log debug "${@}"; true; } + +function help () { + echo "" 1>&2 + echo " ${*}" 1>&2 + echo "" 1>&2 + echo " ${__usage:-No usage available}" 1>&2 + echo "" 1>&2 + + if [[ "${__helptext:-}" ]]; then + echo " ${__helptext}" 1>&2 + echo "" 1>&2 + fi + + exit 1 +} + + +### Parse commandline options +############################################################################## + +# Commandline options. This defines the usage page, and is used to parse cli +# opts & defaults from. The parsing is unforgiving so be precise in your syntax +# - A short option must be preset for every long option; but every short option +# need not have a long option +# - `--` is respected as the separator between options and arguments +# - We do not bash-expand defaults, so setting '~/app' as a default will not resolve to ${HOME}. +# you can use bash variables to work around this (so use ${HOME} instead) + +# shellcheck disable=SC2015 +[[ "${__usage+x}" ]] || read -r -d '' __usage <<-'EOF' || true # exits non-zero when EOF encountered + -f --file [arg] Filename to process. Required. + -t --temp [arg] Location of tempfile. Default="/tmp/bar" + -v Enable verbose mode, print script as it is executed + -d --debug Enables debug mode + -h --help This page + -n --no-color Disable color output + -1 --one Do just one thing + -i --input [arg] File to process. Can be repeated. + -x Specify a flag. Can be repeated. +EOF + +# shellcheck disable=SC2015 +[[ "${__helptext+x}" ]] || read -r -d '' __helptext <<-'EOF' || true # exits non-zero when EOF encountered + This is Bash3 Boilerplate's help text. Feel free to add any description of your + program or elaborate more on command-line arguments. This section is not + parsed and will be added as-is to the help. +EOF + +# Translate usage string -> getopts arguments, and set $arg_ defaults +while read -r __b3bp_tmp_line; do + if [[ "${__b3bp_tmp_line}" =~ ^- ]]; then + # fetch single character version of option string + __b3bp_tmp_opt="${__b3bp_tmp_line%% *}" + __b3bp_tmp_opt="${__b3bp_tmp_opt:1}" + + # fetch long version if present + __b3bp_tmp_long_opt="" + + if [[ "${__b3bp_tmp_line}" = *"--"* ]]; then + __b3bp_tmp_long_opt="${__b3bp_tmp_line#*--}" + __b3bp_tmp_long_opt="${__b3bp_tmp_long_opt%% *}" + fi + + # map opt long name to+from opt short name + printf -v "__b3bp_tmp_opt_long2short_${__b3bp_tmp_long_opt//-/_}" '%s' "${__b3bp_tmp_opt}" + printf -v "__b3bp_tmp_opt_short2long_${__b3bp_tmp_opt}" '%s' "${__b3bp_tmp_long_opt//-/_}" + + # check if option takes an argument + if [[ "${__b3bp_tmp_line}" =~ \[.*\] ]]; then + __b3bp_tmp_opt="${__b3bp_tmp_opt}:" # add : if opt has arg + __b3bp_tmp_init="" # it has an arg. init with "" + printf -v "__b3bp_tmp_has_arg_${__b3bp_tmp_opt:0:1}" '%s' "1" + elif [[ "${__b3bp_tmp_line}" =~ \{.*\} ]]; then + __b3bp_tmp_opt="${__b3bp_tmp_opt}:" # add : if opt has arg + __b3bp_tmp_init="" # it has an arg. init with "" + # remember that this option requires an argument + printf -v "__b3bp_tmp_has_arg_${__b3bp_tmp_opt:0:1}" '%s' "2" + else + __b3bp_tmp_init="0" # it's a flag. init with 0 + printf -v "__b3bp_tmp_has_arg_${__b3bp_tmp_opt:0:1}" '%s' "0" + fi + __b3bp_tmp_opts="${__b3bp_tmp_opts:-}${__b3bp_tmp_opt}" + + if [[ "${__b3bp_tmp_line}" =~ ^Can\ be\ repeated\. ]] || [[ "${__b3bp_tmp_line}" =~ \.\ *Can\ be\ repeated\. ]]; then + # remember that this option can be repeated + printf -v "__b3bp_tmp_is_array_${__b3bp_tmp_opt:0:1}" '%s' "1" + else + printf -v "__b3bp_tmp_is_array_${__b3bp_tmp_opt:0:1}" '%s' "0" + fi + fi + + [[ "${__b3bp_tmp_opt:-}" ]] || continue + + if [[ "${__b3bp_tmp_line}" =~ ^Default= ]] || [[ "${__b3bp_tmp_line}" =~ \.\ *Default= ]]; then + # ignore default value if option does not have an argument + __b3bp_tmp_varname="__b3bp_tmp_has_arg_${__b3bp_tmp_opt:0:1}" + if [[ "${!__b3bp_tmp_varname}" != "0" ]]; then + # take default + __b3bp_tmp_init="${__b3bp_tmp_line##*Default=}" + # strip double quotes from default argument + __b3bp_tmp_re='^"(.*)"$' + if [[ "${__b3bp_tmp_init}" =~ ${__b3bp_tmp_re} ]]; then + __b3bp_tmp_init="${BASH_REMATCH[1]}" + else + # strip single quotes from default argument + __b3bp_tmp_re="^'(.*)'$" + if [[ "${__b3bp_tmp_init}" =~ ${__b3bp_tmp_re} ]]; then + __b3bp_tmp_init="${BASH_REMATCH[1]}" + fi + fi + fi + fi + + if [[ "${__b3bp_tmp_line}" =~ ^Required\. ]] || [[ "${__b3bp_tmp_line}" =~ \.\ *Required\. ]]; then + # remember that this option requires an argument + printf -v "__b3bp_tmp_has_arg_${__b3bp_tmp_opt:0:1}" '%s' "2" + fi + + # Init var with value unless it is an array / a repeatable + __b3bp_tmp_varname="__b3bp_tmp_is_array_${__b3bp_tmp_opt:0:1}" + [[ "${!__b3bp_tmp_varname}" = "0" ]] && printf -v "arg_${__b3bp_tmp_opt:0:1}" '%s' "${__b3bp_tmp_init}" +done <<< "${__usage:-}" + +# run getopts only if options were specified in __usage +if [[ "${__b3bp_tmp_opts:-}" ]]; then + # Allow long options like --this + __b3bp_tmp_opts="${__b3bp_tmp_opts}-:" + + # Reset in case getopts has been used previously in the shell. + OPTIND=1 + + # start parsing command line + set +o nounset # unexpected arguments will cause unbound variables + # to be dereferenced + # Overwrite $arg_ defaults with the actual CLI options + while getopts "${__b3bp_tmp_opts}" __b3bp_tmp_opt; do + [[ "${__b3bp_tmp_opt}" = "?" ]] && help "Invalid use of script: ${*} " + + if [[ "${__b3bp_tmp_opt}" = "-" ]]; then + # OPTARG is long-option-name or long-option=value + if [[ "${OPTARG}" =~ .*=.* ]]; then + # --key=value format + __b3bp_tmp_long_opt=${OPTARG/=*/} + # Set opt to the short option corresponding to the long option + __b3bp_tmp_varname="__b3bp_tmp_opt_long2short_${__b3bp_tmp_long_opt//-/_}" + printf -v "__b3bp_tmp_opt" '%s' "${!__b3bp_tmp_varname}" + OPTARG=${OPTARG#*=} + else + # --key value format + # Map long name to short version of option + __b3bp_tmp_varname="__b3bp_tmp_opt_long2short_${OPTARG//-/_}" + printf -v "__b3bp_tmp_opt" '%s' "${!__b3bp_tmp_varname}" + # Only assign OPTARG if option takes an argument + __b3bp_tmp_varname="__b3bp_tmp_has_arg_${__b3bp_tmp_opt}" + __b3bp_tmp_varvalue="${!__b3bp_tmp_varname}" + [[ "${__b3bp_tmp_varvalue}" != "0" ]] && __b3bp_tmp_varvalue="1" + printf -v "OPTARG" '%s' "${@:OPTIND:${__b3bp_tmp_varvalue}}" + # shift over the argument if argument is expected + ((OPTIND+=__b3bp_tmp_varvalue)) + fi + # we have set opt/OPTARG to the short value and the argument as OPTARG if it exists + fi + + __b3bp_tmp_value="${OPTARG}" + + __b3bp_tmp_varname="__b3bp_tmp_is_array_${__b3bp_tmp_opt:0:1}" + if [[ "${!__b3bp_tmp_varname}" != "0" ]]; then + # repeatables + # shellcheck disable=SC2016 + if [[ -z "${OPTARG}" ]]; then + # repeatable flags, they increcemnt + __b3bp_tmp_varname="arg_${__b3bp_tmp_opt:0:1}" + debug "cli arg ${__b3bp_tmp_varname} = (${__b3bp_tmp_default}) -> ${!__b3bp_tmp_varname}" + # shellcheck disable=SC2004 + __b3bp_tmp_value=$((${!__b3bp_tmp_varname} + 1)) + printf -v "${__b3bp_tmp_varname}" '%s' "${__b3bp_tmp_value}" + else + # repeatable args, they get appended to an array + __b3bp_tmp_varname="arg_${__b3bp_tmp_opt:0:1}[@]" + debug "cli arg ${__b3bp_tmp_varname} append ${__b3bp_tmp_value}" + declare -a "${__b3bp_tmp_varname}"='("${!__b3bp_tmp_varname}" "${__b3bp_tmp_value}")' + fi + else + # non-repeatables + __b3bp_tmp_varname="arg_${__b3bp_tmp_opt:0:1}" + __b3bp_tmp_default="${!__b3bp_tmp_varname}" + + if [[ -z "${OPTARG}" ]]; then + __b3bp_tmp_value=$((__b3bp_tmp_default + 1)) + fi + + printf -v "${__b3bp_tmp_varname}" '%s' "${__b3bp_tmp_value}" + + debug "cli arg ${__b3bp_tmp_varname} = (${__b3bp_tmp_default}) -> ${!__b3bp_tmp_varname}" + fi + done + set -o nounset # no more unbound variable references expected + + shift $((OPTIND-1)) + + if [[ "${1:-}" = "--" ]] ; then + shift + fi +fi + + +### Automatic validation of required option arguments +############################################################################## + +for __b3bp_tmp_varname in ${!__b3bp_tmp_has_arg_*}; do + # validate only options which required an argument + [[ "${!__b3bp_tmp_varname}" = "2" ]] || continue + + __b3bp_tmp_opt_short="${__b3bp_tmp_varname##*_}" + __b3bp_tmp_varname="arg_${__b3bp_tmp_opt_short}" + [[ "${!__b3bp_tmp_varname}" ]] && continue + + __b3bp_tmp_varname="__b3bp_tmp_opt_short2long_${__b3bp_tmp_opt_short}" + printf -v "__b3bp_tmp_opt_long" '%s' "${!__b3bp_tmp_varname}" + [[ "${__b3bp_tmp_opt_long:-}" ]] && __b3bp_tmp_opt_long=" (--${__b3bp_tmp_opt_long//_/-})" + + help "Option -${__b3bp_tmp_opt_short}${__b3bp_tmp_opt_long:-} requires an argument" +done + + +### Cleanup Environment variables +############################################################################## + +for __tmp_varname in ${!__b3bp_tmp_*}; do + unset -v "${__tmp_varname}" +done + +unset -v __tmp_varname + + +### Externally supplied __usage. Nothing else to do here +############################################################################## + +if [[ "${__b3bp_external_usage:-}" = "true" ]]; then + unset -v __b3bp_external_usage + return +fi + + +### Signal trapping and backtracing +############################################################################## + +function __b3bp_cleanup_before_exit () { + info "Cleaning up. Done" +} +trap __b3bp_cleanup_before_exit EXIT + +# requires `set -o errtrace` +__b3bp_err_report() { + local error_code=${?} + error "Error in ${__file} in function ${1} on line ${2}" + exit ${error_code} +} +# Uncomment the following line for always providing an error backtrace +# trap '__b3bp_err_report "${FUNCNAME:-.}" ${LINENO}' ERR + + +### Command-line argument switches (like -d for debugmode, -h for showing helppage) +############################################################################## + +# debug mode +if [[ "${arg_d:?}" = "1" ]]; then + set -o xtrace + PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' + LOG_LEVEL="7" + # Enable error backtracing + trap '__b3bp_err_report "${FUNCNAME:-.}" ${LINENO}' ERR +fi + +# verbose mode +if [[ "${arg_v:?}" = "1" ]]; then + set -o verbose +fi + +# no color mode +if [[ "${arg_n:?}" = "1" ]]; then + NO_COLOR="true" +fi + +# help mode +if [[ "${arg_h:?}" = "1" ]]; then + # Help exists with code 1 + help "Help using ${0}" +fi + + +### Validation. Error out if the things required for your script are not present +############################################################################## + +[[ "${arg_f:-}" ]] || help "Setting a filename with -f or --file is required" +[[ "${LOG_LEVEL:-}" ]] || emergency "Cannot continue without LOG_LEVEL. " + + +### Runtime +############################################################################## + +info "__i_am_main_script: ${__i_am_main_script}" +info "__file: ${__file}" +info "__dir: ${__dir}" +info "__base: ${__base}" +info "OSTYPE: ${OSTYPE}" + +info "arg_f: ${arg_f}" +info "arg_d: ${arg_d}" +info "arg_v: ${arg_v}" +info "arg_h: ${arg_h}" + +# shellcheck disable=SC2015 +if [[ -n "${arg_i:-}" ]] && declare -p arg_i 2> /dev/null | grep -q '^declare \-a'; then + info "arg_i:" + for input_file in "${arg_i[@]}"; do + info " - ${input_file}" + done +elif [[ -n "${arg_i:-}" ]]; then + info "arg_i: ${arg_i}" +else + info "arg_i: 0" +fi + +# shellcheck disable=SC2015 +if [[ -n "${arg_x:-}" ]] && declare -p arg_x 2> /dev/null | grep -q '^declare \-a'; then + info "arg_x: ${#arg_x[@]}" +elif [[ -n "${arg_x:-}" ]]; then + info "arg_x: ${arg_x}" +else + info "arg_x: 0" +fi + +info "$(echo -e "multiple lines example - line #1\\nmultiple lines example - line #2\\nimagine logging the output of 'ls -al /path/'")" + +# All of these go to STDERR, so you can use STDOUT for piping machine readable information to other software +debug "Info useful to developers for debugging the application, not useful during operations." +info "Normal operational messages - may be harvested for reporting, measuring throughput, etc. - no action required." +notice "Events that are unusual but not error conditions - might be summarized in an email to developers or admins to spot potential problems - no immediate action required." +warning "Warning messages, not an error, but indication that an error will occur if action is not taken, e.g. file system 85% full - each item must be resolved within a given time." +error "Non-urgent failures, these should be relayed to developers or admins; each item must be resolved within a given time." +critical "Should be corrected immediately, but indicates failure in a primary system, an example is a loss of a backup ISP connection." +alert "Should be corrected immediately, therefore notify staff who can fix the problem. An example would be the loss of a primary ISP connection." +emergency "A \"panic\" condition usually affecting multiple apps/servers/sites. At this level it would usually notify all tech staff on call." diff --git a/package.json b/package.json new file mode 100644 index 0000000..4583945 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "bash3boilerplate", + "description": "Copypastable templates to write better bash scripts", + "version": "2.7.2", + "packageManager": "yarn@3.6.0+sha224.19e47520fa56c6146388fdeb438d9dcf6630c3f277a2e1180995c3bb", + "engines": { + "node": ">= 18", + "yarn": "3.6.0" + }, + "scripts": { + "lint:shellcheck": "shellcheck --severity=${SHELLCHECK_SEVERITY:-info} $(find . -name '*.sh' -maxdepth 2)", + "lint:style": "test/style.pl $(find . -name '*.sh' -maxdepth 2)", + "lint": "npm-run-all -l 'lint:**'", + "release:major": "env SEMANTIC=major yarn release", + "release:minor": "env SEMANTIC=minor yarn release", + "release:patch": "env SEMANTIC=patch yarn release", + "release": "npm version ${SEMANTIC:-patch} -m \"Release %s\" && yarn version:replace && git commit main.sh src/*.sh -m 'Update version' && git push && git push --tags -f && npm publish", + "test:debug:main:repeated": "env LOG_LEVEL=7 test/acceptance.sh main-repeated", + "test:update": "env SAVE_FIXTURES=true yarn test", + "test": "test/acceptance.sh", + "version:current": "node -e 'console.log(require(\"./package.json\").version)'", + "version:replace": "replace 'v\\d+\\.\\d+\\.\\d+' \"v$(npm run --silent version:current)\" main.sh src/*.sh" + }, + "devDependencies": { + "fakefile": "1.1.0", + "npm-run-all": "4.1.5", + "replace": "1.2.2" + } +} diff --git a/src/ini_val.sh b/src/ini_val.sh new file mode 100755 index 0000000..d811510 --- /dev/null +++ b/src/ini_val.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash +# BASH3 Boilerplate: ini_val +# +# This file: +# +# - Can read and write .ini files using pure bash +# +# Limitations: +# +# - All keys inside a section of the .ini file must be unique +# - Optional comment parameter for the creation of new entries +# +# Usage as a function: +# +# source ini_val.sh +# ini_val data.ini connection.host 127.0.0.1 "Host name or IP address" +# +# Usage as a command: +# +# ini_val.sh data.ini connection.host 127.0.0.1 "Host name or IP address" +# +# Based on a template by BASH3 Boilerplate vv2.7.2 +# http://bash3boilerplate.sh/#authors +# +# The MIT License (MIT) +# Copyright (c) 2013 Kevin van Zonneveld and contributors +# You are not obligated to bundle the LICENSE file with your b3bp projects as long +# as you leave these references intact in the header comments of your source files. + +function ini_val() { + local file="${1:-}" + local sectionkey="${2:-}" + local val="${3:-}" + local comment="${4:-}" + local delim="=" + local comment_delim=";" + local section="" + local key="" + local current="" + # add default section + local section_default="default" + + if [[ ! -f "${file}" ]]; then + # touch file if not exists + touch "${file}" + fi + + # Split on . for section. However, section is optional + IFS='.' read -r section key <<< "${sectionkey}" + if [[ ! "${key}" ]]; then + key="${section}" + # default section if not given + section="${section_default}" + fi + + # get current value (if exists) + current=$(sed -En "/^\[/{h;d;};G;s/^${key}([[:blank:]]*)${delim}(.*)\n\[${section}\]$/\2/p" "${file}"|awk '{$1=$1};1') + # get current comment (if exists) + current_comment=$(sed -En "/^\[${section}\]/,/^\[.*\]/ s|^(${comment_delim}\[${key}\])(.*)|\2|p" "${file}"|awk '{$1=$1};1') + + if ! grep -q "\[${section}\]" "${file}"; then + # create section if not exists (empty line to seperate new section for better readability) + echo >> "${file}" + echo "[${section}]" >> "${file}" + fi + + if [[ ! "${val}" ]]; then + # get a value + echo "${current}" + else + # set a value + if [[ ! "${section}" ]]; then + # if no section is given, propagate the default section + section=${section_default} + fi + + if [[ ! "${comment}" ]]; then + # if no comment given, keep old comment + comment="${current_comment}" + fi + # maintenance area + # a) remove comment if new given / respect section + sed -i.bak "/^\[${section}\]/,/^\[.*\]/ s|^\(${comment_delim}\[${key}\] \).*$||" "${file}" + # b) remove old key / respect section + sed -i.bak "/^\[${section}\]/,/^\[.*\]/ s|^\(${key}=\).*$||" "${file}" + # c) remove all empty lines in ini file + sed -i.bak '/^[[:space:]]*$/d' "${file}" + # d) insert line break before every section for better readability + sed -i.bak $'s/^\\[/\\\n\\[/g' "${file}" + + # add to section + if [[ ! "${comment}" ]]; then + # add new key/value _without_ comment + RET="/\\[${section}\\]/a\\ +${key}${delim}${val}" + else + # add new key/value _with_ preceeding comment + RET="/\\[${section}\\]/a\\ +${comment_delim}[${key}] ${comment}\\ +${key}${delim}${val}" + fi + sed -i.bak -e "${RET}" "${file}" + # this .bak dance is done for BSD/GNU portability: http://stackoverflow.com/a/22084103/151666 + rm -f "${file}.bak" + fi +} + +if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then + export -f ini_val +else + ini_val "${@}" + exit ${?} +fi diff --git a/src/megamount.sh b/src/megamount.sh new file mode 100644 index 0000000..148ff1c --- /dev/null +++ b/src/megamount.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# BASH3 Boilerplate: megamount +# +# This file: +# +# - Takes a URL (smb, nfs, afs) and tries to mount it at a given target directory +# - Forcefully unmounts any active mount at the target directory first +# - Displays the mount's contents for verification +# +# Depends on: +# +# - ./parse_url.sh +# +# Usage as a function: +# +# source megamount.sh +# megamount smb://janedoe:abc123@192.168.0.1/documents /mnt/documents +# +# Usage as a command: +# +# megamount.sh smb://janedoe:abc123@192.168.0.1/documents /mnt/documents +# +# Based on a template by BASH3 Boilerplate vv2.7.2 +# http://bash3boilerplate.sh/#authors +# +# The MIT License (MIT) +# Copyright (c) 2013 Kevin van Zonneveld and contributors +# You are not obligated to bundle the LICENSE file with your b3bp projects as long +# as you leave these references intact in the header comments of your source files. + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# shellcheck source=src/parse_url.sh +source "${__dir}/parse_url.sh" + +function megamount () { + local url="${1}" + local target="${2}" + + local proto + local user + local pass + local host + local port + local path + + proto=$(parse_url "${url}" "proto") + user=$(parse_url "${url}" "user") + pass=$(parse_url "${url}" "pass") + host=$(parse_url "${url}" "host") + port=$(parse_url "${url}" "port") + path=$(parse_url "${url}" "path") + + (umount -lf "${target}" || umount -f "${target}") > /dev/null 2>&1 || true + mkdir -p "${target}" + if [[ "${proto}" = "smb://" ]]; then + mount -t cifs --verbose -o "username=${user},password=${pass},hard" "//${host}/${path}" "${target}" + elif [[ "${proto}" = "afp://" ]]; then + # start syslog-ng + # afpfsd || echo "Unable to run afpfsd. Does /dev/log exist?" && exit 1 + mount_afp "${url}" "${target}" + elif [[ "${proto}" = "nfs://" ]]; then + mount -t nfs --verbose -o "vers=3,nolock,soft,intr,rsize=32768,wsize=32768" "${host}:/${path}" "${target}" + else + echo "ERR: Unknown protocol: '${proto}'" + exit 1 + fi + + # chmod 777 "${target}" + ls -al "${target}/" +} + +if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then + export -f megamount +else + megamount "${@}" + exit ${?} +fi diff --git a/src/parse_url.sh b/src/parse_url.sh new file mode 100644 index 0000000..d9d2704 --- /dev/null +++ b/src/parse_url.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# BASH3 Boilerplate: parse_url +# +# This file: +# +# - Takes a URL and parses protocol, user, pass, host, port, path. +# +# Based on: +# +# - http://stackoverflow.com/a/6174447/151666 +# +# Usage as a function: +# +# source parse_url.sh +# parse_url 'http://johndoe:abc123@example.com:8080/index.html' pass +# +# Usage as a command: +# +# parse_url.sh 'http://johndoe:abc123@example.com:8080/index.html' +# +# Based on a template by BASH3 Boilerplate vv2.7.2 +# http://bash3boilerplate.sh/#authors +# +# The MIT License (MIT) +# Copyright (c) 2013 Kevin van Zonneveld and contributors +# You are not obligated to bundle the LICENSE file with your b3bp projects as long +# as you leave these references intact in the header comments of your source files. + +function parse_url() { + local parse="${1}" + local need="${2:-}" + + local proto + local url + local userpass + local user + local pass + local hostport + local host + local port + local path + + proto="$(echo "${parse}" | grep :// | sed -e's,^\(.*://\).*,\1,g')" + url="${parse/${proto}/}" + userpass="$(echo "${url}" | grep @ | cut -d@ -f1)" + user="$(echo "${userpass}" | grep : | cut -d: -f1)" + pass="$(echo "${userpass}" | grep : | cut -d: -f2)" + hostport="$(echo "${url/${userpass}@/}" | cut -d/ -f1)" + host="$(echo "${hostport}" | grep : | cut -d: -f1)" + port="$(echo "${hostport}" | grep : | cut -d: -f2)" + path="$(echo "${url}" | grep / | cut -d/ -f2-)" + + [[ ! "${user}" ]] && user="${userpass}" + [[ ! "${host}" ]] && host="${hostport}" + if [[ ! "${port}" ]]; then + [[ "${proto}" = "http://" ]] && port="80" + [[ "${proto}" = "https://" ]] && port="443" + [[ "${proto}" = "mysql://" ]] && port="3306" + [[ "${proto}" = "redis://" ]] && port="6379" + fi + + if [[ "${need}" ]]; then + echo "${!need}" + else + echo "" + echo " Use second argument to return just 1 variable." + echo " parse_url() demo: " + echo "" + echo " proto: ${proto}" + echo " user: ${user}" + echo " pass: ${pass}" + echo " host: ${host}" + echo " port: ${port}" + echo " path: ${path}" + echo "" + fi +} + +if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then + export -f parse_url +else + parse_url "${@}" + exit ${?} +fi diff --git a/src/templater.sh b/src/templater.sh new file mode 100755 index 0000000..679048f --- /dev/null +++ b/src/templater.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +# BASH3 Boilerplate: templater +# +# This file: +# +# - takes a source (template) & destination (config) filepath argument +# - then replaces placeholders with variables found in the environment +# +# Usage as a function: +# +# source templater.sh +# export NAME=kevin +# templater input.cfg output.cfg +# +# Usage as a command: +# +# ALLOW_REMAINDERS=1 templater.sh input.cfg output.cfg +# +# Based on a template by BASH3 Boilerplate vv2.7.2 +# http://bash3boilerplate.sh/#authors +# +# The MIT License (MIT) +# Copyright (c) 2013 Kevin van Zonneveld and contributors +# You are not obligated to bundle the LICENSE file with your b3bp projects as long +# as you leave these references intact in the header comments of your source files. + +function templater() { + ALLOW_REMAINDERS="${ALLOW_REMAINDERS:-0}" + + templateSrc="${1:-}" + templateDst="${2:-}" + + if [[ ! -f "${templateSrc}" ]]; then + echo "ERROR: Template source '${templateSrc}' needs to exist" + exit 1 + fi + if [[ ! "${templateDst}" ]]; then + echo "ERROR: Template destination '${templateDst}' needs to be specified" + exit 1 + fi + + if [[ "$(command -v perl)" ]]; then + perl -p -e 's/\$\{(\w+)\}/(exists $ENV{$1} ? $ENV{$1} : "\${$1}")/eg' < "${templateSrc}" > "${templateDst}" + else + cp -f "${templateSrc}" "${templateDst}" + + for var in $(env |awk -F= '{print $1}' |grep -E '^(_[A-Z0-9_]+|[A-Z0-9][A-Z0-9_]*)$'); do + sed -i.bak -e "s#\${${var}}#${!var//#/\\#/}#g" "${templateDst}" + # this .bak dance is done for BSD/GNU portability: http://stackoverflow.com/a/22084103/151666 + rm -f "${templateDst}.bak" + done + fi + + # cat "${templateDst}" + + # shellcheck disable=SC2016 + if grep '${' "${templateDst}" && [[ "${ALLOW_REMAINDERS}" = "0" ]]; then + echo "ERROR: Unable to replace the above template vars" + exit 1 + fi +} + +if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then + export -f templater +else + templater "${@}" + exit ${?} +fi diff --git a/test/acceptance.sh b/test/acceptance.sh new file mode 100755 index 0000000..392d675 --- /dev/null +++ b/test/acceptance.sh @@ -0,0 +1,232 @@ +#!/usr/bin/env bash +# This file: +# +# - Executes one (or all) test scenarios +# - Replaces dynamic things like hostnames, IPs, dates, etc +# - Optionally saves the results as fixtures, that later runs will be compared against +# +# Usage: +# +# ./deploy.sh +# +# Based on a template by BASH3 Boilerplate v2.0.0 +# http://bash3boilerplate.sh/#authors +# +# The MIT License (MIT) +# Copyright (c) 2013 Kevin van Zonneveld and contributors +# You are not obligated to bundle the LICENSE file with your b3bp projects as long +# as you leave these references intact in the header comments of your source files. + +# Exit on error. Append || true if you expect an error. +set -o errexit +# Exit on error inside any functions or subshells. +set -o errtrace +# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR +set -o nounset +# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip` +set -o pipefail +# Turn on traces, useful while debugging but commented out by default +# set -o xtrace + +# Set magic variables for current file, directory, os, etc. +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__file="${__dir}/$(basename "${BASH_SOURCE[0]}")" +__base="$(basename "${__file}" .sh)" +__root="$(cd "$(dirname "${__dir}")" && pwd)" + +__sysTmpDir="${TMPDIR:-/tmp}" +__sysTmpDir="${__sysTmpDir%/}" # <-- remove trailing slash on macosx +__accptstTmpDir=$(mktemp -d "${__sysTmpDir}/${__base}.XXXXXX") + +function cleanup_before_exit () { rm -r "${__accptstTmpDir:?}"; } +trap cleanup_before_exit EXIT + +cmdSed="sed" +cmdTimeout="timeout" + +if [[ "${OSTYPE}" = "darwin"* ]]; then + cmdSed="gsed" + cmdTimeout="gtimeout" +fi + +if [[ ! "$(command -v ${cmdSed})" ]]; then + echo "Please install ${cmdSed}" + exit 1 +fi + +if [[ ! "$(command -v ${cmdTimeout})" ]]; then + echo "Please install ${cmdTimeout}" + exit 1 +fi + +__node="$(command -v node)" +__arch="amd64" + +# explicitly setting NO_COLOR to false will make b3bp ignore TERM +# not being "xterm*" or "screen*" and STDERR not being connected to a terminal +# it's the opposite of NO_COLOR="true" - it forces color, no matter what +export NO_COLOR="false" + +# Running prepare before other scenarios is important on Travis, +# so that stdio can diverge - and we can enforce stricter +# stdio comparison on all other tests. +while IFS=$'\n' read -r scenario; do + scenario="$(dirname "${scenario}")" + scenario="${scenario##${__dir}/scenario/}" + + [[ "${scenario}" = "prepare" ]] && continue + [[ "${1:-}" ]] && [[ "${scenario}" != "${1}" ]] && continue + + echo "==> Scenario: ${scenario}" + pushd "${__dir}/scenario/${scenario}" > /dev/null + + # Run scenario + (${cmdTimeout} --kill-after=6m 5m bash ./run.sh \ + > "${__accptstTmpDir}/${scenario}.stdio" 2>&1; \ + echo "${?}" > "${__accptstTmpDir}/${scenario}.exitcode" \ + ) || true + + # Clear out environmental specifics + for typ in stdio exitcode; do + curFile="${__accptstTmpDir}/${scenario}.${typ}" + "${cmdSed}" -i \ + -e "s@${__node}@{node}@g" "${curFile}" \ + -e "s@${__root}@{root}@g" "${curFile}" \ + -e "s@${__sysTmpDir}@{tmpdir}@g" "${curFile}" \ + -e "s@/tmp@{tmpdir}@g" "${curFile}" \ + -e "s@${HOME:-/home/travis}@{home}@g" "${curFile}" \ + -e "s@${USER:-travis}@{user}@g" "${curFile}" \ + -e "s@travis@{user}@g" "${curFile}" \ + -e "s@kvz@{user}@g" "${curFile}" \ + -e "s@{root}/node_modules/\\.bin/node@{node}@g" "${curFile}" \ + -e "s@{home}/build/{user}/fre{node}@{node}@g" "${curFile}" \ + -e "s@${HOSTNAME}@{hostname}@g" "${curFile}" \ + -e "s@${__arch}@{arch}@g" "${curFile}" \ + -e "s@${OSTYPE}@{OSTYPE}@g" "${curFile}" \ + -e "s@OSX@{os}@g" "${curFile}" \ + -e "s@Linux@{os}@g" "${curFile}" \ + || false + + if grep -q 'ACCPTST:STDIO_REPLACE_IPS' "${curFile}"; then + "${cmdSed}" -i \ + -r 's@[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}@{ip}@g' \ + "${curFile}" + + # IPs vary in length. Ansible uses padding. {ip} does not vary in length + # so kill the padding after it for consistent output + "${cmdSed}" -i \ + -r 's@\{ip\}\s+@{ip} @g' \ + "${curFile}" + fi + if grep -q 'ACCPTST:STDIO_REPLACE_UUIDS' "${curFile}"; then + "${cmdSed}" -i \ + -r 's@[0-9a-f\-]{32,40}@{uuid}@g' \ + "${curFile}" + fi + if grep -q 'ACCPTST:STDIO_REPLACE_BIGINTS' "${curFile}"; then + # Such as: 3811298194 + "${cmdSed}" -i \ + -r 's@[0-9]{7,64}@{bigint}@g' \ + "${curFile}" + fi + if grep -q 'ACCPTST:STDIO_REPLACE_DATETIMES' "${curFile}"; then + # Such as: 2016-02-10 15:38:44.420094 + "${cmdSed}" -i \ + -r 's@[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}@{datetime}@g' \ + "${curFile}" + fi + if grep -q 'ACCPTST:STDIO_REPLACE_LONGTIMES' "${curFile}"; then + # Such as: 2016-02-10 15:38:44.420094 + "${cmdSed}" -i \ + -r 's@[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{6}@{longtime}@g' \ + "${curFile}" + fi + if grep -q 'ACCPTST:STDIO_REPLACE_DURATIONS' "${curFile}"; then + # Such as: 0:00:00.001991 + "${cmdSed}" -i \ + -r 's@[0-9]{1,2}:[0-9]{2}:[0-9]{2}.[0-9]{6}@{duration}@g' \ + "${curFile}" + fi + if grep -q 'ACCPTST:STDIO_REPLACE_REMOTE_EXEC' "${curFile}"; then + grep -Ev 'remote-exec\): [ a-zA-Z]' "${curFile}" > "${__sysTmpDir}/accptst-filtered.txt" + mv "${__sysTmpDir}/accptst-filtered.txt" "${curFile}" + fi + done + + # Save these as new fixtures? + if [[ "${SAVE_FIXTURES:-}" = "true" ]]; then + for typ in stdio exitcode; do + curFile="${__accptstTmpDir}/${scenario}.${typ}" + cp -f \ + "${curFile}" \ + "${__dir}/fixture/${scenario}.${typ}" + done + fi + + # Compare + for typ in stdio exitcode; do + curFile="${__accptstTmpDir}/${scenario}.${typ}" + + echo -n " comparing ${typ}.. " + + if [[ "${typ}" = "stdio" ]]; then + if grep -q 'ACCPTST:STDIO_SKIP_COMPARE' "${curFile}"; then + echo "skip" + continue + fi + fi + + if ! diff --strip-trailing-cr "${__dir}/fixture/${scenario}.${typ}" "${curFile}"; then + echo -e "\\n\\n==> MISMATCH OF: ${scenario}.${typ} ---^" + echo -e "\\n\\n==> EXPECTED STDIO: " + cat "${__dir}/fixture/${scenario}.stdio" || true + echo -e "\\n\\n==> ACTUAL STDIO: " + cat "${__accptstTmpDir}/${scenario}.stdio" || true + exit 1 + fi + + echo "✓" + done + + popd > /dev/null +done <<< "$(find "${__dir}/scenario" -type f -iname 'run.sh')" + +[[ "${1:-}" ]] && exit 0 + +# Ensure correct syntax with all available bashes + +# shellcheck disable=SC2230 +# "command -v" is not a substitute for "which -a" +while IFS=$'\n' read -r bash; do + if [[ "${bash:-}" = "" ]]; then + continue + fi + # shellcheck disable=SC2016 + echo "==> ${bash} -n $(${bash} -c 'echo "(${BASH_VERSION})"')" + pushd "${__root}" > /dev/null + + failed="false" + + while IFS=$'\n' read -r file; do + [[ "${file}" =~ ^\./node_modules/ ]] && continue + + echo -n " ${file}.. " + + if ! "${bash}" -n "${file}" 2>> "${__accptstTmpDir}/${bash//\//.}.err"; then + echo "✗" + failed="true" + continue + fi + + echo "✓" + done <<< "$(find . -type f -iname '*.sh')" + + popd > /dev/null + + if [[ "${failed}" = "true" ]]; then + cat "${__accptstTmpDir}/${bash//\//.}.err" + exit 1 + fi +done <<< "$(which -a bash 2>/dev/null)" + +exit 0 diff --git a/test/fixture/.empty b/test/fixture/.empty new file mode 100644 index 0000000..e69de29 diff --git a/test/fixture/double-source.exitcode b/test/fixture/double-source.exitcode new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/test/fixture/double-source.exitcode @@ -0,0 +1 @@ +1 diff --git a/test/fixture/double-source.stdio b/test/fixture/double-source.stdio new file mode 100644 index 0000000..007a82b --- /dev/null +++ b/test/fixture/double-source.stdio @@ -0,0 +1,25 @@ +ACCPTST:STDIO_REPLACE_DATETIMES +{datetime} UTC [ debug] cli arg arg_f = () -> {tmpdir}/x +{datetime} UTC [ info] __i_am_main_script: 0 +{datetime} UTC [ info] __file: {root}/example.sh +{datetime} UTC [ info] __dir: {root} +{datetime} UTC [ info] __base: example +{datetime} UTC [ info] OSTYPE: {OSTYPE} +{datetime} UTC [ info] arg_f: {tmpdir}/x +{datetime} UTC [ info] arg_d: 0 +{datetime} UTC [ info] arg_v: 0 +{datetime} UTC [ info] arg_h: 0 +{datetime} UTC [ info] arg_i: 0 +{datetime} UTC [ info] arg_x: 0 +{datetime} UTC [ info] multiple lines example - line #1 +{datetime} UTC [ info] multiple lines example - line #2 +{datetime} UTC [ info] imagine logging the output of 'ls -al /path/' +{datetime} UTC [ debug] Info useful to developers for debugging the application, not useful during operations. +{datetime} UTC [ info] Normal operational messages - may be harvested for reporting, measuring throughput, etc. - no action required. +{datetime} UTC [ notice] Events that are unusual but not error conditions - might be summarized in an email to developers or admins to spot potential problems - no immediate action required. +{datetime} UTC [ warning] Warning messages, not an error, but indication that an error will occur if action is not taken, e.g. file system 85% full - each item must be resolved within a given time. +{datetime} UTC [ error] Non-urgent failures, these should be relayed to developers or admins; each item must be resolved within a given time. +{datetime} UTC [ critical] Should be corrected immediately, but indicates failure in a primary system, an example is a loss of a backup ISP connection. +{datetime} UTC [ alert] Should be corrected immediately, therefore notify staff who can fix the problem. An example would be the loss of a primary ISP connection. +{datetime} UTC [emergency] A "panic" condition usually affecting multiple apps/servers/sites. At this level it would usually notify all tech staff on call. +{datetime} UTC [ info] Cleaning up. Done diff --git a/test/fixture/ini_val.exitcode b/test/fixture/ini_val.exitcode new file mode 100644 index 0000000..573541a --- /dev/null +++ b/test/fixture/ini_val.exitcode @@ -0,0 +1 @@ +0 diff --git a/test/fixture/ini_val.stdio b/test/fixture/ini_val.stdio new file mode 100644 index 0000000..28b4c44 --- /dev/null +++ b/test/fixture/ini_val.stdio @@ -0,0 +1,42 @@ +--> command: Read 3 values +exists +127.0.0.1 +nginx, nodejs + +--> command: Replace three values in-place and show result + +[default] +orphan=no more + +[connection] +host=192.168.0.1 + +[software] +packages=vim + +[comment] +;[command] got this new comment +command=works like a chame +;[new_command] last addition will be moved downwards again after next command +new_command=commented too +--> function: Read 3 values +exists +127.0.0.1 +nginx, nodejs + +--> function: Replace three values in-place and show result + +[default] +orphan=no more + +[connection] +host=192.168.0.1 + +[software] +packages=vim + +[comment] +;[command] got this new comment +command=works like a chame +;[new_command] last addition will be moved downwards again after next command +new_command=commented too diff --git a/test/fixture/main-debug.exitcode b/test/fixture/main-debug.exitcode new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/test/fixture/main-debug.exitcode @@ -0,0 +1 @@ +1 diff --git a/test/fixture/main-debug.stdio b/test/fixture/main-debug.stdio new file mode 100644 index 0000000..0f83fa5 --- /dev/null +++ b/test/fixture/main-debug.stdio @@ -0,0 +1,25 @@ +ACCPTST:STDIO_REPLACE_DATETIMES +{datetime} UTC [ debug] cli arg arg_f = () -> {tmpdir}/x +{datetime} UTC [ info] __i_am_main_script: 1 +{datetime} UTC [ info] __file: {root}/main.sh +{datetime} UTC [ info] __dir: {root} +{datetime} UTC [ info] __base: main +{datetime} UTC [ info] OSTYPE: {OSTYPE} +{datetime} UTC [ info] arg_f: {tmpdir}/x +{datetime} UTC [ info] arg_d: 0 +{datetime} UTC [ info] arg_v: 0 +{datetime} UTC [ info] arg_h: 0 +{datetime} UTC [ info] arg_i: 0 +{datetime} UTC [ info] arg_x: 0 +{datetime} UTC [ info] multiple lines example - line #1 +{datetime} UTC [ info] multiple lines example - line #2 +{datetime} UTC [ info] imagine logging the output of 'ls -al /path/' +{datetime} UTC [ debug] Info useful to developers for debugging the application, not useful during operations. +{datetime} UTC [ info] Normal operational messages - may be harvested for reporting, measuring throughput, etc. - no action required. +{datetime} UTC [ notice] Events that are unusual but not error conditions - might be summarized in an email to developers or admins to spot potential problems - no immediate action required. +{datetime} UTC [ warning] Warning messages, not an error, but indication that an error will occur if action is not taken, e.g. file system 85% full - each item must be resolved within a given time. +{datetime} UTC [ error] Non-urgent failures, these should be relayed to developers or admins; each item must be resolved within a given time. +{datetime} UTC [ critical] Should be corrected immediately, but indicates failure in a primary system, an example is a loss of a backup ISP connection. +{datetime} UTC [ alert] Should be corrected immediately, therefore notify staff who can fix the problem. An example would be the loss of a primary ISP connection. +{datetime} UTC [emergency] A "panic" condition usually affecting multiple apps/servers/sites. At this level it would usually notify all tech staff on call. +{datetime} UTC [ info] Cleaning up. Done diff --git a/test/fixture/main-help.exitcode b/test/fixture/main-help.exitcode new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/test/fixture/main-help.exitcode @@ -0,0 +1 @@ +1 diff --git a/test/fixture/main-help.stdio b/test/fixture/main-help.stdio new file mode 100644 index 0000000..b8437bd --- /dev/null +++ b/test/fixture/main-help.stdio @@ -0,0 +1,19 @@ +ACCPTST:STDIO_REPLACE_DATETIMES + + Help using {root}/main.sh + + -f --file [arg] Filename to process. Required. + -t --temp [arg] Location of tempfile. Default="{tmpdir}/bar" + -v Enable verbose mode, print script as it is executed + -d --debug Enables debug mode + -h --help This page + -n --no-color Disable color output + -1 --one Do just one thing + -i --input [arg] File to process. Can be repeated. + -x Specify a flag. Can be repeated. + + This is Bash3 Boilerplate's help text. Feel free to add any description of your + program or elaborate more on command-line arguments. This section is not + parsed and will be added as-is to the help. + +{datetime} UTC [ info] Cleaning up. Done diff --git a/test/fixture/main-longopt.exitcode b/test/fixture/main-longopt.exitcode new file mode 100644 index 0000000..573541a --- /dev/null +++ b/test/fixture/main-longopt.exitcode @@ -0,0 +1 @@ +0 diff --git a/test/fixture/main-longopt.stdio b/test/fixture/main-longopt.stdio new file mode 100644 index 0000000..690d0a6 --- /dev/null +++ b/test/fixture/main-longopt.stdio @@ -0,0 +1,4 @@ +ACCPTST:STDIO_REPLACE_DATETIMES +{datetime} UTC [ info] arg_f: {tmpdir}/x +{datetime} UTC [ info] arg_f: {tmpdir}/x +{datetime} UTC [ info] arg_f: {tmpdir}/x diff --git a/test/fixture/main-nocolor.exitcode b/test/fixture/main-nocolor.exitcode new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/test/fixture/main-nocolor.exitcode @@ -0,0 +1 @@ +1 diff --git a/test/fixture/main-nocolor.stdio b/test/fixture/main-nocolor.stdio new file mode 100644 index 0000000..5200e7f --- /dev/null +++ b/test/fixture/main-nocolor.stdio @@ -0,0 +1,25 @@ +ACCPTST:STDIO_REPLACE_DATETIMES +{datetime} UTC [ debug] cli arg arg_f = () -> {tmpdir}/x +{datetime} UTC [ info] __i_am_main_script: 1 +{datetime} UTC [ info] __file: {root}/main.sh +{datetime} UTC [ info] __dir: {root} +{datetime} UTC [ info] __base: main +{datetime} UTC [ info] OSTYPE: {OSTYPE} +{datetime} UTC [ info] arg_f: {tmpdir}/x +{datetime} UTC [ info] arg_d: 0 +{datetime} UTC [ info] arg_v: 0 +{datetime} UTC [ info] arg_h: 0 +{datetime} UTC [ info] arg_i: 0 +{datetime} UTC [ info] arg_x: 0 +{datetime} UTC [ info] multiple lines example - line #1 +{datetime} UTC [ info] multiple lines example - line #2 +{datetime} UTC [ info] imagine logging the output of 'ls -al /path/' +{datetime} UTC [ debug] Info useful to developers for debugging the application, not useful during operations. +{datetime} UTC [ info] Normal operational messages - may be harvested for reporting, measuring throughput, etc. - no action required. +{datetime} UTC [ notice] Events that are unusual but not error conditions - might be summarized in an email to developers or admins to spot potential problems - no immediate action required. +{datetime} UTC [ warning] Warning messages, not an error, but indication that an error will occur if action is not taken, e.g. file system 85% full - each item must be resolved within a given time. +{datetime} UTC [ error] Non-urgent failures, these should be relayed to developers or admins; each item must be resolved within a given time. +{datetime} UTC [ critical] Should be corrected immediately, but indicates failure in a primary system, an example is a loss of a backup ISP connection. +{datetime} UTC [ alert] Should be corrected immediately, therefore notify staff who can fix the problem. An example would be the loss of a primary ISP connection. +{datetime} UTC [emergency] A "panic" condition usually affecting multiple apps/servers/sites. At this level it would usually notify all tech staff on call. +{datetime} UTC [ info] Cleaning up. Done diff --git a/test/fixture/main-repeated.exitcode b/test/fixture/main-repeated.exitcode new file mode 100644 index 0000000..573541a --- /dev/null +++ b/test/fixture/main-repeated.exitcode @@ -0,0 +1 @@ +0 diff --git a/test/fixture/main-repeated.stdio b/test/fixture/main-repeated.stdio new file mode 100644 index 0000000..5d27ec1 --- /dev/null +++ b/test/fixture/main-repeated.stdio @@ -0,0 +1,9 @@ +ACCPTST:STDIO_REPLACE_DATETIMES +{datetime} UTC [ info] arg_i: +{datetime} UTC [ info] - simple_input +{datetime} UTC [ info] - input_in_quotes +{datetime} UTC [ info] - input with spaces +{datetime} UTC [ info] - input with "quotes" +{datetime} UTC [ info] - last_input +{datetime} UTC [ info] arg_x: 3 +{datetime} UTC [ info] arg_x: 4 diff --git a/test/fixture/main-usage-defaults.exitcode b/test/fixture/main-usage-defaults.exitcode new file mode 100644 index 0000000..573541a --- /dev/null +++ b/test/fixture/main-usage-defaults.exitcode @@ -0,0 +1 @@ +0 diff --git a/test/fixture/main-usage-defaults.stdio b/test/fixture/main-usage-defaults.stdio new file mode 100644 index 0000000..2b93b13 --- /dev/null +++ b/test/fixture/main-usage-defaults.stdio @@ -0,0 +1,7 @@ +ACCPTST:STDIO_REPLACE_DATETIMES +{datetime} UTC [ info] arg_1: 0 +{datetime} UTC [ info] arg_2: 0 +{datetime} UTC [ info] arg_3: 'THREE' +{datetime} UTC [ info] arg_4: "FOUR" +{datetime} UTC [ info] arg_5: OOOPS +{datetime} UTC [ info] arg_6: diff --git a/test/fixture/main-usage-validation.exitcode b/test/fixture/main-usage-validation.exitcode new file mode 100644 index 0000000..573541a --- /dev/null +++ b/test/fixture/main-usage-validation.exitcode @@ -0,0 +1 @@ +0 diff --git a/test/fixture/main-usage-validation.stdio b/test/fixture/main-usage-validation.stdio new file mode 100644 index 0000000..e3968cb --- /dev/null +++ b/test/fixture/main-usage-validation.stdio @@ -0,0 +1,169 @@ +ACCPTST:STDIO_REPLACE_DATETIMES +# complain about -3 + + Option -3 (--three) requires an argument + + -0 --zero Do nothing. + -1 --one Do one thing. Required. + More description. + -2 --two Do two things. + More. Required. Description. + -3 --three [arg] Do three things. + Required. + -4 --four {arg} Do four things. + -5 --five {arg} Do five things. Required. Maybe. + -6 --six [arg] Do six things. Not Required. + Required, it is not. + -7 --seven [arg] Required. Or bust. + -8 --eight [arg] Do eight things. + More.Required.Description. + -a [arg] Do A. Required. + Default="do-a" + -b {arg} Do B.Default="do-b" + -c [arg] Required. Default="do-c" + -d {arg} Default="do-d" + + This is Bash3 Boilerplate's help text. Feel free to add any description of your + program or elaborate more on command-line arguments. This section is not + parsed and will be added as-is to the help. + +# complain about -4 + + Option -4 (--four) requires an argument + + -0 --zero Do nothing. + -1 --one Do one thing. Required. + More description. + -2 --two Do two things. + More. Required. Description. + -3 --three [arg] Do three things. + Required. + -4 --four {arg} Do four things. + -5 --five {arg} Do five things. Required. Maybe. + -6 --six [arg] Do six things. Not Required. + Required, it is not. + -7 --seven [arg] Required. Or bust. + -8 --eight [arg] Do eight things. + More.Required.Description. + -a [arg] Do A. Required. + Default="do-a" + -b {arg} Do B.Default="do-b" + -c [arg] Required. Default="do-c" + -d {arg} Default="do-d" + + This is Bash3 Boilerplate's help text. Feel free to add any description of your + program or elaborate more on command-line arguments. This section is not + parsed and will be added as-is to the help. + +# complain about -5 + + Option -5 (--five) requires an argument + + -0 --zero Do nothing. + -1 --one Do one thing. Required. + More description. + -2 --two Do two things. + More. Required. Description. + -3 --three [arg] Do three things. + Required. + -4 --four {arg} Do four things. + -5 --five {arg} Do five things. Required. Maybe. + -6 --six [arg] Do six things. Not Required. + Required, it is not. + -7 --seven [arg] Required. Or bust. + -8 --eight [arg] Do eight things. + More.Required.Description. + -a [arg] Do A. Required. + Default="do-a" + -b {arg} Do B.Default="do-b" + -c [arg] Required. Default="do-c" + -d {arg} Default="do-d" + + This is Bash3 Boilerplate's help text. Feel free to add any description of your + program or elaborate more on command-line arguments. This section is not + parsed and will be added as-is to the help. + +# complain about -8 (because -7 syntax is not supported) + + Option -8 (--eight) requires an argument + + -0 --zero Do nothing. + -1 --one Do one thing. Required. + More description. + -2 --two Do two things. + More. Required. Description. + -3 --three [arg] Do three things. + Required. + -4 --four {arg} Do four things. + -5 --five {arg} Do five things. Required. Maybe. + -6 --six [arg] Do six things. Not Required. + Required, it is not. + -7 --seven [arg] Required. Or bust. + -8 --eight [arg] Do eight things. + More.Required.Description. + -a [arg] Do A. Required. + Default="do-a" + -b {arg} Do B.Default="do-b" + -c [arg] Required. Default="do-c" + -d {arg} Default="do-d" + + This is Bash3 Boilerplate's help text. Feel free to add any description of your + program or elaborate more on command-line arguments. This section is not + parsed and will be added as-is to the help. + +# complain about -d (because -d syntax is not supported) + + Option -d requires an argument + + -0 --zero Do nothing. + -1 --one Do one thing. Required. + More description. + -2 --two Do two things. + More. Required. Description. + -3 --three [arg] Do three things. + Required. + -4 --four {arg} Do four things. + -5 --five {arg} Do five things. Required. Maybe. + -6 --six [arg] Do six things. Not Required. + Required, it is not. + -7 --seven [arg] Required. Or bust. + -8 --eight [arg] Do eight things. + More.Required.Description. + -a [arg] Do A. Required. + Default="do-a" + -b {arg} Do B.Default="do-b" + -c [arg] Required. Default="do-c" + -d {arg} Default="do-d" + + This is Bash3 Boilerplate's help text. Feel free to add any description of your + program or elaborate more on command-line arguments. This section is not + parsed and will be added as-is to the help. + +# complain about nothing +{datetime} UTC [ info] arg_0: 0 +{datetime} UTC [ info] arg_1: 0 +{datetime} UTC [ info] arg_2: 0 +{datetime} UTC [ info] arg_3: arg3 +{datetime} UTC [ info] arg_4: arg4 +{datetime} UTC [ info] arg_5: arg5 +{datetime} UTC [ info] arg_6: +{datetime} UTC [ info] arg_7: +{datetime} UTC [ info] arg_8: arg8 +{datetime} UTC [ info] arg_a: do-a +{datetime} UTC [ info] arg_b: do-b +{datetime} UTC [ info] arg_c: do-c +{datetime} UTC [ info] arg_d: argd +# test for issue #108 +{datetime} UTC [ info] arg_0: 1 +{datetime} UTC [ info] arg_1: 0 +{datetime} UTC [ info] arg_2: 0 +{datetime} UTC [ info] arg_3: arg3 +{datetime} UTC [ info] arg_4: value +{datetime} UTC [ info] arg_5: arg5 +{datetime} UTC [ info] arg_6: +{datetime} UTC [ info] arg_7: +{datetime} UTC [ info] arg_8: arg8 +{datetime} UTC [ info] arg_a: do-a +{datetime} UTC [ info] arg_b: do-b +{datetime} UTC [ info] arg_c: do-c +{datetime} UTC [ info] arg_d: argd diff --git a/test/fixture/megamount.exitcode b/test/fixture/megamount.exitcode new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/test/fixture/megamount.exitcode @@ -0,0 +1 @@ +1 diff --git a/test/fixture/megamount.stdio b/test/fixture/megamount.stdio new file mode 100644 index 0000000..5859c6a --- /dev/null +++ b/test/fixture/megamount.stdio @@ -0,0 +1,2 @@ +ERR: Unknown protocol: 'foobarfs://' +ERR: Unknown protocol: 'foobarfs://' diff --git a/test/fixture/parse_url.exitcode b/test/fixture/parse_url.exitcode new file mode 100644 index 0000000..573541a --- /dev/null +++ b/test/fixture/parse_url.exitcode @@ -0,0 +1 @@ +0 diff --git a/test/fixture/parse_url.stdio b/test/fixture/parse_url.stdio new file mode 100644 index 0000000..00e30b1 --- /dev/null +++ b/test/fixture/parse_url.stdio @@ -0,0 +1,24 @@ +abc123 + + Use second argument to return just 1 variable. + parse_url() demo: + + proto: http:// + user: johndoe + pass: abc123 + host: example.com + port: 8080 + path: index.html + +abc123 + + Use second argument to return just 1 variable. + parse_url() demo: + + proto: http:// + user: johndoe + pass: abc123 + host: example.com + port: 8080 + path: index.html + diff --git a/test/fixture/templater.exitcode b/test/fixture/templater.exitcode new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/test/fixture/templater.exitcode @@ -0,0 +1 @@ +1 diff --git a/test/fixture/templater.stdio b/test/fixture/templater.stdio new file mode 100644 index 0000000..3a63574 --- /dev/null +++ b/test/fixture/templater.stdio @@ -0,0 +1,14 @@ +-- +[connection] +host = 127.0.0.1 +-- +[connection] +host = 127.0.0.1 +-- +port = ${I_DONT_EXIST} +[connection] +host = 127.0.0.1 +port = ${I_DONT_EXIST} +-- +port = ${I_DONT_EXIST} +ERROR: Unable to replace the above template vars diff --git a/test/scenario/double-source/run.sh b/test/scenario/double-source/run.sh new file mode 100644 index 0000000..29d5ccb --- /dev/null +++ b/test/scenario/double-source/run.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -o pipefail +set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +export LOG_LEVEL=7 + +echo "ACCPTST:STDIO_REPLACE_DATETIMES" + +# shellcheck source=example.sh +source "${__root}/example.sh" -f /tmp/x diff --git a/test/scenario/ini_val/data.ini b/test/scenario/ini_val/data.ini new file mode 100644 index 0000000..450248d --- /dev/null +++ b/test/scenario/ini_val/data.ini @@ -0,0 +1,8 @@ +[default] +orphan=exists + +[connection] +host=127.0.0.1 + +[software] +packages=nginx, nodejs diff --git a/test/scenario/ini_val/run.sh b/test/scenario/ini_val/run.sh new file mode 100755 index 0000000..efc4941 --- /dev/null +++ b/test/scenario/ini_val/run.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +set -o pipefail +set -o errexit +set -o nounset +# set -o xtrace + +# Set magic variables for current FILE & DIR +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +# echo "ACCPTST:STDIO_REPLACE_DATETIMES" + + +# Use as standalone: +cp -f data.ini dummy.ini +echo "--> command: Read 3 values" +bash "${__root}/src/ini_val.sh" ./dummy.ini orphan +bash "${__root}/src/ini_val.sh" ./dummy.ini connection.host +bash "${__root}/src/ini_val.sh" ./dummy.ini software.packages +bash "${__root}/src/ini_val.sh" ./dummy.ini comment.command + +echo "--> command: Replace three values in-place and show result" +bash "${__root}/src/ini_val.sh" ./dummy.ini orphan "no more" +bash "${__root}/src/ini_val.sh" ./dummy.ini connection.host "192.168.0.1" +bash "${__root}/src/ini_val.sh" ./dummy.ini software.packages "vim" + +bash "${__root}/src/ini_val.sh" ./dummy.ini comment.command "commented" "this key is commented" +bash "${__root}/src/ini_val.sh" ./dummy.ini comment.new_command "commented too" "last addition will be moved downwards again after next command" +bash "${__root}/src/ini_val.sh" ./dummy.ini comment.command "works like a chame" "got this new comment" +cat dummy.ini +rm -f dummy.ini + +# Use as include: +cp -f data.ini dummy.ini + +# shellcheck source=main.sh +source "${__root}/src/ini_val.sh" + +echo "--> function: Read 3 values" +ini_val ./dummy.ini orphan +ini_val ./dummy.ini connection.host +ini_val ./dummy.ini software.packages +ini_val ./dummy.ini comment.command + +echo "--> function: Replace three values in-place and show result" +ini_val ./dummy.ini orphan "no more" +ini_val ./dummy.ini connection.host "192.168.0.1" +ini_val ./dummy.ini software.packages "vim" + +ini_val ./dummy.ini comment.command "commented" "this key is commited" +ini_val ./dummy.ini comment.new_command "commented too" "last addition will be moved downwards again after next command" +ini_val ./dummy.ini comment.command "works like a chame" "got this new comment" +cat dummy.ini +rm -f dummy.ini diff --git a/test/scenario/main-debug/run.sh b/test/scenario/main-debug/run.sh new file mode 100644 index 0000000..a7407e4 --- /dev/null +++ b/test/scenario/main-debug/run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -o pipefail +set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +echo "ACCPTST:STDIO_REPLACE_DATETIMES" + +env LOG_LEVEL=7 bash "${__root}/main.sh" -f /tmp/x diff --git a/test/scenario/main-help/run.sh b/test/scenario/main-help/run.sh new file mode 100755 index 0000000..b600c25 --- /dev/null +++ b/test/scenario/main-help/run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -o pipefail +set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +echo "ACCPTST:STDIO_REPLACE_DATETIMES" + +bash "${__root}/main.sh" -f /tmp/x -h diff --git a/test/scenario/main-longopt/run.sh b/test/scenario/main-longopt/run.sh new file mode 100644 index 0000000..3d2cc77 --- /dev/null +++ b/test/scenario/main-longopt/run.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# set -o pipefail +# set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +echo "ACCPTST:STDIO_REPLACE_DATETIMES" + +( + env LOG_LEVEL=6 bash "${__root}/main.sh" --file /tmp/x; + env LOG_LEVEL=6 bash "${__root}/main.sh" --file=/tmp/x; + env LOG_LEVEL=6 bash "${__root}/main.sh" -f /tmp/x +) 2>&1 |grep arg_f diff --git a/test/scenario/main-nocolor/run.sh b/test/scenario/main-nocolor/run.sh new file mode 100644 index 0000000..a7971bf --- /dev/null +++ b/test/scenario/main-nocolor/run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -o pipefail +set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +echo "ACCPTST:STDIO_REPLACE_DATETIMES" + +env LOG_LEVEL=7 NO_COLOR=true bash "${__root}/main.sh" -f /tmp/x diff --git a/test/scenario/main-repeated/run.sh b/test/scenario/main-repeated/run.sh new file mode 100644 index 0000000..b72b7c1 --- /dev/null +++ b/test/scenario/main-repeated/run.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# set -o pipefail +# set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +echo "ACCPTST:STDIO_REPLACE_DATETIMES" + +( + env LOG_LEVEL="${LOG_LEVEL:-6}" bash "${__root}/main.sh" -f dummy -i simple_input -i "input_in_quotes" -i "input with spaces" -i "input with \"quotes\"" -i last_input +) 2>&1 |grep arg_i -A 5 + +( + env LOG_LEVEL="${LOG_LEVEL:-6}" bash "${__root}/main.sh" -x -f dummy -x -x + env LOG_LEVEL="${LOG_LEVEL:-6}" bash "${__root}/main.sh" -f dummy -xxxx +) 2>&1 |grep arg_x diff --git a/test/scenario/main-usage-defaults/run.sh b/test/scenario/main-usage-defaults/run.sh new file mode 100755 index 0000000..d0c7703 --- /dev/null +++ b/test/scenario/main-usage-defaults/run.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +set -o pipefail +set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +read -r -d '' __usage <<-'EOF' || true # exits non-zero when EOF encountered + -1 --one Do one thing. Default="ONE" + More description. + -2 --two Do two things. + More description. Default="TWO" + -3 --three [arg] Do three things. Default="'THREE'" + More description. + -4 --four [arg] Do four things. + More description. Default='"FOUR"' + -5 --five [arg] Do five things. Default="FIVE" + More description. Default='OOOPS' + -6 --six [arg] Do six things. + More description. +EOF + +export __usage + +echo "ACCPTST:STDIO_REPLACE_DATETIMES" + +# shellcheck source=main.sh +source "${__root}/main.sh" + +for argument in ${!arg_*}; do info "${argument}: ${!argument}"; done diff --git a/test/scenario/main-usage-validation/run.sh b/test/scenario/main-usage-validation/run.sh new file mode 100755 index 0000000..60fa066 --- /dev/null +++ b/test/scenario/main-usage-validation/run.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# shellcheck source=main.sh + +set -o pipefail +set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +read -r -d '' __usage <<-'EOF' || true # exits non-zero when EOF encountered + -0 --zero Do nothing. + -1 --one Do one thing. Required. + More description. + -2 --two Do two things. + More. Required. Description. + -3 --three [arg] Do three things. + Required. + -4 --four {arg} Do four things. + -5 --five {arg} Do five things. Required. Maybe. + -6 --six [arg] Do six things. Not Required. + Required, it is not. + -7 --seven [arg] Required. Or bust. + -8 --eight [arg] Do eight things. + More.Required.Description. + -a [arg] Do A. Required. + Default="do-a" + -b {arg} Do B.Default="do-b" + -c [arg] Required. Default="do-c" + -d {arg} Default="do-d" +EOF + +export __usage +export NO_COLOR="true" + +echo "ACCPTST:STDIO_REPLACE_DATETIMES" + +echo "# complain about -3" +(source "${__root}/main.sh") || true + +echo "# complain about -4" +(source "${__root}/main.sh" -3 arg3) || true + +echo "# complain about -5" +(source "${__root}/main.sh" -3 arg3 -4 arg4) || true + +echo "# complain about -8 (because -7 syntax is not supported)" +(source "${__root}/main.sh" -3 arg3 -4 arg4 -5 arg5) || true + +echo "# complain about -d (because -d syntax is not supported)" +(source "${__root}/main.sh" -3 arg3 -4 arg4 -5 arg5 -8 arg8) || true + +echo "# complain about nothing" +( + source "${__root}/main.sh" -3 arg3 -4 arg4 -5 arg5 -8 arg8 -d argd + for argument in ${!arg_*}; do info "${argument}: ${!argument}"; done +) + +echo "# test for issue #108" +( + source "${__root}/main.sh" -3 arg3 -5 arg5 -8 arg8 -d argd --four value --zero + for argument in ${!arg_*}; do info "${argument}: ${!argument}"; done +) diff --git a/test/scenario/megamount/run.sh b/test/scenario/megamount/run.sh new file mode 100644 index 0000000..517cff5 --- /dev/null +++ b/test/scenario/megamount/run.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -o pipefail +set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +__sysTmpDir="${TMPDIR:-/tmp}" +__sysTmpDir="${__sysTmpDir%/}" # <-- remove trailing slash on macosx + +# Currently I only know how to test a failing condition here since +# it's too invasive to create actual mounts to play with on a system + +bash "${__root}/src/megamount.sh" 'foobarfs://janedoe:abc123@192.168.0.1/documents' "${__sysTmpDir}/mnt/documents" || true + +# shellcheck source=src/megamount.sh +source "${__root}/src/megamount.sh" + +megamount 'foobarfs://janedoe:abc123@192.168.0.1/documents' "${__sysTmpDir}/mnt/documents" diff --git a/test/scenario/parse_url/run.sh b/test/scenario/parse_url/run.sh new file mode 100644 index 0000000..47a18aa --- /dev/null +++ b/test/scenario/parse_url/run.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -o pipefail +set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +bash "${__root}/src/parse_url.sh" 'http://johndoe:abc123@example.com:8080/index.html' pass +bash "${__root}/src/parse_url.sh" 'http://johndoe:abc123@example.com:8080/index.html' + +# shellcheck source=src/parse_url.sh +source "${__root}/src/parse_url.sh" + +parse_url 'http://johndoe:abc123@example.com:8080/index.html' pass +parse_url 'http://johndoe:abc123@example.com:8080/index.html' diff --git a/test/scenario/templater/app.template.cfg b/test/scenario/templater/app.template.cfg new file mode 100644 index 0000000..dc3f85f --- /dev/null +++ b/test/scenario/templater/app.template.cfg @@ -0,0 +1,2 @@ +[connection] +host = ${TARGET_HOST} diff --git a/test/scenario/templater/break.template.cfg b/test/scenario/templater/break.template.cfg new file mode 100644 index 0000000..e265cba --- /dev/null +++ b/test/scenario/templater/break.template.cfg @@ -0,0 +1,3 @@ +[connection] +host = ${TARGET_HOST} +port = ${I_DONT_EXIST} diff --git a/test/scenario/templater/run.sh b/test/scenario/templater/run.sh new file mode 100644 index 0000000..da0abbf --- /dev/null +++ b/test/scenario/templater/run.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -o pipefail +set -o errexit +set -o nounset +# set -o xtrace + +__dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +__file="${__dir}/$(basename "${BASH_SOURCE[0]}")" +__base="$(basename "${__file}" .sh)" + +__root="$(cd "$(dirname "$(dirname "$(dirname "${__dir}")")")" && pwd)" + +__templaterTmpFile=$(mktemp "${TMPDIR:-/tmp}/${__base}.XXXXXX") +function cleanup_before_exit () { rm "${__templaterTmpFile:?}"; } +trap cleanup_before_exit EXIT + +echo "--" +env TARGET_HOST="127.0.0.1" bash "${__root}/src/templater.sh" ./app.template.cfg "${__templaterTmpFile}" +cat "${__templaterTmpFile}" + +echo "--" +export TARGET_HOST="127.0.0.1" + +# shellcheck source=src/templater.sh +source "${__root}/src/templater.sh" + +templater ./app.template.cfg "${__templaterTmpFile}" +cat "${__templaterTmpFile}" + +echo "--" +env ALLOW_REMAINDERS="1" TARGET_HOST="127.0.0.1" bash "${__root}/src/templater.sh" ./break.template.cfg "${__templaterTmpFile}" +cat "${__templaterTmpFile}" + +echo "--" +env TARGET_HOST="127.0.0.1" bash "${__root}/src/templater.sh" ./break.template.cfg "${__templaterTmpFile}" +cat "${__templaterTmpFile}" diff --git a/test/style.pl b/test/style.pl new file mode 100755 index 0000000..9dffcb5 --- /dev/null +++ b/test/style.pl @@ -0,0 +1,45 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +die "usage: $0 \n" if (not @ARGV); + +my $rc = 0; +my $file = shift; + +open(my $fh, '<', $file) or die "Cannot open \`$file' for read: $!\n"; +while (<$fh>) { + next if (/^\s*#/); + + my $errors = 0; + + # remove everything between single quotes + # this will remove too much in case of: echo "var='$var'" + # and thus miss an opportunity to complain later on + # also it mangles the input line irreversible + s/'[^']+'/'___'/g; + + # highlight unbraced variables-- + # unless properly backslash'ed + $errors += s/((?:^|[^\\]))(((\\\\)+)?\$\w)/$1\033[31m$2\033[0m/g; + + # highlight single square brackets + $errors += s/((?:^|\s+))\[([^\[].+[^\]])\](\s*(;|&&|\|\|))/$1\033[31m\[\033[0m$2\033[31m\]\033[0m$3/g; + + # highlight double equal sign + $errors += s/(\[\[.*)(==)(.*\]\])/$1\033[31m$2\033[0m$3/g; + + # highlight tabs mixed with whitespace at beginning of lines + $errors += s/^( *)(\t+ *)/\033[31m\[$2\]\033[0m/; + + # highlight trailing whitespace + $errors += s/([ \t]+)$/\033[31m\[$1\]\033[0m/; + + next if (not $errors); + print "${file}[$.]: $_"; + $rc = 1; +} +close($fh); + +exit $rc; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..eef5da4 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1372 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 6 + cacheKey: 8 + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b + languageName: node + linkType: hard + +"ansi-styles@npm:^3.2.1": + version: 3.2.1 + resolution: "ansi-styles@npm:3.2.1" + dependencies: + color-convert: ^1.9.0 + checksum: d85ade01c10e5dd77b6c89f34ed7531da5830d2cb5882c645f330079975b716438cd7ebb81d0d6e6b4f9c577f19ae41ab55f07f19786b02f9dfd9e0377395665 + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: ^2.0.1 + checksum: 513b44c3b2105dd14cc42a19271e80f386466c4be574bccf60b627432f9198571ebf4ab1e4c3ba17347658f4ee1711c163d574248c0c1cdc2d5917a0ad582ec4 + languageName: node + linkType: hard + +"array-buffer-byte-length@npm:^1.0.0": + version: 1.0.0 + resolution: "array-buffer-byte-length@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + is-array-buffer: ^3.0.1 + checksum: 044e101ce150f4804ad19c51d6c4d4cfa505c5b2577bd179256e4aa3f3f6a0a5e9874c78cd428ee566ac574c8a04d7ce21af9fe52e844abfdccb82b33035a7c3 + languageName: node + linkType: hard + +"arraybuffer.prototype.slice@npm:^1.0.1": + version: 1.0.1 + resolution: "arraybuffer.prototype.slice@npm:1.0.1" + dependencies: + array-buffer-byte-length: ^1.0.0 + call-bind: ^1.0.2 + define-properties: ^1.2.0 + get-intrinsic: ^1.2.1 + is-array-buffer: ^3.0.2 + is-shared-array-buffer: ^1.0.2 + checksum: e3e9b2a3e988ebfeddce4c7e8f69df730c9e48cb04b0d40ff0874ce3d86b3d1339dd520ffde5e39c02610bc172ecfbd4bc93324b1cabd9554c44a56b131ce0ce + languageName: node + linkType: hard + +"available-typed-arrays@npm:^1.0.5": + version: 1.0.5 + resolution: "available-typed-arrays@npm:1.0.5" + checksum: 20eb47b3cefd7db027b9bbb993c658abd36d4edd3fe1060e83699a03ee275b0c9b216cc076ff3f2db29073225fb70e7613987af14269ac1fe2a19803ccc97f1a + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 + languageName: node + linkType: hard + +"bash3boilerplate@workspace:.": + version: 0.0.0-use.local + resolution: "bash3boilerplate@workspace:." + dependencies: + fakefile: 1.1.0 + npm-run-all: 4.1.5 + replace: 1.2.2 + languageName: unknown + linkType: soft + +"brace-expansion@npm:^1.1.7": + version: 1.1.11 + resolution: "brace-expansion@npm:1.1.11" + dependencies: + balanced-match: ^1.0.0 + concat-map: 0.0.1 + checksum: faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 + languageName: node + linkType: hard + +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2": + version: 1.0.2 + resolution: "call-bind@npm:1.0.2" + dependencies: + function-bind: ^1.1.1 + get-intrinsic: ^1.0.2 + checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 + languageName: node + linkType: hard + +"camelcase@npm:^5.0.0": + version: 5.3.1 + resolution: "camelcase@npm:5.3.1" + checksum: e6effce26b9404e3c0f301498184f243811c30dfe6d0b9051863bd8e4034d09c8c2923794f280d6827e5aa055f6c434115ff97864a16a963366fb35fd673024b + languageName: node + linkType: hard + +"chalk@npm:2.4.2, chalk@npm:^2.4.1": + version: 2.4.2 + resolution: "chalk@npm:2.4.2" + dependencies: + ansi-styles: ^3.2.1 + escape-string-regexp: ^1.0.5 + supports-color: ^5.3.0 + checksum: ec3661d38fe77f681200f878edbd9448821924e0f93a9cefc0e26a33b145f1027a2084bf19967160d11e1f03bfe4eaffcabf5493b89098b2782c3fe0b03d80c2 + languageName: node + linkType: hard + +"cliui@npm:^6.0.0": + version: 6.0.0 + resolution: "cliui@npm:6.0.0" + dependencies: + string-width: ^4.2.0 + strip-ansi: ^6.0.0 + wrap-ansi: ^6.2.0 + checksum: 4fcfd26d292c9f00238117f39fc797608292ae36bac2168cfee4c85923817d0607fe21b3329a8621e01aedf512c99b7eaa60e363a671ffd378df6649fb48ae42 + languageName: node + linkType: hard + +"color-convert@npm:^1.9.0": + version: 1.9.3 + resolution: "color-convert@npm:1.9.3" + dependencies: + color-name: 1.1.3 + checksum: fd7a64a17cde98fb923b1dd05c5f2e6f7aefda1b60d67e8d449f9328b4e53b228a428fd38bfeaeb2db2ff6b6503a776a996150b80cdf224062af08a5c8a3a203 + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: ~1.1.4 + checksum: 79e6bdb9fd479a205c71d89574fccfb22bd9053bd98c6c4d870d65c132e5e904e6034978e55b43d69fcaa7433af2016ee203ce76eeba9cfa554b373e7f7db336 + languageName: node + linkType: hard + +"color-name@npm:1.1.3": + version: 1.1.3 + resolution: "color-name@npm:1.1.3" + checksum: 09c5d3e33d2105850153b14466501f2bfb30324a2f76568a408763a3b7433b0e50e5b4ab1947868e65cb101bb7cb75029553f2c333b6d4b8138a73fcc133d69d + languageName: node + linkType: hard + +"color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af + languageName: node + linkType: hard + +"cross-spawn@npm:^6.0.5": + version: 6.0.6 + resolution: "cross-spawn@npm:6.0.6" + dependencies: + nice-try: ^1.0.4 + path-key: ^2.0.1 + semver: ^5.5.0 + shebang-command: ^1.2.0 + which: ^1.2.9 + checksum: a6e2e5b04a0e0f806c1df45f92cd079b65f95fbe5a7650ee1ab60318c33a6c156a8a2f8b6898f57764f7363ec599a0625e9855dfa78d52d2d73dbd32eb11c25e + languageName: node + linkType: hard + +"decamelize@npm:^1.2.0": + version: 1.2.0 + resolution: "decamelize@npm:1.2.0" + checksum: ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa + languageName: node + linkType: hard + +"define-properties@npm:^1.1.3, define-properties@npm:^1.1.4, define-properties@npm:^1.2.0": + version: 1.2.0 + resolution: "define-properties@npm:1.2.0" + dependencies: + has-property-descriptors: ^1.0.0 + object-keys: ^1.1.1 + checksum: e60aee6a19b102df4e2b1f301816804e81ab48bb91f00d0d935f269bf4b3f79c88b39e4f89eaa132890d23267335fd1140dfcd8d5ccd61031a0a2c41a54e33a6 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192 + languageName: node + linkType: hard + +"error-ex@npm:^1.3.1": + version: 1.3.2 + resolution: "error-ex@npm:1.3.2" + dependencies: + is-arrayish: ^0.2.1 + checksum: c1c2b8b65f9c91b0f9d75f0debaa7ec5b35c266c2cac5de412c1a6de86d4cbae04ae44e510378cb14d032d0645a36925d0186f8bb7367bcc629db256b743a001 + languageName: node + linkType: hard + +"es-abstract@npm:^1.20.4, es-abstract@npm:^1.22.1": + version: 1.22.1 + resolution: "es-abstract@npm:1.22.1" + dependencies: + array-buffer-byte-length: ^1.0.0 + arraybuffer.prototype.slice: ^1.0.1 + available-typed-arrays: ^1.0.5 + call-bind: ^1.0.2 + es-set-tostringtag: ^2.0.1 + es-to-primitive: ^1.2.1 + function.prototype.name: ^1.1.5 + get-intrinsic: ^1.2.1 + get-symbol-description: ^1.0.0 + globalthis: ^1.0.3 + gopd: ^1.0.1 + has: ^1.0.3 + has-property-descriptors: ^1.0.0 + has-proto: ^1.0.1 + has-symbols: ^1.0.3 + internal-slot: ^1.0.5 + is-array-buffer: ^3.0.2 + is-callable: ^1.2.7 + is-negative-zero: ^2.0.2 + is-regex: ^1.1.4 + is-shared-array-buffer: ^1.0.2 + is-string: ^1.0.7 + is-typed-array: ^1.1.10 + is-weakref: ^1.0.2 + object-inspect: ^1.12.3 + object-keys: ^1.1.1 + object.assign: ^4.1.4 + regexp.prototype.flags: ^1.5.0 + safe-array-concat: ^1.0.0 + safe-regex-test: ^1.0.0 + string.prototype.trim: ^1.2.7 + string.prototype.trimend: ^1.0.6 + string.prototype.trimstart: ^1.0.6 + typed-array-buffer: ^1.0.0 + typed-array-byte-length: ^1.0.0 + typed-array-byte-offset: ^1.0.0 + typed-array-length: ^1.0.4 + unbox-primitive: ^1.0.2 + which-typed-array: ^1.1.10 + checksum: 614e2c1c3717cb8d30b6128ef12ea110e06fd7d75ad77091ca1c5dbfb00da130e62e4bbbbbdda190eada098a22b27fe0f99ae5a1171dac2c8663b1e8be8a3a9b + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.0.1": + version: 2.0.1 + resolution: "es-set-tostringtag@npm:2.0.1" + dependencies: + get-intrinsic: ^1.1.3 + has: ^1.0.3 + has-tostringtag: ^1.0.0 + checksum: ec416a12948cefb4b2a5932e62093a7cf36ddc3efd58d6c58ca7ae7064475ace556434b869b0bbeb0c365f1032a8ccd577211101234b69837ad83ad204fff884 + languageName: node + linkType: hard + +"es-to-primitive@npm:^1.2.1": + version: 1.2.1 + resolution: "es-to-primitive@npm:1.2.1" + dependencies: + is-callable: ^1.1.4 + is-date-object: ^1.0.1 + is-symbol: ^1.0.2 + checksum: 4ead6671a2c1402619bdd77f3503991232ca15e17e46222b0a41a5d81aebc8740a77822f5b3c965008e631153e9ef0580540007744521e72de8e33599fca2eed + languageName: node + linkType: hard + +"escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 + languageName: node + linkType: hard + +"fakefile@npm:1.1.0": + version: 1.1.0 + resolution: "fakefile@npm:1.1.0" + dependencies: + fs-extra: 0.26.5 + checksum: eaa56f2d101807bd02e2e6dc11c8790b7e9f51344d52a40fa6e49300e720cd80233c20d9098146cb3e1452cafd16efcabe97f58527ebb4c73b6c32561a8c3902 + languageName: node + linkType: hard + +"find-up@npm:^4.1.0": + version: 4.1.0 + resolution: "find-up@npm:4.1.0" + dependencies: + locate-path: ^5.0.0 + path-exists: ^4.0.0 + checksum: 4c172680e8f8c1f78839486e14a43ef82e9decd0e74145f40707cc42e7420506d5ec92d9a11c22bd2c48fb0c384ea05dd30e10dd152fefeec6f2f75282a8b844 + languageName: node + linkType: hard + +"for-each@npm:^0.3.3": + version: 0.3.3 + resolution: "for-each@npm:0.3.3" + dependencies: + is-callable: ^1.1.3 + checksum: 6c48ff2bc63362319c65e2edca4a8e1e3483a2fabc72fbe7feaf8c73db94fc7861bd53bc02c8a66a0c1dd709da6b04eec42e0abdd6b40ce47305ae92a25e5d28 + languageName: node + linkType: hard + +"fs-extra@npm:0.26.5": + version: 0.26.5 + resolution: "fs-extra@npm:0.26.5" + dependencies: + graceful-fs: ^4.1.2 + jsonfile: ^2.1.0 + klaw: ^1.0.0 + path-is-absolute: ^1.0.0 + rimraf: ^2.2.8 + checksum: c421033be5e30e5f45e39ed885eda933c3e9a318e86dae657254455281f58a24df467bfaf3d248c564c02cd4e147670862f6a6e28b46c5e90d2232783026b592 + languageName: node + linkType: hard + +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0 + languageName: node + linkType: hard + +"function-bind@npm:^1.1.1": + version: 1.1.1 + resolution: "function-bind@npm:1.1.1" + checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a + languageName: node + linkType: hard + +"function.prototype.name@npm:^1.1.5": + version: 1.1.6 + resolution: "function.prototype.name@npm:1.1.6" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + functions-have-names: ^1.2.3 + checksum: 7a3f9bd98adab09a07f6e1f03da03d3f7c26abbdeaeee15223f6c04a9fb5674792bdf5e689dac19b97ac71de6aad2027ba3048a9b883aa1b3173eed6ab07f479 + languageName: node + linkType: hard + +"functions-have-names@npm:^1.2.3": + version: 1.2.3 + resolution: "functions-have-names@npm:1.2.3" + checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 + languageName: node + linkType: hard + +"get-caller-file@npm:^2.0.1": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9 + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.0, get-intrinsic@npm:^1.2.1": + version: 1.2.1 + resolution: "get-intrinsic@npm:1.2.1" + dependencies: + function-bind: ^1.1.1 + has: ^1.0.3 + has-proto: ^1.0.1 + has-symbols: ^1.0.3 + checksum: 5b61d88552c24b0cf6fa2d1b3bc5459d7306f699de060d76442cce49a4721f52b8c560a33ab392cf5575b7810277d54ded9d4d39a1ea61855619ebc005aa7e5f + languageName: node + linkType: hard + +"get-symbol-description@npm:^1.0.0": + version: 1.0.0 + resolution: "get-symbol-description@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.1.1 + checksum: 9ceff8fe968f9270a37a1f73bf3f1f7bda69ca80f4f80850670e0e7b9444ff99323f7ac52f96567f8b5f5fbe7ac717a0d81d3407c7313e82810c6199446a5247 + languageName: node + linkType: hard + +"glob@npm:^7.1.3": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: ^1.0.0 + inflight: ^1.0.4 + inherits: 2 + minimatch: ^3.1.1 + once: ^1.3.0 + path-is-absolute: ^1.0.0 + checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 + languageName: node + linkType: hard + +"globalthis@npm:^1.0.3": + version: 1.0.3 + resolution: "globalthis@npm:1.0.3" + dependencies: + define-properties: ^1.1.3 + checksum: fbd7d760dc464c886d0196166d92e5ffb4c84d0730846d6621a39fbbc068aeeb9c8d1421ad330e94b7bca4bb4ea092f5f21f3d36077812af5d098b4dc006c998 + languageName: node + linkType: hard + +"gopd@npm:^1.0.1": + version: 1.0.1 + resolution: "gopd@npm:1.0.1" + dependencies: + get-intrinsic: ^1.1.3 + checksum: a5ccfb8806e0917a94e0b3de2af2ea4979c1da920bc381667c260e00e7cafdbe844e2cb9c5bcfef4e5412e8bf73bab837285bc35c7ba73aaaf0134d4583393a6 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 + languageName: node + linkType: hard + +"has-bigints@npm:^1.0.1, has-bigints@npm:^1.0.2": + version: 1.0.2 + resolution: "has-bigints@npm:1.0.2" + checksum: 390e31e7be7e5c6fe68b81babb73dfc35d413604d7ee5f56da101417027a4b4ce6a27e46eff97ad040c835b5d228676eae99a9b5c3bc0e23c8e81a49241ff45b + languageName: node + linkType: hard + +"has-flag@npm:^3.0.0": + version: 3.0.0 + resolution: "has-flag@npm:3.0.0" + checksum: 4a15638b454bf086c8148979aae044dd6e39d63904cd452d970374fa6a87623423da485dfb814e7be882e05c096a7ccf1ebd48e7e7501d0208d8384ff4dea73b + languageName: node + linkType: hard + +"has-property-descriptors@npm:^1.0.0": + version: 1.0.0 + resolution: "has-property-descriptors@npm:1.0.0" + dependencies: + get-intrinsic: ^1.1.1 + checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb + languageName: node + linkType: hard + +"has-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "has-proto@npm:1.0.1" + checksum: febc5b5b531de8022806ad7407935e2135f1cc9e64636c3916c6842bd7995994ca3b29871ecd7954bd35f9e2986c17b3b227880484d22259e2f8e6ce63fd383e + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": + version: 1.0.3 + resolution: "has-symbols@npm:1.0.3" + checksum: a054c40c631c0d5741a8285010a0777ea0c068f99ed43e5d6eb12972da223f8af553a455132fdb0801bdcfa0e0f443c0c03a68d8555aa529b3144b446c3f2410 + languageName: node + linkType: hard + +"has-tostringtag@npm:^1.0.0": + version: 1.0.0 + resolution: "has-tostringtag@npm:1.0.0" + dependencies: + has-symbols: ^1.0.2 + checksum: cc12eb28cb6ae22369ebaad3a8ab0799ed61270991be88f208d508076a1e99abe4198c965935ce85ea90b60c94ddda73693b0920b58e7ead048b4a391b502c1c + languageName: node + linkType: hard + +"has@npm:^1.0.3": + version: 1.0.3 + resolution: "has@npm:1.0.3" + dependencies: + function-bind: ^1.1.1 + checksum: b9ad53d53be4af90ce5d1c38331e712522417d017d5ef1ebd0507e07c2fbad8686fffb8e12ddecd4c39ca9b9b47431afbb975b8abf7f3c3b82c98e9aad052792 + languageName: node + linkType: hard + +"hosted-git-info@npm:^2.1.4": + version: 2.8.9 + resolution: "hosted-git-info@npm:2.8.9" + checksum: c955394bdab888a1e9bb10eb33029e0f7ce5a2ac7b3f158099dc8c486c99e73809dca609f5694b223920ca2174db33d32b12f9a2a47141dc59607c29da5a62dd + languageName: node + linkType: hard + +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" + dependencies: + once: ^1.3.0 + wrappy: 1 + checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd + languageName: node + linkType: hard + +"inherits@npm:2": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 + languageName: node + linkType: hard + +"internal-slot@npm:^1.0.5": + version: 1.0.5 + resolution: "internal-slot@npm:1.0.5" + dependencies: + get-intrinsic: ^1.2.0 + has: ^1.0.3 + side-channel: ^1.0.4 + checksum: 97e84046bf9e7574d0956bd98d7162313ce7057883b6db6c5c7b5e5f05688864b0978ba07610c726d15d66544ffe4b1050107d93f8a39ebc59b15d8b429b497a + languageName: node + linkType: hard + +"is-array-buffer@npm:^3.0.1, is-array-buffer@npm:^3.0.2": + version: 3.0.2 + resolution: "is-array-buffer@npm:3.0.2" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.2.0 + is-typed-array: ^1.1.10 + checksum: dcac9dda66ff17df9cabdc58214172bf41082f956eab30bb0d86bc0fab1e44b690fc8e1f855cf2481245caf4e8a5a006a982a71ddccec84032ed41f9d8da8c14 + languageName: node + linkType: hard + +"is-arrayish@npm:^0.2.1": + version: 0.2.1 + resolution: "is-arrayish@npm:0.2.1" + checksum: eef4417e3c10e60e2c810b6084942b3ead455af16c4509959a27e490e7aee87cfb3f38e01bbde92220b528a0ee1a18d52b787e1458ee86174d8c7f0e58cd488f + languageName: node + linkType: hard + +"is-bigint@npm:^1.0.1": + version: 1.0.4 + resolution: "is-bigint@npm:1.0.4" + dependencies: + has-bigints: ^1.0.1 + checksum: c56edfe09b1154f8668e53ebe8252b6f185ee852a50f9b41e8d921cb2bed425652049fbe438723f6cb48a63ca1aa051e948e7e401e093477c99c84eba244f666 + languageName: node + linkType: hard + +"is-boolean-object@npm:^1.1.0": + version: 1.1.2 + resolution: "is-boolean-object@npm:1.1.2" + dependencies: + call-bind: ^1.0.2 + has-tostringtag: ^1.0.0 + checksum: c03b23dbaacadc18940defb12c1c0e3aaece7553ef58b162a0f6bba0c2a7e1551b59f365b91e00d2dbac0522392d576ef322628cb1d036a0fe51eb466db67222 + languageName: node + linkType: hard + +"is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 61fd57d03b0d984e2ed3720fb1c7a897827ea174bd44402878e059542ea8c4aeedee0ea0985998aa5cc2736b2fa6e271c08587addb5b3959ac52cf665173d1ac + languageName: node + linkType: hard + +"is-core-module@npm:^2.13.0": + version: 2.13.0 + resolution: "is-core-module@npm:2.13.0" + dependencies: + has: ^1.0.3 + checksum: 053ab101fb390bfeb2333360fd131387bed54e476b26860dc7f5a700bbf34a0ec4454f7c8c4d43e8a0030957e4b3db6e16d35e1890ea6fb654c833095e040355 + languageName: node + linkType: hard + +"is-date-object@npm:^1.0.1": + version: 1.0.5 + resolution: "is-date-object@npm:1.0.5" + dependencies: + has-tostringtag: ^1.0.0 + checksum: baa9077cdf15eb7b58c79398604ca57379b2fc4cf9aa7a9b9e295278648f628c9b201400c01c5e0f7afae56507d741185730307cbe7cad3b9f90a77e5ee342fc + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 + languageName: node + linkType: hard + +"is-negative-zero@npm:^2.0.2": + version: 2.0.2 + resolution: "is-negative-zero@npm:2.0.2" + checksum: f3232194c47a549da60c3d509c9a09be442507616b69454716692e37ae9f37c4dea264fb208ad0c9f3efd15a796a46b79df07c7e53c6227c32170608b809149a + languageName: node + linkType: hard + +"is-number-object@npm:^1.0.4": + version: 1.0.7 + resolution: "is-number-object@npm:1.0.7" + dependencies: + has-tostringtag: ^1.0.0 + checksum: d1e8d01bb0a7134c74649c4e62da0c6118a0bfc6771ea3c560914d52a627873e6920dd0fd0ebc0e12ad2ff4687eac4c308f7e80320b973b2c8a2c8f97a7524f7 + languageName: node + linkType: hard + +"is-regex@npm:^1.1.4": + version: 1.1.4 + resolution: "is-regex@npm:1.1.4" + dependencies: + call-bind: ^1.0.2 + has-tostringtag: ^1.0.0 + checksum: 362399b33535bc8f386d96c45c9feb04cf7f8b41c182f54174c1a45c9abbbe5e31290bbad09a458583ff6bf3b2048672cdb1881b13289569a7c548370856a652 + languageName: node + linkType: hard + +"is-shared-array-buffer@npm:^1.0.2": + version: 1.0.2 + resolution: "is-shared-array-buffer@npm:1.0.2" + dependencies: + call-bind: ^1.0.2 + checksum: 9508929cf14fdc1afc9d61d723c6e8d34f5e117f0bffda4d97e7a5d88c3a8681f633a74f8e3ad1fe92d5113f9b921dc5ca44356492079612f9a247efbce7032a + languageName: node + linkType: hard + +"is-string@npm:^1.0.5, is-string@npm:^1.0.7": + version: 1.0.7 + resolution: "is-string@npm:1.0.7" + dependencies: + has-tostringtag: ^1.0.0 + checksum: 323b3d04622f78d45077cf89aab783b2f49d24dc641aa89b5ad1a72114cfeff2585efc8c12ef42466dff32bde93d839ad321b26884cf75e5a7892a938b089989 + languageName: node + linkType: hard + +"is-symbol@npm:^1.0.2, is-symbol@npm:^1.0.3": + version: 1.0.4 + resolution: "is-symbol@npm:1.0.4" + dependencies: + has-symbols: ^1.0.2 + checksum: 92805812ef590738d9de49d677cd17dfd486794773fb6fa0032d16452af46e9b91bb43ffe82c983570f015b37136f4b53b28b8523bfb10b0ece7a66c31a54510 + languageName: node + linkType: hard + +"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.9": + version: 1.1.12 + resolution: "is-typed-array@npm:1.1.12" + dependencies: + which-typed-array: ^1.1.11 + checksum: 4c89c4a3be07186caddadf92197b17fda663a9d259ea0d44a85f171558270d36059d1c386d34a12cba22dfade5aba497ce22778e866adc9406098c8fc4771796 + languageName: node + linkType: hard + +"is-weakref@npm:^1.0.2": + version: 1.0.2 + resolution: "is-weakref@npm:1.0.2" + dependencies: + call-bind: ^1.0.2 + checksum: 95bd9a57cdcb58c63b1c401c60a474b0f45b94719c30f548c891860f051bc2231575c290a6b420c6bc6e7ed99459d424c652bd5bf9a1d5259505dc35b4bf83de + languageName: node + linkType: hard + +"isarray@npm:^2.0.5": + version: 2.0.5 + resolution: "isarray@npm:2.0.5" + checksum: bd5bbe4104438c4196ba58a54650116007fa0262eccef13a4c55b2e09a5b36b59f1e75b9fcc49883dd9d4953892e6fc007eef9e9155648ceea036e184b0f930a + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 26bf6c5480dda5161c820c5b5c751ae1e766c587b1f951ea3fcfc973bafb7831ae5b54a31a69bd670220e42e99ec154475025a468eae58ea262f813fdc8d1c62 + languageName: node + linkType: hard + +"json-parse-better-errors@npm:^1.0.1": + version: 1.0.2 + resolution: "json-parse-better-errors@npm:1.0.2" + checksum: ff2b5ba2a70e88fd97a3cb28c1840144c5ce8fae9cbeeddba15afa333a5c407cf0e42300cd0a2885dbb055227fe68d405070faad941beeffbfde9cf3b2c78c5d + languageName: node + linkType: hard + +"jsonfile@npm:^2.1.0": + version: 2.4.0 + resolution: "jsonfile@npm:2.4.0" + dependencies: + graceful-fs: ^4.1.6 + dependenciesMeta: + graceful-fs: + optional: true + checksum: f5064aabbc9e35530dc471d8b203ae1f40dbe949ddde4391c6f6a6d310619a15f0efdae5587df594d1d70c555193aaeee9d2ed4aec9ffd5767bd5e4e62d49c3d + languageName: node + linkType: hard + +"klaw@npm:^1.0.0": + version: 1.3.1 + resolution: "klaw@npm:1.3.1" + dependencies: + graceful-fs: ^4.1.9 + dependenciesMeta: + graceful-fs: + optional: true + checksum: 8f69e4797c26e7c3f2426bfa85f38a3da3c2cb1b4c6bd850d2377aed440d41ce9d806f2885c2e2e224372c56af4b1d43b8a499adecf9a05e7373dc6b8b7c52e4 + languageName: node + linkType: hard + +"load-json-file@npm:^4.0.0": + version: 4.0.0 + resolution: "load-json-file@npm:4.0.0" + dependencies: + graceful-fs: ^4.1.2 + parse-json: ^4.0.0 + pify: ^3.0.0 + strip-bom: ^3.0.0 + checksum: 8f5d6d93ba64a9620445ee9bde4d98b1eac32cf6c8c2d20d44abfa41a6945e7969456ab5f1ca2fb06ee32e206c9769a20eec7002fe290de462e8c884b6b8b356 + languageName: node + linkType: hard + +"locate-path@npm:^5.0.0": + version: 5.0.0 + resolution: "locate-path@npm:5.0.0" + dependencies: + p-locate: ^4.1.0 + checksum: 83e51725e67517287d73e1ded92b28602e3ae5580b301fe54bfb76c0c723e3f285b19252e375712316774cf52006cb236aed5704692c32db0d5d089b69696e30 + languageName: node + linkType: hard + +"memorystream@npm:^0.3.1": + version: 0.3.1 + resolution: "memorystream@npm:0.3.1" + checksum: f18b42440d24d09516d01466c06adf797df7873f0d40aa7db02e5fb9ed83074e5e65412d0720901d7069363465f82dc4f8bcb44f0cde271567a61426ce6ca2e9 + languageName: node + linkType: hard + +"minimatch@npm:3.0.5": + version: 3.0.5 + resolution: "minimatch@npm:3.0.5" + dependencies: + brace-expansion: ^1.1.7 + checksum: a3b84b426eafca947741b864502cee02860c4e7b145de11ad98775cfcf3066fef422583bc0ffce0952ddf4750c1ccf4220b1556430d4ce10139f66247d87d69e + languageName: node + linkType: hard + +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: ^1.1.7 + checksum: c154e566406683e7bcb746e000b84d74465b3a832c45d59912b9b55cd50dee66e5c4b1e5566dba26154040e51672f9aa450a9aef0c97cfc7336b78b7afb9540a + languageName: node + linkType: hard + +"nice-try@npm:^1.0.4": + version: 1.0.5 + resolution: "nice-try@npm:1.0.5" + checksum: 0b4af3b5bb5d86c289f7a026303d192a7eb4417231fe47245c460baeabae7277bcd8fd9c728fb6bd62c30b3e15cd6620373e2cf33353b095d8b403d3e8a15aff + languageName: node + linkType: hard + +"normalize-package-data@npm:^2.3.2": + version: 2.5.0 + resolution: "normalize-package-data@npm:2.5.0" + dependencies: + hosted-git-info: ^2.1.4 + resolve: ^1.10.0 + semver: 2 || 3 || 4 || 5 + validate-npm-package-license: ^3.0.1 + checksum: 7999112efc35a6259bc22db460540cae06564aa65d0271e3bdfa86876d08b0e578b7b5b0028ee61b23f1cae9fc0e7847e4edc0948d3068a39a2a82853efc8499 + languageName: node + linkType: hard + +"npm-run-all@npm:4.1.5": + version: 4.1.5 + resolution: "npm-run-all@npm:4.1.5" + dependencies: + ansi-styles: ^3.2.1 + chalk: ^2.4.1 + cross-spawn: ^6.0.5 + memorystream: ^0.3.1 + minimatch: ^3.0.4 + pidtree: ^0.3.0 + read-pkg: ^3.0.0 + shell-quote: ^1.6.1 + string.prototype.padend: ^3.0.0 + bin: + npm-run-all: bin/npm-run-all/index.js + run-p: bin/run-p/index.js + run-s: bin/run-s/index.js + checksum: 373b72c6a36564da13c1642c1fd9bb4dcc756bce7a3648f883772f02661095319820834ff813762d2fee403e9b40c1cd27c8685807c107440f10eb19c006d4a0 + languageName: node + linkType: hard + +"object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": + version: 1.12.3 + resolution: "object-inspect@npm:1.12.3" + checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db + languageName: node + linkType: hard + +"object-keys@npm:^1.1.1": + version: 1.1.1 + resolution: "object-keys@npm:1.1.1" + checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a + languageName: node + linkType: hard + +"object.assign@npm:^4.1.4": + version: 4.1.4 + resolution: "object.assign@npm:4.1.4" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + has-symbols: ^1.0.3 + object-keys: ^1.1.1 + checksum: 76cab513a5999acbfe0ff355f15a6a125e71805fcf53de4e9d4e082e1989bdb81d1e329291e1e4e0ae7719f0e4ef80e88fb2d367ae60500d79d25a6224ac8864 + languageName: node + linkType: hard + +"once@npm:^1.3.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: 1 + checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 + languageName: node + linkType: hard + +"p-limit@npm:^2.2.0": + version: 2.3.0 + resolution: "p-limit@npm:2.3.0" + dependencies: + p-try: ^2.0.0 + checksum: 84ff17f1a38126c3314e91ecfe56aecbf36430940e2873dadaa773ffe072dc23b7af8e46d4b6485d302a11673fe94c6b67ca2cfbb60c989848b02100d0594ac1 + languageName: node + linkType: hard + +"p-locate@npm:^4.1.0": + version: 4.1.0 + resolution: "p-locate@npm:4.1.0" + dependencies: + p-limit: ^2.2.0 + checksum: 513bd14a455f5da4ebfcb819ef706c54adb09097703de6aeaa5d26fe5ea16df92b48d1ac45e01e3944ce1e6aa2a66f7f8894742b8c9d6e276e16cd2049a2b870 + languageName: node + linkType: hard + +"p-try@npm:^2.0.0": + version: 2.2.0 + resolution: "p-try@npm:2.2.0" + checksum: f8a8e9a7693659383f06aec604ad5ead237c7a261c18048a6e1b5b85a5f8a067e469aa24f5bc009b991ea3b058a87f5065ef4176793a200d4917349881216cae + languageName: node + linkType: hard + +"parse-json@npm:^4.0.0": + version: 4.0.0 + resolution: "parse-json@npm:4.0.0" + dependencies: + error-ex: ^1.3.1 + json-parse-better-errors: ^1.0.1 + checksum: 0fe227d410a61090c247e34fa210552b834613c006c2c64d9a05cfe9e89cf8b4246d1246b1a99524b53b313e9ac024438d0680f67e33eaed7e6f38db64cfe7b5 + languageName: node + linkType: hard + +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 505807199dfb7c50737b057dd8d351b82c033029ab94cb10a657609e00c1bc53b951cfdbccab8de04c5584d5eff31128ce6afd3db79281874a5ef2adbba55ed1 + languageName: node + linkType: hard + +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 + languageName: node + linkType: hard + +"path-key@npm:^2.0.1": + version: 2.0.1 + resolution: "path-key@npm:2.0.1" + checksum: f7ab0ad42fe3fb8c7f11d0c4f849871e28fbd8e1add65c370e422512fc5887097b9cf34d09c1747d45c942a8c1e26468d6356e2df3f740bf177ab8ca7301ebfd + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 49abf3d81115642938a8700ec580da6e830dde670be21893c62f4e10bd7dd4c3742ddc603fe24f898cba7eb0c6bc1777f8d9ac14185d34540c6d4d80cd9cae8a + languageName: node + linkType: hard + +"path-type@npm:^3.0.0": + version: 3.0.0 + resolution: "path-type@npm:3.0.0" + dependencies: + pify: ^3.0.0 + checksum: 735b35e256bad181f38fa021033b1c33cfbe62ead42bb2222b56c210e42938eecb272ae1949f3b6db4ac39597a61b44edd8384623ec4d79bfdc9a9c0f12537a6 + languageName: node + linkType: hard + +"pidtree@npm:^0.3.0": + version: 0.3.1 + resolution: "pidtree@npm:0.3.1" + bin: + pidtree: bin/pidtree.js + checksum: eb49025099f1af89a4696f7673351421f13420f3397b963c901fe23a1c9c2ff50f4750321970d4472c0ffbb065e4a6c3c27f75e226cc62284b19e21d32ce7012 + languageName: node + linkType: hard + +"pify@npm:^3.0.0": + version: 3.0.0 + resolution: "pify@npm:3.0.0" + checksum: 6cdcbc3567d5c412450c53261a3f10991665d660961e06605decf4544a61a97a54fefe70a68d5c37080ff9d6f4cf51444c90198d1ba9f9309a6c0d6e9f5c4fde + languageName: node + linkType: hard + +"read-pkg@npm:^3.0.0": + version: 3.0.0 + resolution: "read-pkg@npm:3.0.0" + dependencies: + load-json-file: ^4.0.0 + normalize-package-data: ^2.3.2 + path-type: ^3.0.0 + checksum: 398903ebae6c7e9965419a1062924436cc0b6f516c42c4679a90290d2f87448ed8f977e7aa2dbba4aa1ac09248628c43e493ac25b2bc76640e946035200e34c6 + languageName: node + linkType: hard + +"regexp.prototype.flags@npm:^1.5.0": + version: 1.5.0 + resolution: "regexp.prototype.flags@npm:1.5.0" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.2.0 + functions-have-names: ^1.2.3 + checksum: c541687cdbdfff1b9a07f6e44879f82c66bbf07665f9a7544c5fd16acdb3ec8d1436caab01662d2fbcad403f3499d49ab0b77fbc7ef29ef961d98cc4bc9755b4 + languageName: node + linkType: hard + +"replace@npm:1.2.2": + version: 1.2.2 + resolution: "replace@npm:1.2.2" + dependencies: + chalk: 2.4.2 + minimatch: 3.0.5 + yargs: ^15.3.1 + bin: + replace: bin/replace.js + search: bin/search.js + checksum: 1d69f43937a5fdf9dea278e78d6f3b51c1889ba5135bd201918bbda6330684adf8276e8e90e1c021034420dd4df239e51c23ca40752cb9bc6180c153d6d46a37 + languageName: node + linkType: hard + +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: fb47e70bf0001fdeabdc0429d431863e9475e7e43ea5f94ad86503d918423c1543361cc5166d713eaa7029dd7a3d34775af04764bebff99ef413111a5af18c80 + languageName: node + linkType: hard + +"require-main-filename@npm:^2.0.0": + version: 2.0.0 + resolution: "require-main-filename@npm:2.0.0" + checksum: e9e294695fea08b076457e9ddff854e81bffbe248ed34c1eec348b7abbd22a0d02e8d75506559e2265e96978f3c4720bd77a6dad84755de8162b357eb6c778c7 + languageName: node + linkType: hard + +"resolve@npm:^1.10.0": + version: 1.22.4 + resolution: "resolve@npm:1.22.4" + dependencies: + is-core-module: ^2.13.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: 23f25174c2736ce24c6d918910e0d1f89b6b38fefa07a995dff864acd7863d59a7f049e691f93b4b2ee29696303390d921552b6d1b841ed4a8101f517e1d0124 + languageName: node + linkType: hard + +"resolve@patch:resolve@^1.10.0#~builtin": + version: 1.22.4 + resolution: "resolve@patch:resolve@npm%3A1.22.4#~builtin::version=1.22.4&hash=c3c19d" + dependencies: + is-core-module: ^2.13.0 + path-parse: ^1.0.7 + supports-preserve-symlinks-flag: ^1.0.0 + bin: + resolve: bin/resolve + checksum: c45f2545fdc4d21883861b032789e20aa67a2f2692f68da320cc84d5724cd02f2923766c5354b3210897e88f1a7b3d6d2c7c22faeead8eed7078e4c783a444bc + languageName: node + linkType: hard + +"rimraf@npm:^2.2.8": + version: 2.7.1 + resolution: "rimraf@npm:2.7.1" + dependencies: + glob: ^7.1.3 + bin: + rimraf: ./bin.js + checksum: cdc7f6eacb17927f2a075117a823e1c5951792c6498ebcce81ca8203454a811d4cf8900314154d3259bb8f0b42ab17f67396a8694a54cae3283326e57ad250cd + languageName: node + linkType: hard + +"safe-array-concat@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-array-concat@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.2.0 + has-symbols: ^1.0.3 + isarray: ^2.0.5 + checksum: f43cb98fe3b566327d0c09284de2b15fb85ae964a89495c1b1a5d50c7c8ed484190f4e5e71aacc167e16231940079b326f2c0807aea633d47cc7322f40a6b57f + languageName: node + linkType: hard + +"safe-regex-test@npm:^1.0.0": + version: 1.0.0 + resolution: "safe-regex-test@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.1.3 + is-regex: ^1.1.4 + checksum: bc566d8beb8b43c01b94e67de3f070fd2781685e835959bbbaaec91cc53381145ca91f69bd837ce6ec244817afa0a5e974fc4e40a2957f0aca68ac3add1ddd34 + languageName: node + linkType: hard + +"semver@npm:2 || 3 || 4 || 5": + version: 5.3.0 + resolution: "semver@npm:5.3.0" + bin: + semver: ./bin/semver + checksum: 2717b14299c76a4b35aec0aafebca22a3644da2942d2a4095f26e36d77a9bbe17a9a3a5199795f83edd26323d5c22024a2d9d373a038dec4e023156fa166d314 + languageName: node + linkType: hard + +"semver@npm:^5.5.0": + version: 5.7.2 + resolution: "semver@npm:5.7.2" + bin: + semver: bin/semver + checksum: fb4ab5e0dd1c22ce0c937ea390b4a822147a9c53dbd2a9a0132f12fe382902beef4fbf12cf51bb955248d8d15874ce8cd89532569756384f994309825f10b686 + languageName: node + linkType: hard + +"set-blocking@npm:^2.0.0": + version: 2.0.0 + resolution: "set-blocking@npm:2.0.0" + checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 + languageName: node + linkType: hard + +"shebang-command@npm:^1.2.0": + version: 1.2.0 + resolution: "shebang-command@npm:1.2.0" + dependencies: + shebang-regex: ^1.0.0 + checksum: 9eed1750301e622961ba5d588af2212505e96770ec376a37ab678f965795e995ade7ed44910f5d3d3cb5e10165a1847f52d3348c64e146b8be922f7707958908 + languageName: node + linkType: hard + +"shebang-regex@npm:^1.0.0": + version: 1.0.0 + resolution: "shebang-regex@npm:1.0.0" + checksum: 404c5a752cd40f94591dfd9346da40a735a05139dac890ffc229afba610854d8799aaa52f87f7e0c94c5007f2c6af55bdcaeb584b56691926c5eaf41dc8f1372 + languageName: node + linkType: hard + +"shell-quote@npm:^1.6.1": + version: 1.8.1 + resolution: "shell-quote@npm:1.8.1" + checksum: 5f01201f4ef504d4c6a9d0d283fa17075f6770bfbe4c5850b074974c68062f37929ca61700d95ad2ac8822e14e8c4b990ca0e6e9272e64befd74ce5e19f0736b + languageName: node + linkType: hard + +"side-channel@npm:^1.0.4": + version: 1.0.4 + resolution: "side-channel@npm:1.0.4" + dependencies: + call-bind: ^1.0.0 + get-intrinsic: ^1.0.2 + object-inspect: ^1.9.0 + checksum: 351e41b947079c10bd0858364f32bb3a7379514c399edb64ab3dce683933483fc63fb5e4efe0a15a2e8a7e3c436b6a91736ddb8d8c6591b0460a24bb4a1ee245 + languageName: node + linkType: hard + +"spdx-correct@npm:^3.0.0": + version: 3.2.0 + resolution: "spdx-correct@npm:3.2.0" + dependencies: + spdx-expression-parse: ^3.0.0 + spdx-license-ids: ^3.0.0 + checksum: e9ae98d22f69c88e7aff5b8778dc01c361ef635580e82d29e5c60a6533cc8f4d820803e67d7432581af0cc4fb49973125076ee3b90df191d153e223c004193b2 + languageName: node + linkType: hard + +"spdx-exceptions@npm:^2.1.0": + version: 2.3.0 + resolution: "spdx-exceptions@npm:2.3.0" + checksum: cb69a26fa3b46305637123cd37c85f75610e8c477b6476fa7354eb67c08128d159f1d36715f19be6f9daf4b680337deb8c65acdcae7f2608ba51931540687ac0 + languageName: node + linkType: hard + +"spdx-expression-parse@npm:^3.0.0": + version: 3.0.1 + resolution: "spdx-expression-parse@npm:3.0.1" + dependencies: + spdx-exceptions: ^2.1.0 + spdx-license-ids: ^3.0.0 + checksum: a1c6e104a2cbada7a593eaa9f430bd5e148ef5290d4c0409899855ce8b1c39652bcc88a725259491a82601159d6dc790bedefc9016c7472f7de8de7361f8ccde + languageName: node + linkType: hard + +"spdx-license-ids@npm:^3.0.0": + version: 3.0.13 + resolution: "spdx-license-ids@npm:3.0.13" + checksum: 3469d85c65f3245a279fa11afc250c3dca96e9e847f2f79d57f466940c5bb8495da08a542646086d499b7f24a74b8d0b42f3fc0f95d50ff99af1f599f6360ad7 + languageName: node + linkType: hard + +"string-width@npm:^4.1.0, string-width@npm:^4.2.0": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: ^8.0.0 + is-fullwidth-code-point: ^3.0.0 + strip-ansi: ^6.0.1 + checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb + languageName: node + linkType: hard + +"string.prototype.padend@npm:^3.0.0": + version: 3.1.4 + resolution: "string.prototype.padend@npm:3.1.4" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 76e07238fe31dc12177428f0436b7ed6985f6a7ba97470fd53e4f0a6d9860bfee127d81957f3073cc879b434233df143825d140581e1340278053ad993c92f6c + languageName: node + linkType: hard + +"string.prototype.trim@npm:^1.2.7": + version: 1.2.7 + resolution: "string.prototype.trim@npm:1.2.7" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 05b7b2d6af63648e70e44c4a8d10d8cc457536df78b55b9d6230918bde75c5987f6b8604438c4c8652eb55e4fc9725d2912789eb4ec457d6995f3495af190c09 + languageName: node + linkType: hard + +"string.prototype.trimend@npm:^1.0.6": + version: 1.0.6 + resolution: "string.prototype.trimend@npm:1.0.6" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 0fdc34645a639bd35179b5a08227a353b88dc089adf438f46be8a7c197fc3f22f8514c1c9be4629b3cd29c281582730a8cbbad6466c60f76b5f99cf2addb132e + languageName: node + linkType: hard + +"string.prototype.trimstart@npm:^1.0.6": + version: 1.0.6 + resolution: "string.prototype.trimstart@npm:1.0.6" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.1.4 + es-abstract: ^1.20.4 + checksum: 89080feef416621e6ef1279588994305477a7a91648d9436490d56010a1f7adc39167cddac7ce0b9884b8cdbef086987c4dcb2960209f2af8bac0d23ceff4f41 + languageName: node + linkType: hard + +"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: ^5.0.1 + checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + languageName: node + linkType: hard + +"strip-bom@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-bom@npm:3.0.0" + checksum: 8d50ff27b7ebe5ecc78f1fe1e00fcdff7af014e73cf724b46fb81ef889eeb1015fc5184b64e81a2efe002180f3ba431bdd77e300da5c6685d702780fbf0c8d5b + languageName: node + linkType: hard + +"supports-color@npm:^5.3.0": + version: 5.5.0 + resolution: "supports-color@npm:5.5.0" + dependencies: + has-flag: ^3.0.0 + checksum: 95f6f4ba5afdf92f495b5a912d4abee8dcba766ae719b975c56c084f5004845f6f5a5f7769f52d53f40e21952a6d87411bafe34af4a01e65f9926002e38e1dac + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 53b1e247e68e05db7b3808b99b892bd36fb096e6fba213a06da7fab22045e97597db425c724f2bbd6c99a3c295e1e73f3e4de78592289f38431049e1277ca0ae + languageName: node + linkType: hard + +"typed-array-buffer@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-buffer@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.2.1 + is-typed-array: ^1.1.10 + checksum: 3e0281c79b2a40cd97fe715db803884301993f4e8c18e8d79d75fd18f796e8cd203310fec8c7fdb5e6c09bedf0af4f6ab8b75eb3d3a85da69328f28a80456bd3 + languageName: node + linkType: hard + +"typed-array-byte-length@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-byte-length@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + for-each: ^0.3.3 + has-proto: ^1.0.1 + is-typed-array: ^1.1.10 + checksum: b03db16458322b263d87a702ff25388293f1356326c8a678d7515767ef563ef80e1e67ce648b821ec13178dd628eb2afdc19f97001ceae7a31acf674c849af94 + languageName: node + linkType: hard + +"typed-array-byte-offset@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-byte-offset@npm:1.0.0" + dependencies: + available-typed-arrays: ^1.0.5 + call-bind: ^1.0.2 + for-each: ^0.3.3 + has-proto: ^1.0.1 + is-typed-array: ^1.1.10 + checksum: 04f6f02d0e9a948a95fbfe0d5a70b002191fae0b8fe0fe3130a9b2336f043daf7a3dda56a31333c35a067a97e13f539949ab261ca0f3692c41603a46a94e960b + languageName: node + linkType: hard + +"typed-array-length@npm:^1.0.4": + version: 1.0.4 + resolution: "typed-array-length@npm:1.0.4" + dependencies: + call-bind: ^1.0.2 + for-each: ^0.3.3 + is-typed-array: ^1.1.9 + checksum: 2228febc93c7feff142b8c96a58d4a0d7623ecde6c7a24b2b98eb3170e99f7c7eff8c114f9b283085cd59dcd2bd43aadf20e25bba4b034a53c5bb292f71f8956 + languageName: node + linkType: hard + +"unbox-primitive@npm:^1.0.2": + version: 1.0.2 + resolution: "unbox-primitive@npm:1.0.2" + dependencies: + call-bind: ^1.0.2 + has-bigints: ^1.0.2 + has-symbols: ^1.0.3 + which-boxed-primitive: ^1.0.2 + checksum: b7a1cf5862b5e4b5deb091672ffa579aa274f648410009c81cca63fed3b62b610c4f3b773f912ce545bb4e31edc3138975b5bc777fc6e4817dca51affb6380e9 + languageName: node + linkType: hard + +"validate-npm-package-license@npm:^3.0.1": + version: 3.0.4 + resolution: "validate-npm-package-license@npm:3.0.4" + dependencies: + spdx-correct: ^3.0.0 + spdx-expression-parse: ^3.0.0 + checksum: 35703ac889d419cf2aceef63daeadbe4e77227c39ab6287eeb6c1b36a746b364f50ba22e88591f5d017bc54685d8137bc2d328d0a896e4d3fd22093c0f32a9ad + languageName: node + linkType: hard + +"which-boxed-primitive@npm:^1.0.2": + version: 1.0.2 + resolution: "which-boxed-primitive@npm:1.0.2" + dependencies: + is-bigint: ^1.0.1 + is-boolean-object: ^1.1.0 + is-number-object: ^1.0.4 + is-string: ^1.0.5 + is-symbol: ^1.0.3 + checksum: 53ce774c7379071729533922adcca47220228405e1895f26673bbd71bdf7fb09bee38c1d6399395927c6289476b5ae0629863427fd151491b71c4b6cb04f3a5e + languageName: node + linkType: hard + +"which-module@npm:^2.0.0": + version: 2.0.1 + resolution: "which-module@npm:2.0.1" + checksum: 1967b7ce17a2485544a4fdd9063599f0f773959cca24176dbe8f405e55472d748b7c549cd7920ff6abb8f1ab7db0b0f1b36de1a21c57a8ff741f4f1e792c52be + languageName: node + linkType: hard + +"which-typed-array@npm:^1.1.10, which-typed-array@npm:^1.1.11": + version: 1.1.11 + resolution: "which-typed-array@npm:1.1.11" + dependencies: + available-typed-arrays: ^1.0.5 + call-bind: ^1.0.2 + for-each: ^0.3.3 + gopd: ^1.0.1 + has-tostringtag: ^1.0.0 + checksum: 711ffc8ef891ca6597b19539075ec3e08bb9b4c2ca1f78887e3c07a977ab91ac1421940505a197758fb5939aa9524976d0a5bbcac34d07ed6faa75cedbb17206 + languageName: node + linkType: hard + +"which@npm:^1.2.9": + version: 1.3.1 + resolution: "which@npm:1.3.1" + dependencies: + isexe: ^2.0.0 + bin: + which: ./bin/which + checksum: f2e185c6242244b8426c9df1510e86629192d93c1a986a7d2a591f2c24869e7ffd03d6dac07ca863b2e4c06f59a4cc9916c585b72ee9fa1aa609d0124df15e04 + languageName: node + linkType: hard + +"wrap-ansi@npm:^6.2.0": + version: 6.2.0 + resolution: "wrap-ansi@npm:6.2.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: 6cd96a410161ff617b63581a08376f0cb9162375adeb7956e10c8cd397821f7eb2a6de24eb22a0b28401300bf228c86e50617cd568209b5f6775b93c97d2fe3a + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 + languageName: node + linkType: hard + +"y18n@npm:^4.0.0": + version: 4.0.3 + resolution: "y18n@npm:4.0.3" + checksum: 014dfcd9b5f4105c3bb397c1c8c6429a9df004aa560964fb36732bfb999bfe83d45ae40aeda5b55d21b1ee53d8291580a32a756a443e064317953f08025b1aa4 + languageName: node + linkType: hard + +"yargs-parser@npm:^18.1.2": + version: 18.1.3 + resolution: "yargs-parser@npm:18.1.3" + dependencies: + camelcase: ^5.0.0 + decamelize: ^1.2.0 + checksum: 60e8c7d1b85814594d3719300ecad4e6ae3796748b0926137bfec1f3042581b8646d67e83c6fc80a692ef08b8390f21ddcacb9464476c39bbdf52e34961dd4d9 + languageName: node + linkType: hard + +"yargs@npm:^15.3.1": + version: 15.4.1 + resolution: "yargs@npm:15.4.1" + dependencies: + cliui: ^6.0.0 + decamelize: ^1.2.0 + find-up: ^4.1.0 + get-caller-file: ^2.0.1 + require-directory: ^2.1.1 + require-main-filename: ^2.0.0 + set-blocking: ^2.0.0 + string-width: ^4.2.0 + which-module: ^2.0.0 + y18n: ^4.0.0 + yargs-parser: ^18.1.2 + checksum: 40b974f508d8aed28598087720e086ecd32a5fd3e945e95ea4457da04ee9bdb8bdd17fd91acff36dc5b7f0595a735929c514c40c402416bbb87c03f6fb782373 + languageName: node + linkType: hard