From f42899b107f3ea5eaa2c0cd078aa837539908ac5 Mon Sep 17 00:00:00 2001 From: Brian Zalewski Date: Sat, 24 Dec 2022 15:04:59 -0500 Subject: [PATCH] Latest --- .config/Brewfile | 221 +++ .config/ansible-lint.yml | 33 + .config/bash/try-catch.sh | 85 + .config/codeclimate.yml | 50 + .config/commitlintrc.cjs | 3 + .config/common-keywords.json | 23 + .config/cspell.json | 306 ++++ .config/docs/.devcontainer/Dockerfile | 1 + .config/docs/.devcontainer/devcontainer.json | 109 ++ .config/docs/README.md | 80 + .config/docs/blueprint-contributing.md | 14 + .config/docs/blueprint-readme-playbook.md | 15 + .config/docs/blueprint-readme-role.md | 16 + .../common/contributing/code-of-conduct.md | 3 + .../docs/common/contributing/contributors.md | 5 + .config/docs/common/contributing/header.md | 5 + .../docs/common/contributing/styleguides.md | 19 + .config/docs/common/readme/contribute.md | 39 + .config/docs/common/readme/header.md | 11 + .config/docs/common/readme/license.md | 3 + .config/docs/contributing/code-format.md | 48 + .config/docs/contributing/code-style.md | 105 ++ .config/docs/contributing/commenting.md | 140 ++ .config/docs/contributing/dev-environment.md | 45 + .config/docs/contributing/docs.md | 39 + .config/docs/contributing/linting.md | 36 + .config/docs/contributing/philosophy.md | 11 + .config/docs/contributing/pull-requests.md | 3 + .config/docs/contributing/supported-os.md | 44 + .config/docs/contributing/testing.md | 62 + .config/docs/local/package-lock.json | 1 + .config/docs/local/yarn.lock | 1246 +++++++++++++++ .config/docs/readme-playbook/architecture.md | 3 + .config/docs/readme-playbook/dependencies.md | 36 + .config/docs/readme-playbook/introduction.md | 16 + .../readme-playbook/managing-environments.md | 17 + .config/docs/readme-playbook/philosophy.md | 20 + .../docs/readme-playbook/quick-description.md | 1 + .config/docs/readme-playbook/quick-start.md | 35 + .config/docs/readme-playbook/software.md | 82 + .config/docs/readme-playbook/subheader.md | 38 + .config/docs/readme-playbook/supported-os.md | 5 + .config/docs/readme-playbook/web-apps.md | 35 + .config/docs/readme-role/dependencies.md | 24 + .config/docs/readme-role/example.md | 15 + .config/docs/readme-role/overview.md | 3 + .config/docs/readme-role/quick-description.md | 3 + .config/docs/readme-role/quick-start.md | 19 + .config/docs/readme-role/subheader.md | 53 + .config/docs/readme-role/supported-os.md | 9 + .config/docs/variables.json | 592 +++++++ .config/flake8.toml | 4 + .config/hadolint.yml | 8 + .config/hbs.cjs | 70 + .config/husky/.gitignore | 1 + .config/husky/commit-msg | 34 + .config/husky/post-checkout | 36 + .config/husky/post-commit | 34 + .config/husky/post-merge | 34 + .config/husky/post-rewrite | 35 + .config/husky/pre-commit | 49 + .config/husky/pre-push | 35 + .config/husky/prepare-commit-msg | 28 + .config/log | 393 +++++ .config/molecule/config.yml | 27 + .config/molecule/docker.create.yml | 179 +++ .config/molecule/docker.destroy.yml | 53 + .config/molecule/files/windows_auth.py | 224 +++ .../filter_plugins/get_docker_networks.py | 37 + .config/molecule/gce.create.yml | 39 + .config/molecule/gce.destroy.yml | 46 + .config/molecule/handlers/main.yml | 51 + .config/molecule/prepare.yml | 20 + .../molecule/tasks/create_linux_instance.yml | 64 + .../tasks/create_windows_instance.yml | 65 + .config/molecule/vagrant.create.yml | 63 + .config/molecule/vagrant.destroy.yml | 43 + .config/nodemon.json | 4 + .config/package-lock.json | 1 + .config/prettierignore | 26 + .config/proselint.json | 5 + .config/requirements.txt | 25 + .config/run | 4 + .config/taskfiles/README.md | 3 + .../taskfiles/ansible/Taskfile-ansibler.yml | 119 ++ .../taskfiles/ansible/Taskfile-playbook.yml | 515 ++++++ .../taskfiles/ansible/Taskfile-populate.yml | 180 +++ .config/taskfiles/ansible/Taskfile-test.yml | 985 ++++++++++++ .config/taskfiles/ansible/Taskfile.yml | 609 +++++++ .config/taskfiles/app/Taskfile-virtualbox.yml | 34 + .config/taskfiles/app/Taskfile.yml | 29 + .../boilerplate/Taskfile-populate.yml | 192 +++ .../taskfiles/boilerplate/Taskfile-prompt.yml | 187 +++ .config/taskfiles/boilerplate/Taskfile.yml | 136 ++ .config/taskfiles/ci/Taskfile-github.yml | 45 + .config/taskfiles/ci/Taskfile.yml | 190 +++ .../taskfiles/cloud/Taskfile-cloudflare.yml | 19 + .config/taskfiles/cloud/Taskfile-dyno.yml | 591 +++++++ .config/taskfiles/cloud/Taskfile-heroku.yml | 19 + .config/taskfiles/cloud/Taskfile-s3.yml | 62 + .config/taskfiles/cloud/Taskfile.yml | 5 + .config/taskfiles/common/Taskfile-code.yml | 25 + .config/taskfiles/common/Taskfile-start.yml | 176 ++ .config/taskfiles/common/Taskfile-update.yml | 373 +++++ .config/taskfiles/common/Taskfile-util.yml | 88 + .config/taskfiles/common/Taskfile.yml | 236 +++ .config/taskfiles/docker/Taskfile-build.yml | 190 +++ .config/taskfiles/docker/Taskfile-test.yml | 206 +++ .config/taskfiles/docker/Taskfile-update.yml | 147 ++ .config/taskfiles/docker/Taskfile.yml | 371 +++++ .config/taskfiles/dotfiles/Taskfile.yml | 10 + .config/taskfiles/fix/Taskfile.yml | 527 ++++++ .config/taskfiles/git/Taskfile-bug.yml | 53 + .config/taskfiles/git/Taskfile-github.yml | 168 ++ .config/taskfiles/git/Taskfile-gitlab.yml | 477 ++++++ .config/taskfiles/git/Taskfile-gitomatic.yml | 13 + .config/taskfiles/git/Taskfile-hook.yml | 103 ++ .config/taskfiles/git/Taskfile-issues.yml | 82 + .config/taskfiles/git/Taskfile.yml | 322 ++++ .config/taskfiles/go/Taskfile-goreleaser.yml | 58 + .config/taskfiles/go/Taskfile-test.yml | 21 + .config/taskfiles/go/Taskfile.yml | 109 ++ .config/taskfiles/image/Taskfile.yml | 170 ++ .../taskfiles/install/Taskfile-ansible.yml | 67 + .config/taskfiles/install/Taskfile-apt.yml | 99 ++ .config/taskfiles/install/Taskfile-gh.yml | 1366 ++++++++++++++++ .config/taskfiles/install/Taskfile-github.yml | 72 + .config/taskfiles/install/Taskfile-go.yml | 151 ++ .config/taskfiles/install/Taskfile-npm.yml | 576 +++++++ .config/taskfiles/install/Taskfile-pipx.yml | 237 +++ .config/taskfiles/install/Taskfile-python.yml | 193 +++ .config/taskfiles/install/Taskfile-qubes.yml | 14 + .../install/Taskfile-requirements.yml | 156 ++ .config/taskfiles/install/Taskfile-rust.yml | 61 + .../taskfiles/install/Taskfile-service.yml | 9 + .../taskfiles/install/Taskfile-software.yml | 1412 +++++++++++++++++ .config/taskfiles/install/Taskfile-tap.yml | 49 + .config/taskfiles/install/Taskfile-ventoy.yml | 99 ++ .config/taskfiles/install/Taskfile.yml | 391 +++++ .../taskfiles/lint/Taskfile-codeclimate.yml | 42 + .config/taskfiles/lint/Taskfile-esprint.yml | 8 + .config/taskfiles/lint/Taskfile-markdown.yml | 66 + .config/taskfiles/lint/Taskfile-prose.yml | 58 + .config/taskfiles/lint/Taskfile.yml | 701 ++++++++ .config/taskfiles/log/Taskfile.yml | 60 + .config/taskfiles/nest/Taskfile.yml | 28 + .config/taskfiles/npm/Taskfile-bundle.yml | 127 ++ .config/taskfiles/npm/Taskfile-cov.yml | 72 + .config/taskfiles/npm/Taskfile-doc.yml | 46 + .config/taskfiles/npm/Taskfile.yml | 485 ++++++ .config/taskfiles/packer/Taskfile-build.yml | 123 ++ .config/taskfiles/packer/Taskfile-update.yml | 130 ++ .config/taskfiles/packer/Taskfile.yml | 127 ++ .../taskfiles/publish/Taskfile-android.yml | 24 + .config/taskfiles/publish/Taskfile-brew.yml | 17 + .config/taskfiles/publish/Taskfile-chrome.yml | 10 + .../taskfiles/publish/Taskfile-firefox.yml | 9 + .config/taskfiles/publish/Taskfile-ios.yml | 17 + .../taskfiles/publish/Taskfile-menubar.yml | 5 + .config/taskfiles/publish/Taskfile-opera.yml | 10 + .config/taskfiles/publish/Taskfile-snap.yml | 58 + .config/taskfiles/publish/Taskfile.yml | 109 ++ .config/taskfiles/python/Taskfile-test.yml | 49 + .config/taskfiles/python/Taskfile.yml | 105 ++ .config/taskfiles/release/Taskfile.yml | 622 ++++++++ .config/taskfiles/security/Taskfile-disk.yml | 69 + .config/taskfiles/security/Taskfile-gpg.yml | 71 + .config/taskfiles/security/Taskfile-ssh.yml | 69 + .../taskfiles/security/Taskfile-yubikey.yml | 323 ++++ .config/taskfiles/security/Taskfile.yml | 220 +++ .config/taskfiles/symlink/Taskfile.yml | 53 + .config/taskfiles/ui/Taskfile.yml | 7 + .config/taskfiles/update/Taskfile.yml | 11 + .../taskfiles/upstream/Taskfile-common.yml | 289 ++++ .../upstream/Taskfile-commondocs.yml | 52 + .config/taskfiles/upstream/Taskfile-docs.yml | 118 ++ .../taskfiles/upstream/Taskfile-project.yml | 141 ++ .../taskfiles/upstream/Taskfile-shared.yml | 44 + .config/taskfiles/upstream/Taskfile.yml | 330 ++++ .config/taskfiles/vagrant/Taskfile-qubes.yml | 21 + .config/taskfiles/vagrant/Taskfile.yml | 60 + .config/taskfiles/vscode/Taskfile.yml | 105 ++ .config/taskfiles/web/Taskfile-cloudflare.yml | 13 + .config/taskfiles/web/Taskfile-ionic.yml | 54 + .config/taskfiles/web/Taskfile-nx.yml | 7 + .config/taskfiles/web/Taskfile-profile.yml | 27 + .config/taskfiles/web/Taskfile.yml | 5 + .config/variables.json | 631 ++++++++ .config/yamllint.yml | 53 + .config/yarn.lock | 1246 +++++++++++++++ 190 files changed, 25488 insertions(+) create mode 100644 .config/Brewfile create mode 100644 .config/ansible-lint.yml create mode 100644 .config/bash/try-catch.sh create mode 100644 .config/codeclimate.yml create mode 100644 .config/commitlintrc.cjs create mode 100644 .config/common-keywords.json create mode 100644 .config/cspell.json create mode 100644 .config/docs/.devcontainer/Dockerfile create mode 100644 .config/docs/.devcontainer/devcontainer.json create mode 100644 .config/docs/README.md create mode 100644 .config/docs/blueprint-contributing.md create mode 100644 .config/docs/blueprint-readme-playbook.md create mode 100644 .config/docs/blueprint-readme-role.md create mode 100644 .config/docs/common/contributing/code-of-conduct.md create mode 100644 .config/docs/common/contributing/contributors.md create mode 100644 .config/docs/common/contributing/header.md create mode 100644 .config/docs/common/contributing/styleguides.md create mode 100644 .config/docs/common/readme/contribute.md create mode 100644 .config/docs/common/readme/header.md create mode 100644 .config/docs/common/readme/license.md create mode 100644 .config/docs/contributing/code-format.md create mode 100644 .config/docs/contributing/code-style.md create mode 100644 .config/docs/contributing/commenting.md create mode 100644 .config/docs/contributing/dev-environment.md create mode 100644 .config/docs/contributing/docs.md create mode 100644 .config/docs/contributing/linting.md create mode 100644 .config/docs/contributing/philosophy.md create mode 100644 .config/docs/contributing/pull-requests.md create mode 100644 .config/docs/contributing/supported-os.md create mode 100644 .config/docs/contributing/testing.md create mode 100644 .config/docs/local/package-lock.json create mode 100644 .config/docs/local/yarn.lock create mode 100644 .config/docs/readme-playbook/architecture.md create mode 100644 .config/docs/readme-playbook/dependencies.md create mode 100644 .config/docs/readme-playbook/introduction.md create mode 100644 .config/docs/readme-playbook/managing-environments.md create mode 100644 .config/docs/readme-playbook/philosophy.md create mode 100644 .config/docs/readme-playbook/quick-description.md create mode 100644 .config/docs/readme-playbook/quick-start.md create mode 100644 .config/docs/readme-playbook/software.md create mode 100644 .config/docs/readme-playbook/subheader.md create mode 100644 .config/docs/readme-playbook/supported-os.md create mode 100644 .config/docs/readme-playbook/web-apps.md create mode 100644 .config/docs/readme-role/dependencies.md create mode 100644 .config/docs/readme-role/example.md create mode 100644 .config/docs/readme-role/overview.md create mode 100644 .config/docs/readme-role/quick-description.md create mode 100644 .config/docs/readme-role/quick-start.md create mode 100644 .config/docs/readme-role/subheader.md create mode 100644 .config/docs/readme-role/supported-os.md create mode 100644 .config/docs/variables.json create mode 100644 .config/flake8.toml create mode 100644 .config/hadolint.yml create mode 100644 .config/hbs.cjs create mode 100644 .config/husky/.gitignore create mode 100755 .config/husky/commit-msg create mode 100755 .config/husky/post-checkout create mode 100755 .config/husky/post-commit create mode 100755 .config/husky/post-merge create mode 100755 .config/husky/post-rewrite create mode 100755 .config/husky/pre-commit create mode 100755 .config/husky/pre-push create mode 100755 .config/husky/prepare-commit-msg create mode 100755 .config/log create mode 100644 .config/molecule/config.yml create mode 100644 .config/molecule/docker.create.yml create mode 100644 .config/molecule/docker.destroy.yml create mode 100644 .config/molecule/files/windows_auth.py create mode 100644 .config/molecule/filter_plugins/get_docker_networks.py create mode 100644 .config/molecule/gce.create.yml create mode 100644 .config/molecule/gce.destroy.yml create mode 100644 .config/molecule/handlers/main.yml create mode 100644 .config/molecule/prepare.yml create mode 100644 .config/molecule/tasks/create_linux_instance.yml create mode 100644 .config/molecule/tasks/create_windows_instance.yml create mode 100644 .config/molecule/vagrant.create.yml create mode 100644 .config/molecule/vagrant.destroy.yml create mode 100644 .config/nodemon.json create mode 100644 .config/package-lock.json create mode 100644 .config/prettierignore create mode 100644 .config/proselint.json create mode 100644 .config/requirements.txt create mode 100644 .config/run create mode 100644 .config/taskfiles/README.md create mode 100644 .config/taskfiles/ansible/Taskfile-ansibler.yml create mode 100644 .config/taskfiles/ansible/Taskfile-playbook.yml create mode 100644 .config/taskfiles/ansible/Taskfile-populate.yml create mode 100644 .config/taskfiles/ansible/Taskfile-test.yml create mode 100644 .config/taskfiles/ansible/Taskfile.yml create mode 100644 .config/taskfiles/app/Taskfile-virtualbox.yml create mode 100644 .config/taskfiles/app/Taskfile.yml create mode 100644 .config/taskfiles/boilerplate/Taskfile-populate.yml create mode 100644 .config/taskfiles/boilerplate/Taskfile-prompt.yml create mode 100644 .config/taskfiles/boilerplate/Taskfile.yml create mode 100644 .config/taskfiles/ci/Taskfile-github.yml create mode 100644 .config/taskfiles/ci/Taskfile.yml create mode 100644 .config/taskfiles/cloud/Taskfile-cloudflare.yml create mode 100644 .config/taskfiles/cloud/Taskfile-dyno.yml create mode 100644 .config/taskfiles/cloud/Taskfile-heroku.yml create mode 100644 .config/taskfiles/cloud/Taskfile-s3.yml create mode 100644 .config/taskfiles/cloud/Taskfile.yml create mode 100644 .config/taskfiles/common/Taskfile-code.yml create mode 100644 .config/taskfiles/common/Taskfile-start.yml create mode 100644 .config/taskfiles/common/Taskfile-update.yml create mode 100644 .config/taskfiles/common/Taskfile-util.yml create mode 100644 .config/taskfiles/common/Taskfile.yml create mode 100644 .config/taskfiles/docker/Taskfile-build.yml create mode 100644 .config/taskfiles/docker/Taskfile-test.yml create mode 100644 .config/taskfiles/docker/Taskfile-update.yml create mode 100644 .config/taskfiles/docker/Taskfile.yml create mode 100644 .config/taskfiles/dotfiles/Taskfile.yml create mode 100644 .config/taskfiles/fix/Taskfile.yml create mode 100644 .config/taskfiles/git/Taskfile-bug.yml create mode 100644 .config/taskfiles/git/Taskfile-github.yml create mode 100644 .config/taskfiles/git/Taskfile-gitlab.yml create mode 100644 .config/taskfiles/git/Taskfile-gitomatic.yml create mode 100644 .config/taskfiles/git/Taskfile-hook.yml create mode 100644 .config/taskfiles/git/Taskfile-issues.yml create mode 100644 .config/taskfiles/git/Taskfile.yml create mode 100644 .config/taskfiles/go/Taskfile-goreleaser.yml create mode 100644 .config/taskfiles/go/Taskfile-test.yml create mode 100644 .config/taskfiles/go/Taskfile.yml create mode 100644 .config/taskfiles/image/Taskfile.yml create mode 100644 .config/taskfiles/install/Taskfile-ansible.yml create mode 100644 .config/taskfiles/install/Taskfile-apt.yml create mode 100644 .config/taskfiles/install/Taskfile-gh.yml create mode 100644 .config/taskfiles/install/Taskfile-github.yml create mode 100644 .config/taskfiles/install/Taskfile-go.yml create mode 100644 .config/taskfiles/install/Taskfile-npm.yml create mode 100644 .config/taskfiles/install/Taskfile-pipx.yml create mode 100644 .config/taskfiles/install/Taskfile-python.yml create mode 100644 .config/taskfiles/install/Taskfile-qubes.yml create mode 100644 .config/taskfiles/install/Taskfile-requirements.yml create mode 100644 .config/taskfiles/install/Taskfile-rust.yml create mode 100644 .config/taskfiles/install/Taskfile-service.yml create mode 100644 .config/taskfiles/install/Taskfile-software.yml create mode 100644 .config/taskfiles/install/Taskfile-tap.yml create mode 100644 .config/taskfiles/install/Taskfile-ventoy.yml create mode 100644 .config/taskfiles/install/Taskfile.yml create mode 100644 .config/taskfiles/lint/Taskfile-codeclimate.yml create mode 100644 .config/taskfiles/lint/Taskfile-esprint.yml create mode 100644 .config/taskfiles/lint/Taskfile-markdown.yml create mode 100644 .config/taskfiles/lint/Taskfile-prose.yml create mode 100644 .config/taskfiles/lint/Taskfile.yml create mode 100644 .config/taskfiles/log/Taskfile.yml create mode 100644 .config/taskfiles/nest/Taskfile.yml create mode 100644 .config/taskfiles/npm/Taskfile-bundle.yml create mode 100644 .config/taskfiles/npm/Taskfile-cov.yml create mode 100644 .config/taskfiles/npm/Taskfile-doc.yml create mode 100644 .config/taskfiles/npm/Taskfile.yml create mode 100644 .config/taskfiles/packer/Taskfile-build.yml create mode 100644 .config/taskfiles/packer/Taskfile-update.yml create mode 100644 .config/taskfiles/packer/Taskfile.yml create mode 100644 .config/taskfiles/publish/Taskfile-android.yml create mode 100644 .config/taskfiles/publish/Taskfile-brew.yml create mode 100644 .config/taskfiles/publish/Taskfile-chrome.yml create mode 100644 .config/taskfiles/publish/Taskfile-firefox.yml create mode 100644 .config/taskfiles/publish/Taskfile-ios.yml create mode 100644 .config/taskfiles/publish/Taskfile-menubar.yml create mode 100644 .config/taskfiles/publish/Taskfile-opera.yml create mode 100644 .config/taskfiles/publish/Taskfile-snap.yml create mode 100644 .config/taskfiles/publish/Taskfile.yml create mode 100644 .config/taskfiles/python/Taskfile-test.yml create mode 100644 .config/taskfiles/python/Taskfile.yml create mode 100644 .config/taskfiles/release/Taskfile.yml create mode 100644 .config/taskfiles/security/Taskfile-disk.yml create mode 100644 .config/taskfiles/security/Taskfile-gpg.yml create mode 100644 .config/taskfiles/security/Taskfile-ssh.yml create mode 100644 .config/taskfiles/security/Taskfile-yubikey.yml create mode 100644 .config/taskfiles/security/Taskfile.yml create mode 100644 .config/taskfiles/symlink/Taskfile.yml create mode 100644 .config/taskfiles/ui/Taskfile.yml create mode 100644 .config/taskfiles/update/Taskfile.yml create mode 100644 .config/taskfiles/upstream/Taskfile-common.yml create mode 100644 .config/taskfiles/upstream/Taskfile-commondocs.yml create mode 100644 .config/taskfiles/upstream/Taskfile-docs.yml create mode 100644 .config/taskfiles/upstream/Taskfile-project.yml create mode 100644 .config/taskfiles/upstream/Taskfile-shared.yml create mode 100644 .config/taskfiles/upstream/Taskfile.yml create mode 100644 .config/taskfiles/vagrant/Taskfile-qubes.yml create mode 100644 .config/taskfiles/vagrant/Taskfile.yml create mode 100644 .config/taskfiles/vscode/Taskfile.yml create mode 100644 .config/taskfiles/web/Taskfile-cloudflare.yml create mode 100644 .config/taskfiles/web/Taskfile-ionic.yml create mode 100644 .config/taskfiles/web/Taskfile-nx.yml create mode 100644 .config/taskfiles/web/Taskfile-profile.yml create mode 100644 .config/taskfiles/web/Taskfile.yml create mode 100644 .config/variables.json create mode 100644 .config/yamllint.yml create mode 100644 .config/yarn.lock diff --git a/.config/Brewfile b/.config/Brewfile new file mode 100644 index 00000000..7deb6799 --- /dev/null +++ b/.config/Brewfile @@ -0,0 +1,221 @@ +# Standard Homebrew taps +tap "homebrew/cask" +tap "homebrew/core" +tap "homebrew/bundle" +tap "homebrew/services" + +# Homebrew Formulae +# e.g. `brew install ` +# @brew [act](https://github.com/nektos/act) - Run GitHub Actions locally +brew "act" +# @brew [appium](https://appium.io/) - A framework focused on native Android/iOS testing +brew "appium" +# @brew [azure-cli](https://docs.microsoft.com/en-us/cli/azure/) - The official CLI for interacting with Microsoft Azure +brew "azure-cli" +# @brew [bat](https://github.com/sharkdp/bat) - Clone of cat with syntax highlighting and Git integration +brew "bat" +# @brew [bitwarden-cli](https://github.com/bitwarden/cli) - Access and manage a BitWarden instance via CLI +brew "bitwarden-cli" +# @brew [codeclimate](https://github.com/codeclimate/codeclimate) - Interact with CodeClimate via CLI +# tap "codeclimate/formulae" +# brew "codeclimate" +##### ERROR ##### +# ==> Installing codeclimate from codeclimate/formulae +# ==> make install +# Last 15 lines from /home/megabyte/.cache/Homebrew/Logs/codeclimate/01.make: +# 2022-03-11 08:34:36 +0000 +# +# make +# install +# +# bin/check +# Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? +# Unable to run `docker version', the docker daemon may not be running +# Please ensure `docker version' succeeds and try again +# make: *** [Makefile:43: install] Error 1 +# +# If reporting this issue please do so at (not Homebrew/brew or Homebrew/core): +# https://github.com/codeclimate/homebrew-formulae/issues +# +# Installing codeclimate has failed! +##### ERROR ##### +# @brew [croc](https://github.com/schollz/croc) - A sharing tool that helps transfer files from one computer to another +brew "croc" +# @brew [curl](https://curl.se) - An HTTP command-line tool +brew "curl" +# @brew [dasel](https://github.com/TomWright/dasel) - Select, put, and delete data from JSON, TOML, YAML, XML and CSV files +brew "dasel" +# @brew [direnv](https://github.com/direnv/direnv) - Loads and unloads environment variables based on the directory you are in +brew "direnv" +# @brew [dive](https://github.com/wagoodman/dive) - Tool for exploring layer in a Docker image +brew "dive" +# @brew [docker](https://www.docker.com/) - A powerful toolchain for developing containerized applications +if OS.linux? + brew "docker" +end +# @brew [docker-slim](https://github.com/docker-slim/docker-slim) - A tool that shrinks Docker images and makes them more secure +brew "docker-slim" +# @brew [Dockle](https://github.com/goodwithtech/dockle) - A container image security scanner +tap "goodwithtech/r" +brew "goodwithtech/r/dockle" +# @brew [exiftool](https://exiftool.org) - A library for reading and writing EXIF data to files +brew "exiftool" +# @brew [ffsend](https://github.com/timvisee/ffsend) - Fully featured Firefox Send client that makes sharing files easy +brew "ffsend" +# @brew [gh](https://github.com/cli/cli) - The official GitHub command line tool +brew "gh" +# @brew [git](https://git-scm.com) - Tool for interacting with git repositories +brew "git" +# @brew [gitlab-runner](https://docs.gitlab.com/runner/) - Test GitLab CI configurations and add self-hosted runners +brew "gitlab-runner" +# @brew [gitleaks](https://github.com/zricethezav/gitleaks) - Scans git repos for secrets +brew "gitleaks" +# @brew [git-subrepo](https://github.com/ingydotnet/git-subrepo) - An alternative to git submodules +brew "git-subrepo" +# @brew [glab](https://glab.readthedocs.io/) - Open-source GitLab CLI +brew "glab" +# @brew [go](https://go.dev) - Open source programming language +brew "go" +# @brew [goofys](https://github.com/kahing/goofys) - High-performance, POSIX-ish Amazon S3 file system written in Go +brew "goofys" +# @brew [grex](https://github.com/pemistahl/grex) - Generate regular expressions by providing target matches +brew "grex" +# @brew [helm](https://helm.sh/) - The self-proclaimed package manager for Kubernetes +brew "helm" +# @brew [htmlq](https://github.com/mgdm/htmlq) - Use CSS to extract content from HTML via a CLI +brew "htmlq" +# @brew [hyperfine](https://github.com/sharkdp/hyperfine) - Command-line benchmarking tool +brew "hyperfine" +# @brew [jo](https://github.com/jpmens/jo) - JSON output from scripts +brew "jo" +# @brew [jq](https://stedolan.github.io/jq/) - Lightweight and flexible command-line JSON processor +brew "jq" +# @brew [kubectx](https://github.com/ahmetb/kubectx) - A tool for switching between Kubernetes clusters and namespaces +brew "kubectx" +# @brew [kubernetes-cli](https://kubernetes.io/docs/reference/kubectl/kubectl/) - The CLI for Kubernetes (also known as kubectl) +brew "kubernetes-cli" +# @brew [mc](https://github.com/minio/mc) - Replacement for ls, cp and other commands that are compatible with file-system-mounted S3 buckets +tap "minio/stable" +brew "minio/stable/mc" +# @brew [mkcert](https://github.com/FiloSottile/mkcert) - Simple tool to make locally trusted development certificates +brew "mkcert" +# @brew [node](https://nodejs.org/) - A JavaScript engine, based on the ultra fast V8-engine +brew "node" +# @brew [openssh](https://www.openssh.com/) - OpenBSD freely-licensed SSH connectivity tools +brew "openssh" +# @brew [ots](https://ots.sniptt.com) - Share end-to-end encrypted secrets with others via a one-time URL +brew "ots" +# @brew [oq](https://blacksmoke16.github.io/oq) - Performant, and portable jq wrapper that supports formats other than JSON +brew "oq" +# @brew [php](https://www.php.net/) - General-purpose scripting language +# brew "php", restart_service: false +# @brew [poetry](https://python-poetry.org/) - A Python project package management tool and more +brew "poetry" +# @brew [pup](https://github.com/EricChiang/pup) - Parse HTML with a CLI +brew "pup" +# @brew [python](https://www.python.org/) - Interpreted, interactive, object-oriented programming language +brew "python@3.10" +# @brew [rsync](https://rsync.samba.org/) - Tool to do fast, incremental file transfers +brew "rsync" +# @brew [ruby](https://www.ruby-lang.org/) - Powerful, clean, object-oriented scripting language +brew "ruby" +# @brew [sshpass](https://github.com/hudochenkov/homebrew-sshpass) - Library that allows Ansible to connect over SSH with a password +tap "hudochenkov/sshpass" +brew "hudochenkov/sshpass/sshpass" +# @brew [sysbench](https://github.com/akopytov/sysbench) - System performance benchmark tool +brew "sysbench" +# @brew [task](https://github.com/go-task/homebrew-tap) - A parallel task runner +tap "go-task/tap" +brew "go-task/tap/go-task" +# @brew [teleport](https://github.com/bbatsche/homebrew-teleport) - An identity-aware SSH client for teams +brew "teleport" +# @brew [terraform](https://www.terraform.io/) - An infrastructure-as-code tool that allows you to define both cloud and on-prem resources +brew "terraform" +# @brew [tokei](https://github.com/XAMPPRocky/tokei) - Count and display the lines of code and the language used in a project +brew "tokei" +# @brew [trivy](https://aquasecurity.github.io/trivy/v0.18.3/) - Scan images for vulnerabilities +tap "aquasecurity/trivy" +brew "aquasecurity/trivy/trivy" +# @brew [up](https://github.com/akavel/up) - Write Linux pipes with an instant live preview +brew "up" +# @brew [waypoint](https://www.waypointproject.io/) - Tool to build, deploy, and release any application on any platform +tap "hashicorp/tap" +brew "hashicorp/tap/waypoint" +# @brew [wireshark](https://www.wireshark.org) - Graphical network analyzer and capture tool (CLI) +if OS.linux? + brew "wireshark" +end +# @brew [yarn](https://yarnpkg.com/) - JavaScript package manager from Facebook +brew "yarn" +# @brew [yq](https://github.com/mikefarah/yq) - Process and manipulate YAML documents +brew "yq" +# @brew [coreutils](https://www.gnu.org/software/coreutils) - A suite of basic UNIX tools published to improve compatibility between Linux and macOS scripts +if OS.mac? + brew "coreutils" +end + +# Homebrew Casks (only available on macOS) +# e.g. `brew install --cask ` +# @cask [altair](https://altair.sirmuel.design/) - GraphQL GUI client +cask "altair" +# @cask [balenaetcher](https://balena.io/etcher) - Tool to flash OS images to SD cards & USB drives +cask "balenaetcher" +# @cask [bitwarden](https://bitwarden.com/) - Desktop client for BitWarden +cask "bitwarden" +# @cask [docker](https://docker.com) - The desktop GUI for Docker, a virtualization platform for containers and microservices +cask "docker" +# @cask [firefox](https://www.mozilla.org/firefox/) - A popular web browser +cask "firefox" +# @cask [gimp](https://www.gimp.org/) - Free and open-source image editor +cask "gimp" +# @cask [google-chrome](https://www.google.com/chrome/) - Sandbox-based web browser published by Google +cask "google-chrome" +# @cask [gcloud](https://cloud.google.com/sdk/gcloud) - The official Google Cloud Platform SDK CLI tool +cask "google-cloud-sdk" +# @cask [iterm2](https://www.iterm2.com/) - An improved terminal for macOS +cask "iterm2" +# @cask [java](https://www.java.com/en/) - Libraries required for running and developing Java applications +cask "java" unless system "/usr/libexec/java_home --failfast" +# @cask [lens](https://k8slens.dev/) - An IDE for Kubernetes +cask "lens" +# @cask [microsoft-teams](https://teams.microsoft.com/downloads) - Meet, chat, call, and collaborate in just one place +cask "microsoft-teams" +# @cask [osxfuse](https://github.com/osxfuse/osxfuse) - Extends macOS by adding support for user space file systems +cask "osxfuse" +# @cask [postman](https://www.postman.com/) - Collaboration platform for API development +cask "postman" +# @cask [slack](https://slack.com/) - Team communication and collaboration software +cask "slack" +# @cask [skype](https://www.skype.com/) - Video chat, voice call, and instant messaging application +cask "skype" +# @cask [teamviewer](https://www.teamviewer.com/) - Remote access and connectivity software focused on security +cask "teamviewer" +# @cask [vagrant](https://www.vagrantup.com/) - Command-line, configuration-driven CLI for launching virtualization tools +cask "vagrant" +# @cask [virtualbox](https://www.virtualbox.org/) - A popular virtualization platform for virtual machines +cask "virtualbox" +# @cask [visual-studio-code](https://code.visualstudio.com/) - Open source code editor +cask "visual-studio-code" +# @cask [vmware-fusion](https://www.vmware.com/products/fusion.html) - Create, manage, and run virtual machines +cask "vmware-fusion" +# @cask [wireshark](https://www.wireshark.org) - Graphical network analyzer and capture tool +cask "wireshark" + + +# Examples below +# 'brew install --with-rmtp', 'brew services restart' on version changes +# brew "denji/nginx/nginx-full", args: ["with-rmtp"], restart_service: :changed +# 'brew install', always 'brew services restart', 'brew link', 'brew unlink mysql' (if it is installed) +# brew "mysql@5.6", restart_service: true, link: true, conflicts_with: ["mysql"] +# 'brew install --cask' +# cask "google-chrome" +# 'brew install --cask --appdir=~/my-apps/Applications' +# cask "firefox", args: { appdir: "~/my-apps/Applications" } +# always upgrade auto-updated or unversioned cask to latest version even if already installed +# cask "opera", greedy: true +# 'brew install --cask' only if '/usr/libexec/java_home --failfast' fails +# cask "java" unless system "/usr/libexec/java_home --failfast" +# 'mas install' +# mas "1Password", id: 443987910 +# 'whalebrew install' +# whalebrew "whalebrew/wget" diff --git a/.config/ansible-lint.yml b/.config/ansible-lint.yml new file mode 100644 index 00000000..72465534 --- /dev/null +++ b/.config/ansible-lint.yml @@ -0,0 +1,33 @@ +--- +enable_list: + - fqcn-builtins + - no-log-password + - no-same-owner + +exclude_paths: + - ../.autodoc/ + - ../.cache/ + - ../.common/ + - ../.config/ + - ../.git/ + - ../.github/ + - ../.gitlab/ + - ../.husky/ + - ../.modules/ + - ../.npm/ + - ../.pnpm-store/ + - ../.shared/ + - ../.task/ + - ../.venv/ + - ../.vscode/ + - ../build/ + - ../dist/ + - ../molecule/ + - ../node_modules/ + - ../pnpm-lock.yaml + - ../roles/ + - ../venv/ + +offline: true + +skip_list: [] diff --git a/.config/bash/try-catch.sh b/.config/bash/try-catch.sh new file mode 100644 index 00000000..a9ae744d --- /dev/null +++ b/.config/bash/try-catch.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +# Try / catch in bash +# +# ```` +# #!/bin/bash +# export AnException=100 +# export AnotherException=101 +# +# # start with a try +# try +# ( # open a subshell !!! +# echo "do something" +# [ someErrorCondition ] && throw $AnException +# +# echo "do something more" +# executeCommandThatMightFail || throw $AnotherException +# +# throwErrors # automaticatly end the try block, if command-result is non-null +# echo "now on to something completely different" +# executeCommandThatMightFail +# +# echo "it's a wonder we came so far" +# executeCommandThatFailsForSure || true # ignore a single failing command +# +# ignoreErrors # ignore failures of commands until further notice +# executeCommand1ThatFailsForSure +# local result = $(executeCommand2ThatFailsForSure) +# [ result != "expected error" ] && throw $AnException # ok, if it's not an expected error, we want to bail out! +# executeCommand3ThatFailsForSure +# +# # make sure to clear $ex_code, otherwise catch * will run +# # echo "finished" does the trick for this example +# echo "finished" +# ) +# # directly after closing the subshell you need to connect a group to the catch using || +# catch || { +# # now you can handle +# case $ex_code in +# $AnException) +# echo "AnException was thrown" +# ;; +# $AnotherException) +# echo "AnotherException was thrown" +# ;; +# *) +# echo "An unexpected exception was thrown" +# throw $ex_code # you can rethrow the "exception" causing the script to exit if not caught +# ;; +# esac +# } +# ``` +# Source: https://stackoverflow.com/a/25180186` + +# shellcheck disable=SC2034 +Logger="${BASH_SOURCE[0]}../log" + +# @description Turn on fail on errors mode +function try() { + [[ $- = *e* ]]; SAVED_OPT_E=$? + set +e +} + +# @description Turn on fail on errors mode +function throw() { + exit "$1" +} + +# @description Turn on fail on errors mode +function catch() { + export ex_code=$? + # shellcheck disable=SC2004 + (( $SAVED_OPT_E )) && set +e + return $ex_code +} + +# @description Turn on fail on errors mode +function throwErrors() { + set -e +} + +# @description Do not fail on errors mode +function ignoreErrors() { + set +e +} diff --git a/.config/codeclimate.yml b/.config/codeclimate.yml new file mode 100644 index 00000000..a451d1ec --- /dev/null +++ b/.config/codeclimate.yml @@ -0,0 +1,50 @@ +--- +version: '2' + +plugins: + ansible-lint: + enabled: true + editorconfig: + enabled: true + eslint: + enabled: true + jscpd: + enabled: true + shellcheck: + enabled: true + yamllint: + enabled: true + +exclude_patterns: + - _generated_/ + - .common/ + - .config/ + - .git/ + - .go/ + - .modules/ + - .npm/ + - .pnpm-store/ + - .task/ + - .travis.yml + - .venv/ + - .vscode/ + - '*.hbs.yml' + - '**/*_test.go' + - '**/*.d.ts' + - '**/node_modules/' + - '**/spec/' + - '**/test/' + - '**/tests/' + - '**/vendor/' + - build/ + - config/ + - db/ + - deprecated/ + - dist/ + - features/ + - pnpm-lock.yaml + - roles/ + - script/ + - test-output/ + - testdata/ + - Tests/ diff --git a/.config/commitlintrc.cjs b/.config/commitlintrc.cjs new file mode 100644 index 00000000..a4f43697 --- /dev/null +++ b/.config/commitlintrc.cjs @@ -0,0 +1,3 @@ +module.exports = { + extends: ['@commitlint/config-conventional'] +} diff --git a/.config/common-keywords.json b/.config/common-keywords.json new file mode 100644 index 00000000..d0c0fe40 --- /dev/null +++ b/.config/common-keywords.json @@ -0,0 +1,23 @@ +{ + "keywords": [ + "ansible", + "ansible-playbook", + "archlinux", + "centos", + "debian", + "doctor", + "fedora", + "install", + "installdoc", + "installdoctor", + "macos", + "mblabs", + "megabytelabs", + "molecule", + "playbook", + "professormanhattan", + "ubuntu", + "washingtondc", + "windows" + ] +} diff --git a/.config/cspell.json b/.config/cspell.json new file mode 100644 index 00000000..20749ed8 --- /dev/null +++ b/.config/cspell.json @@ -0,0 +1,306 @@ +{ + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/master/cspell.schema.json", + "flagWords": [], + "ignorePaths": [ + ".autodoc/**", + ".cache/**", + ".common/**", + ".config/**", + ".git/**", + ".github/**", + ".gitlab/**", + ".husky/**", + ".modules/**", + ".npm/**", + ".pnpm-store/**", + ".shared/**", + ".task/**", + ".venv/**", + ".vscode/**", + "build/**", + "dist/**", + "package.json", + "package-lock.json", + "slim.report.json", + "yarn.lock", + "tsconfig.json", + "node_modules/**", + "pnpm-lock.yaml", + "roles/**", + "venv/**" + ], + "language": "en", + "version": "0.1", + "words": [ + "Kompose", + "Kubuntu", + "Malwarebytes", + "Portainer", + "Privoxy", + "Qubes", + "Remmina", + "Suricata", + "Tizen", + "Wireshark", + "XBMC", + "Zalewski", + "androidsdk", + "androidstudio", + "ansible", + "ansibler", + "appium", + "appnest", + "aptcacherng", + "archlinux", + "argparse", + "autodoc", + "autojump", + "autokey", + "awscli", + "azurecli", + "backdoors", + "balena", + "bandizip", + "bandwhich", + "bcrypt", + "bento", + "bgblack", + "bgblue", + "bgcyan", + "bggreen", + "bgmagenta", + "bgred", + "bgwhite", + "bgyellow", + "bitwarden", + "blazingly", + "boilerplates", + "bravebrowser", + "brewfile", + "broot", + "brotli", + "browserslist", + "buildr", + "caniuse", + "catfs", + "centos", + "certbot", + "chdir", + "choco", + "chokidar", + "circleci", + "cloudflared", + "cmds", + "cocoapods", + "codecov", + "cointop", + "commitizen", + "commitlint", + "commondocs", + "concat", + "consultemplate", + "cosmiconfig", + "cpus", + "debloat", + "defaultbrowser", + "deno", + "deps", + "diffsofancy", + "direnv", + "dmginstall", + "dnsmasq", + "dockerhub", + "dockerpushrm", + "dockerslim", + "donothing", + "dotenv", + "dotfile", + "dotfiles", + "easyengine", + "editorconfig", + "elasticagent", + "enablerepo", + "endlessh", + "epel", + "esbuild", + "esnext", + "exiftool", + "favicons", + "ffsend", + "filebrowser", + "filezilla", + "fontinstall", + "freemium", + "ghorg", + "gitdocker", + "gitextras", + "gitfilterrepo", + "githubbinary", + "githubcli", + "gitlab", + "gitlabrunner", + "gitlfs", + "gitmoji", + "gitomatic", + "gitsecret", + "gitsome", + "gitstats", + "goofys", + "googleassistant", + "googlecloudsdk", + "googler", + "gping", + "gvisor", + "heyhey", + "hostnames", + "httpie", + "hyperv", + "hyperv", + "idempotence", + "inkscape", + "installdoc", + "installdoctor", + "intellij", + "iterm", + "jenv", + "jetpack", + "jsdoc", + "koalaman", + "kodi", + "kubernetes", + "leasot", + "ledgerlive", + "libc", + "liquidjs", + "lpass", + "lxdc", + "mailspring", + "makepkg", + "mblabs", + "mcfly", + "megabytelabs", + "microsoftedge", + "minikube", + "minipass", + "mjml", + "mkdir", + "modifyvm", + "monero", + "motrix", + "multipass", + "mvdan", + "natdnshostresolver", + "netaddr", + "netdata", + "nextcloud", + "nmap", + "noconfirm", + "noqa", + "noqa", + "normit", + "onionshare", + "opencollective", + "optionator", + "pacman", + "pacman", + "pagespeed", + "pandoc", + "peco", + "pfsense", + "pgcli", + "pihole", + "pino", + "pino", + "pipx", + "platformtools", + "plex", + "pnpm", + "pnpx", + "portout", + "posix", + "postcss", + "prebuild", + "preload", + "prepended", + "prfssr", + "professormanhattan", + "proselint", + "pushrm", + "pyenv", + "pypi", + "pywinrm", + "qbittorrent", + "qlplugins", + "qubes", + "rclone", + "readlink", + "recoverpy", + "remotedesktop", + "restic", + "rimraf", + "ripgrep", + "rkhunter", + "sandboxed", + "sbin", + "scrcpy", + "screencast", + "screencasts", + "sdkman", + "sdkmanage", + "seconion", + "serializers", + "sharex", + "shdoc", + "shellcheck", + "shfmt", + "shotcut", + "shotwell", + "signale", + "sindresorhus", + "slackterm", + "sleekfast", + "sshtarpit", + "sshvault", + "submodule", + "submodules", + "switchhosts", + "symlinking", + "sysdig", + "taskfile", + "taskfiles", + "teamviewer", + "terminalizer", + "tfenv", + "tflint", + "tmpfs", + "transpiled", + "trec", + "tsdoc", + "typedoc", + "typeof", + "unsanitized", + "untracked", + "venv", + "vfile", + "videoblobs", + "virtualbox", + "washingtondc", + "wazuh", + "webp", + "windowsadmincenter", + "windowspowertoys", + "wireshark", + "wkhtmltopdf", + "wpcli", + "xmlbuilder", + "xrdp", + "yamllint", + "yargs", + "yarnhook", + "yocto", + "youtubedl", + "zalewski", + "zorin", + "zoxide" + ] +} diff --git a/.config/docs/.devcontainer/Dockerfile b/.config/docs/.devcontainer/Dockerfile new file mode 100644 index 00000000..c9032d95 --- /dev/null +++ b/.config/docs/.devcontainer/Dockerfile @@ -0,0 +1 @@ +FROM megabytelabs/devcontainer:latest diff --git a/.config/docs/.devcontainer/devcontainer.json b/.config/docs/.devcontainer/devcontainer.json new file mode 100644 index 00000000..897caec4 --- /dev/null +++ b/.config/docs/.devcontainer/devcontainer.json @@ -0,0 +1,109 @@ +{ + "build": { + "args": { + "DOCKER_VERSION": "latest", + "ENABLE_NONROOT_DOCKER": "true", + "INSTALL_ZSH": "true", + "UPGRADE_PACKAGES": "true", + "USERNAME": "megabyte", + "USE_MOBY": "true" + } + }, + "dockerFile": "Dockerfile", + "extensions": [ + "Angular.ng-template", + "attilabuti.vscode-mjml", + "bierner.markdown-emoji", + "ChakrounAnas.turbo-console-log", + "ChFlick.firecode", + "chrmarti.regex", + "CoenraadS.bracket-pair-colorizer", + "cweijan.vscode-mysql-client2", + "DavidAnson.vscode-markdownlint", + "dbaeumer.vscode-eslint", + "denoland.vscode-deno", + "dracula-theme.theme-dracula", + "drewbourne.vscode-remark-lint", + "eamodio.gitlens", + "EditorConfig.EditorConfig", + "esbenp.prettier-vscode", + "ericadamski.carbon-now-sh", + "firefox-devtools.vscode-firefox-debug", + "firsttris.vscode-jest-runner", + "formulahendry.auto-rename-tag", + "formulahendry.code-runner", + "foxundermoon.shell-format", + "GitHub.vscode-pull-request-github", + "GitLab.gitlab-workflow", + "GoogleCloudTools.cloudcode", + "golang.Go", + "HashiCorp.terraform", + "hediet.vscode-drawio", + "IBM.output-colorizer", + "johnpapa.vscode-peacock", + "Kelvin.vscode-sshfs", + "KnisterPeter.vscode-commitizen", + "kruemelkatze.vscode-dashboard", + "mads-hartmann.bash-ide-vscode", + "mechatroner.rainbow-csv", + "msjsdiag.debugger-for-chrome", + "msjsdiag.debugger-for-edge", + "ms-azuretools.vscode-docker", + "ms-kubernetes-tools.vscode-kubernetes-tools", + "ms-vscode-remote.remote-containers", + "ms-vscode-remote.remote-ssh", + "ms-vscode-remote.remote-wsl", + "ms-python.python", + "ms-vscode.PowerShell", + "ms-vscode.vscode-typescript-tslint-plugin", + "MS-vsliveshare.vsliveshare", + "MS-vsliveshare.vsliveshare-audio", + "njpwerner.autodocstring", + "nrwl.angular-console", + "paulvarache.vscode-taskfile", + "philnash.ngrok-for-vscode", + "PKief.material-icon-theme", + "pnp.polacode", + "pranaygp.vscode-css-peek", + "quicktype.quicktype", + "RandomFractalsInc.vscode-data-preview", + "rbbit.typescript-hero", + "redhat.ansible", + "redhat.vscode-yaml", + "richie5um2.vscode-sort-json", + "Rubymaniac.vscode-paste-and-indent", + "salbert.comment-ts", + "shd101wyy.markdown-preview-enhanced", + "snipsnapdev.snipsnap-vscode", + "softwaredotcom.swdc-vscode", + "steoates.autoimport", + "stylelint.vscode-stylelint", + "TabNine.tabnine-vscode", + "timonwong.shellcheck", + "toba.vsfire", + "tyriar.sort-lines", + "usernamehw.errorlens", + "valentjn.vscode-ltex", + "VisualStudioExptTeam.vscodeintellicode", + "vsciot-vscode.vscode-arduino", + "vsls-contrib.codetour", + "vsls-contrib.gistfs", + "WallabyJs.quokka-vscode", + "wayou.vscode-todo-highlight", + "wix.vscode-import-cost", + "yatki.vscode-surround" + ], + "forwardPorts": [2222, 5901, 6080, 8001, 8014], + "hostRequirements": { + "cpus": 2, + "memory": "8gb", + "storage": "16gb" + }, + "mounts": ["source=dind-var-lib-docker,target=/var/lib/docker,type=volume"], + "name": "Megabyte Labs DevContainer Code Rocket Pack", + "overrideCommand": false, + "postCreateCommand": "task start", + "remoteUser": "megabyte", + "runArgs": ["--init", "--privileged", "--shm-size=4g"], + "settings": {} +} diff --git a/.config/docs/README.md b/.config/docs/README.md new file mode 100644 index 00000000..fbc837e9 --- /dev/null +++ b/.config/docs/README.md @@ -0,0 +1,80 @@ +
+
+ + Documentation logo + +
+
+
+

Common Documentation

+

Documentation partials and JSON variables for generating sweet READMEs and templated files for hundreds of repositories

+
+ +[![-----------------------------------------------------](https://gitlab.com/megabyte-labs/assets/-/raw/master/png/aqua-divider.png)](#table-of-contents) + +## Table of Contents + +- [➤ Summary](#summary) +- [➤ Repository Types](#repository-types) +- [➤ Requirements](#repository-pipeline-order) +- [➤ Flow Summary](#flow-summary) +- [➤ `common.json`](#common-json) + +[![-----------------------------------------------------](https://gitlab.com/megabyte-labs/assets/-/raw/master/png/aqua-divider.png)](#summary) + +## Summary + +In all of our projects, we strive to maintain useful and informative documentation. However, with hundreds of projects and limited man power, it can be tricky. To solve this problem, we re-use documentation partials to generate the documentation in each of our repositories. + +There are two repositories responsible for generating the documentation for each project: + +1. **[Shared documentation repository](https://gitlab.com/megabyte-labs/documentation/shared):** This repository contains documentation partials that are used throughout all of our repositories. +2. **Project-type documentation repository:** This repository is where we store documentation that is specific to the type of project that downstream repository is. For example, if the downstream project is an Ansible role, then the repositories that will be used to generate the documentation will be the shared documentation repository and the [Ansible documentation repository](https://gitlab.com/megabyte-labs/documentation/ansible). + +[![-----------------------------------------------------](https://gitlab.com/megabyte-labs/assets/-/raw/master/png/aqua-divider.png)](#repository-types) + +## Repository Types + +We currently use this method to scaffold our projects of the following types: + +1. [Angular](https://gitlab.com/megabyte-labs/documentation/angular) +2. [Ansible](https://gitlab.com/megabyte-labs/documentation/ansible) +3. [Dockerfile](https://gitlab.com/megabyte-labs/documentation/dockerfile) +4. [Go](https://gitlab.com/megabyte-labs/documentation/go) +5. [NPM](https://gitlab.com/megabyte-labs/documentation/npm) +6. [Packer](https://gitlab.com/megabyte-labs/documentation/packer) +7. [Python](https://gitlab.com/megabyte-labs/documentation/python) + +[![-----------------------------------------------------](https://gitlab.com/megabyte-labs/assets/-/raw/master/png/aqua-divider.png)](#repository-pipeline-order) + +## Repository Pipeline Order + +Whenever a change is made to the shared documentation repository, the pipeline for the project-specific repositories will trigger (unless it is configured not to do so). Part of that pipeline includes cloning the shared documentation repository into the project-specific repository. When this happens, the `common/` folder in the shared repository is copied over to the project-specific repository. + +After the `common/` folder is copied over, the project-specific repository will trigger the pipeline for the project-specific common files repository (e.g. [Ansible common files repository](https://gitlab.com/megabyte-labs/common/ansible)). When this is happens, the project-specific documentation repository is added to the project-specific common files repository in the `docs/` folder. + +Finally, after the project-specific common files repository is up-to-date, the files it contains are propagated out to the individual projects that all of these repositories are for. This whole process allows us to update, say, a spelling error in the documentation to every project in our eco-system without an repetition. + +[![-----------------------------------------------------](https://gitlab.com/megabyte-labs/assets/-/raw/master/png/aqua-divider.png)](#flow-summary) + +## Flow Summary + +To summarize, the order of the flow is: + +1. [Shared documentation repository](https://gitlab.com/megabyte-labs/documentation/shared) +2. Project-specific documentation repository (e.g. [Ansible documentation](https://gitlab.com/megabyte-labs/documentation/ansible)) +3. Project-specific common files repository (e.g. [Ansible common files](https://gitlab.com/megabyte-labs/common/ansible)) +4. Individual project repository (e.g. [Ansible role for Android Studio](https://gitlab.com/megabyte-labs/ansible-roles/androidstudio)) + +So, with synchronization turned on, a change to the shared documentation repository would trigger updates for the most repositories since it is the highest upstream repository. + +[![-----------------------------------------------------](https://gitlab.com/megabyte-labs/assets/-/raw/master/png/aqua-divider.png)](#common-json) + +## `common.json` + +In both the shared documentation repository and the project-specific documentation repositories there is a file called `common.json` in the root of the projects. These files contain variables that are used to dynamically inject variables into the documentation and other files. The `common.json` files in both repositories are merged when there are updates to create the `variables.json` file that is in each project-specific documentation repository. During this process, the variables in the project-specific `common.json` file takes precedence over the variables in the shared `common.json` file. There are a few other steps that are made to create the final version of the `.variables.json` that each project uses to generate documentation and other files. In order of precedence, the variables are acquired from: + +1. The variables in the `"blueprint"` section of the `package.json` file that is located in each downstream project +2. The variables stored in the `common.{{ project_subgroup }}.json` file stored in the common files repository for each project type (e.g. the [Android Studio Ansible project](https://gitlab.com/megabyte-labs/ansible-roles/androidstudio) uses the `common.role.json` file in the [Ansible common files repository](https://gitlab.com/megabyte-labs/common/ansible) since the project subtype is a role) +3. The `common.json` file in the project-type-specific documentation repository (e.g. for the Android Studio Ansible role this would be the [Ansible documentation repository](https://gitlab.com/megabyte-labs/documentation/ansible)) +4. The `common.json` file in the [shared documentation repository](https://gitlab.com/megabyte-labs/documentation/shared) diff --git a/.config/docs/blueprint-contributing.md b/.config/docs/blueprint-contributing.md new file mode 100644 index 00000000..549778c2 --- /dev/null +++ b/.config/docs/blueprint-contributing.md @@ -0,0 +1,14 @@ +{{ load:.config/docs/common/contributing/header.md }} +{{ template:toc }} +{{ load:.config/docs/common/contributing/code-of-conduct.md }} +{{ load:.config/docs/contributing/philosophy.md }} +{{ load:.config/docs/contributing/supported-os.md }} +{{ load:.config/docs/contributing/dev-environment.md }} +{{ load:.config/docs/contributing/pull-requests.md }} +{{ load:.config/docs/contributing/code-format.md }} +{{ load:.config/docs/contributing/code-style.md }} +{{ load:.config/docs/contributing/commenting.md }} +{{ load:.config/docs/contributing/docs.md }} +{{ load:.config/docs/contributing/testing.md }} +{{ load:.config/docs/contributing/linting.md }} +{{ load:.config/docs/common/contributing/troubleshooting.md }} diff --git a/.config/docs/blueprint-readme-playbook.md b/.config/docs/blueprint-readme-playbook.md new file mode 100644 index 00000000..f20ea3c6 --- /dev/null +++ b/.config/docs/blueprint-readme-playbook.md @@ -0,0 +1,15 @@ +{{ load:.config/docs/common/readme/header.md }} +{{ load:.config/docs/readme-playbook/subheader.md }} +{{ load:.config/docs/readme-playbook/quick-description.md }} +{{ template:toc }} +{{ load:.config/docs/readme-playbook/introduction.md }} +{{ load:.config/docs/readme-playbook/quick-start.md }} +{{ load:.config/docs/readme-playbook/supported-os.md }} +{{ load:.config/docs/readme-playbook/dependencies.md }} +{{ load:.config/docs/readme-playbook/software.md }} +{{ load:.config/docs/readme-playbook/web-apps.md}} +{{ load:.config/docs/readme-playbook/philosophy.md }} +{{ load:.config/docs/readme-playbook/architecture.md }} +{{ load:.config/docs/readme-playbook/managing-environments.md }} +{{ load:.config/docs/common/readme/contribute.md }} +{{ load:.config/docs/common/readme/license.md }} diff --git a/.config/docs/blueprint-readme-role.md b/.config/docs/blueprint-readme-role.md new file mode 100644 index 00000000..d28e77a7 --- /dev/null +++ b/.config/docs/blueprint-readme-role.md @@ -0,0 +1,16 @@ +{{ load:.config/docs/common/readme/header.md }} +{{ load:.config/docs/readme-role/subheader.md }} +{{ load:.config/docs/readme-role/quick-description.md }} +{{ template:toc }} +{{ load:.config/docs/readme-role/overview.md }} +{{ load:.autodoc/ansible_actions.md }} +{{ load:.config/docs/readme-role/quick-start.md }} +{{ load:.autodoc/ansible_variables.md }} +{{ load:.config/docs/readme-role/supported-os.md }} +{{ load:.config/docs/readme-role/dependencies.md }} +{{ load:.autodoc/collection_dependencies.md }} +{{ load:.config/docs/readme-role/example.md }} +{{ load:.autodoc/ansible_tags.md }} +{{ load:.config/docs/common/readme/contribute.md }} +{{ load:.autodoc/ansible_todo.md }} +{{ load:.config/docs/common/readme/license.md }} diff --git a/.config/docs/common/contributing/code-of-conduct.md b/.config/docs/common/contributing/code-of-conduct.md new file mode 100644 index 00000000..452f9ffa --- /dev/null +++ b/.config/docs/common/contributing/code-of-conduct.md @@ -0,0 +1,3 @@ +## Code of Conduct + +This project and everyone participating in it is governed by the [Code of Conduct]({{ repository.github }}{{ repository.location.conduct.github }}). By participating, you are expected to uphold this code. Please report unacceptable behavior to [{{ email.help }}](mailto:{{ email.help }}). diff --git a/.config/docs/common/contributing/contributors.md b/.config/docs/common/contributing/contributors.md new file mode 100644 index 00000000..64f4437f --- /dev/null +++ b/.config/docs/common/contributing/contributors.md @@ -0,0 +1,5 @@ +## Contributors + +Thank you so much to our contributors! + +{{ contributors_list }} diff --git a/.config/docs/common/contributing/header.md b/.config/docs/common/contributing/header.md new file mode 100644 index 00000000..6b3c6ac9 --- /dev/null +++ b/.config/docs/common/contributing/header.md @@ -0,0 +1,5 @@ +
+

Contributing Guide

+
+ +First of all, thanks for visiting this page 😊 ❤️ ! We are *stoked* that you may be considering contributing to this project. You should read this guide if you are considering creating a pull request or plan to modify the code for your own purposes. diff --git a/.config/docs/common/contributing/styleguides.md b/.config/docs/common/contributing/styleguides.md new file mode 100644 index 00000000..d12b8007 --- /dev/null +++ b/.config/docs/common/contributing/styleguides.md @@ -0,0 +1,19 @@ +## Style Guides + +All code projects have their own style. Coding style will vary from coder to coder. Although we do not have a strict style guide for each project, we do require that you be well-versed in what coding style is most acceptable and _best_. To do this, you should read through style guides that are made available by organizations that have put a lot of effort into studying the reason for coding one way or another. + +### Recommended Style Guides + +Style guides are generally written for a specific language but a great place to start learning about the best coding practices is on [Google Style Guides](https://google.github.io/styleguide/). Follow the link and you will see style guides for most popular languages. We also recommend that you look through the following style guides, depending on what language you are coding with: + +* [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) +* [Angular Style Guide](https://angular.io/guide/styleguide) +* [Effective Go](https://go.dev/doc/effective_go) +* [PEP 8 Python Style Guide](https://www.python.org/dev/peps/pep-0008/) +* [Git Style Guide](https://github.com/agis/git-style-guide) + +For more informative links, refer to the [GitHub Awesome Guidelines List](https://github.com/Kristories/awesome-guidelines). + +### Strict Linting + +One way we enforce code style is by including the best standard linters into our projects. We normally keep the settings pretty strict. Although it may seem pointless and annoying at first, these linters will make you a better coder since you will learn to adapt your style to the style of the group of people who spent countless hours creating the linter in the first place. diff --git a/.config/docs/common/readme/contribute.md b/.config/docs/common/readme/contribute.md new file mode 100644 index 00000000..17a5ad5c --- /dev/null +++ b/.config/docs/common/readme/contribute.md @@ -0,0 +1,39 @@ +## Contributing + +Contributions, issues, and feature requests are welcome! Feel free to check the [issues page]({{ repository.github }}{{ repository.location.issues.github }}). If you would like to contribute, please take a look at the [contributing guide]({{ repository.github }}{{ repository.location.contributing.github }}). + +
+Sponsorship +
+
+
+Dear Awesome Person,

+{{ sponsorship.text }} +

Sincerely,

+ +**_{{ sponsorship.author }}_**

+ +
+ + + Open Collective sponsors + + + GitHub sponsors + + + Patreon + + +### Affiliates + +Below you will find a list of services we leverage that offer special incentives for signing up for their services through our special links: + + + MailChimp + + + DigitalOcean Referral Badge + + +
diff --git a/.config/docs/common/readme/header.md b/.config/docs/common/readme/header.md new file mode 100644 index 00000000..ff507dba --- /dev/null +++ b/.config/docs/common/readme/header.md @@ -0,0 +1,11 @@ +
+
+ + {{ name }} logo + +
+
+
+

{{ docs.header_title_pre }}{{ title }}{{ docs.header_title_post }}

+

{{ docs.header_description_pre }}{{ organization }}

{{ docs.header_description_post }}
+
diff --git a/.config/docs/common/readme/license.md b/.config/docs/common/readme/license.md new file mode 100644 index 00000000..0dd177cb --- /dev/null +++ b/.config/docs/common/readme/license.md @@ -0,0 +1,3 @@ +## License + +Copyright © {{ copyright }} [{{ company }}]({{ link.home }}). This project is [{{ license }}]({{ repository.gitlab }}{{ repository.location.license.gitlab }}) licensed. diff --git a/.config/docs/contributing/code-format.md b/.config/docs/contributing/code-format.md new file mode 100644 index 00000000..f9bc71b1 --- /dev/null +++ b/.config/docs/contributing/code-format.md @@ -0,0 +1,48 @@ +## Code Format + +We try to structure our Ansible task and variable files consistently across all [our Ansible projects]({{ repository.group.ansible_roles }}). This allows us to do things like use RegEx to make ecosystem wide changes. A good way of making sure that your code follows the format we are using is to: + +1. Clone the [main playbook repository]({{ project.playbooks }}) (a.k.a. [Install Doctor]({{ link.installdoctor }})) +2. Use Visual Studio Code to search for code examples of how we are performing similar tasks + +For example: + +- All of our roles use a similar pattern for the `tasks/main.yml` file +- The file names and variable names are consistent across our roles +- Contributors automatically format some parts of their code by leveraging our pre-commit hook (which is installed when you run `bash .start.sh` in the root of a project) + +### Code Format Example + +To dive a little deeper, take the following block of code that was retrieved from the `tasks/main.yml` file in the [Android Studio role](https://github.com/InstallDoc/androidstudio) as an example: + +```yaml +--- +- name: Include variables based on the operating system + include_vars: '{{ {{ ansible_os_family }} }}.yml' + +- name: Include tasks based on the operating system + become: true + block: + - include_tasks: 'install-{{ {{ ansible_os_family }} }}.yml' +``` + +If you compare the block of code above to other `tasks/main.yml` files in other roles (which you can find in our [Ansible Roles group]({{ repository.group.ansible_roles }}) or our [main playbook]({{ project.playbooks }})) (a.k.a. [Install Doctor]({{ link.installdoctor }})), you will see that the files are either identical or nearly identical. There is an exception. Some roles will exclude the first task titled "Include variables based on the operating system" when variables are not required for the role. Our goal is to be consistent but not to the point where we are degrading the functionality of our code or including code that is unnecessary. + +In general, it is up to the developer to browse through our projects to get a feel for the code format we use. A good idea is to clone [Install Doctor]({{ project.playbooks }}), search for how Ansible modules are used, and then mimic the format. For instance, if you are adding a task that installs a snap package, then you would search for `community.general.snap:` in the main playbook to see the format we are using so you can mimic the style. + +### Platform-Specific Roles + +If you have a role that only installs software made for Windows 10 then ensure that the tasks are only run when the system is a Windows system by using `when:` in the `tasks/main.yml` file. Take the following `main.yml` as an example: + +```yaml +--- +- name: Include variables based on the operating system + include_vars: 'ansible_os_family.yml' + when: ansible_os_family == 'Windows' + +- name: Include tasks based on the operating system + become: true + block: + - include_tasks: 'install-ansible_os_family.yml' + when: ansible_os_family == 'Windows' +``` diff --git a/.config/docs/contributing/code-style.md b/.config/docs/contributing/code-style.md new file mode 100644 index 00000000..c283112a --- /dev/null +++ b/.config/docs/contributing/code-style.md @@ -0,0 +1,105 @@ +## Code Style + +We try to follow the same code style across all our Ansible repositories. If something is done one way somewhere, then it should be done the same way elsewhere. It is up to you to [browse through our roles]({{ repository.group.ansible_roles }}) to get a feel for how everything should be styled. You should clone [the main playbooks repository]({{ project.playbooks }}) (a.k.a. [Install Doctor]({{ link.installdoctor }})), initialize all the submodules either via `bash .start.sh` or `git submodule update --init --recursive`, and search through the code base to see how we are _styling_ different task types. Below are some examples: + +### Arrays + +When there is only one parameter, then you should inline it. + +**❌ BAD** + +```yaml +when: + - install_minikube +``` + +**✅ GOOD** + +```yaml +when: install_minikube +``` + +**✅ ALSO GOOD** + +```yaml +when: + - install_minikube + - install_hyperv_plugin +``` + +### Alphabetical Order + +Anywhere an array/list is used, the list should be ordered alphabetically (if possible). + +**❌ BAD** + +```yaml +autokey_dependencies: + - pkg-config + - make + - git +``` + +**✅ GOOD** + +```yaml +autokey_dependencies: + - git + - make + - pkg-config +``` + +### Dependency Variables + +In many cases, a role will require that specific software package dependencies are met before running. These dependencies are usually an array of packages that need to be installed. + +Say the application being installed is Android Studio. The dependency array should be assigned to a variable titled `androidstudio_dependencies` (where "androidstudio" is retrieved from the `.galaxy_info.role_name` field in the `meta/main.yml` file) and placed in `vars/main.yml`. + +**✅ GOOD example of defining the variable in the `vars/main.yml` file** + +```yaml +--- +androidstudio_dependencies: + - ffmpeg + - coolpackage + - anotherpackage +``` + +**❌ BAD example of integrating the variable into a task file:** + +```yaml +- name: "Ensure {{ {{ app_name }} }}'s dependencies are installed" + community.general.pacman: + name: '{{ {{ android_studio_deps }} }}' + state: present +``` + +**✅ GOOD example of integrating the variable into a task file:** + +```yaml +- name: "Ensure {{ {{ app_name }} }}'s dependencies are installed" + community.general.pacman: + name: '{{ {{ androidstudio_dependencies }} }}' + state: present +``` + +If there are dependencies that are specific to a certain OS, then the dependency variable should be titled `{{ {{ .galaxy_info.role_name }} }}_dependencies_{{ {{ os_family }} }}`. For Android Studio, a Fedora-specific dependency list should be named `androidstudio_dependencies_fedora`. In practice, this would look like: + +```yaml +- name: "Ensure {{ {{ app_name }} }}'s dependencies are installed (Fedora)" + dnf: + name: '{{ {{ androidstudio_dependencies_fedora }} }}' + state: present + when: ansible_distribution == 'Fedora' +``` + +### DRY + +DRY stands for "Don't Repeat Yourself." Whenever there is code that is duplicated across multiple task files, you should separate it into a different file and then include it like in the following example: + +**✅ GOOD** + +```yaml +- name: Run generic Linux tasks + include_tasks: install-Linux.yml +``` diff --git a/.config/docs/contributing/commenting.md b/.config/docs/contributing/commenting.md new file mode 100644 index 00000000..2fd01031 --- /dev/null +++ b/.config/docs/contributing/commenting.md @@ -0,0 +1,140 @@ +## Commenting + +We strive to make our roles easy to understand. Commenting is a major part of making our roles easier to grasp. Several types of comments are supported in such a way that they are extracted and injected into our documentation. This project uses [mod-ansible-autodoc]({{ link.mod_ansible_autodoc }}) (a pet project of ours and a fork of [ansible-autodoc](https://pypi.org/project/ansible-autodoc/)) to scan through specially marked up comments and generate documentation out of them. The module also allows the use of markdown in comments so feel free to **bold**, _italicize_, and `code_block` as necessary. Although it is perfectly acceptable to use regular comments, in most cases you should use one of the following types of _special_ comments: + +- [Variable comments](#variable-comments) +- [Action comments](#action-comments) +- [TODO comments](#todo-comments) + +### Variable Comments + +It is usually not necessary to add full-fledged comments to anything in the `vars/` folder but the `defaults/main.yml` file is a different story. The `defaults/main.yml` file must be fully commented since it is where we store all the variables that our users can customize. **`defaults/main.yml` is the only place where comments using the following format should be present.** + +Each variable in `defaults/main.yml` should be added and documented using the following format: + + +```yaml + # @var variable_name: default_value + # The description of the variable which should be no longer than 160 characters per line. + # You can separate the description into new lines so you do not pass the 160 character + # limit + variable_name: default_value +``` + + +There may be cases where an example is helpful. In these cases, use the following format: + + +```yaml + # @var variable_name: [] + # The description of the variable which should be no longer than 160 characters per line. + # You can separate the description into new lines so you do not pass the 160 character + # limit + variable_name: [] + # @example # + # variable_name: + # - name: jimmy + # param: henry + # - name: albert + # @end +``` + + +Each variable-comment block in `defaults/main.yml` should be separated by a line return. You can see an example of a `defaults/main.yml` file using this special [variable syntax in the Docker role]({{ link.docker_role }}/blob/master/defaults/main.yml). + +### Action Comments + +Action comments allow us to describe what the role does. Each action comment should include an action group as well as a description of the feature or "action". Most of the action comments should probably be added to the `tasks/main.yml` file although there could be cases where an action comment is added in a specific task file (like `install-Darwin.yml`, for instance). Action comments allow us to group similar tasks into lists under the action comment's group. + +#### Example Action Comment Implementation + +The following is an example of the implementation of action comments. You can find the [source here]({{ link.docker_role }}/blob/master/tasks/main.yml) as well as an example of why and how you would include an [action comment outside of the `tasks/main.yml` file here]({{ link.docker_role }}/blob/master/tasks/compose-Darwin.yml). + + +```yaml + # @action Ensures Docker is installed + # Installs Docker on the target machine. + # @action Ensures Docker is installed + # Ensures Docker is started on boot. + - name: Include tasks based on the operating system + block: + - include_tasks: 'install-{{ {{ ansible_os_family }} }}.yml' + when: not docker_snap_install + + # @action Ensures Docker is installed + # If the target Docker host is a Linux machine and the `docker_snap_install` variable + # is set to true, then Docker will be installed as a snap package. + - name: Install Docker via snap + community.general.snap: + name: docker + when: + - ansible_os_family not in ('Windows', 'Darwin') + - docker_snap_install + + # @action Installs Docker Compose + # Installs Docker Compose if the `docker_install_compose` variable is set to true. + - name: Install Docker Compose (based on OS) + block: + - include_tasks: 'compose-{{ {{ ansible_os_family }} }}.yml' + when: docker_install_compose | bool +``` + + +#### Example Action Comment Generated Output + +The block of code above will generate markdown that would look similar to this: + +**Ensures Docker is installed** + +- Installs Docker on the target machine. +- Ensures Docker is started on boot. +- If the target Docker host is a Linux machine and the `docker_snap_install` variable is set to true, then Docker will be installed as a snap package. + +**Installs Docker Compose** + +- Installs Docker Compose if the `docker_install_compose` variable is set to true. + +#### Action Comment Guidelines + +- The wording of each action should be in active tense, describing a capability of the role. So instead of calling an action "Generate TLS certificates," we would call it, "Generates TLS certificates." +- The bulk of the action comments should be placed in the `tasks/main.yml` file. However, there may be use cases for putting an action comment in another file. For instance, if the business logic is different for Windows hosts, then we might add action comments to the `install-Windows.yml` file explaining the different logic. +- The goal of action comments are to present our users with some easy to understand bullet points about exactly what the role does and also elaborate on some of the higher-level technical details. + +### TODO Comments + +TODO comments are similar to action comments in the sense that through automation similar comments will be grouped together. You should use them anytime you find a bug, think of an improvement, spot something that needs testing, or realize there is a desirable feature missing. Take the following as an example: + +#### Example TODO Comment Implementation + + +```yaml + # @todo Bug: bug description + # @todo improvement: improvement description + # @todo Bug: another bug description +``` + + +#### Example TODO Comment Generated Output + +The above code will output something that looks like this: + +**Bug** + +- bug description +- another bug description + +**improvement** + +- improvement description + +Notice how the title for _improvement_ is not capitalized. It should be capitalized so make sure you pay attention to that detail. + +#### TODO Comment Guidelines + +- A TODO comment can be placed anywhere as long as no lines pass the limit of 160 characters. +- Try using similar TODO comment groups. Nothing is set in stone yet but try to use the following categories unless you really believe we need a new category: + - Bug + - Feature + - Improvement + - Test +- Ensure you capitalize the category diff --git a/.config/docs/contributing/dev-environment.md b/.config/docs/contributing/dev-environment.md new file mode 100644 index 00000000..7784d89d --- /dev/null +++ b/.config/docs/contributing/dev-environment.md @@ -0,0 +1,45 @@ +## Setting Up Development Environment + +Before contributing to this project, you will have to make sure you have the tools that are utilized. We have made it incredibly easy to get started - just run `bash .start.sh` in the root of the repository. Most of the requirements (listed below) will automatically install (rootlessly) if they are missing from your system when you initialize the project by running `bash .start.sh`. + +### Requirements + +- **[Task](https://github.com/ProfessorManhattan/ansible-task)** +- **[Python 3](https://github.com/ProfessorManhattan/ansible-python)**, along with the `python3-netaddr` and `python3-pip` libraries (i.e. `sudo apt-get install python3 python3-netaddr python3-pip`) +- **[Docker](https://github.com/ProfessorManhattan/ansible-docker)** +- **[Node.js](https://github.com/ProfessorManhattan/ansible-nodejs)** >=12 which is used for the development environment which includes a pre-commit hook +- **[VirtualBox](https://github.com/ProfessorManhattan/ansible-virtualbox)** which is used for running Molecule tests + +Docker and VirtualBox must be installed with root priviledges. If they are missing from your system, running `bash .start.sh` will prompt you for your password and automatically install them. Otherwise, you can follow the official [directions for installing Docker](https://docs.docker.com/get-docker/) and [directions for installing VirtualBox](https://www.virtualbox.org/manual/ch02.html). + +### Getting Started + +With all the requirements installed, navigate to the root directory and run the following command to set up the development environment which includes installing the Python dependencies and installing the Ansible Galaxy dependencies: + +```terminal +bash .start.sh +``` + +This will install all the dependencies and automatically register a pre-commit hook. More specifically, `bash .start.sh` will: + +1. Install Task which provides an easy-to-use interface for performing common tasks while leveraging parallel execution +2. Install missing development tools like Node.js and Python +3. Install the Node.js development environment dependencies +4. Install a pre-commit hook using [husky]({{ misc.husky }}) +5. Ensure that meta files and documentation are up-to-date +6. Install the Python 3 requirements +7. Install the Ansible Galaxy requirements +8. Re-generate documentation using the latest sources +9. Perform other miscellaneous tasks depending on the project type + +### Tasks Available + +With the dependencies installed, you can see a list of the available commands by running `task --list`. This will log a help menu to the console informing you about the available commands and what they do. After running the command, you will see something that looks like this: + +```shell +❯ task --list + +{{ task_list_output }} +``` + +Using the information provided above by running `task --list`, we can see that the `task lint:all` command will lint the project with all the available linters. You can see exactly what each command is doing by checking out the `Taskfile.yml` file (and following the imports). You can also get a detailed summary of any task reported by `task --list` by running `task group:task-name --summary`. diff --git a/.config/docs/contributing/docs.md b/.config/docs/contributing/docs.md new file mode 100644 index 00000000..14100249 --- /dev/null +++ b/.config/docs/contributing/docs.md @@ -0,0 +1,39 @@ +## Updating Meta Files and Documentation + +Since we have hundreds of Ansible roles to maintain, the majority of the files inside each role are shared across all our Ansible projects. We synchronize these common files across all our repositories with various build tools. When you clone a new repository, the first command you should run is `bash .start.sh`. This will install missing software requirements, run the full update sequence, and ensure everything is up-to-date. To synchronize the project at a later point in time, you can run `task common:update` which runs most of the logic executed by running `bash .start.sh`. + +### The `"blueprint" package.json` Field and `@appnest/readme` + +In the root of all of our Ansible repositories, we include a file named `package.json`. In the key named `"blueprint"`, there are variables that are used in our build tools. Most of the variables stored in `"blueprint"` are used for generating documentation. All of our documentation is generated using variables and document partials that we feed into a project called `[@appnest/readme]({{ misc.appnest }})` (which is in charge of generating the final README/CONTRIBUTING guides). When `@appnest/readme` is run, it includes the variables stored in `"blueprint"` in the context that it uses to inject variables in the documentation. You can view the documentation partials by checking out the `./.common` folder which is a submodule that is shared across all of our Ansible projects. + +For every role that is included in our eco-system, we require certain fields to be filled out in the `"blueprint"` section of the `package.json` file. Lucky for you, most of the fields in the file are auto-generated. The fields that need to be filled out as well as descriptions of what they should contain are listed in the chart below: + +{{ blueprint_requirements }} + +### `meta/main.yml` Description + +The most important piece of text in each of our Ansible projects is the [Ansible Galaxy]({{ profile_link.galaxy }}) description located in `meta/main.yml`. This text is used in search results on Ansible Galaxy and GitHub. It is also spun to generate multiple variants so it has to be worded in a way that makes sense with our different variants. Take the following as an example: + +**The `meta/main.yml` description example:** + +- Installs Android Studio and sets up Android SDKs on nearly any OS + +**Gets spun and used by our automated documentation framework in the following formats:** + +- Installs Android Studio and sets up Android SDKs on nearly any OS +- An Ansible role that _installs Android Studio and sets up Android SDKs on nearly any OS_ +- This repository is the home of an Ansible role that _installs Ansible Studio and sets up Android SDKs on nearly any OS_. + +It is important that all three variants of the `meta/main.yml` description make sense and be proper English. The `meta/main.yml` description should succinctly describe what the role does and possibly even describe what the product does if it is not well-known like Android Studio. An example of a description that includes an overview of the product would be something like, "Installs HTTPie (a user-friendly, command-line HTTP client) on nearly any platform," for the [HTTPie role](https://github.com/ProfessorManhattan/ansible-httpie) or "Installs Packer (an automation tool for building machine images) on nearly any platform" for the [Packer role](https://github.com/ProfessorManhattan/ansible-packer). + +### `logo.png` + +We include a `logo.png` file in all of our Ansible projects. This image is automatically integrated with GitLab so that a thumbnail appears next to the project. It is also shown in the README to give the user a better idea of what the role does. All roles should include the `logo.png` file. When adding a `logo.png` file please _strictly_ adhere to the steps below: + +1. Use Google image search to find a logo that best represents the product. Ensure the image is a `.png` file and that it has a transparent background, if possible. Ideally, the image should be the official logo for software that the Ansible role/project installs. The image should be at least 200x200 pixels. +2. After downloading the image, ensure you have the sharp-cli installed by running `npm install -g sharp-cli`. +3. Resize the image to 200x200 pixels by running `sharp -i file_location.png -o logo.png resize 200 200`. +4. Compress the resized image by dragging and dropping the resized image into the [TinyPNG web application]({{ misc.tinypng }}). +5. Download the compressed image and add it to the root of the Ansible project. Make sure it is named `logo.png`. + +Alternatively, you can use our pre-commit hook to automatically take care of steps 2-5 when the `logo.png` file is staged with git. diff --git a/.config/docs/contributing/linting.md b/.config/docs/contributing/linting.md new file mode 100644 index 00000000..5554f4df --- /dev/null +++ b/.config/docs/contributing/linting.md @@ -0,0 +1,36 @@ +## Linting + +The process of running linters is mostly automated. Molecule is configured to lint so you will see linting errors when you run `molecule test` (note that not all Molecule scenarios include automatic linting). There is also a pre-commit hook that lints your code and performs other validations before allowing a `git commit` to go through. If you followed the [Setting Up Development Environment](#setting-up-development-environment) section, you should be all set to have your code automatically linted before pushing changes to the repository. + +**Please note that before creating a pull request, all lint errors should be resolved.** If you would like to view all the steps we take to ensure great code then check out `.husky/pre-commit` and the other files in the `.husky/` folder. + +### Fixing Ansible Lint Errors + +You can manually run Ansible Lint by executing the following command in the project's root: + +```shell +task lint:ansible +``` + +Most errors will be self-explanatory and simple to fix. Other errors might require testing and research. Below are some tips on fixing the trickier errors. + +#### [208] File permissions unset or incorrect + +If you get this error, do research to figure out the minimum permissions necessary for the file. After you change the permission, test the role (since changing permissions can easily break things). + +#### [301] Command should not change things if nothing needs doing + +This error can be solved by telling Ansible what files the command creates or deletes. When you specify what file a `command:` or `shell:` creates and/or deletes, Ansible will check for the presence or absence of the file to determine if the system is already in the desired state. If it is in the desired state, then Ansible skips the task. Refer to the [documentation for ansible.builtin.command](https://docs.ansible.com/ansible/latest/collections/ansible/builtin/command_module.html) for further details. + +Here is an example of code that will remove the error: + +```yaml +- name: Run command if /path/to/database does not exist + command: /usr/bin/make_database.sh db_user db_name + args: + creates: /path/to/database # If the command deletes something, then you can swap out creates with removes +``` + +#### [305] Use shell only when shell functionality is required + +Only use the Ansible `shell:` task when absolutely necessary. If you get this error then test if replacing `shell:` with `command:` resolves the error. If that does not work and you can not figure out how to properly configure the environment for `command:` to work, then you can add `# noqa 305` at the end of the line that includes the `name:` property. The same is true for other linting errors - `# noqa` followed by the reported lint error code will instruct `ansible-lint` to ignore the error. diff --git a/.config/docs/contributing/philosophy.md b/.config/docs/contributing/philosophy.md new file mode 100644 index 00000000..2bb72a2b --- /dev/null +++ b/.config/docs/contributing/philosophy.md @@ -0,0 +1,11 @@ +## Philosophy + +When you are working with one of our Ansible projects, try asking yourself, "**How can this be improved?**" For example, in the case of the [Android Studio role](https://github.com/ProfessorManhattan/ansible-androidstudio), the role installs Android Studio but there may be additional tasks that should be automated. Consider the following examples: + +- _The software is installed but is asking for a license key._ - In this case, we should provide an option for automatically installing the license key using a CLI command. +- _The software supports plugins_ - We should provide an option for specifying the plugins that are automatically installed. +- _In the case of Android Studio, many users have to install SDKs before using the software._ - We should offer the capability to automatically install user-specified SDKs. +- _The software has configuration files with commonly tweaked settings._ - We should provide the ability to change these settings via variables stored in `defaults/main.yml`. +- _The software has the capability to integrate with another piece of software in the [main playbook]({{ repository.playbooks }})_. - This integration should be automated. + +Ideally, you should use the software installed by the [main playbook]({{ repository.playbooks }}). This is really the only way of testing whether or not the software was installed properly and has all the common settings automated. The software installed by the main playbook is all widely-acclaimed, cross-platform software that many people find useful. diff --git a/.config/docs/contributing/pull-requests.md b/.config/docs/contributing/pull-requests.md new file mode 100644 index 00000000..eec9d3a0 --- /dev/null +++ b/.config/docs/contributing/pull-requests.md @@ -0,0 +1,3 @@ +## Pull Requests + +All pull requests should be associated with issues. Although not strictly required, the pull requests should be made to [the GitLab repository issues board]({{ repository.gitlab }}{{ repository.location.issues.gitlab }}) instead of the [GitHub mirror repository]({{ repository.github }}{{ repository.location.issues.github}}). This is because we use GitLab as our primary repository and mirror the changes to GitHub for the community. diff --git a/.config/docs/contributing/supported-os.md b/.config/docs/contributing/supported-os.md new file mode 100644 index 00000000..7063cb4e --- /dev/null +++ b/.config/docs/contributing/supported-os.md @@ -0,0 +1,44 @@ +## Supported Operating Systems + +All of our roles should run without error on the following operating systems: + +- Archlinux (Latest) +- CentOS 7 and 8 +- Debian 9 and 10 +- Fedora (Latest) +- Ubuntu (16.04, 18.04, 20.04, and Latest) +- Mac OS X (Latest) +- Windows 10 (Latest) + +### Other Operating Systems + +Although we do not have a timeline set up, we are considering adding support for the following operating systems: + +- **Qubes** +- Elementary OS +- Zorin +- OpenSUSE +- Manjaro +- FreeBSD +- Mint + +### Code Style for Platform-Specific Roles + +If you have a role that only installs software made for Windows 10 then ensure that the tasks are only run when the system is a Windows system by using `when:` in the `tasks/main.yml` file. Take the following `main.yml` as an example: + +```yaml +--- +- name: Include variables based on the operating system + include_vars: '{{ ansible_os_family }}.yml' + when: ansible_os_family == 'Windows' + +- name: Include tasks based on the operating system + become: true + block: + - include_tasks: 'install-{{ ansible_os_family }}.yml' + when: ansible_os_family == 'Windows' +``` + +### Preferred Installation Method for Mac OS X + +We currently support installing applications with both [Homebrew](https://brew.sh/) casks and [mas](https://github.com/mas-cli/mas). Since mas does not allow automated logins to the App Store (and requires that the application was already installed by the account signed into the App Store GUI), we prefer the use of homebrew casks for installing applications. diff --git a/.config/docs/contributing/testing.md b/.config/docs/contributing/testing.md new file mode 100644 index 00000000..789b973e --- /dev/null +++ b/.config/docs/contributing/testing.md @@ -0,0 +1,62 @@ +## Testing + +You can test all of the operating systems we support by running the following command in the root of the project: + +```shell +molecule test +``` + +The command `molecule test` will spin up VirtualBox VMs for all the OSes we support and run the role(s). _Do this before committing code._ If you are committing code for only one OS and can not create the fix or feature for the other operating systems then please, at the very minimum, [file an issue]({{ repository.gitlab }}{{ repository.location.issues }}) so someone else can pick it up. + +### Idempotence + +It is important to note that `molecule test` tests for idempotence. To pass the idempotence test means that if you run the role twice in a row then Ansible should not report any changes the second time around. + +### Debugging + +If you would like to shell into a container for debugging, you can do that by running: + +```shell +task common:shell +``` + +### Molecule Documentation + +For more information about Ansible Molecule, check out [the docs](https://molecule.readthedocs.io/en/latest/). + +### Testing Desktop Environments + +Some of our roles include applications like [Android Studio](https://github.com/ProfessorManhattan/ansible-androidstudio). You can not fully test Android Studio from a Docker command line. In cases like this, you should use our desktop scenarios to provision a desktop GUI-enabled VM to test things like: + +- Making sure the Android Studio shortcut is in the applications menu +- Opening Android Studio to make sure it is behaving as expected +- Seeing if there is anything we can automate (e.g. if there is a "Terms of Usage" you have to click OK at then we should automate that process if possible) + +You can specify which scenario you want to test by passing the `-s` flag with the name of the scenario you want to run. For instance, if you wanted to test on Ubuntu Desktop, you would run the following command: + +```shell +molecule test -s ubuntu-desktop +``` + +This would run the Molecule test on Ubuntu Desktop. + +By default, the `molecule test` command will destroy the VM after the test is complete. To run the Ubuntu Desktop test and then open the desktop GUI you would have to: + +1. Run `molecule converge -s ubuntu-desktop` +2. Open the VM through the VirtualBox UI (the username and password are both _vagrant_) + +You can obtain a list of all possible scenarios by looking in the `molecule/` folder. The `molecule/default/` folder is run when you do not pass a scenario. All the other scenarios can be run by manually specifying the scenario (e.g. `molecule test -s ubuntu-desktop` will run the test using the scenario in `molecule/ubuntu-desktop/`). + +### Molecule Scenario Descriptions + +The chart below provides a list of the scenarios we include in all of our Ansible projects along with a brief description of what they are included for. + +{{ molecule_descriptions }} + +### Continuous Integration (CI) + +You might have noticed that there are no CI tests in the chart above for macOS and Windows. Due to the limitations of Docker, we use other methods to test macOS and Windows automatically with CI. After a project has passed various linting tests on GitLab CI, the following methods are used to test the Ansible play: + +- Linux platforms are tested using Molecule and Docker on GitLab CI in parallel. ([Link to GitLab CI configuration]({{ repository.group.ci }}/-/blob/master/test/molecule.gitlab-ci.yml)) +- Windows is tested using GitLab CI without Molecule. ([Link to GitLab CI configuration]({{ repository.group.ci }}/-/blob/master/test/windows-ansible-test.gitlab-ci.yml)) +- macOS is tested using GitHub Actions after the code is automatically synchronized between GitLab and GitHub. ([Link to the macOS GitHub Action configuration]({{ repository.github }}/-/blob/master/.github/workflows/macOS.yml)) diff --git a/.config/docs/local/package-lock.json b/.config/docs/local/package-lock.json new file mode 100644 index 00000000..0fec7695 --- /dev/null +++ b/.config/docs/local/package-lock.json @@ -0,0 +1 @@ +{"name":"@installdoc/docs-ansible","preserveSymlinks":false,"version":"0.0.1","lockfileVersion":1,"dependencies":{"@ampproject/remapping":{"version":"2.1.2","dev":true,"integrity":"sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==","requires":{"@jridgewell/trace-mapping":"0.3.4"}},"@babel/code-frame":{"version":"7.16.7","dev":true,"integrity":"sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==","requires":{"@babel/highlight":"7.16.10"}},"@babel/compat-data":{"version":"7.17.0","dev":true,"integrity":"sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng=="},"@babel/core":{"version":"7.17.5","dev":true,"integrity":"sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA==","requires":{"@ampproject/remapping":"2.1.2","@babel/code-frame":"7.16.7","@babel/generator":"7.17.3","@babel/helper-compilation-targets":"7.16.7","@babel/helper-module-transforms":"7.16.7","@babel/helpers":"7.17.2","@babel/parser":"7.17.3","@babel/template":"7.16.7","@babel/traverse":"7.17.3","@babel/types":"7.17.0","convert-source-map":"1.8.0","debug":"4.3.3","gensync":"1.0.0-beta.2","json5":"2.2.0","semver":"6.3.0"}},"@babel/eslint-parser":{"version":"7.17.0","dev":true,"integrity":"sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==","requires":{"@babel/core":"7.17.5","eslint":"8.9.0","eslint-scope":"5.1.1","eslint-visitor-keys":"2.1.0","semver":"6.3.0"}},"@babel/generator":{"version":"7.17.3","dev":true,"integrity":"sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==","requires":{"@babel/types":"7.17.0","jsesc":"2.5.2","source-map":"0.5.7"}},"@babel/helper-compilation-targets":{"version":"7.16.7","dev":true,"integrity":"sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==","requires":{"@babel/compat-data":"7.17.0","@babel/core":"7.17.5","@babel/helper-validator-option":"7.16.7","browserslist":"4.19.3","semver":"6.3.0"}},"@babel/helper-environment-visitor":{"version":"7.16.7","dev":true,"integrity":"sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-function-name":{"version":"7.16.7","dev":true,"integrity":"sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==","requires":{"@babel/helper-get-function-arity":"7.16.7","@babel/template":"7.16.7","@babel/types":"7.17.0"}},"@babel/helper-get-function-arity":{"version":"7.16.7","dev":true,"integrity":"sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-hoist-variables":{"version":"7.16.7","dev":true,"integrity":"sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-module-imports":{"version":"7.16.7","dev":true,"integrity":"sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-module-transforms":{"version":"7.16.7","dev":true,"integrity":"sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==","requires":{"@babel/helper-environment-visitor":"7.16.7","@babel/helper-module-imports":"7.16.7","@babel/helper-simple-access":"7.16.7","@babel/helper-split-export-declaration":"7.16.7","@babel/helper-validator-identifier":"7.16.7","@babel/template":"7.16.7","@babel/traverse":"7.17.3","@babel/types":"7.17.0"}},"@babel/helper-simple-access":{"version":"7.16.7","dev":true,"integrity":"sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-split-export-declaration":{"version":"7.16.7","dev":true,"integrity":"sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-validator-identifier":{"version":"7.16.7","dev":true,"integrity":"sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="},"@babel/helper-validator-option":{"version":"7.16.7","dev":true,"integrity":"sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ=="},"@babel/helpers":{"version":"7.17.2","dev":true,"integrity":"sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==","requires":{"@babel/template":"7.16.7","@babel/traverse":"7.17.3","@babel/types":"7.17.0"}},"@babel/highlight":{"version":"7.16.10","dev":true,"integrity":"sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==","requires":{"@babel/helper-validator-identifier":"7.16.7","chalk":"2.4.2","js-tokens":"4.0.0"}},"@babel/parser":{"version":"7.17.3","dev":true,"integrity":"sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA=="},"@babel/runtime":{"version":"7.17.2","dev":true,"integrity":"sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==","requires":{"regenerator-runtime":"0.13.9"}},"@babel/runtime-corejs3":{"version":"7.17.2","dev":true,"integrity":"sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==","requires":{"core-js-pure":"3.21.1","regenerator-runtime":"0.13.9"}},"@babel/template":{"version":"7.16.7","dev":true,"integrity":"sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==","requires":{"@babel/code-frame":"7.16.7","@babel/parser":"7.17.3","@babel/types":"7.17.0"}},"@babel/traverse":{"version":"7.17.3","dev":true,"integrity":"sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==","requires":{"@babel/code-frame":"7.16.7","@babel/generator":"7.17.3","@babel/helper-environment-visitor":"7.16.7","@babel/helper-function-name":"7.16.7","@babel/helper-hoist-variables":"7.16.7","@babel/helper-split-export-declaration":"7.16.7","@babel/parser":"7.17.3","@babel/types":"7.17.0","debug":"4.3.3","globals":"11.12.0"},"dependencies":{"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}}}},"@babel/types":{"version":"7.17.0","dev":true,"integrity":"sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==","requires":{"@babel/helper-validator-identifier":"7.16.7","to-fast-properties":"2.0.0"}},"@commitlint/cli":{"version":"15.0.0","dev":true,"integrity":"sha512-Y5xmDCweytqzo4N4lOI2YRiuX35xTjcs8n5hUceBH8eyK0YbwtgWX50BJOH2XbkwEmII9blNhlBog6AdQsqicg==","requires":{"@commitlint/format":"15.0.0","@commitlint/lint":"15.0.0","@commitlint/load":"15.0.0","@commitlint/read":"15.0.0","@commitlint/types":"15.0.0","lodash":"4.17.21","resolve-from":"5.0.0","resolve-global":"1.0.0","yargs":"17.3.1"}},"@commitlint/config-conventional":{"version":"16.2.1","dev":true,"integrity":"sha512-cP9gArx7gnaj4IqmtCIcHdRjTYdRUi6lmGE+lOzGGjGe45qGOS8nyQQNvkNy2Ey2VqoSWuXXkD8zCUh6EHf1Ww==","requires":{"conventional-changelog-conventionalcommits":"4.6.3"}},"@commitlint/ensure":{"version":"15.0.0","dev":true,"integrity":"sha512-7DV4iNIald3vycwaWBNGk5FbonaNzOlU8nBe5m5AgU2dIeNKuXwLm+zzJzG27j0Ho56rgz//3F6RIvmsoxY9ZA==","requires":{"@commitlint/types":"15.0.0","lodash":"4.17.21"}},"@commitlint/execute-rule":{"version":"15.0.0","dev":true,"integrity":"sha512-pyE4ApxjbWhb1TXz5vRiGwI2ssdMMgZbaaheZq1/7WC0xRnqnIhE1yUC1D2q20qPtvkZPstTYvMiRVtF+DvjUg=="},"@commitlint/format":{"version":"15.0.0","dev":true,"integrity":"sha512-bPhAfqwRhPk92WiuY0ktEJNpRRHSCd+Eg1MdhGyL9Bl3U25E5zvuInA+dNctnzZiOBSH/37ZaD0eOKCpQE6acg==","requires":{"@commitlint/types":"15.0.0","chalk":"4.1.2"}},"@commitlint/is-ignored":{"version":"15.0.0","dev":true,"integrity":"sha512-edtnkf2QZ/7e/YCJDgn1WDw9wfF1WfOitW5YEoSOb4SxjJEb/oE87kxNPZ2j8mnDMuunspcMfGHeg6fRlwaEWg==","requires":{"@commitlint/types":"15.0.0","semver":"7.3.5"}},"@commitlint/lint":{"version":"15.0.0","dev":true,"integrity":"sha512-hUi2+Im/2dJ5FBvWnodypTkg+5haCgsDzB0fyMApWLUA1IucYUAqRCQCW5em1Mhk9Crw1pd5YzFNikhIclkqCw==","requires":{"@commitlint/is-ignored":"15.0.0","@commitlint/parse":"15.0.0","@commitlint/rules":"15.0.0","@commitlint/types":"15.0.0"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"@commitlint/load":{"version":"15.0.0","dev":true,"integrity":"sha512-Ak1YPeOhvxmY3ioe0o6m1yLGvUAYb4BdfGgShU8jiTCmU3Mnmms0Xh/kfQz8AybhezCC3AmVTyBLaBZxOHR8kg==","requires":{"@commitlint/execute-rule":"15.0.0","@commitlint/resolve-extends":"15.0.0","@commitlint/types":"15.0.0","@endemolshinegroup/cosmiconfig-typescript-loader":"3.0.2","chalk":"4.1.2","cosmiconfig":"7.0.1","lodash":"4.17.21","resolve-from":"5.0.0","typescript":"4.5.5"},"dependencies":{"resolve-from":{"version":"4.0.0","dev":true,"integrity":"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="}}},"@commitlint/message":{"version":"15.0.0","dev":true,"integrity":"sha512-L8euabzboKavPuDJsdIYAY2wx97LbiGEYsckMo6NmV8pOun50c8hQx6ouXFSAx4pp+mX9yUGmMiVqfrk2LKDJQ=="},"@commitlint/parse":{"version":"15.0.0","dev":true,"integrity":"sha512-7fweM67tZfBNS7zw1KTuuT5K2u9nGytUJqFqT/1Ln3Na9cBCsoAqR47mfsNOTlRCgGwakm4xiQ7BpS2gN0OGuw==","requires":{"@commitlint/types":"15.0.0","conventional-changelog-angular":"5.0.13","conventional-commits-parser":"3.2.4"}},"@commitlint/read":{"version":"15.0.0","dev":true,"integrity":"sha512-5yI1o2HKZFVe7RTjL7IhuhHMKar/MDNY34vEHqqz9gMI7BK/rdP8uVb4Di1efl2V0UPnwID0nPKWESjQ8Ti0gw==","requires":{"@commitlint/top-level":"15.0.0","@commitlint/types":"15.0.0","fs-extra":"10.0.0","git-raw-commits":"2.0.11"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"find-up":{"version":"5.0.0","dev":true,"integrity":"sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==","requires":{"locate-path":"6.0.0","path-exists":"4.0.0"}},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"locate-path":{"version":"6.0.0","dev":true,"integrity":"sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==","requires":{"p-locate":"5.0.0"}},"p-limit":{"version":"3.1.0","dev":true,"integrity":"sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==","requires":{"yocto-queue":"0.1.0"}},"p-locate":{"version":"5.0.0","dev":true,"integrity":"sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==","requires":{"p-limit":"3.1.0"}},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"@commitlint/resolve-extends":{"version":"15.0.0","dev":true,"integrity":"sha512-7apfRJjgJsKja7lHsPfEFixKjA/fk/UeD3owkOw1174yYu4u8xBDLSeU3IinGPdMuF9m245eX8wo7vLUy+EBSg==","requires":{"import-fresh":"3.3.0","lodash":"4.17.21","resolve-from":"5.0.0","resolve-global":"1.0.0"}},"@commitlint/rules":{"version":"15.0.0","dev":true,"integrity":"sha512-SqXfp6QUlwBS+0IZm4FEA/NmmAwcFQIkG3B05BtemOVWXQdZ8j1vV6hDwvA9oMPCmUSrrGpHOtZK7HaHhng2yA==","requires":{"@commitlint/ensure":"15.0.0","@commitlint/message":"15.0.0","@commitlint/to-lines":"15.0.0","@commitlint/types":"15.0.0","execa":"5.1.1"}},"@commitlint/to-lines":{"version":"15.0.0","dev":true,"integrity":"sha512-mY3MNA9ujPqVpiJjTYG9MDsYCobue5PJFO0MfcIzS1mCVvngH8ZFTPAh1fT5t+t1h876boS88+9WgqjRvbYItw=="},"@commitlint/top-level":{"version":"15.0.0","dev":true,"integrity":"sha512-7Gz3t7xcuuUw1d1Nou6YLaztzp2Em+qZ6YdCzrqYc+aquca3Vt0O696nuiBDU/oE+tls4Hx2CNpAbWhTgEwB5A==","requires":{"find-up":"5.0.0"}},"@commitlint/types":{"version":"15.0.0","dev":true,"integrity":"sha512-OMSLX+QJnyNoTwws54ULv9sOvuw9GdVezln76oyUd4YbMMJyaav62aSXDuCdWyL2sm9hTkSzyEi52PNaIj/vqw==","requires":{"chalk":"4.1.2"}},"@cspell/cspell-bundled-dicts":{"version":"5.18.5","dev":true,"integrity":"sha512-jFvwF8bb8HUYqMUPQiGZUHAf8zfriZRagzoCW8w4NLLJB1IZNGlQvQCQskQG9cYtOmKAYHCbOwm8SjA9FKwQow==","requires":{"@cspell/dict-ada":"2.0.0","@cspell/dict-aws":"2.0.0","@cspell/dict-bash":"2.0.1","@cspell/dict-companies":"2.0.2","@cspell/dict-cpp":"2.0.0","@cspell/dict-cryptocurrencies":"2.0.0","@cspell/dict-csharp":"2.0.1","@cspell/dict-css":"2.0.0","@cspell/dict-dart":"1.1.0","@cspell/dict-django":"2.0.0","@cspell/dict-dotnet":"2.0.0","@cspell/dict-elixir":"2.0.0","@cspell/dict-en-gb":"1.1.33","@cspell/dict-en_us":"2.1.7","@cspell/dict-filetypes":"2.0.1","@cspell/dict-fonts":"2.0.0","@cspell/dict-fullstack":"2.0.4","@cspell/dict-golang":"2.0.0","@cspell/dict-haskell":"2.0.0","@cspell/dict-html":"3.0.0","@cspell/dict-html-symbol-entities":"2.0.0","@cspell/dict-java":"2.0.0","@cspell/dict-latex":"2.0.0","@cspell/dict-lorem-ipsum":"2.0.0","@cspell/dict-lua":"2.0.0","@cspell/dict-node":"2.0.0","@cspell/dict-npm":"2.0.1","@cspell/dict-php":"2.0.0","@cspell/dict-powershell":"2.0.0","@cspell/dict-public-licenses":"1.0.4","@cspell/dict-python":"2.0.6","@cspell/dict-r":"1.0.2","@cspell/dict-ruby":"2.0.0","@cspell/dict-rust":"2.0.0","@cspell/dict-scala":"2.0.0","@cspell/dict-software-terms":"2.1.1","@cspell/dict-swift":"1.0.2","@cspell/dict-typescript":"2.0.0","@cspell/dict-vue":"2.0.2"}},"@cspell/cspell-pipe":{"version":"5.18.5","dev":true,"integrity":"sha512-U/4e4Zm7Mm23SuJu6b49+9Do/2aS+c9sPQa1Z9ZZqHQ4BqswJagk5oZ0V45BjYJ/0acHSRpIxbndpVJ01cjf8A=="},"@cspell/cspell-types":{"version":"5.18.5","dev":true,"integrity":"sha512-yvDFCUa1CbjBuMkFCh+yUAAaG6VW5WXoewzLwhMFsMV1GZmkbftOcvZq0YuZviNsjdBViDH0dhKdlzwC953upg=="},"@cspell/dict-ada":{"version":"2.0.0","dev":true,"integrity":"sha512-4gfJEYXVwz6IN2LBaT6QoUV4pqaR35i0z0u9O684vLuVczvNJIHa4vNaSEFBr9d6xxncUyqstgP9P73ajJjh9A=="},"@cspell/dict-aws":{"version":"2.0.0","dev":true,"integrity":"sha512-NKz7pDZ7pwj/b33i3f4WLpC1rOOUMmENwYgftxU+giU2YBeKM2wZbMTSEIzsrel56r0UlQYmdIVlP/B4nnVaoQ=="},"@cspell/dict-bash":{"version":"2.0.1","dev":true,"integrity":"sha512-pBx3T/5w7fPF8XD5cx3NwtRFvNpQYmYqzM043NKP2hDmlx4uFwbH599Lvt5mwCMZKfIoRXaNUQvq7se2gstQjw=="},"@cspell/dict-companies":{"version":"2.0.2","dev":true,"integrity":"sha512-LPKwBMAWRz+p1R8q+TV6E1sGOOTvxJOaJeXNN++CZQ7i6JMn5Rf+BSxagwkeK6z3o9vIC5ZE4AcQ5BMkvyjqGw=="},"@cspell/dict-cpp":{"version":"2.0.0","dev":true,"integrity":"sha512-EflHLs2pHEEXZM6jPfTGR/KHZKQtJlvzqgkg1zaA1YKv5HQNw9Wy5KVPGEV2bjPcFsZJO3xXjO1KBZcoOPjPmA=="},"@cspell/dict-cryptocurrencies":{"version":"2.0.0","dev":true,"integrity":"sha512-nREysmmfOp7L2YCRAUufQahwD5/Punzb5AZ6eyg4zUamdRWHgBFphb5/9h2flt1vgdUfhc6hZcML21Ci7iXjaA=="},"@cspell/dict-csharp":{"version":"2.0.1","dev":true,"integrity":"sha512-ZzAr+WRP2FUtXHZtfhe8f3j9vPjH+5i44Hcr5JqbWxmqciGoTbWBPQXwu9y+J4mbdC69HSWRrVGkNJ8rQk8pSw=="},"@cspell/dict-css":{"version":"2.0.0","dev":true,"integrity":"sha512-MrFyswFHnPh4H0u6IlV4eHy+ZCUrrHzeL161LyTOqCvaKpbZavMgNYXzZqTF9xafO0iLgwKrl+Gkclu1KVBg0Q=="},"@cspell/dict-dart":{"version":"1.1.0","dev":true,"integrity":"sha512-bBqZINm+RVjMgUrAhRzv/xx3jc3dkIqO0higPbsK+63IAtMNY3EiQnEO4eapbU+qAhyvICY9hZQZXy5Ux4p+Pw=="},"@cspell/dict-django":{"version":"2.0.0","dev":true,"integrity":"sha512-GkJdJv6cmzrKcmq2/oxTXjKF5uv71r4eTqnFmgPbNBW1t+G4VYpzOf0QrVQrhx2RC4DdW5XfcTf+iS0FxHOTmw=="},"@cspell/dict-dotnet":{"version":"2.0.0","dev":true,"integrity":"sha512-WOHfjwMuLbo76khDsDa1lJvP/dXcwXVwonWwfUFRt82BL/GtyMalh1HEtCWwKDuK/9f8PCEt/EZMkHT3D5ZV3w=="},"@cspell/dict-elixir":{"version":"2.0.0","dev":true,"integrity":"sha512-NeDObcqiYuqWRrzMAQLZDSrZlChTEZwTA2zHdI2nPtpeDl4FQcTz2BHP8zVt6Lj6G2QHJmNGmQtSmDguX86NYA=="},"@cspell/dict-en-gb":{"version":"1.1.33","dev":true,"integrity":"sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g=="},"@cspell/dict-en_us":{"version":"2.1.7","dev":true,"integrity":"sha512-7IeAHZjXiWSIKFx/3CIlY6misvg2KyJ2KO3tSVSKuAlC3UXHGVOcbcY0kQ95IJeKbB6Ot6aW/Aaw73Nzhuurrg=="},"@cspell/dict-filetypes":{"version":"2.0.1","dev":true,"integrity":"sha512-bQ7K3U/3hKO2lpQjObf0veNP/n50qk5CVezSwApMBckf/sAVvDTR1RGAvYdr+vdQnkdQrk6wYmhbshXi0sLDVg=="},"@cspell/dict-fonts":{"version":"2.0.0","dev":true,"integrity":"sha512-AgkTalphfDPtKFPYmEExDcj8rRCh86xlOSXco8tehOEkYVYbksOk9XH0YVH34RFpy93YBd2nnVGLgyGVwagcPw=="},"@cspell/dict-fullstack":{"version":"2.0.4","dev":true,"integrity":"sha512-+JtYO58QAXnetRN+MGVzI8YbkbFTLpYfl/Cw/tmNqy7U1IDVC4sTXQ2pZvbbeKQWFHBqYvBs0YASV+mTouXYBw=="},"@cspell/dict-golang":{"version":"2.0.0","dev":true,"integrity":"sha512-rUeZJR/S/ZjAsOURtxsAO6xDQhL0IzF458ScahaeOqe0zVL3tx7tCLikCgT92NWPs3BNqmsZGqYSDbn/1KsSIA=="},"@cspell/dict-haskell":{"version":"2.0.0","dev":true,"integrity":"sha512-cjX1Br+gSWqtcmJD/IMHz1UoP3pUaKIIKy/JfhEs7ANtRt6hhfEKe9dl2kQzDkkKt4pXol+YgdYxL/sVc/nLgQ=="},"@cspell/dict-html":{"version":"3.0.0","dev":true,"integrity":"sha512-VzZs/UtyRe4spdaH5SWakik+K3vB2fTyW3kdgGQbzjPGHyb5OXI5fmxQcX0yaSv5RkL0igVROHhu2ARUudoTpw=="},"@cspell/dict-html-symbol-entities":{"version":"2.0.0","dev":true,"integrity":"sha512-71S5wGCe7dq6C+zGDwsEAe5msub/irrLi6SExeG11a/EkpA3RKAEheDGPk0hOY4+vOcIFHaApxOjLTtgQfYWfA=="},"@cspell/dict-java":{"version":"2.0.0","dev":true,"integrity":"sha512-9f5LDATlAiXRGqxLxgqbOLlQxuMW2zcN7tBgxwtN+4u90vM03ZUOR/gKIuDV/y0ZuAiWBIjA73cjk8DJ13Q1eA=="},"@cspell/dict-latex":{"version":"2.0.0","dev":true,"integrity":"sha512-H6RRwbHhQ9ARoO1R57SDqB+q/J5jUDdVnkdfukJkA+HNlJBhCcDuzGOIJqr+GBkJYDkF3obZ3LEOk2lUfT+Eyg=="},"@cspell/dict-lorem-ipsum":{"version":"2.0.0","dev":true,"integrity":"sha512-jKogAKtqvgPMleL6usyj3rZ0m8sVUR6drrD+wMnWSfdx1BmUyTsYiuh/mPEfLAebaYHELWSLQG3rDZRvV9Riqg=="},"@cspell/dict-lua":{"version":"2.0.0","dev":true,"integrity":"sha512-7WUEBEspSKtsq104WdIys1+DLqAxpJPzw74Py1TuE3fI5GvlzeSZkRFP2ya54GB2lCO4C3mq4M8EnitpibVDfw=="},"@cspell/dict-node":{"version":"2.0.0","dev":true,"integrity":"sha512-tPPl3liJORa/l6AoYqh/7rjoM7bdtaIXnIN6ox7CE0flZcBS5rWOB6mzEY3rpu/XJX0pjbBiIoqrolDkVl1RTQ=="},"@cspell/dict-npm":{"version":"2.0.1","dev":true,"integrity":"sha512-LRaJFSQfI0BIbbksPFE6fUjAyRFZRcknfOnYC/5c1wB/vsKH6KsqxTeCWNmHTYrk4KdBLZROhsHJXQIoqVTd4w=="},"@cspell/dict-php":{"version":"2.0.0","dev":true,"integrity":"sha512-29WgU77eTO985LvMHwPi1pcpfopfCWfTdffDyqya0JIfOSaFUrlYKzGPkE4mRxcz2G3hXsaM0SRvBNdIRwEdUg=="},"@cspell/dict-powershell":{"version":"2.0.0","dev":true,"integrity":"sha512-6uvEhLiGmG3u9TFkM1TYcky6aL9Yk7Sk3KJwoTYBaQJY2KqrprgyQtW6yxIw9oU52VRHlq3KKvSAA9Q26+SIkQ=="},"@cspell/dict-public-licenses":{"version":"1.0.4","dev":true,"integrity":"sha512-h4xULfVEDUeWyvp1OO19pcGDqWcBEQ7WGMp3QBHyYpjsamlzsyYYjCRSY2ZvpM7wruDmywSRFmRHJ/+uNFT7nA=="},"@cspell/dict-python":{"version":"2.0.6","dev":true,"integrity":"sha512-54ICgMRiGwavorg8UJC38Fwx8tW8WKj8pimJmFUd0F/ImQ8wmeg4VrmyMach5MZVUaw1qUe2aP5uSyqA15Q0mg=="},"@cspell/dict-r":{"version":"1.0.2","dev":true,"integrity":"sha512-Rp3d4sgD6izW9TW5yVI3D//3HTl9oOGBuzTvXRdoHksVPRvzIu2liVhj8MnQ3XIRe5Kc6IhLBAm6izuV2BpGwQ=="},"@cspell/dict-ruby":{"version":"2.0.0","dev":true,"integrity":"sha512-ux73GEIZrApxIG/BDnpdxWE7r9TY3n+3HFAEp+LDJjSjpwpn2VXopd7GsjwsvmlAv5F3Jch8tzgzujFZkvqdoA=="},"@cspell/dict-rust":{"version":"2.0.0","dev":true,"integrity":"sha512-EWlQivTKXMU3TTcq/Pi6KPKTQADknasQ700UrxRPzxhwQ4sKVZ88GDu6VZJlsbFUz8Vko289KS6wjiox/7WpmQ=="},"@cspell/dict-scala":{"version":"2.0.0","dev":true,"integrity":"sha512-MUwA2YKpqaQOSR4V1/CVGRNk8Ii5kf6I8Ch+4/BhRZRQXuwWbi21rDRYWPqdQWps7VNzAbbMA+PQDWsD5YY38g=="},"@cspell/dict-software-terms":{"version":"2.1.1","dev":true,"integrity":"sha512-PmmqysKSvNwksjEfXrzD1wEVvctR6qppxDhwNc4IQQjwpjmtN8e+8HiXxIbCsBcll1rO0vOmnhpXUdl+d9apXQ=="},"@cspell/dict-swift":{"version":"1.0.2","dev":true,"integrity":"sha512-IrMcRO7AYB2qU5cj4ttZyEbd04DRNOG6Iha106qGGmn4P096m+Y7lOnSLJx/rZbD/cAT3Z/7i465Lr1J93j7yg=="},"@cspell/dict-typescript":{"version":"2.0.0","dev":true,"integrity":"sha512-WFBahxsnD2y4Os14tE5Zxh31Ggn4DzGOAu3UoxYl1lLLxaszx4RH7LmAeFuznySboiaBeRBbpfJOjQA796O6VQ=="},"@cspell/dict-vue":{"version":"2.0.2","dev":true,"integrity":"sha512-/MB0RS0Gn01s4pgmjy0FvsLfr3RRMrRphEuvTRserNcM8XVtoIVAtrjig/Gg0DPwDrN8Clm0L1j7iQay6S8D0g=="},"@endemolshinegroup/cosmiconfig-typescript-loader":{"version":"3.0.2","dev":true,"integrity":"sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==","requires":{"cosmiconfig":"7.0.1","lodash.get":"4.4.2","make-error":"1.3.6","ts-node":"9.1.1","tslib":"2.3.1"}},"@es-joy/jsdoccomment":{"version":"0.20.1","dev":true,"integrity":"sha512-oeJK41dcdqkvdZy/HctKklJNkt/jh+av3PZARrZEl+fs/8HaHeeYoAvEwOV0u5I6bArTF17JEsTZMY359e/nfQ==","requires":{"comment-parser":"1.3.0","esquery":"1.4.0","jsdoc-type-pratt-parser":"2.2.3"}},"@eslint/eslintrc":{"version":"1.1.0","dev":true,"integrity":"sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==","requires":{"ajv":"6.12.6","debug":"4.3.3","espree":"9.3.1","globals":"13.12.1","ignore":"4.0.6","import-fresh":"3.3.0","js-yaml":"4.1.0","minimatch":"3.1.2","strip-json-comments":"3.1.1"},"dependencies":{"ignore":{"version":"4.0.6","dev":true,"integrity":"sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg=="}}},"@gitmoji/commit-types":{"version":"1.1.5","dev":true,"integrity":"sha512-8D3FZMRY+gtYpTcHG1SOGmm9CFqxNh6rI9xDoCydxHxnWgqInbdF3nk9gibW5gXA58Hf2cVcJaLEcGOKLRAtmw=="},"@gitmoji/parser-opts":{"version":"1.3.0","dev":true,"integrity":"sha512-NqfoW12eXMdDJz3g3NHnopEgDoqdY1YshO6XxIVPOqotGTDcjD1sL28AVKskbYveGmHBPdHieRRv6M9Iq7dt4w=="},"@humanwhocodes/config-array":{"version":"0.9.3","dev":true,"integrity":"sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==","requires":{"@humanwhocodes/object-schema":"1.2.1","debug":"4.3.3","minimatch":"3.1.2"}},"@humanwhocodes/object-schema":{"version":"1.2.1","dev":true,"integrity":"sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="},"@jest/types":{"version":"26.6.2","dev":true,"integrity":"sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==","requires":{"@types/istanbul-lib-coverage":"2.0.4","@types/istanbul-reports":"3.0.1","@types/node":"17.0.18","@types/yargs":"15.0.14","chalk":"4.1.2"}},"@jridgewell/resolve-uri":{"version":"3.0.5","dev":true,"integrity":"sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew=="},"@jridgewell/sourcemap-codec":{"version":"1.4.11","dev":true,"integrity":"sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg=="},"@jridgewell/trace-mapping":{"version":"0.3.4","dev":true,"integrity":"sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==","requires":{"@jridgewell/resolve-uri":"3.0.5","@jridgewell/sourcemap-codec":"1.4.11"}},"@kwsites/file-exists":{"version":"1.1.1","dev":true,"integrity":"sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==","requires":{"debug":"4.3.3"}},"@kwsites/promise-deferred":{"version":"1.1.1","dev":true,"integrity":"sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="},"@microsoft/tsdoc":{"version":"0.13.2","dev":true,"integrity":"sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg=="},"@microsoft/tsdoc-config":{"version":"0.15.2","dev":true,"integrity":"sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==","requires":{"@microsoft/tsdoc":"0.13.2","ajv":"6.12.6","jju":"1.4.0","resolve":"1.19.0"}},"@nodelib/fs.scandir":{"version":"2.1.5","dev":true,"integrity":"sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==","requires":{"@nodelib/fs.stat":"2.0.5","run-parallel":"1.2.0"}},"@nodelib/fs.stat":{"version":"2.0.5","dev":true,"integrity":"sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="},"@nodelib/fs.walk":{"version":"1.2.8","dev":true,"integrity":"sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==","requires":{"@nodelib/fs.scandir":"2.1.5","fastq":"1.13.0"}},"@octokit/auth-token":{"version":"2.5.0","dev":true,"integrity":"sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==","requires":{"@octokit/types":"6.34.0"}},"@octokit/core":{"version":"3.5.1","dev":true,"integrity":"sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==","requires":{"@octokit/auth-token":"2.5.0","@octokit/graphql":"4.8.0","@octokit/request":"5.6.3","@octokit/request-error":"2.1.0","@octokit/types":"6.34.0","before-after-hook":"2.2.2","universal-user-agent":"6.0.0"}},"@octokit/endpoint":{"version":"6.0.12","dev":true,"integrity":"sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==","requires":{"@octokit/types":"6.34.0","is-plain-object":"5.0.0","universal-user-agent":"6.0.0"}},"@octokit/graphql":{"version":"4.8.0","dev":true,"integrity":"sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==","requires":{"@octokit/request":"5.6.3","@octokit/types":"6.34.0","universal-user-agent":"6.0.0"}},"@octokit/openapi-types":{"version":"11.2.0","dev":true,"integrity":"sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA=="},"@octokit/plugin-paginate-rest":{"version":"2.17.0","dev":true,"integrity":"sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==","requires":{"@octokit/core":"3.5.1","@octokit/types":"6.34.0"}},"@octokit/plugin-request-log":{"version":"1.0.4","dev":true,"integrity":"sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==","requires":{"@octokit/core":"3.5.1"}},"@octokit/plugin-rest-endpoint-methods":{"version":"5.13.0","dev":true,"integrity":"sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==","requires":{"@octokit/core":"3.5.1","@octokit/types":"6.34.0","deprecation":"2.3.1"}},"@octokit/request":{"version":"5.6.3","dev":true,"integrity":"sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==","requires":{"@octokit/endpoint":"6.0.12","@octokit/request-error":"2.1.0","@octokit/types":"6.34.0","is-plain-object":"5.0.0","node-fetch":"2.6.7","universal-user-agent":"6.0.0"}},"@octokit/request-error":{"version":"2.1.0","dev":true,"integrity":"sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==","requires":{"@octokit/types":"6.34.0","deprecation":"2.3.1","once":"1.4.0"}},"@octokit/rest":{"version":"18.12.0","dev":true,"integrity":"sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==","requires":{"@octokit/core":"3.5.1","@octokit/plugin-paginate-rest":"2.17.0","@octokit/plugin-request-log":"1.0.4","@octokit/plugin-rest-endpoint-methods":"5.13.0"}},"@octokit/types":{"version":"6.34.0","dev":true,"integrity":"sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==","requires":{"@octokit/openapi-types":"11.2.0"}},"@phenomnomnominal/tsquery":{"version":"4.2.0","dev":true,"integrity":"sha512-hR2U3uVcrrdkuG30ItQ+uFDs4ncZAybxWG0OjTE8ptPzVoU7GVeXpy+vMU8zX9EbmjGeITPw/su5HjYQyAH8bA==","requires":{"esquery":"1.4.0","typescript":"4.5.5"}},"@prettier/plugin-php":{"version":"0.17.6","dev":true,"integrity":"sha512-Nxkq+Gl8bGbutRV7e3/p5d+Bcftn75oH61RT8xzk44T5ET7fVP50pUdaOdvt704GSNr6wwmfBW8MhBz5IKt+fA==","requires":{"linguist-languages":"7.15.0","mem":"8.1.1","php-parser":"3.0.2","prettier":"2.5.1"}},"@prettier/plugin-pug":{"version":"1.19.2","dev":true,"integrity":"sha512-Izo+m3pb15Ym7t3J+u22kun7bQXZOj+lAqkIc5ifxwd9En9OqcCShZz7eABORmzmY276IjSfY92Ijd23nLR68Q==","requires":{"prettier":"2.5.1","pug-lexer":"5.0.1"}},"@prettier/plugin-ruby":{"version":"2.0.0","dev":true,"integrity":"sha512-pA0rjTi5J7cT86XPNhXp7CpdV2Tlyj5oqDIsnQRxMu2P7LY2KJI/pyOSM8pzTH8BgRyQfe1P1NPCcTwxUnUWtQ==","requires":{"prettier":"2.5.1"}},"@prettier/plugin-xml":{"version":"1.2.0","dev":true,"integrity":"sha512-bFvVAZKs59XNmntYjyefn3K4TBykS6E+d6ZW8IcylAs88ZO+TzLhp0dPpi0VKfPzq1Nb+kpDnPRTiwb4zY6NgA==","requires":{"@xml-tools/parser":"1.0.11","prettier":"2.5.1"}},"@semantic-release/changelog":{"version":"6.0.1","dev":true,"integrity":"sha512-FT+tAGdWHr0RCM3EpWegWnvXJ05LQtBkQUaQRIExONoXjVjLuOILNm4DEKNaV+GAQyJjbLRVs57ti//GypH6PA==","requires":{"@semantic-release/error":"3.0.0","aggregate-error":"3.1.0","fs-extra":"9.1.0","lodash":"4.17.21","semantic-release":"19.0.2"}},"@semantic-release/commit-analyzer":{"version":"9.0.2","dev":true,"integrity":"sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==","requires":{"conventional-changelog-angular":"5.0.13","conventional-commits-filter":"2.0.7","conventional-commits-parser":"3.2.4","debug":"4.3.3","import-from":"4.0.0","lodash":"4.17.21","micromatch":"4.0.4","semantic-release":"19.0.2"}},"@semantic-release/error":{"version":"3.0.0","dev":true,"integrity":"sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw=="},"@semantic-release/exec":{"version":"6.0.3","dev":true,"integrity":"sha512-bxAq8vLOw76aV89vxxICecEa8jfaWwYITw6X74zzlO0mc/Bgieqx9kBRz9z96pHectiTAtsCwsQcUyLYWnp3VQ==","requires":{"@semantic-release/error":"3.0.0","aggregate-error":"3.1.0","debug":"4.3.3","execa":"5.1.1","lodash":"4.17.21","parse-json":"5.2.0","semantic-release":"19.0.2"}},"@semantic-release/git":{"version":"10.0.1","dev":true,"integrity":"sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==","requires":{"@semantic-release/error":"3.0.0","aggregate-error":"3.1.0","debug":"4.3.3","dir-glob":"3.0.1","execa":"5.1.1","lodash":"4.17.21","micromatch":"4.0.4","p-reduce":"2.1.0","semantic-release":"19.0.2"}},"@semantic-release/github":{"version":"8.0.2","dev":true,"integrity":"sha512-wIbfhOeuxlYzMTjtSAa2xgr54n7ZuPAS2gadyTWBpUt2PNAPgla7A6XxCXJnaKPgfVF0iFfSk3B+KlVKk6ByVg==","requires":{"@octokit/rest":"18.12.0","@semantic-release/error":"2.2.0","aggregate-error":"3.1.0","bottleneck":"2.19.5","debug":"4.3.3","dir-glob":"3.0.1","fs-extra":"10.0.0","globby":"11.1.0","http-proxy-agent":"5.0.0","https-proxy-agent":"5.0.0","issue-parser":"6.0.0","lodash":"4.17.21","mime":"3.0.0","p-filter":"2.1.0","p-retry":"4.6.1","semantic-release":"19.0.2","url-join":"4.0.1"},"dependencies":{"@semantic-release/error":{"version":"2.2.0","dev":true,"integrity":"sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg=="}}},"@semantic-release/gitlab":{"version":"7.0.4","dev":true,"integrity":"sha512-TL6kT526+ir/uehMFdTlJNXUj+p+SjPAYUkit6lh5Rs8kxeHQ01bgmpYLQlc94ZDpy9x2Tzcb/NRwKojkmLG4A==","requires":{"@semantic-release/error":"3.0.0","aggregate-error":"3.1.0","debug":"4.3.3","dir-glob":"3.0.1","escape-string-regexp":"3.0.0","form-data":"4.0.0","fs-extra":"10.0.0","globby":"11.1.0","got":"11.8.3","lodash":"4.17.21","parse-path":"4.0.3","semantic-release":"19.0.2","url-join":"4.0.1"},"dependencies":{"escape-string-regexp":{"version":"3.0.0","dev":true,"integrity":"sha512-11dXIUC3umvzEViLP117d0KN6LJzZxh5+9F4E/7WLAAw7GrHk8NpUR+g9iJi/pe9C0py4F8rs0hreyRCwlAuZg=="},"get-stream":{"version":"5.2.0","dev":true,"integrity":"sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==","requires":{"pump":"3.0.0"}},"quick-lru":{"version":"5.1.1","dev":true,"integrity":"sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="}}},"@semantic-release/npm":{"version":"9.0.0","dev":true,"integrity":"sha512-hj2jqayS2SPUmFtCMCOQMX975uMDfRoymj1HvMSwYdaoI6hVZvhrTFPBgJeM85O0C+G3IFviAUar5gel/1VGDQ==","requires":{"@semantic-release/error":"3.0.0","aggregate-error":"3.1.0","execa":"5.1.1","fs-extra":"10.0.0","lodash":"4.17.21","nerf-dart":"1.0.0","normalize-url":"6.1.0","npm":"8.5.1","rc":"1.2.8","read-pkg":"5.2.0","registry-auth-token":"4.2.1","semantic-release":"19.0.2","semver":"7.3.5","tempy":"1.0.1"},"dependencies":{"hosted-git-info":{"version":"2.8.9","dev":true,"integrity":"sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="},"normalize-package-data":{"version":"2.5.0","dev":true,"integrity":"sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==","requires":{"hosted-git-info":"2.8.9","resolve":"1.22.0","semver":"5.7.1","validate-npm-package-license":"3.0.4"}},"type-fest":{"version":"0.6.0","dev":true,"integrity":"sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="}}},"@semantic-release/release-notes-generator":{"version":"10.0.3","dev":true,"integrity":"sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==","requires":{"conventional-changelog-angular":"5.0.13","conventional-changelog-writer":"5.0.1","conventional-commits-filter":"2.0.7","conventional-commits-parser":"3.2.4","debug":"4.3.3","get-stream":"6.0.1","import-from":"4.0.0","into-stream":"6.0.0","lodash":"4.17.21","read-pkg-up":"7.0.1","semantic-release":"19.0.2"},"dependencies":{"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"@sindresorhus/is":{"version":"4.4.0","dev":true,"integrity":"sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ=="},"@solidity-parser/parser":{"version":"0.14.1","dev":true,"integrity":"sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw==","requires":{"antlr4ts":"0.5.0-alpha.4"}},"@szmarczak/http-timer":{"version":"4.0.6","dev":true,"integrity":"sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==","requires":{"defer-to-connect":"2.0.1"}},"@testing-library/dom":{"version":"7.31.2","dev":true,"integrity":"sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==","requires":{"@babel/code-frame":"7.16.7","@babel/runtime":"7.17.2","@types/aria-query":"4.2.2","aria-query":"4.2.2","chalk":"4.1.2","dom-accessibility-api":"0.5.11","lz-string":"1.4.4","pretty-format":"26.6.2"}},"@tokenizer/token":{"version":"0.3.0","dev":true,"integrity":"sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="},"@tootallnate/once":{"version":"2.0.0","dev":true,"integrity":"sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="},"@types/aria-query":{"version":"4.2.2","dev":true,"integrity":"sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig=="},"@types/cacheable-request":{"version":"6.0.2","dev":true,"integrity":"sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==","requires":{"@types/http-cache-semantics":"4.0.1","@types/keyv":"3.1.3","@types/node":"17.0.18","@types/responselike":"1.0.0"}},"@types/eslint":{"version":"7.29.0","dev":true,"integrity":"sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==","requires":{"@types/estree":"0.0.51","@types/json-schema":"7.0.9"}},"@types/estree":{"version":"0.0.51","dev":true,"integrity":"sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ=="},"@types/http-cache-semantics":{"version":"4.0.1","dev":true,"integrity":"sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="},"@types/istanbul-lib-coverage":{"version":"2.0.4","dev":true,"integrity":"sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g=="},"@types/istanbul-lib-report":{"version":"3.0.0","dev":true,"integrity":"sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==","requires":{"@types/istanbul-lib-coverage":"2.0.4"}},"@types/istanbul-reports":{"version":"3.0.1","dev":true,"integrity":"sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==","requires":{"@types/istanbul-lib-report":"3.0.0"}},"@types/json-schema":{"version":"7.0.9","dev":true,"integrity":"sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="},"@types/json5":{"version":"0.0.29","dev":true,"integrity":"sha1-7ihweulOEdK4J7y+UnC86n8+ce4="},"@types/keyv":{"version":"3.1.3","dev":true,"integrity":"sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==","requires":{"@types/node":"17.0.18"}},"@types/mdast":{"version":"3.0.10","dev":true,"integrity":"sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==","requires":{"@types/unist":"2.0.6"}},"@types/minimist":{"version":"1.2.2","dev":true,"integrity":"sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ=="},"@types/node":{"version":"17.0.18","dev":true,"integrity":"sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA=="},"@types/normalize-package-data":{"version":"2.4.1","dev":true,"integrity":"sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw=="},"@types/parse-json":{"version":"4.0.0","dev":true,"integrity":"sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="},"@types/responselike":{"version":"1.0.0","dev":true,"integrity":"sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==","requires":{"@types/node":"17.0.18"}},"@types/retry":{"version":"0.12.1","dev":true,"integrity":"sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g=="},"@types/unist":{"version":"2.0.6","dev":true,"integrity":"sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="},"@types/yargs":{"version":"17.0.8","dev":true,"integrity":"sha512-wDeUwiUmem9FzsyysEwRukaEdDNcwbROvQ9QGRKaLI6t+IltNzbn4/i4asmB10auvZGQCzSQ6t0GSczEThlUXw==","requires":{"@types/yargs-parser":"20.2.1"}},"@types/yargs-parser":{"version":"20.2.1","dev":true,"integrity":"sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="},"@typescript-eslint/eslint-plugin":{"version":"5.12.0","dev":true,"integrity":"sha512-fwCMkDimwHVeIOKeBHiZhRUfJXU8n6xW1FL9diDxAyGAFvKcH4csy0v7twivOQdQdA0KC8TDr7GGRd3L4Lv0rQ==","requires":{"@typescript-eslint/parser":"5.12.0","@typescript-eslint/scope-manager":"5.12.0","@typescript-eslint/type-utils":"5.12.0","@typescript-eslint/utils":"5.12.0","debug":"4.3.3","eslint":"8.9.0","functional-red-black-tree":"1.0.1","ignore":"5.2.0","regexpp":"3.2.0","semver":"7.3.5","tsutils":"3.21.0","typescript":"4.5.5"},"dependencies":{"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"@typescript-eslint/experimental-utils":{"version":"5.12.0","dev":true,"integrity":"sha512-iFVADWH2CmiDF+E9kFK2r474BO2JILDKw1NVD5ytqHrM3ezsfdu5uo6B+77DH0suM7iUC/yOayHNziuiI9BPbQ==","requires":{"@typescript-eslint/utils":"5.12.0","eslint":"8.9.0"}},"@typescript-eslint/parser":{"version":"5.12.0","dev":true,"integrity":"sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog==","requires":{"@typescript-eslint/scope-manager":"5.12.0","@typescript-eslint/types":"5.12.0","@typescript-eslint/typescript-estree":"5.12.0","debug":"4.3.3","eslint":"8.9.0","typescript":"4.5.5"},"dependencies":{"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"@typescript-eslint/scope-manager":{"version":"5.12.0","dev":true,"integrity":"sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==","requires":{"@typescript-eslint/types":"5.12.0","@typescript-eslint/visitor-keys":"5.12.0"}},"@typescript-eslint/type-utils":{"version":"5.12.0","dev":true,"integrity":"sha512-9j9rli3zEBV+ae7rlbBOotJcI6zfc6SHFMdKI9M3Nc0sy458LJ79Os+TPWeBBL96J9/e36rdJOfCuyRSgFAA0Q==","requires":{"@typescript-eslint/utils":"5.12.0","debug":"4.3.3","eslint":"8.9.0","tsutils":"3.21.0","typescript":"4.5.5"}},"@typescript-eslint/types":{"version":"5.12.0","dev":true,"integrity":"sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ=="},"@typescript-eslint/typescript-estree":{"version":"5.12.0","dev":true,"integrity":"sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ==","requires":{"@typescript-eslint/types":"5.12.0","@typescript-eslint/visitor-keys":"5.12.0","debug":"4.3.3","globby":"11.1.0","is-glob":"4.0.3","semver":"7.3.5","tsutils":"3.21.0","typescript":"4.5.5"}},"@typescript-eslint/utils":{"version":"5.12.0","dev":true,"integrity":"sha512-k4J2WovnMPGI4PzKgDtQdNrCnmBHpMUFy21qjX2CoPdoBcSBIMvVBr9P2YDP8jOqZOeK3ThOL6VO/sy6jtnvzw==","requires":{"@types/json-schema":"7.0.9","@typescript-eslint/scope-manager":"5.12.0","@typescript-eslint/types":"5.12.0","@typescript-eslint/typescript-estree":"5.12.0","eslint":"8.9.0","eslint-scope":"5.1.1","eslint-utils":"3.0.0"}},"@typescript-eslint/visitor-keys":{"version":"5.12.0","dev":true,"integrity":"sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==","requires":{"@typescript-eslint/types":"5.12.0","eslint-visitor-keys":"3.3.0"}},"@washingtondc/development":{"version":"1.0.10","dev":true,"integrity":"sha512-rO/ROg5jBNjx3HQIqsVDBWwOFD9sa6UwhsUphfwTcPCJLQ+bu0NFb3yGOpHvmbpU9AWtFS6ZcG2UHRvdPed01Q==","requires":{"@commitlint/cli":"15.0.0","@commitlint/config-conventional":"15.0.0","commitizen":"4.2.4","cspell":"5.13.1","cz-emoji-conventional":"1.0.1","git-notify":"0.2.3","husky":"7.0.4","leasot":"12.0.0","lint-staged":"12.1.2","liquidjs":"9.28.5","only-allow":"1.0.0","open-cli":"7.0.1","shellcheck":"1.0.0","typescript":"4.5.5","yarnhook":"0.5.1"},"dependencies":{"@commitlint/config-conventional":{"version":"15.0.0","dev":true,"integrity":"sha512-eZBRL8Lk3hMNHp1wUMYj0qrZQEsST1ai7KHR8J1IDD9aHgT7L2giciibuQ+Og7vxVhR5WtYDvh9xirXFVPaSkQ==","requires":{"conventional-changelog-conventionalcommits":"4.6.3"}},"glob":{"version":"7.1.4","dev":true,"integrity":"sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==","requires":{"fs.realpath":"1.0.0","inflight":"1.0.6","inherits":"2.0.4","minimatch":"3.1.2","once":"1.4.0","path-is-absolute":"1.0.1"}}}},"@xml-tools/parser":{"version":"1.0.11","dev":true,"integrity":"sha512-aKqQ077XnR+oQtHJlrAflaZaL7qZsulWc/i/ZEooar5JiWj1eLt0+Wg28cpa+XLney107wXqneC+oG1IZvxkTA==","requires":{"chevrotain":"7.1.1"}},"JSONStream":{"version":"1.3.5","dev":true,"integrity":"sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==","requires":{"jsonparse":"1.3.1","through":"2.3.8"}},"acorn":{"version":"8.7.0","dev":true,"integrity":"sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="},"acorn-jsx":{"version":"5.3.2","dev":true,"integrity":"sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==","requires":{"acorn":"8.7.0"}},"agent-base":{"version":"6.0.2","dev":true,"integrity":"sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==","requires":{"debug":"4.3.3"}},"aggregate-error":{"version":"3.1.0","dev":true,"integrity":"sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==","requires":{"clean-stack":"2.2.0","indent-string":"4.0.0"}},"ajv":{"version":"6.12.6","dev":true,"integrity":"sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==","requires":{"fast-deep-equal":"3.1.3","fast-json-stable-stringify":"2.1.0","json-schema-traverse":"0.4.1","uri-js":"4.4.1"}},"ansi-align":{"version":"3.0.1","dev":true,"integrity":"sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==","requires":{"string-width":"4.2.3"}},"ansi-bgblack":{"version":"0.1.1","dev":true,"integrity":"sha1-poulAHiHcBtqr74/oNrf36juPKI=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgblue":{"version":"0.1.1","dev":true,"integrity":"sha1-Z73ATtybm1J4lp2hlt6j11yMNhM=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgcyan":{"version":"0.1.1","dev":true,"integrity":"sha1-WEiUJWAL3p9VBwaN2Wnr/bUP52g=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bggreen":{"version":"0.1.1","dev":true,"integrity":"sha1-TjGRJIUplD9DIelr8THRwTgWr0k=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgmagenta":{"version":"0.1.1","dev":true,"integrity":"sha1-myhDLAduqpmUGGcqPvvhk5HCx6E=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgred":{"version":"0.1.1","dev":true,"integrity":"sha1-p2+Sg4OCukMpCmwXeEJPmE1vEEE=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgwhite":{"version":"0.1.1","dev":true,"integrity":"sha1-ZQRlE3elim7OzQMxmU5IAljhG6g=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgyellow":{"version":"0.1.1","dev":true,"integrity":"sha1-w/4usIzUdmSAKeaHTRWgs49h1E8=","requires":{"ansi-wrap":"0.1.0"}},"ansi-black":{"version":"0.1.1","dev":true,"integrity":"sha1-9hheiJNgslRaHsUMC/Bj/EMDJFM=","requires":{"ansi-wrap":"0.1.0"}},"ansi-blue":{"version":"0.1.1","dev":true,"integrity":"sha1-FbgEmQ6S/JyoxUds6PaZd3wh7b8=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bold":{"version":"0.1.1","dev":true,"integrity":"sha1-PmOVCvWswq4uZw5vZ96xFdGl9QU=","requires":{"ansi-wrap":"0.1.0"}},"ansi-colors":{"version":"4.1.1","dev":true,"integrity":"sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA=="},"ansi-cyan":{"version":"0.1.1","dev":true,"integrity":"sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=","requires":{"ansi-wrap":"0.1.0"}},"ansi-dim":{"version":"0.1.1","dev":true,"integrity":"sha1-QN5MYDqoCG2Oeoa4/5mNXDbu/Ww=","requires":{"ansi-wrap":"0.1.0"}},"ansi-escapes":{"version":"3.2.0","dev":true,"integrity":"sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="},"ansi-gray":{"version":"0.1.1","dev":true,"integrity":"sha1-KWLPVOyXksSFEKPetSRDaGHvclE=","requires":{"ansi-wrap":"0.1.0"}},"ansi-green":{"version":"0.1.1","dev":true,"integrity":"sha1-il2al55FjVfEDjNYCzc5C44Q0Pc=","requires":{"ansi-wrap":"0.1.0"}},"ansi-grey":{"version":"0.1.1","dev":true,"integrity":"sha1-WdmLasK6GfilF5jphT+6eDOaM8E=","requires":{"ansi-wrap":"0.1.0"}},"ansi-hidden":{"version":"0.1.1","dev":true,"integrity":"sha1-7WpMSY0rt8uyidvyqNHcyFZ/rg8=","requires":{"ansi-wrap":"0.1.0"}},"ansi-inverse":{"version":"0.1.1","dev":true,"integrity":"sha1-tq9Fgm/oJr+1KKbHmIV5Q1XM0mk=","requires":{"ansi-wrap":"0.1.0"}},"ansi-italic":{"version":"0.1.1","dev":true,"integrity":"sha1-EEdDRj9iXBQqA2c5z4XtpoiYbyM=","requires":{"ansi-wrap":"0.1.0"}},"ansi-magenta":{"version":"0.1.1","dev":true,"integrity":"sha1-BjtboW+z8j4c/aKwfAqJ3hHkMK4=","requires":{"ansi-wrap":"0.1.0"}},"ansi-red":{"version":"0.1.1","dev":true,"integrity":"sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=","requires":{"ansi-wrap":"0.1.0"}},"ansi-regex":{"version":"5.0.1","dev":true,"integrity":"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="},"ansi-reset":{"version":"0.1.1","dev":true,"integrity":"sha1-5+cSksPH3c1NYu9KbHwFmAkRw7c=","requires":{"ansi-wrap":"0.1.0"}},"ansi-strikethrough":{"version":"0.1.1","dev":true,"integrity":"sha1-2Eh3FAss/wfRyT685pkE9oiF5Wg=","requires":{"ansi-wrap":"0.1.0"}},"ansi-styles":{"version":"4.3.0","dev":true,"integrity":"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==","requires":{"color-convert":"2.0.1"}},"ansi-underline":{"version":"0.1.1","dev":true,"integrity":"sha1-38kg9Ml7WXfqFi34/7mIMIqqcaQ=","requires":{"ansi-wrap":"0.1.0"}},"ansi-white":{"version":"0.1.1","dev":true,"integrity":"sha1-nHe3wZPF7pkuYBHTbsTJIbRXiUQ=","requires":{"ansi-wrap":"0.1.0"}},"ansi-wrap":{"version":"0.1.0","dev":true,"integrity":"sha1-qCJQ3bABXponyoLoLqYDu/pF768="},"ansi-yellow":{"version":"0.1.1","dev":true,"integrity":"sha1-y5NW8vRscy8OMZnmEClVp32oPB0=","requires":{"ansi-wrap":"0.1.0"}},"ansicolors":{"version":"0.3.2","dev":true,"integrity":"sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk="},"antlr4ts":{"version":"0.5.0-alpha.4","dev":true,"integrity":"sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ=="},"arg":{"version":"4.1.3","dev":true,"integrity":"sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="},"argparse":{"version":"2.0.1","dev":true,"integrity":"sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="},"argv-formatter":{"version":"1.0.0","dev":true,"integrity":"sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk="},"aria-query":{"version":"4.2.2","dev":true,"integrity":"sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==","requires":{"@babel/runtime":"7.17.2","@babel/runtime-corejs3":"7.17.2"}},"arr-diff":{"version":"4.0.0","dev":true,"integrity":"sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="},"arr-flatten":{"version":"1.1.0","dev":true,"integrity":"sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="},"arr-union":{"version":"3.1.0","dev":true,"integrity":"sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="},"array-ify":{"version":"1.0.0","dev":true,"integrity":"sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4="},"array-includes":{"version":"3.1.4","dev":true,"integrity":"sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3","es-abstract":"1.19.1","get-intrinsic":"1.1.1","is-string":"1.0.7"}},"array-sort":{"version":"0.1.4","dev":true,"integrity":"sha512-BNcM+RXxndPxiZ2rd76k6nyQLRZr2/B/sdi8pQ+Joafr5AH279L40dfokSUTp8O+AaqYjXWhblBWa2st2nc4fQ==","requires":{"default-compare":"1.0.0","get-value":"2.0.6","kind-of":"5.1.0"},"dependencies":{"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}},"array-timsort":{"version":"1.0.3","dev":true,"integrity":"sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ=="},"array-union":{"version":"2.1.0","dev":true,"integrity":"sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="},"array-unique":{"version":"0.3.2","dev":true,"integrity":"sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="},"array.prototype.flat":{"version":"1.2.5","dev":true,"integrity":"sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3","es-abstract":"1.19.1"}},"arrify":{"version":"1.0.1","dev":true,"integrity":"sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="},"assign-symbols":{"version":"1.0.0","dev":true,"integrity":"sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="},"astral-regex":{"version":"2.0.0","dev":true,"integrity":"sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="},"async":{"version":"3.2.3","dev":true,"integrity":"sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="},"asynckit":{"version":"0.4.0","dev":true,"integrity":"sha1-x57Zf380y48robyXkLzDZkdLS3k="},"at-least-node":{"version":"1.0.0","dev":true,"integrity":"sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="},"atob":{"version":"2.1.2","dev":true,"integrity":"sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="},"author-regex":{"version":"1.0.0","dev":true,"integrity":"sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA="},"autolinker":{"version":"0.28.1","dev":true,"integrity":"sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=","requires":{"gulp-header":"1.8.12"}},"balanced-match":{"version":"1.0.2","dev":true,"integrity":"sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="},"base":{"version":"0.11.2","dev":true,"integrity":"sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==","requires":{"cache-base":"1.0.1","class-utils":"0.3.6","component-emitter":"1.3.0","define-property":"1.0.0","isobject":"3.0.1","mixin-deep":"1.3.2","pascalcase":"0.1.1"}},"before-after-hook":{"version":"2.2.2","dev":true,"integrity":"sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ=="},"bent":{"version":"7.3.12","dev":true,"integrity":"sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w==","requires":{"bytesish":"0.4.4","caseless":"0.12.0","is-stream":"2.0.1"}},"big-integer":{"version":"1.6.51","dev":true,"integrity":"sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg=="},"binary-search-bounds":{"version":"2.0.5","dev":true,"integrity":"sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA=="},"bottleneck":{"version":"2.19.5","dev":true,"integrity":"sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="},"boxen":{"version":"5.1.2","dev":true,"integrity":"sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==","requires":{"ansi-align":"3.0.1","camelcase":"6.3.0","chalk":"4.1.2","cli-boxes":"2.2.1","string-width":"4.2.3","type-fest":"0.20.2","widest-line":"3.1.0","wrap-ansi":"7.0.0"}},"brace-expansion":{"version":"1.1.11","dev":true,"integrity":"sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==","requires":{"balanced-match":"1.0.2","concat-map":"0.0.1"}},"braces":{"version":"3.0.2","dev":true,"integrity":"sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==","requires":{"fill-range":"7.0.1"}},"browserslist":{"version":"4.19.3","dev":true,"integrity":"sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==","requires":{"caniuse-lite":"1.0.30001312","electron-to-chromium":"1.4.71","escalade":"3.1.1","node-releases":"2.0.2","picocolors":"1.0.0"}},"buffer-from":{"version":"1.1.2","dev":true,"integrity":"sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="},"builtin-modules":{"version":"3.2.0","dev":true,"integrity":"sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA=="},"bytesish":{"version":"0.4.4","dev":true,"integrity":"sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ=="},"cache-base":{"version":"1.0.1","dev":true,"integrity":"sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==","requires":{"collection-visit":"1.0.0","component-emitter":"1.3.0","get-value":"2.0.6","has-value":"1.0.0","isobject":"3.0.1","set-value":"2.0.1","to-object-path":"0.3.0","union-value":"1.0.1","unset-value":"1.0.0"},"dependencies":{"isobject":{"version":"2.1.0","dev":true,"integrity":"sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=","requires":{"isarray":"1.0.0"}}}},"cacheable-lookup":{"version":"5.0.4","dev":true,"integrity":"sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="},"cacheable-request":{"version":"7.0.2","dev":true,"integrity":"sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==","requires":{"clone-response":"1.0.2","get-stream":"5.2.0","http-cache-semantics":"4.1.0","keyv":"4.1.1","lowercase-keys":"2.0.0","normalize-url":"6.1.0","responselike":"2.0.0"}},"cachedir":{"version":"2.2.0","dev":true,"integrity":"sha512-VvxA0xhNqIIfg0V9AmJkDg91DaJwryutH5rVEZAhcNi4iJFj9f+QxmAjgK1LT9I8OgToX27fypX6/MeCXVbBjQ=="},"call-bind":{"version":"1.0.2","dev":true,"integrity":"sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==","requires":{"function-bind":"1.1.1","get-intrinsic":"1.1.1"}},"callsites":{"version":"3.1.0","dev":true,"integrity":"sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="},"camelcase":{"version":"5.3.1","dev":true,"integrity":"sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="},"camelcase-keys":{"version":"6.2.2","dev":true,"integrity":"sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==","requires":{"camelcase":"5.3.1","map-obj":"4.3.0","quick-lru":"4.0.1"}},"caniuse-lite":{"version":"1.0.30001312","dev":true,"integrity":"sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ=="},"cardinal":{"version":"2.1.1","dev":true,"integrity":"sha1-fMEFXYItISlU0HsIXeolHMe8VQU=","requires":{"ansicolors":"0.3.2","redeyed":"2.1.1"}},"caseless":{"version":"0.12.0","dev":true,"integrity":"sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="},"chalk":{"version":"4.1.2","dev":true,"integrity":"sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==","requires":{"ansi-styles":"4.3.0","supports-color":"7.2.0"}},"character-entities":{"version":"1.2.4","dev":true,"integrity":"sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw=="},"character-entities-legacy":{"version":"1.1.4","dev":true,"integrity":"sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA=="},"character-parser":{"version":"2.2.0","dev":true,"integrity":"sha1-x84o821LzZdE5f/CxfzeHHMmH8A=","requires":{"is-regex":"1.1.4"}},"character-reference-invalid":{"version":"1.1.4","dev":true,"integrity":"sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg=="},"chardet":{"version":"0.7.0","dev":true,"integrity":"sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="},"chevrotain":{"version":"7.1.1","dev":true,"integrity":"sha512-wy3mC1x4ye+O+QkEinVJkPf5u2vsrDIYW9G7ZuwFl6v/Yu0LwUuT2POsb+NUWApebyxfkQq6+yDfRExbnI5rcw==","requires":{"regexp-to-ast":"0.5.0"}},"ci-info":{"version":"3.3.0","dev":true,"integrity":"sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw=="},"class-utils":{"version":"0.3.6","dev":true,"integrity":"sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==","requires":{"arr-union":"3.1.0","define-property":"0.2.5","isobject":"3.0.1","static-extend":"0.1.2"},"dependencies":{"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}},"clean-regexp":{"version":"1.0.0","dev":true,"integrity":"sha1-jffHquUf02h06PjQW5GAvBGj/tc=","requires":{"escape-string-regexp":"1.0.5"}},"clean-stack":{"version":"2.2.0","dev":true,"integrity":"sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="},"clear-module":{"version":"4.1.2","dev":true,"integrity":"sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==","requires":{"parent-module":"2.0.0","resolve-from":"5.0.0"}},"cli-boxes":{"version":"2.2.1","dev":true,"integrity":"sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw=="},"cli-cursor":{"version":"2.1.0","dev":true,"integrity":"sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=","requires":{"restore-cursor":"2.0.0"}},"cli-table3":{"version":"0.6.1","dev":true,"integrity":"sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==","requires":{"string-width":"4.2.3"}},"cli-truncate":{"version":"3.1.0","dev":true,"integrity":"sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==","requires":{"slice-ansi":"5.0.0","string-width":"5.1.0"}},"cli-width":{"version":"2.2.1","dev":true,"integrity":"sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="},"cliui":{"version":"7.0.4","dev":true,"integrity":"sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==","requires":{"string-width":"4.2.3","strip-ansi":"6.0.1","wrap-ansi":"7.0.0"}},"clone-response":{"version":"1.0.2","dev":true,"integrity":"sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=","requires":{"mimic-response":"1.0.1"}},"collection-visit":{"version":"1.0.0","dev":true,"integrity":"sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=","requires":{"map-visit":"1.0.0","object-visit":"1.0.1"}},"color-convert":{"version":"2.0.1","dev":true,"integrity":"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==","requires":{"color-name":"1.1.4"}},"color-name":{"version":"1.1.4","dev":true,"integrity":"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="},"colorette":{"version":"2.0.16","dev":true,"integrity":"sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g=="},"combined-stream":{"version":"1.0.8","dev":true,"integrity":"sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==","requires":{"delayed-stream":"1.0.0"}},"commander":{"version":"8.3.0","dev":true,"integrity":"sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="},"comment-json":{"version":"4.2.2","dev":true,"integrity":"sha512-H8T+kl3nZesZu41zO2oNXIJWojNeK3mHxCLrsBNu6feksBXsgb+PtYz5daP5P86A0F3sz3840KVYehr04enISQ==","requires":{"array-timsort":"1.0.3","core-util-is":"1.0.3","esprima":"4.0.1","has-own-prop":"2.0.0","repeat-string":"1.6.1"}},"comment-parser":{"version":"1.3.0","dev":true,"integrity":"sha512-hRpmWIKgzd81vn0ydoWoyPoALEOnF4wt8yKD35Ib1D6XC2siLiYaiqfGkYrunuKdsXGwpBpHU3+9r+RVw2NZfA=="},"commitizen":{"version":"4.2.4","dev":true,"integrity":"sha512-LlZChbDzg3Ir3O2S7jSo/cgWp5/QwylQVr59K4xayVq8S4/RdKzSyJkghAiZZHfhh5t4pxunUoyeg0ml1q/7aw==","requires":{"cachedir":"2.2.0","cz-conventional-changelog":"3.2.0","dedent":"0.7.0","detect-indent":"6.0.0","find-node-modules":"2.1.2","find-root":"1.1.0","fs-extra":"8.1.0","glob":"7.1.4","inquirer":"6.5.2","is-utf8":"0.2.1","lodash":"4.17.21","minimist":"1.2.5","strip-bom":"4.0.0","strip-json-comments":"3.0.1"},"dependencies":{"ansi-regex":{"version":"3.0.0","dev":true,"integrity":"sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="},"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"fs-extra":{"version":"8.1.0","dev":true,"integrity":"sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==","requires":{"graceful-fs":"4.2.9","jsonfile":"4.0.0","universalify":"0.1.2"}},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"is-fullwidth-code-point":{"version":"2.0.0","dev":true,"integrity":"sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="},"jsonfile":{"version":"4.0.0","dev":true,"integrity":"sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss="},"mimic-fn":{"version":"1.2.0","dev":true,"integrity":"sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="},"onetime":{"version":"2.0.1","dev":true,"integrity":"sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=","requires":{"mimic-fn":"1.2.0"}},"string-width":{"version":"2.1.1","dev":true,"integrity":"sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==","requires":{"is-fullwidth-code-point":"2.0.0","strip-ansi":"4.0.0"},"dependencies":{"strip-ansi":{"version":"4.0.0","dev":true,"integrity":"sha1-qEeQIusaw2iocTibY1JixQXuNo8=","requires":{"ansi-regex":"3.0.0"}}}},"strip-ansi":{"version":"5.2.0","dev":true,"integrity":"sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==","requires":{"ansi-regex":"4.1.0"},"dependencies":{"ansi-regex":{"version":"4.1.0","dev":true,"integrity":"sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="}}},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="},"universalify":{"version":"0.1.2","dev":true,"integrity":"sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="},"which":{"version":"1.3.1","dev":true,"integrity":"sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==","requires":{"isexe":"2.0.0"}}}},"common-tags":{"version":"1.8.2","dev":true,"integrity":"sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="},"compare-func":{"version":"2.0.0","dev":true,"integrity":"sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==","requires":{"array-ify":"1.0.0","dot-prop":"5.3.0"}},"component-emitter":{"version":"1.3.0","dev":true,"integrity":"sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="},"concat-map":{"version":"0.0.1","dev":true,"integrity":"sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="},"concat-with-sourcemaps":{"version":"1.1.0","dev":true,"integrity":"sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==","requires":{"source-map":"0.6.1"}},"configstore":{"version":"5.0.1","dev":true,"integrity":"sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==","requires":{"dot-prop":"5.3.0","graceful-fs":"4.2.9","make-dir":"3.1.0","unique-string":"2.0.0","write-file-atomic":"3.0.3","xdg-basedir":"4.0.0"}},"conventional-changelog-angular":{"version":"5.0.13","dev":true,"integrity":"sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==","requires":{"compare-func":"2.0.0","q":"1.5.1"}},"conventional-changelog-conventionalcommits":{"version":"4.6.3","dev":true,"integrity":"sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==","requires":{"compare-func":"2.0.0","lodash":"4.17.21","q":"1.5.1"}},"conventional-changelog-emoji-config":{"version":"1.4.8","dev":true,"integrity":"sha512-YssHA4Xcbha2h+tbq25f3apS6AsLSQSQJVPcgic5i09DNfiOnWhxdBIn/GcmoT+TS7+DlzCMufE2j6dMiuYOWg==","requires":{"conventional-changelog-gitmoji-config":"1.4.3","conventional-changelog-writer":"5.0.1","conventional-commits-parser":"3.2.4","cosmiconfig":"7.0.1","git-cz-emoji":"1.1.24","semantic-release":"19.0.2","tslib":"2.3.1"},"dependencies":{"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"conventional-changelog-gitmoji-config":{"version":"1.4.3","dev":true,"integrity":"sha512-8HqbhNFuya85ICZvXv1ck2a2B7KlSVPGEFtK+Jar/qCUuq+E0PiINBJbpYcHOfsEucDRW4W4AUMQH7ap6+0DdQ==","requires":{"@gitmoji/commit-types":"1.1.5","@gitmoji/parser-opts":"1.3.0","cosmiconfig":"7.0.1"}},"conventional-changelog-writer":{"version":"5.0.1","dev":true,"integrity":"sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==","requires":{"conventional-commits-filter":"2.0.7","dateformat":"3.0.3","handlebars":"4.7.7","json-stringify-safe":"5.0.1","lodash":"4.17.21","meow":"8.1.2","semver":"6.3.0","split":"1.0.1","through2":"4.0.2"}},"conventional-commit-types":{"version":"3.0.0","dev":true,"integrity":"sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg=="},"conventional-commits-filter":{"version":"2.0.7","dev":true,"integrity":"sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==","requires":{"lodash.ismatch":"4.4.0","modify-values":"1.0.1"}},"conventional-commits-parser":{"version":"3.2.4","dev":true,"integrity":"sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==","requires":{"JSONStream":"1.3.5","is-text-path":"1.0.1","lodash":"4.17.21","meow":"8.1.2","split2":"3.2.2","through2":"4.0.2"}},"convert-source-map":{"version":"1.8.0","dev":true,"integrity":"sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==","requires":{"safe-buffer":"5.1.2"}},"copy-descriptor":{"version":"0.1.1","dev":true,"integrity":"sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="},"core-js":{"version":"3.21.1","dev":true,"integrity":"sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig=="},"core-js-pure":{"version":"3.21.1","dev":true,"integrity":"sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ=="},"core-util-is":{"version":"1.0.3","dev":true,"integrity":"sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="},"cosmiconfig":{"version":"7.0.1","dev":true,"integrity":"sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==","requires":{"@types/parse-json":"4.0.0","import-fresh":"3.3.0","parse-json":"5.2.0","path-type":"4.0.0","yaml":"1.10.2"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"create-eslint-index":{"version":"1.0.0","dev":true,"integrity":"sha1-2VQ3LYbVeS/NZ+nyt5GxqxYkEbs=","requires":{"lodash.get":"4.4.2"}},"create-frame":{"version":"1.0.0","dev":true,"integrity":"sha1-i5XyaR4ySbYIBEPjPQutn49pdao=","requires":{"define-property":"0.2.5","extend-shallow":"2.0.1","isobject":"3.0.1","lazy-cache":"2.0.2"},"dependencies":{"define-property":{"version":"0.2.5","dev":true,"integrity":"sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=","requires":{"is-descriptor":"0.1.6"}},"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}},"create-require":{"version":"1.1.1","dev":true,"integrity":"sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="},"cross-spawn":{"version":"7.0.3","dev":true,"integrity":"sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==","requires":{"path-key":"3.1.1","shebang-command":"2.0.0","which":"2.0.2"}},"crypto-random-string":{"version":"2.0.0","dev":true,"integrity":"sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="},"cspell":{"version":"5.13.1","dev":true,"integrity":"sha512-uH0JpQEdMmc5peRXCUuF6Vdp8Bn5mD9YdzUb3Gxd/korSHjxf2nW+c6humaSfW2sHGf67fsyKwlde5jT2HHTyw==","requires":{"chalk":"4.1.2","commander":"8.3.0","comment-json":"4.2.2","cspell-gitignore":"5.18.5","cspell-glob":"5.18.5","cspell-lib":"5.18.5","fast-json-stable-stringify":"2.1.0","file-entry-cache":"6.0.1","fs-extra":"10.0.0","get-stdin":"8.0.0","glob":"7.2.0","imurmurhash":"0.1.4","strip-ansi":"6.0.1","vscode-uri":"3.0.3"},"dependencies":{"find-up":{"version":"5.0.0","dev":true,"integrity":"sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==","requires":{"locate-path":"6.0.0","path-exists":"4.0.0"}},"locate-path":{"version":"6.0.0","dev":true,"integrity":"sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==","requires":{"p-locate":"5.0.0"}},"p-limit":{"version":"3.1.0","dev":true,"integrity":"sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==","requires":{"yocto-queue":"0.1.0"}},"p-locate":{"version":"5.0.0","dev":true,"integrity":"sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==","requires":{"p-limit":"3.1.0"}},"parent-module":{"version":"2.0.0","dev":true,"integrity":"sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==","requires":{"callsites":"3.1.0"}},"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"cspell-gitignore":{"version":"5.18.5","dev":true,"integrity":"sha512-YKYFYMswkia0Uc5CMOapLwo8OKRfP+QbqyODTJ7IJACT7KCOSEj7A3B250LR2mWyvThyIUB+2c7+6ePdFCnh2g==","requires":{"cspell-glob":"5.18.5","find-up":"5.0.0"}},"cspell-glob":{"version":"5.18.5","dev":true,"integrity":"sha512-Tr/wMHpJ5zvD4qV4d5is1WJ6OQZSQSjiWoLCQ8pslpltGJhjYXPh3W9A8n4Ghr4AUUJNLKEQyCX+Z1kcA3hgOQ==","requires":{"micromatch":"4.0.4"}},"cspell-io":{"version":"5.18.5","dev":true,"integrity":"sha512-Ar2shXmKtLP935Linv+162xY6SNqIrwLI3rBRXs0/KnD/YdcLJQB0iBgFqvfvg7TcPg+EZOf9Oc6EvTLg2eprg=="},"cspell-lib":{"version":"5.18.5","dev":true,"integrity":"sha512-yrUk3MbRXy/YGNIcLfURDnw4fRiXcbHo9K5B6IhwYfHKc3VM6QgvEQ0ce44uzZ+AEZzWuQ++GbhUih+bSJ87DQ==","requires":{"@cspell/cspell-bundled-dicts":"5.18.5","@cspell/cspell-types":"5.18.5","clear-module":"4.1.2","comment-json":"4.2.2","configstore":"5.0.1","cosmiconfig":"7.0.1","cspell-glob":"5.18.5","cspell-io":"5.18.5","cspell-trie-lib":"5.18.5","fast-equals":"3.0.0","find-up":"5.0.0","fs-extra":"10.0.0","gensequence":"3.1.1","import-fresh":"3.3.0","resolve-from":"5.0.0","resolve-global":"1.0.0","vscode-uri":"3.0.3"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"cspell-trie-lib":{"version":"5.18.5","dev":true,"integrity":"sha512-FifImmkcArPYiE8fLXcbB/yS15QyWwvHw/gpCPEkcuJMJH2gxC+HOE909JnBsyPyjCaX5gHWiIf7ePjdXlWsDg==","requires":{"@cspell/cspell-pipe":"5.18.5","fs-extra":"10.0.0","gensequence":"3.1.1"}},"cz-conventional-changelog":{"version":"3.2.0","dev":true,"integrity":"sha512-yAYxeGpVi27hqIilG1nh4A9Bnx4J3Ov+eXy4koL3drrR+IO9GaWPsKjik20ht608Asqi8TQPf0mczhEeyAtMzg==","requires":{"chalk":"2.4.2","commitizen":"4.2.4","conventional-commit-types":"3.0.0","lodash.map":"4.6.0","longest":"2.0.1","word-wrap":"1.2.3"}},"cz-emoji-conventional":{"version":"1.0.1","dev":true,"integrity":"sha512-jY+jmmbQ9n671gLWSSI34a7efDz1YPfzM3QZC5T+r8CJCCo1/myW8Ik09NV9KQ5hVSc2BBfvcq7e5IsBOZjyjg==","requires":{"chalk":"4.1.2","commitizen":"4.2.4","word-wrap":"1.2.3"},"dependencies":{"ansi-regex":{"version":"3.0.0","dev":true,"integrity":"sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="},"fs-extra":{"version":"8.1.0","dev":true,"integrity":"sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==","requires":{"graceful-fs":"4.2.9","jsonfile":"4.0.0","universalify":"0.1.2"}},"glob":{"version":"7.1.4","dev":true,"integrity":"sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==","requires":{"fs.realpath":"1.0.0","inflight":"1.0.6","inherits":"2.0.4","minimatch":"3.1.2","once":"1.4.0","path-is-absolute":"1.0.1"}},"is-fullwidth-code-point":{"version":"2.0.0","dev":true,"integrity":"sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="},"jsonfile":{"version":"4.0.0","dev":true,"integrity":"sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss="},"mimic-fn":{"version":"1.2.0","dev":true,"integrity":"sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="},"onetime":{"version":"2.0.1","dev":true,"integrity":"sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=","requires":{"mimic-fn":"1.2.0"}},"string-width":{"version":"2.1.1","dev":true,"integrity":"sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==","requires":{"is-fullwidth-code-point":"2.0.0","strip-ansi":"4.0.0"},"dependencies":{"strip-ansi":{"version":"4.0.0","dev":true,"integrity":"sha1-qEeQIusaw2iocTibY1JixQXuNo8=","requires":{"ansi-regex":"3.0.0"}}}},"strip-ansi":{"version":"5.2.0","dev":true,"integrity":"sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==","requires":{"ansi-regex":"4.1.0"},"dependencies":{"ansi-regex":{"version":"4.1.0","dev":true,"integrity":"sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="}}},"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="},"universalify":{"version":"0.1.2","dev":true,"integrity":"sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="},"which":{"version":"1.3.1","dev":true,"integrity":"sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==","requires":{"isexe":"2.0.0"}}}},"dargs":{"version":"7.0.0","dev":true,"integrity":"sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg=="},"date.js":{"version":"0.3.3","dev":true,"integrity":"sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw==","requires":{"debug":"3.1.0"}},"dateformat":{"version":"3.0.3","dev":true,"integrity":"sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="},"debug":{"version":"4.3.3","dev":true,"integrity":"sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==","requires":{"ms":"2.1.2"}},"decamelize":{"version":"1.2.0","dev":true,"integrity":"sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="},"decamelize-keys":{"version":"1.1.0","dev":true,"integrity":"sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=","requires":{"decamelize":"1.2.0","map-obj":"1.0.1"},"dependencies":{"map-obj":{"version":"1.0.1","dev":true,"integrity":"sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0="}}},"decode-uri-component":{"version":"0.2.0","dev":true,"integrity":"sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="},"decompress-response":{"version":"6.0.0","dev":true,"integrity":"sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==","requires":{"mimic-response":"3.1.0"},"dependencies":{"mimic-response":{"version":"3.1.0","dev":true,"integrity":"sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="}}},"dedent":{"version":"0.7.0","dev":true,"integrity":"sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw="},"deep-extend":{"version":"0.6.0","dev":true,"integrity":"sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="},"deep-is":{"version":"0.1.4","dev":true,"integrity":"sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="},"deepmerge-ts":{"version":"2.0.1","dev":true,"integrity":"sha512-7xeG0xMleW+gyrtUsdOeR6tCLwkyYDh3koIuvc8TxBcDh3WlaBQiEbFwEzk8clKomJZMhmoyxo7Y9CRrrrLVlg==","requires":{"is-plain-object":"5.0.0"}},"default-compare":{"version":"1.0.0","dev":true,"integrity":"sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==","requires":{"kind-of":"5.1.0"}},"defer-to-connect":{"version":"2.0.1","dev":true,"integrity":"sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="},"define-lazy-prop":{"version":"2.0.0","dev":true,"integrity":"sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="},"define-properties":{"version":"1.1.3","dev":true,"integrity":"sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==","requires":{"object-keys":"1.1.1"}},"define-property":{"version":"1.0.0","dev":true,"integrity":"sha1-dp66rz9KY6rTr56NMEybvnm/sOY=","requires":{"is-descriptor":"1.0.2"},"dependencies":{"is-accessor-descriptor":{"version":"1.0.0","dev":true,"integrity":"sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==","requires":{"kind-of":"6.0.3"}},"is-data-descriptor":{"version":"1.0.0","dev":true,"integrity":"sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==","requires":{"kind-of":"6.0.3"}},"is-descriptor":{"version":"1.0.2","dev":true,"integrity":"sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==","requires":{"is-accessor-descriptor":"1.0.0","is-data-descriptor":"1.0.0","kind-of":"6.0.3"}}}},"del":{"version":"6.0.0","dev":true,"integrity":"sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==","requires":{"globby":"11.1.0","graceful-fs":"4.2.9","is-glob":"4.0.3","is-path-cwd":"2.2.0","is-path-inside":"3.0.3","p-map":"4.0.0","rimraf":"3.0.2","slash":"3.0.0"}},"delayed-stream":{"version":"1.0.0","dev":true,"integrity":"sha1-3zrhmayt+31ECqrgsp4icrJOxhk="},"deprecation":{"version":"2.3.1","dev":true,"integrity":"sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="},"detect-file":{"version":"1.0.0","dev":true,"integrity":"sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc="},"detect-indent":{"version":"6.0.0","dev":true,"integrity":"sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA=="},"detect-newline":{"version":"3.1.0","dev":true,"integrity":"sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA=="},"diff":{"version":"4.0.2","dev":true,"integrity":"sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="},"dir-glob":{"version":"3.0.1","dev":true,"integrity":"sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==","requires":{"path-type":"4.0.0"}},"doctrine":{"version":"3.0.0","dev":true,"integrity":"sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==","requires":{"esutils":"2.0.3"}},"dom-accessibility-api":{"version":"0.5.11","dev":true,"integrity":"sha512-7X6GvzjYf4yTdRKuCVScV+aA9Fvh5r8WzWrXBH9w82ZWB/eYDMGCnazoC/YAqAzUJWHzLOnZqr46K3iEyUhUvw=="},"dom-serializer":{"version":"1.3.2","dev":true,"integrity":"sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==","requires":{"domelementtype":"2.2.0","domhandler":"4.3.0","entities":"2.2.0"}},"domelementtype":{"version":"2.2.0","dev":true,"integrity":"sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="},"domhandler":{"version":"4.3.0","dev":true,"integrity":"sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==","requires":{"domelementtype":"2.2.0"}},"domutils":{"version":"2.8.0","dev":true,"integrity":"sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==","requires":{"dom-serializer":"1.3.2","domelementtype":"2.2.0","domhandler":"4.3.0"},"dependencies":{"entities":{"version":"2.2.0","dev":true,"integrity":"sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="}}},"dot-prop":{"version":"5.3.0","dev":true,"integrity":"sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==","requires":{"is-obj":"2.0.0"}},"dot-properties":{"version":"1.0.1","dev":true,"integrity":"sha512-cjIHHKlf2dPINJ5Io3lPocWvWmthXn3ztqyHVzUfufRiCiPECb0oiEqEGbEGaunFZtcMvwgUcxP9CTpLG4KCsA=="},"duplexer2":{"version":"0.1.4","dev":true,"integrity":"sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=","requires":{"readable-stream":"2.3.7"}},"eastasianwidth":{"version":"0.2.0","dev":true,"integrity":"sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="},"editorconfig":{"version":"0.15.3","dev":true,"integrity":"sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==","requires":{"commander":"2.20.3","lru-cache":"4.1.5","semver":"5.7.1","sigmund":"1.0.1"}},"electron-to-chromium":{"version":"1.4.71","dev":true,"integrity":"sha512-Hk61vXXKRb2cd3znPE9F+2pLWdIOmP7GjiTj45y6L3W/lO+hSnUSUhq+6lEaERWBdZOHbk2s3YV5c9xVl3boVw=="},"emoji-regex":{"version":"8.0.0","dev":true,"integrity":"sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="},"end-of-stream":{"version":"1.4.4","dev":true,"integrity":"sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==","requires":{"once":"1.4.0"}},"enquirer":{"version":"2.3.6","dev":true,"integrity":"sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==","requires":{"ansi-colors":"4.1.1"}},"ent":{"version":"2.2.0","dev":true,"integrity":"sha1-6WQhkyWiHQX0RGai9obtbOX13R0="},"entities":{"version":"3.0.1","dev":true,"integrity":"sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q=="},"env-ci":{"version":"5.5.0","dev":true,"integrity":"sha512-o0JdWIbOLP+WJKIUt36hz1ImQQFuN92nhsfTkHHap+J8CiI8WgGpH/a9jEGHh4/TU5BUUGjlnKXNoDb57+ne+A==","requires":{"execa":"5.1.1","fromentries":"1.3.2","java-properties":"1.0.2"}},"eol":{"version":"0.9.1","dev":true,"integrity":"sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg=="},"error-ex":{"version":"1.3.2","dev":true,"integrity":"sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==","requires":{"is-arrayish":"0.2.1"}},"error-symbol":{"version":"0.1.0","dev":true,"integrity":"sha1-Ck2uN9YA0VopukU9jvkg8YRDM/Y="},"es-abstract":{"version":"1.19.1","dev":true,"integrity":"sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==","requires":{"call-bind":"1.0.2","es-to-primitive":"1.2.1","function-bind":"1.1.1","get-intrinsic":"1.1.1","get-symbol-description":"1.0.0","has":"1.0.3","has-symbols":"1.0.2","internal-slot":"1.0.3","is-callable":"1.2.4","is-negative-zero":"2.0.2","is-regex":"1.1.4","is-shared-array-buffer":"1.0.1","is-string":"1.0.7","is-weakref":"1.0.2","object-inspect":"1.12.0","object-keys":"1.1.1","object.assign":"4.1.2","string.prototype.trimend":"1.0.4","string.prototype.trimstart":"1.0.4","unbox-primitive":"1.0.1"}},"es-to-primitive":{"version":"1.2.1","dev":true,"integrity":"sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==","requires":{"is-callable":"1.2.4","is-date-object":"1.0.5","is-symbol":"1.0.4"}},"escalade":{"version":"3.1.1","dev":true,"integrity":"sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="},"escape-string-regexp":{"version":"1.0.5","dev":true,"integrity":"sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="},"eslint":{"version":"8.9.0","dev":true,"integrity":"sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==","requires":{"@eslint/eslintrc":"1.1.0","@humanwhocodes/config-array":"0.9.3","ajv":"6.12.6","chalk":"4.1.2","cross-spawn":"7.0.3","debug":"4.3.3","doctrine":"3.0.0","escape-string-regexp":"4.0.0","eslint-scope":"7.1.1","eslint-utils":"3.0.0","eslint-visitor-keys":"3.3.0","espree":"9.3.1","esquery":"1.4.0","esutils":"2.0.3","fast-deep-equal":"3.1.3","file-entry-cache":"6.0.1","functional-red-black-tree":"1.0.1","glob-parent":"6.0.2","globals":"13.12.1","ignore":"5.2.0","import-fresh":"3.3.0","imurmurhash":"0.1.4","is-glob":"4.0.3","js-yaml":"4.1.0","json-stable-stringify-without-jsonify":"1.0.1","levn":"0.4.1","lodash.merge":"4.6.2","minimatch":"3.1.2","natural-compare":"1.4.0","optionator":"0.9.1","regexpp":"3.2.0","strip-ansi":"6.0.1","strip-json-comments":"3.1.1","text-table":"0.2.0","v8-compile-cache":"2.3.0"},"dependencies":{"eslint-scope":{"version":"7.1.1","dev":true,"integrity":"sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==","requires":{"esrecurse":"4.3.0","estraverse":"5.3.0"}},"estraverse":{"version":"5.3.0","dev":true,"integrity":"sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="},"glob-parent":{"version":"6.0.2","dev":true,"integrity":"sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==","requires":{"is-glob":"4.0.3"}}}},"eslint-ast-utils":{"version":"1.1.0","dev":true,"integrity":"sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==","requires":{"lodash.get":"4.4.2","lodash.zip":"4.2.0"}},"eslint-config-prettier":{"version":"8.4.0","dev":true,"integrity":"sha512-CFotdUcMY18nGRo5KGsnNxpznzhkopOcOo0InID+sgQssPrzjvsyKZPvOgymTFeHrFuC3Tzdf2YndhXtULK9Iw==","requires":{"eslint":"8.9.0"}},"eslint-config-strict-mode":{"version":"1.0.44","dev":true,"integrity":"sha512-kM+KvUGpx6/Uow0d2+wCVLjVMtphht+JT6r7lpiysj51ci7PnppUQV422GDYO5D9O4q43P7H8wdsMblOGwbM+Q==","requires":{"@typescript-eslint/eslint-plugin":"5.12.0","@typescript-eslint/parser":"5.12.0","eslint":"8.9.0","eslint-config-prettier":"8.4.0","eslint-formatter-git-log":"0.5.3","eslint-formatter-gitlab":"3.0.0","eslint-formatter-pretty":"4.1.0","eslint-formatter-summary":"1.1.0","eslint-plugin-angular":"4.1.0","eslint-plugin-array-func":"3.1.7","eslint-plugin-editorconfig":"3.2.0","eslint-plugin-eslint-comments":"3.2.0","eslint-plugin-etc":"2.0.2","eslint-plugin-ext":"0.1.0","eslint-plugin-filenames":"1.3.2","eslint-plugin-fp":"2.3.0","eslint-plugin-functional":"4.2.0","eslint-plugin-html":"6.2.0","eslint-plugin-import":"2.25.4","eslint-plugin-jest":"25.7.0","eslint-plugin-jest-async":"1.0.3","eslint-plugin-jest-dom":"3.9.4","eslint-plugin-jest-formatting":"3.1.0","eslint-plugin-jsdoc":"37.9.4","eslint-plugin-json-schema-validator":"1.2.49","eslint-plugin-jsonc":"1.7.0","eslint-plugin-no-constructor-bind":"2.0.4","eslint-plugin-no-explicit-type-exports":"0.12.1","eslint-plugin-no-secrets":"0.8.9","eslint-plugin-no-unsanitized":"3.2.0","eslint-plugin-no-use-extend-native":"0.5.0","eslint-plugin-node":"11.1.0","eslint-plugin-optimize-regex":"1.2.1","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-prettier":"4.0.0","eslint-plugin-promise":"5.2.0","eslint-plugin-regexp":"1.5.1","eslint-plugin-rxjs":"4.0.4","eslint-plugin-security":"1.4.0","eslint-plugin-sonarjs":"0.10.0","eslint-plugin-sort-class-members":"1.14.1","eslint-plugin-sort-keys-fix":"1.1.2","eslint-plugin-switch-case":"1.1.2","eslint-plugin-toml":"0.2.0","eslint-plugin-tsdoc":"0.2.14","eslint-plugin-typescript-sort-keys":"2.1.0","eslint-plugin-unicorn":"37.0.1","eslint-plugin-unused-imports":"1.1.5","eslint-plugin-woke":"1.0.1","eslint-plugin-yml":"0.10.1","parse-gitignore":"1.0.1","tslib":"2.3.1","yaml":"1.10.2"},"dependencies":{"ansi-escapes":{"version":"4.3.2","dev":true,"integrity":"sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==","requires":{"type-fest":"0.21.3"}},"commander":{"version":"2.20.3","dev":true,"integrity":"sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="},"decamelize":{"version":"5.0.1","dev":true,"integrity":"sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA=="},"escape-string-regexp":{"version":"4.0.0","dev":true,"integrity":"sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="},"find-up":{"version":"2.1.0","dev":true,"integrity":"sha1-RdG35QbHF93UgndaK3eSCjwMV6c=","requires":{"locate-path":"2.0.0"}},"hosted-git-info":{"version":"2.8.9","dev":true,"integrity":"sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="},"locate-path":{"version":"2.0.0","dev":true,"integrity":"sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=","requires":{"p-locate":"2.0.0","path-exists":"3.0.0"}},"normalize-package-data":{"version":"2.5.0","dev":true,"integrity":"sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==","requires":{"hosted-git-info":"2.8.9","resolve":"1.22.0","semver":"5.7.1","validate-npm-package-license":"3.0.4"}},"p-limit":{"version":"1.3.0","dev":true,"integrity":"sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==","requires":{"p-try":"1.0.0"}},"p-locate":{"version":"2.0.0","dev":true,"integrity":"sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=","requires":{"p-limit":"1.3.0"}},"p-try":{"version":"1.0.0","dev":true,"integrity":"sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="},"path-exists":{"version":"3.0.0","dev":true,"integrity":"sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="},"resolve-from":{"version":"4.0.0","dev":true,"integrity":"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="},"source-map":{"version":"0.5.7","dev":true,"integrity":"sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="},"strip-bom":{"version":"3.0.0","dev":true,"integrity":"sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="},"strip-json-comments":{"version":"3.1.1","dev":true,"integrity":"sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="},"type-fest":{"version":"0.20.2","dev":true,"integrity":"sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="},"yargs-parser":{"version":"21.0.0","dev":true,"integrity":"sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA=="}}},"eslint-etc":{"version":"5.1.0","dev":true,"integrity":"sha512-Rmjl01h5smi5cbsFne2xpTuch2xNnwXiX2lbS4HttXUN5FwXKAwG1UEFBVGO1nC091YO/QyVahyfNPJSX2ae+g==","requires":{"@typescript-eslint/experimental-utils":"5.12.0","eslint":"8.9.0","tsutils":"3.21.0","tsutils-etc":"1.4.1","typescript":"4.5.5"}},"eslint-formatter-git-log":{"version":"0.5.3","dev":true,"integrity":"sha512-lwYPyg6fq6IQyNwLHkls1sjIXNWvY6RQx8S8hLTcgC+ldKYHd8Dc0G1qBpbQXoBFBrUmz73ZNyP1lMsAP8A33A==","requires":{"chalk":"2.4.2","eslint":"8.9.0"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"eslint-formatter-gitlab":{"version":"3.0.0","dev":true,"integrity":"sha512-fqZ2G45rgbrHcFunqmwuG5Qo6QAOlxEsR+KdOP08T1Xegw5tJhHh9KFWMSct8q6x8xCMUyYGHypZd342bLUttA==","requires":{"chalk":"4.1.2","eslint":"8.9.0","js-yaml":"4.1.0"}},"eslint-formatter-pretty":{"version":"4.1.0","dev":true,"integrity":"sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==","requires":{"@types/eslint":"7.29.0","ansi-escapes":"4.3.2","chalk":"4.1.2","eslint-rule-docs":"1.1.231","log-symbols":"4.1.0","plur":"4.0.0","string-width":"4.2.3","supports-hyperlinks":"2.2.0"},"dependencies":{"type-fest":{"version":"0.21.3","dev":true,"integrity":"sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="}}},"eslint-formatter-summary":{"version":"1.1.0","dev":true,"integrity":"sha512-teOh471ZYeEShXhBFb16X87buUjNZa2TMNn3CSf//DIKLNneqbk7u5i9hDDiIaQVvZbBXJHkgYxj8urcNKWbXw==","requires":{"chalk":"4.1.2","core-js":"3.21.1"}},"eslint-import-resolver-node":{"version":"0.3.6","dev":true,"integrity":"sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==","requires":{"debug":"3.2.7","resolve":"1.22.0"},"dependencies":{"debug":{"version":"3.2.7","dev":true,"integrity":"sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==","requires":{"ms":"2.1.3"}},"ms":{"version":"2.1.3","dev":true,"integrity":"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="}}},"eslint-module-utils":{"version":"2.7.3","dev":true,"integrity":"sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==","requires":{"debug":"3.2.7","find-up":"2.1.0"},"dependencies":{"debug":{"version":"3.2.7","dev":true,"integrity":"sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==","requires":{"ms":"2.1.3"}},"ms":{"version":"2.1.3","dev":true,"integrity":"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="}}},"eslint-plugin-angular":{"version":"4.1.0","dev":true,"integrity":"sha512-dacledMPxVOZA3T0xcYFuvrMCy5dHxg0ZTMWUaHqSBQef3/XLyXJ9s1LNj0NikJ/dYx6OhqlnnNpKmrJhEUB+Q=="},"eslint-plugin-array-func":{"version":"3.1.7","dev":true,"integrity":"sha512-fB5TBICjHSTGToNTbCCgR8zsngpUkoCM31EMh/M/NEAyNg90i5rUuG0dnNNBML2n0BzM0nBE3sPvo2SEWf6jlA==","requires":{"eslint":"8.9.0"}},"eslint-plugin-editorconfig":{"version":"3.2.0","dev":true,"integrity":"sha512-XiUg69+qgv6BekkPCjP8+2DMODzPqtLV5i0Q9FO1v40P62pfodG1vjIihVbw/338hS5W26S+8MTtXaAlrg37QQ==","requires":{"@typescript-eslint/eslint-plugin":"5.12.0","editorconfig":"0.15.3","eslint":"8.9.0","klona":"2.0.5"},"dependencies":{"lru-cache":{"version":"4.1.5","dev":true,"integrity":"sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==","requires":{"pseudomap":"1.0.2","yallist":"2.1.2"}},"semver":{"version":"5.7.1","dev":true,"integrity":"sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="},"yallist":{"version":"2.1.2","dev":true,"integrity":"sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="}}},"eslint-plugin-es":{"version":"3.0.1","dev":true,"integrity":"sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==","requires":{"eslint":"8.9.0","eslint-utils":"2.1.0","regexpp":"3.2.0"}},"eslint-plugin-eslint-comments":{"version":"3.2.0","dev":true,"integrity":"sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==","requires":{"escape-string-regexp":"1.0.5","eslint":"8.9.0","ignore":"5.2.0"}},"eslint-plugin-etc":{"version":"2.0.2","dev":true,"integrity":"sha512-g3b95LCdTCwZA8On9EICYL8m1NMWaiGfmNUd/ftZTeGZDXrwujKXUr+unYzqKjKFo1EbqJ31vt+Dqzrdm/sUcw==","requires":{"@phenomnomnominal/tsquery":"4.2.0","@typescript-eslint/experimental-utils":"5.12.0","eslint":"8.9.0","eslint-etc":"5.1.0","requireindex":"1.2.0","tslib":"2.3.1","tsutils":"3.21.0","typescript":"4.5.5"},"dependencies":{"estraverse":{"version":"5.3.0","dev":true,"integrity":"sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="},"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-ext":{"version":"0.1.0","dev":true,"integrity":"sha512-CbZgte+kC8u6uymkwtgDPHLgA3IRbhermH88o9VXDh4Pa1ds1QIo0ojJc+mvq5zjf3mm4GT/pTTFYZT9nQORyg=="},"eslint-plugin-filenames":{"version":"1.3.2","dev":true,"integrity":"sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==","requires":{"eslint":"8.9.0","lodash.camelcase":"4.3.0","lodash.kebabcase":"4.1.1","lodash.snakecase":"4.1.1","lodash.upperfirst":"4.3.1"}},"eslint-plugin-fp":{"version":"2.3.0","dev":true,"integrity":"sha1-N20qEIcQ6YGYC9w4deO5kg2gSJw=","requires":{"create-eslint-index":"1.0.0","eslint":"8.9.0","eslint-ast-utils":"1.1.0","lodash":"4.17.21","req-all":"0.1.0"}},"eslint-plugin-functional":{"version":"4.2.0","dev":true,"integrity":"sha512-3v1DuKQTGwJo93UQ5SKzEjvJTaMGfznzwgGjWEBhLXxJfOMhcW7O6QUO1pmb5aLou9hoh7r31lkPvWmbIbIbew==","requires":{"@typescript-eslint/experimental-utils":"5.12.0","deepmerge-ts":"2.0.1","escape-string-regexp":"4.0.0","eslint":"8.9.0","typescript":"4.5.5"},"dependencies":{"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-html":{"version":"6.2.0","dev":true,"integrity":"sha512-vi3NW0E8AJombTvt8beMwkL1R/fdRWl4QSNRNMhVQKWm36/X0KF0unGNAY4mqUF06mnwVWZcIcerrCnfn9025g==","requires":{"htmlparser2":"7.2.0"}},"eslint-plugin-import":{"version":"2.25.4","dev":true,"integrity":"sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==","requires":{"array-includes":"3.1.4","array.prototype.flat":"1.2.5","debug":"2.6.9","doctrine":"2.1.0","eslint":"8.9.0","eslint-import-resolver-node":"0.3.6","eslint-module-utils":"2.7.3","has":"1.0.3","is-core-module":"2.8.1","is-glob":"4.0.3","minimatch":"3.1.2","object.values":"1.1.5","resolve":"1.22.0","tsconfig-paths":"3.12.0"},"dependencies":{"debug":{"version":"2.6.9","dev":true,"integrity":"sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==","requires":{"ms":"2.0.0"}},"doctrine":{"version":"2.1.0","dev":true,"integrity":"sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==","requires":{"esutils":"2.0.3"}},"ms":{"version":"2.0.0","dev":true,"integrity":"sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="}}},"eslint-plugin-jest":{"version":"25.7.0","dev":true,"integrity":"sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==","requires":{"@typescript-eslint/eslint-plugin":"5.12.0","@typescript-eslint/experimental-utils":"5.12.0","eslint":"8.9.0"},"dependencies":{"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-jest-async":{"version":"1.0.3","dev":true,"integrity":"sha1-aRlnAtivhWVbIaeJpv11ygo3gA8=","requires":{"requireindex":"1.1.0"},"dependencies":{"requireindex":{"version":"1.1.0","dev":true,"integrity":"sha1-5UBLgVV+91225JxacgBIk/4D4WI="}}},"eslint-plugin-jest-dom":{"version":"3.9.4","dev":true,"integrity":"sha512-VRkaALGIhyxinnewZFHe2WJsRWp3TONpXysVXK1IUNJHCpJAIM9yRrI7fQ8i5F6UYE7+DAnvNhSSJZesLTonug==","requires":{"@babel/runtime":"7.17.2","@testing-library/dom":"7.31.2","eslint":"8.9.0","requireindex":"1.2.0"},"dependencies":{"@types/yargs":{"version":"15.0.14","dev":true,"integrity":"sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==","requires":{"@types/yargs-parser":"20.2.1"}},"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"eslint-plugin-jest-formatting":{"version":"3.1.0","dev":true,"integrity":"sha512-XyysraZ1JSgGbLSDxjj5HzKKh0glgWf+7CkqxbTqb7zEhW7X2WHo5SBQ8cGhnszKN+2Lj3/oevBlHNbHezoc/A==","requires":{"eslint":"8.9.0"}},"eslint-plugin-jsdoc":{"version":"37.9.4","dev":true,"integrity":"sha512-VxCyGgUNNnj2T4bb1OqltkbsPp3ehRzR5onIfh6zGrAvISmvgX/sbxUlh3YyGqWtjOTSBCURdKdmelSXEIHnlA==","requires":{"@es-joy/jsdoccomment":"0.20.1","comment-parser":"1.3.0","debug":"4.3.3","escape-string-regexp":"4.0.0","eslint":"8.9.0","esquery":"1.4.0","regextras":"0.8.0","semver":"7.3.5","spdx-expression-parse":"3.0.1"},"dependencies":{"estraverse":{"version":"5.3.0","dev":true,"integrity":"sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="}}},"eslint-plugin-json-schema-validator":{"version":"1.2.49","dev":true,"integrity":"sha512-Ib7PVrho5a08OI05VyVEkwb2u1jDWeDoHsIngaZFVi4OsnLObcW6gb3xO6VSYHUDvC5CTQans7vVjLUslcCMiA==","requires":{"ajv":"8.10.0","debug":"4.3.3","eslint":"8.9.0","eslint-utils":"3.0.0","json-schema-migrate":"2.0.0","jsonc-eslint-parser":"1.4.1","minimatch":"3.1.2","toml-eslint-parser":"0.2.1","tunnel-agent":"0.6.0","yaml-eslint-parser":"0.4.1"},"dependencies":{"acorn":{"version":"7.4.1","dev":true,"integrity":"sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="},"ajv":{"version":"8.10.0","dev":true,"integrity":"sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==","requires":{"fast-deep-equal":"3.1.3","json-schema-traverse":"1.0.0","require-from-string":"2.0.2","uri-js":"4.4.1"}},"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="},"espree":{"version":"6.2.1","dev":true,"integrity":"sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==","requires":{"acorn":"7.4.1","acorn-jsx":"5.3.2","eslint-visitor-keys":"1.3.0"}},"json-schema-traverse":{"version":"1.0.0","dev":true,"integrity":"sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="},"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"eslint-plugin-jsonc":{"version":"1.7.0","dev":true,"integrity":"sha512-pb3CAD9B0zhv3r9Bg9AdzswL50I3mbIq1ys+tNeuaDeibFlweo84SBNm22oqaFx/Dka+YZw2SLukAkQlJzSHMQ==","requires":{"eslint":"8.9.0","eslint-utils":"3.0.0","jsonc-eslint-parser":"1.4.1","natural-compare":"1.4.0"},"dependencies":{"acorn":{"version":"7.4.1","dev":true,"integrity":"sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="},"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="},"espree":{"version":"6.2.1","dev":true,"integrity":"sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==","requires":{"acorn":"7.4.1","acorn-jsx":"5.3.2","eslint-visitor-keys":"1.3.0"}},"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"eslint-plugin-no-constructor-bind":{"version":"2.0.4","dev":true,"integrity":"sha512-r0CGAE5SrRYt1OdACNiZGiOcBbFslKIPnMrFo3kPmX3iKZOm8HRD2eIbqhlc9lSSiBWcPZxXErXnroqgt+dKBg==","requires":{"requireindex":"1.2.0"}},"eslint-plugin-no-explicit-type-exports":{"version":"0.12.1","dev":true,"integrity":"sha512-m1v/f+LYVygCY735KfCovkoXYPbZH5zxEj/tuLOnMwX/qbJEJoRb9evul88Ois5HidvKbiMdMg/tXU55Ki++jg==","requires":{"@typescript-eslint/experimental-utils":"2.34.0","@typescript-eslint/parser":"5.12.0","eslint":"8.9.0","eslint-import-resolver-node":"0.3.6","eslint-module-utils":"2.7.3"},"dependencies":{"@typescript-eslint/experimental-utils":{"version":"2.34.0","dev":true,"integrity":"sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==","requires":{"@types/json-schema":"7.0.9","@typescript-eslint/typescript-estree":"2.34.0","eslint":"8.9.0","eslint-scope":"5.1.1","eslint-utils":"2.1.0"}},"@typescript-eslint/typescript-estree":{"version":"2.34.0","dev":true,"integrity":"sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==","requires":{"debug":"4.3.3","eslint-visitor-keys":"1.3.0","glob":"7.2.0","is-glob":"4.0.3","lodash":"4.17.21","semver":"7.3.5","tsutils":"3.21.0","typescript":"4.5.5"}},"eslint-utils":{"version":"2.1.0","dev":true,"integrity":"sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==","requires":{"eslint-visitor-keys":"1.3.0"}},"eslint-visitor-keys":{"version":"1.3.0","dev":true,"integrity":"sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="},"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-no-secrets":{"version":"0.8.9","dev":true,"integrity":"sha512-CqaBxXrImABCtxMWspAnm8d5UKkpNylC7zqVveb+fJHEvsSiNGJlSWzdSIvBUnW1XhJXkzifNIZQC08rEII5Ng==","requires":{"eslint":"8.9.0"}},"eslint-plugin-no-unsanitized":{"version":"3.2.0","dev":true,"integrity":"sha512-92opuXbjWmXcod94EyCKhp36V1QHLM/ArAST2ssgKOojALne0eZvSPfrg4oyr0EwTXvy0RJNe/Tkm33VkDUrKQ==","requires":{"eslint":"8.9.0"}},"eslint-plugin-no-use-extend-native":{"version":"0.5.0","dev":true,"integrity":"sha512-dBNjs8hor8rJgeXLH4HTut5eD3RGWf9JUsadIfuL7UosVQ/dnvOKwxEcRrXrFxrMZ8llUVWT+hOimxJABsAUzQ==","requires":{"is-get-set-prop":"1.0.0","is-js-type":"2.0.0","is-obj-prop":"1.0.0","is-proto-prop":"2.0.0"}},"eslint-plugin-node":{"version":"11.1.0","dev":true,"integrity":"sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==","requires":{"eslint":"8.9.0","eslint-plugin-es":"3.0.1","eslint-utils":"2.1.0","ignore":"5.2.0","minimatch":"3.1.2","resolve":"1.22.0","semver":"6.3.0"},"dependencies":{"eslint-utils":{"version":"2.1.0","dev":true,"integrity":"sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==","requires":{"eslint-visitor-keys":"1.3.0"}},"eslint-visitor-keys":{"version":"1.3.0","dev":true,"integrity":"sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="},"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"eslint-plugin-optimize-regex":{"version":"1.2.1","dev":true,"integrity":"sha512-fUaU7Tj1G/KSTDTABJw4Wp427Rl7RPl9ViYTu1Jrv36fJw4DFhd4elPdXiuYtdPsNsvzn9GcVlKEssGIVjw0UQ==","requires":{"regexp-tree":"0.1.24"}},"eslint-plugin-prefer-arrow":{"version":"1.2.3","dev":true,"integrity":"sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==","requires":{"eslint":"8.9.0"}},"eslint-plugin-prettier":{"version":"4.0.0","dev":true,"integrity":"sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==","requires":{"eslint":"8.9.0","eslint-config-prettier":"8.4.0","prettier-linter-helpers":"1.0.0"}},"eslint-plugin-promise":{"version":"5.2.0","dev":true,"integrity":"sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==","requires":{"eslint":"8.9.0"}},"eslint-plugin-regexp":{"version":"1.5.1","dev":true,"integrity":"sha512-5v0rQIi54m2KycQHqmOAHrZhvI56GHmI2acr6zEffAqfeifTtobAEapv9Uf4o8//lGvwVkHKyjLoSbBNEFcfOA==","requires":{"comment-parser":"1.3.0","eslint":"8.9.0","eslint-utils":"3.0.0","grapheme-splitter":"1.0.4","jsdoctypeparser":"9.0.0","refa":"0.9.1","regexp-ast-analysis":"0.3.0","regexpp":"3.2.0","scslre":"0.1.6"},"dependencies":{"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="}}},"eslint-plugin-rxjs":{"version":"4.0.4","dev":true,"integrity":"sha512-7JopYQRqS5TYBNXioTLtS+W6+IKC1rsC7q8Dtou8E5gMuPxuEFqxU1x2161bwadaK3+h6sR+xiGjEhiE6JwjUA==","requires":{"@typescript-eslint/experimental-utils":"5.12.0","common-tags":"1.8.2","decamelize":"5.0.1","eslint":"8.9.0","eslint-etc":"5.1.0","requireindex":"1.2.0","rxjs-report-usage":"1.0.6","tslib":"2.3.1","tsutils":"3.21.0","tsutils-etc":"1.4.1","typescript":"4.5.5"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"globals":{"version":"11.12.0","dev":true,"integrity":"sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-security":{"version":"1.4.0","dev":true,"integrity":"sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==","requires":{"safe-regex":"1.1.0"}},"eslint-plugin-sonarjs":{"version":"0.10.0","dev":true,"integrity":"sha512-FBRIBmWQh2UAfuLSnuYEfmle33jIup9hfkR0X8pkfjeCKNpHUG8qyZI63ahs3aw8CJrv47QJ9ccdK3ZxKH016A==","requires":{"eslint":"8.9.0"}},"eslint-plugin-sort-class-members":{"version":"1.14.1","dev":true,"integrity":"sha512-/Q/cm3h4N9DBNYvJMQMhluucSmr3Yydr9U0BgGcXUQe/rgWdXKSymZ5Ewcf4vmAG0bbTmAYmekuMnYYrqlu9Rg==","requires":{"eslint":"8.9.0"}},"eslint-plugin-sort-keys-fix":{"version":"1.1.2","dev":true,"integrity":"sha512-DNPHFGCA0/hZIsfODbeLZqaGY/+q3vgtshF85r+YWDNCQ2apd9PNs/zL6ttKm0nD1IFwvxyg3YOTI7FHl4unrw==","requires":{"espree":"6.2.1","esutils":"2.0.3","natural-compare":"1.4.0","requireindex":"1.2.0"},"dependencies":{"acorn":{"version":"7.4.1","dev":true,"integrity":"sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="},"eslint-visitor-keys":{"version":"1.3.0","dev":true,"integrity":"sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="},"espree":{"version":"6.2.1","dev":true,"integrity":"sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==","requires":{"acorn":"7.4.1","acorn-jsx":"5.3.2","eslint-visitor-keys":"1.3.0"}}}},"eslint-plugin-switch-case":{"version":"1.1.2","dev":true,"integrity":"sha1-piLbDExECChSa28m8ADXHnSFADI=","requires":{"lodash.last":"3.0.0","lodash.zipobject":"4.1.3"}},"eslint-plugin-toml":{"version":"0.2.0","dev":true,"integrity":"sha512-Y4eGb9q7i0i+UyZAXl3QAqcxkds2X7tfctXPllL7X+PpBXD/3wiqq1RNeoiLmHI9evpO1BjyuakffJZuGM7ElA==","requires":{"debug":"4.3.3","eslint":"8.9.0","lodash":"4.17.21","toml-eslint-parser":"0.2.1"},"dependencies":{"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="}}},"eslint-plugin-tsdoc":{"version":"0.2.14","dev":true,"integrity":"sha512-fJ3fnZRsdIoBZgzkQjv8vAj6NeeOoFkTfgosj6mKsFjX70QV256sA/wq+y/R2+OL4L8E79VVaVWrPeZnKNe8Ng==","requires":{"@microsoft/tsdoc":"0.13.2","@microsoft/tsdoc-config":"0.15.2"},"dependencies":{"resolve":{"version":"1.19.0","dev":true,"integrity":"sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==","requires":{"is-core-module":"2.8.1","path-parse":"1.0.7"}}}},"eslint-plugin-typescript-sort-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-ET7ABypdz19m47QnKynzNfWPi4CTNQ5jQQC1X5d0gojIwblkbGiCa5IilsqzBTmqxZ0yXDqKBO/GBkBFQCOFsg==","requires":{"@typescript-eslint/experimental-utils":"5.12.0","@typescript-eslint/parser":"5.12.0","eslint":"8.9.0","json-schema":"0.4.0","natural-compare-lite":"1.4.0","typescript":"4.5.5"},"dependencies":{"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-unicorn":{"version":"37.0.1","dev":true,"integrity":"sha512-E1jq5u9ojnadisJcPi+hMXTGSiIzkIUMDvWsBudsCGXvKUB2aNSU2TcfyW2/jAS5A4ryBXfzxLykMxX1EdluSQ==","requires":{"@babel/helper-validator-identifier":"7.16.7","ci-info":"3.3.0","clean-regexp":"1.0.0","eslint":"8.9.0","eslint-template-visitor":"2.3.2","eslint-utils":"3.0.0","esquery":"1.4.0","indent-string":"4.0.0","is-builtin-module":"3.1.0","lodash":"4.17.21","pluralize":"8.0.0","read-pkg-up":"7.0.1","regexp-tree":"0.1.24","safe-regex":"2.1.1","semver":"7.3.5","strip-indent":"3.0.0"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="},"globals":{"version":"11.12.0","dev":true,"integrity":"sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"json5":{"version":"2.2.0","dev":true,"integrity":"sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==","requires":{"minimist":"1.2.5"}},"safe-buffer":{"version":"5.1.2","dev":true,"integrity":"sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="},"safe-regex":{"version":"2.1.1","dev":true,"integrity":"sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==","requires":{"regexp-tree":"0.1.24"}},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"type-fest":{"version":"0.8.1","dev":true,"integrity":"sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="}}},"eslint-plugin-unused-imports":{"version":"1.1.5","dev":true,"integrity":"sha512-TeV8l8zkLQrq9LBeYFCQmYVIXMjfHgdRQLw7dEZp4ZB3PeR10Y5Uif11heCsHRmhdRIYMoewr1d9ouUHLbLHew==","requires":{"@typescript-eslint/eslint-plugin":"5.12.0","eslint":"8.9.0","eslint-rule-composer":"0.3.0"}},"eslint-plugin-woke":{"version":"1.0.1","dev":true,"integrity":"sha512-cDRyZMNJkrbycju6OKhGnF29j0kbzV9AnLk3IPz6Kn0vWrSf8RyUdS5at97o/CJ8SEN2e6furQch1o/0LIrfMg==","requires":{"eslint":"8.9.0","requireindex":"1.1.0"},"dependencies":{"requireindex":{"version":"1.1.0","dev":true,"integrity":"sha1-5UBLgVV+91225JxacgBIk/4D4WI="}}},"eslint-plugin-yml":{"version":"0.10.1","dev":true,"integrity":"sha512-af0WgO3qaH+RW6jv1s6RzXKlg2NZLisN95lqGUf1KqBT6rEJyGSCpM49QYaSTvzmMaB/gcdbrnAfNoYwUn0Yig==","requires":{"debug":"4.3.3","eslint":"8.9.0","lodash":"4.17.21","natural-compare":"1.4.0","yaml-eslint-parser":"0.4.1"},"dependencies":{"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="}}},"eslint-rule-composer":{"version":"0.3.0","dev":true,"integrity":"sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg=="},"eslint-rule-docs":{"version":"1.1.231","dev":true,"integrity":"sha512-egHz9A1WG7b8CS0x1P6P/Rj5FqZOjray/VjpJa14tMZalfRKvpE2ONJ3plCM7+PcinmU4tcmbPLv0VtwzSdLVA=="},"eslint-scope":{"version":"5.1.1","dev":true,"integrity":"sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==","requires":{"esrecurse":"4.3.0","estraverse":"4.3.0"}},"eslint-template-visitor":{"version":"2.3.2","dev":true,"integrity":"sha512-3ydhqFpuV7x1M9EK52BPNj6V0Kwu0KKkcIAfpUhwHbR8ocRln/oUHgfxQupY8O1h4Qv/POHDumb/BwwNfxbtnA==","requires":{"@babel/core":"7.17.5","@babel/eslint-parser":"7.17.0","eslint":"8.9.0","eslint-visitor-keys":"2.1.0","esquery":"1.4.0","multimap":"1.1.0"},"dependencies":{"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"eslint-utils":{"version":"3.0.0","dev":true,"integrity":"sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==","requires":{"eslint":"8.9.0","eslint-visitor-keys":"2.1.0"},"dependencies":{"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="}}},"eslint-visitor-keys":{"version":"3.3.0","dev":true,"integrity":"sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA=="},"espree":{"version":"9.3.1","dev":true,"integrity":"sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==","requires":{"acorn":"8.7.0","acorn-jsx":"5.3.2","eslint-visitor-keys":"3.3.0"}},"esprima":{"version":"4.0.1","dev":true,"integrity":"sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="},"esquery":{"version":"1.4.0","dev":true,"integrity":"sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==","requires":{"estraverse":"5.3.0"}},"esrecurse":{"version":"4.3.0","dev":true,"integrity":"sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==","requires":{"estraverse":"5.3.0"},"dependencies":{"estraverse":{"version":"5.3.0","dev":true,"integrity":"sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="}}},"estraverse":{"version":"4.3.0","dev":true,"integrity":"sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="},"esutils":{"version":"2.0.3","dev":true,"integrity":"sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="},"execa":{"version":"5.1.1","dev":true,"integrity":"sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==","requires":{"cross-spawn":"7.0.3","get-stream":"6.0.1","human-signals":"2.1.0","is-stream":"2.0.1","merge-stream":"2.0.0","npm-run-path":"4.0.1","onetime":"5.1.2","signal-exit":"3.0.7","strip-final-newline":"2.0.0"}},"expand-brackets":{"version":"2.1.4","dev":true,"integrity":"sha1-t3c14xXOMPa27/D4OwQVGiJEliI=","requires":{"debug":"2.6.9","define-property":"0.2.5","extend-shallow":"2.0.1","posix-character-classes":"0.1.1","regex-not":"1.0.2","snapdragon":"0.8.2","to-regex":"3.0.2"},"dependencies":{"define-property":{"version":"0.2.5","dev":true,"integrity":"sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=","requires":{"is-descriptor":"0.1.6"}}}},"expand-tilde":{"version":"2.0.2","dev":true,"integrity":"sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=","requires":{"homedir-polyfill":"1.0.3"}},"extend-shallow":{"version":"2.0.1","dev":true,"integrity":"sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=","requires":{"is-extendable":"0.1.1"}},"external-editor":{"version":"3.1.0","dev":true,"integrity":"sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==","requires":{"chardet":"0.7.0","iconv-lite":"0.4.24","tmp":"0.0.33"}},"extglob":{"version":"2.0.4","dev":true,"integrity":"sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==","requires":{"array-unique":"0.3.2","define-property":"1.0.0","expand-brackets":"2.1.4","extend-shallow":"2.0.1","fragment-cache":"0.2.1","regex-not":"1.0.2","snapdragon":"0.8.2","to-regex":"3.0.2"},"dependencies":{"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}},"falsey":{"version":"0.3.2","dev":true,"integrity":"sha512-lxEuefF5MBIVDmE6XeqCdM4BWk1+vYmGZtkbKZ/VFcg6uBBw6fXNEbWmxCjDdQlFc9hy450nkiWwM3VAW6G1qg==","requires":{"kind-of":"5.1.0"},"dependencies":{"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}},"fast-deep-equal":{"version":"3.1.3","dev":true,"integrity":"sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="},"fast-diff":{"version":"1.2.0","dev":true,"integrity":"sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w=="},"fast-equals":{"version":"3.0.0","dev":true,"integrity":"sha512-Af7nSOpf7617idrFg0MJY6x7yVDPoO80aSwtKTC0afT8B/SsmvTpA+2a+uPLmhVF5IHmY5NPuBAA3dJrp55rJA=="},"fast-glob":{"version":"3.2.11","dev":true,"integrity":"sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==","requires":{"@nodelib/fs.stat":"2.0.5","@nodelib/fs.walk":"1.2.8","glob-parent":"5.1.2","merge2":"1.4.1","micromatch":"4.0.4"}},"fast-json-stable-stringify":{"version":"2.1.0","dev":true,"integrity":"sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="},"fast-levenshtein":{"version":"2.0.6","dev":true,"integrity":"sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="},"fastq":{"version":"1.13.0","dev":true,"integrity":"sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==","requires":{"reusify":"1.0.4"}},"figures":{"version":"2.0.0","dev":true,"integrity":"sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=","requires":{"escape-string-regexp":"1.0.5"}},"file-entry-cache":{"version":"6.0.1","dev":true,"integrity":"sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==","requires":{"flat-cache":"3.0.4"}},"file-type":{"version":"16.5.3","dev":true,"integrity":"sha512-uVsl7iFhHSOY4bEONLlTK47iAHtNsFHWP5YE4xJfZ4rnX7S1Q3wce09XgqSC7E/xh8Ncv/be1lNoyprlUH/x6A==","requires":{"readable-web-to-node-stream":"3.0.2","strtok3":"6.3.0","token-types":"4.1.1"}},"fill-range":{"version":"7.0.1","dev":true,"integrity":"sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==","requires":{"to-regex-range":"5.0.1"}},"filter-obj":{"version":"1.1.0","dev":true,"integrity":"sha1-mzERErxsYSehbgFsbF1/GeCAXFs="},"find-node-modules":{"version":"2.1.2","dev":true,"integrity":"sha512-x+3P4mbtRPlSiVE1Qco0Z4YLU8WFiFcuWTf3m75OV9Uzcfs2Bg+O9N+r/K0AnmINBW06KpfqKwYJbFlFq4qNug==","requires":{"findup-sync":"4.0.0","merge":"2.1.1"}},"find-parent-dir":{"version":"0.3.1","dev":true,"integrity":"sha512-o4UcykWV/XN9wm+jMEtWLPlV8RXCZnMhQI6F6OdHeSez7iiJWePw8ijOlskJZMsaQoGR/b7dH6lO02HhaTN7+A=="},"find-root":{"version":"1.1.0","dev":true,"integrity":"sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="},"find-up":{"version":"4.1.0","dev":true,"integrity":"sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==","requires":{"locate-path":"5.0.0","path-exists":"4.0.0"}},"find-versions":{"version":"4.0.0","dev":true,"integrity":"sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==","requires":{"semver-regex":"3.1.3"}},"findup-sync":{"version":"4.0.0","dev":true,"integrity":"sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==","requires":{"detect-file":"1.0.0","is-glob":"4.0.3","micromatch":"4.0.4","resolve-dir":"1.0.1"}},"flat-cache":{"version":"3.0.4","dev":true,"integrity":"sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==","requires":{"flatted":"3.2.5","rimraf":"3.0.2"}},"flatted":{"version":"3.2.5","dev":true,"integrity":"sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg=="},"for-in":{"version":"1.0.2","dev":true,"integrity":"sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="},"for-own":{"version":"1.0.0","dev":true,"integrity":"sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=","requires":{"for-in":"1.0.2"}},"form-data":{"version":"4.0.0","dev":true,"integrity":"sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==","requires":{"asynckit":"0.4.0","combined-stream":"1.0.8","mime-types":"2.1.34"}},"fragment-cache":{"version":"0.2.1","dev":true,"integrity":"sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=","requires":{"map-cache":"0.2.2"}},"from2":{"version":"2.3.0","dev":true,"integrity":"sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=","requires":{"inherits":"2.0.4","readable-stream":"2.3.7"}},"fromentries":{"version":"1.3.2","dev":true,"integrity":"sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg=="},"fs-exists-sync":{"version":"0.1.0","dev":true,"integrity":"sha1-mC1ok6+RjnLQjeyehnP/K1qNat0="},"fs-extra":{"version":"10.0.0","dev":true,"integrity":"sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==","requires":{"graceful-fs":"4.2.9","jsonfile":"6.1.0","universalify":"2.0.0"}},"fs.realpath":{"version":"1.0.0","dev":true,"integrity":"sha1-FQStJSMVjKpA20onh8sBQRmU6k8="},"function-bind":{"version":"1.1.1","dev":true,"integrity":"sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="},"functional-red-black-tree":{"version":"1.0.1","dev":true,"integrity":"sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="},"gensequence":{"version":"3.1.1","dev":true,"integrity":"sha512-ys3h0hiteRwmY6BsvSttPmkhC0vEQHPJduANBRtH/dlDPZ0UBIb/dXy80IcckXyuQ6LKg+PloRqvGER9IS7F7g=="},"gensync":{"version":"1.0.0-beta.2","dev":true,"integrity":"sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="},"get-caller-file":{"version":"2.0.5","dev":true,"integrity":"sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="},"get-intrinsic":{"version":"1.1.1","dev":true,"integrity":"sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==","requires":{"function-bind":"1.1.1","has":"1.0.3","has-symbols":"1.0.2"}},"get-object":{"version":"0.2.0","dev":true,"integrity":"sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw=","requires":{"is-number":"2.1.0","isobject":"0.2.0"},"dependencies":{"is-number":{"version":"2.1.0","dev":true,"integrity":"sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=","requires":{"kind-of":"3.2.2"}},"isobject":{"version":"0.2.0","dev":true,"integrity":"sha1-o0MhkvObkQtfAsyYlIeDbscKqF4="},"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"get-set-props":{"version":"0.1.0","dev":true,"integrity":"sha1-mYR1wXhEVobQsyJG2l3428++jqM="},"get-stdin":{"version":"8.0.0","dev":true,"integrity":"sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg=="},"get-stream":{"version":"6.0.1","dev":true,"integrity":"sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="},"get-symbol-description":{"version":"1.0.0","dev":true,"integrity":"sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==","requires":{"call-bind":"1.0.2","get-intrinsic":"1.1.1"}},"get-value":{"version":"2.0.6","dev":true,"integrity":"sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="},"git-cz-emoji":{"version":"1.1.24","dev":true,"integrity":"sha512-m9XItxRoL6k/NCAeWubMJW2m3r29V6JkQSpZk5vtFY6IVJ8S4l6l7AVreOsRemKoWiVzxtb2I4xBbJNt1vlrNw==","requires":{"chalk":"4.1.2","commitizen":"4.2.4","tslib":"2.3.1","word-wrap":"1.2.3"},"dependencies":{"ansi-regex":{"version":"3.0.0","dev":true,"integrity":"sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="},"fs-extra":{"version":"8.1.0","dev":true,"integrity":"sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==","requires":{"graceful-fs":"4.2.9","jsonfile":"4.0.0","universalify":"0.1.2"}},"glob":{"version":"7.1.4","dev":true,"integrity":"sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==","requires":{"fs.realpath":"1.0.0","inflight":"1.0.6","inherits":"2.0.4","minimatch":"3.1.2","once":"1.4.0","path-is-absolute":"1.0.1"}},"is-fullwidth-code-point":{"version":"2.0.0","dev":true,"integrity":"sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="},"jsonfile":{"version":"4.0.0","dev":true,"integrity":"sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss="},"mimic-fn":{"version":"1.2.0","dev":true,"integrity":"sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="},"onetime":{"version":"2.0.1","dev":true,"integrity":"sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=","requires":{"mimic-fn":"1.2.0"}},"string-width":{"version":"2.1.1","dev":true,"integrity":"sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==","requires":{"is-fullwidth-code-point":"2.0.0","strip-ansi":"4.0.0"},"dependencies":{"strip-ansi":{"version":"4.0.0","dev":true,"integrity":"sha1-qEeQIusaw2iocTibY1JixQXuNo8=","requires":{"ansi-regex":"3.0.0"}}}},"strip-ansi":{"version":"5.2.0","dev":true,"integrity":"sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==","requires":{"ansi-regex":"4.1.0"},"dependencies":{"ansi-regex":{"version":"4.1.0","dev":true,"integrity":"sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="}}},"universalify":{"version":"0.1.2","dev":true,"integrity":"sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="},"which":{"version":"1.3.1","dev":true,"integrity":"sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==","requires":{"isexe":"2.0.0"}}}},"git-log-parser":{"version":"1.2.0","dev":true,"integrity":"sha1-LmpMGxP8AAKCB7p5WnrDFme5/Uo=","requires":{"argv-formatter":"1.0.0","spawn-error-forwarder":"1.0.0","split2":"1.0.0","stream-combiner2":"1.1.1","through2":"2.0.5","traverse":"0.6.6"}},"git-notify":{"version":"0.2.3","dev":true,"integrity":"sha512-fnUsm66BY6yQptuvnnQYseRKBFIO0l/IbLJJ01FuIZvCsFmRboWeaAT4BMIfdnaBC1F+63t4qM6ytej/y8gEGQ==","requires":{"boxen":"5.1.2","chalk":"4.1.2","detect-newline":"3.1.0","git-raw-commits":"2.0.11","meow":"9.0.0","simple-git":"2.48.0"},"dependencies":{"camelcase":{"version":"6.3.0","dev":true,"integrity":"sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="},"meow":{"version":"9.0.0","dev":true,"integrity":"sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==","requires":{"@types/minimist":"1.2.2","camelcase-keys":"6.2.2","decamelize":"1.2.0","decamelize-keys":"1.1.0","hard-rejection":"2.1.0","minimist-options":"4.1.0","normalize-package-data":"3.0.3","read-pkg-up":"7.0.1","redent":"3.0.0","trim-newlines":"3.0.1","type-fest":"0.18.1","yargs-parser":"20.2.9"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"camelcase":{"version":"5.3.1","dev":true,"integrity":"sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"type-fest":{"version":"0.18.1","dev":true,"integrity":"sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="}}},"type-fest":{"version":"0.20.2","dev":true,"integrity":"sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="}}},"git-raw-commits":{"version":"2.0.11","dev":true,"integrity":"sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==","requires":{"dargs":"7.0.0","lodash":"4.17.21","meow":"8.1.2","split2":"3.2.2","through2":"4.0.2"}},"glob":{"version":"7.2.0","dev":true,"integrity":"sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==","requires":{"fs.realpath":"1.0.0","inflight":"1.0.6","inherits":"2.0.4","minimatch":"3.1.2","once":"1.4.0","path-is-absolute":"1.0.1"}},"glob-parent":{"version":"5.1.2","dev":true,"integrity":"sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==","requires":{"is-glob":"4.0.3"}},"global-dirs":{"version":"0.1.1","dev":true,"integrity":"sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=","requires":{"ini":"1.3.8"}},"global-modules":{"version":"1.0.0","dev":true,"integrity":"sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==","requires":{"global-prefix":"1.0.2","is-windows":"1.0.2","resolve-dir":"1.0.1"}},"global-prefix":{"version":"1.0.2","dev":true,"integrity":"sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=","requires":{"expand-tilde":"2.0.2","homedir-polyfill":"1.0.3","ini":"1.3.8","is-windows":"1.0.2","which":"1.3.1"}},"globals":{"version":"13.12.1","dev":true,"integrity":"sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==","requires":{"type-fest":"0.20.2"}},"globby":{"version":"11.1.0","dev":true,"integrity":"sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==","requires":{"array-union":"2.1.0","dir-glob":"3.0.1","fast-glob":"3.2.11","ignore":"5.2.0","merge2":"1.4.1","slash":"3.0.0"}},"got":{"version":"11.8.3","dev":true,"integrity":"sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==","requires":{"@sindresorhus/is":"4.4.0","@szmarczak/http-timer":"4.0.6","@types/cacheable-request":"6.0.2","@types/responselike":"1.0.0","cacheable-lookup":"5.0.4","cacheable-request":"7.0.2","decompress-response":"6.0.0","http2-wrapper":"1.0.3","lowercase-keys":"2.0.0","p-cancelable":"2.1.1","responselike":"2.0.0"}},"graceful-fs":{"version":"4.2.9","dev":true,"integrity":"sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ=="},"grapheme-splitter":{"version":"1.0.4","dev":true,"integrity":"sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="},"gulp-header":{"version":"1.8.12","dev":true,"integrity":"sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==","requires":{"concat-with-sourcemaps":"1.1.0","lodash.template":"4.5.0","through2":"2.0.5"}},"handlebars":{"version":"4.7.7","dev":true,"integrity":"sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==","requires":{"minimist":"1.2.5","neo-async":"2.6.2","source-map":"0.6.1","wordwrap":"1.0.0"}},"handlebars-helper-create-frame":{"version":"0.1.0","dev":true,"integrity":"sha1-iqUdEK62QI/MZgXUDXc1YohIegM=","requires":{"create-frame":"1.0.0","isobject":"3.0.1"}},"handlebars-helpers":{"version":"0.10.0","dev":true,"integrity":"sha512-QiyhQz58u/DbuV41VnfpE0nhy6YCH4vB514ajysV8SoKmP+DxU+pR+fahVyNECHj+jiwEN2VrvxD/34/yHaLUg==","requires":{"arr-flatten":"1.1.0","array-sort":"0.1.4","create-frame":"1.0.0","define-property":"1.0.0","falsey":"0.3.2","for-in":"1.0.2","for-own":"1.0.0","get-object":"0.2.0","get-value":"2.0.6","handlebars":"4.7.7","handlebars-helper-create-frame":"0.1.0","handlebars-utils":"1.0.6","has-value":"1.0.0","helper-date":"1.0.1","helper-markdown":"1.0.0","helper-md":"0.2.2","html-tag":"2.0.0","is-even":"1.0.0","is-glob":"4.0.3","is-number":"4.0.0","kind-of":"6.0.3","lazy-cache":"2.0.2","logging-helpers":"1.0.0","micromatch":"3.1.10","relative":"3.0.2","striptags":"3.2.0","to-gfm-code-block":"0.1.1","year":"0.2.1"},"dependencies":{"ansi-colors":{"version":"0.2.0","dev":true,"integrity":"sha1-csMd4qDZoszQysMMyYI+6y9kNLU=","requires":{"ansi-bgblack":"0.1.1","ansi-bgblue":"0.1.1","ansi-bgcyan":"0.1.1","ansi-bggreen":"0.1.1","ansi-bgmagenta":"0.1.1","ansi-bgred":"0.1.1","ansi-bgwhite":"0.1.1","ansi-bgyellow":"0.1.1","ansi-black":"0.1.1","ansi-blue":"0.1.1","ansi-bold":"0.1.1","ansi-cyan":"0.1.1","ansi-dim":"0.1.1","ansi-gray":"0.1.1","ansi-green":"0.1.1","ansi-grey":"0.1.1","ansi-hidden":"0.1.1","ansi-inverse":"0.1.1","ansi-italic":"0.1.1","ansi-magenta":"0.1.1","ansi-red":"0.1.1","ansi-reset":"0.1.1","ansi-strikethrough":"0.1.1","ansi-underline":"0.1.1","ansi-white":"0.1.1","ansi-yellow":"0.1.1","lazy-cache":"2.0.2"}},"argparse":{"version":"1.0.10","dev":true,"integrity":"sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==","requires":{"sprintf-js":"1.0.3"}},"braces":{"version":"2.3.2","dev":true,"integrity":"sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==","requires":{"arr-flatten":"1.1.0","array-unique":"0.3.2","extend-shallow":"2.0.1","fill-range":"4.0.0","isobject":"3.0.1","repeat-element":"1.1.4","snapdragon":"0.8.2","snapdragon-node":"2.1.1","split-string":"3.1.0","to-regex":"3.0.2"}},"debug":{"version":"3.1.0","dev":true,"integrity":"sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==","requires":{"ms":"2.0.0"}},"fill-range":{"version":"4.0.0","dev":true,"integrity":"sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=","requires":{"extend-shallow":"2.0.1","is-number":"3.0.0","repeat-string":"1.6.1","to-regex-range":"2.1.1"}},"is-number":{"version":"4.0.0","dev":true,"integrity":"sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ=="},"is-plain-object":{"version":"2.0.4","dev":true,"integrity":"sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==","requires":{"isobject":"3.0.1"}},"micromatch":{"version":"3.1.10","dev":true,"integrity":"sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==","requires":{"arr-diff":"4.0.0","array-unique":"0.3.2","braces":"2.3.2","define-property":"2.0.2","extend-shallow":"3.0.2","extglob":"2.0.4","fragment-cache":"0.2.1","kind-of":"6.0.3","nanomatch":"1.2.13","object.pick":"1.3.0","regex-not":"1.0.2","snapdragon":"0.8.2","to-regex":"3.0.2"},"dependencies":{"debug":{"version":"2.6.9","dev":true,"integrity":"sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==","requires":{"ms":"2.0.0"}},"define-property":{"version":"2.0.2","dev":true,"integrity":"sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==","requires":{"is-descriptor":"1.0.2","isobject":"3.0.1"}},"extend-shallow":{"version":"3.0.2","dev":true,"integrity":"sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=","requires":{"assign-symbols":"1.0.0","is-extendable":"1.0.1"},"dependencies":{"is-extendable":{"version":"1.0.1","dev":true,"integrity":"sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==","requires":{"is-plain-object":"2.0.4"}}}},"has-value":{"version":"0.3.1","dev":true,"integrity":"sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=","requires":{"get-value":"2.0.6","has-values":"0.1.4","isobject":"2.1.0"}},"has-values":{"version":"0.1.4","dev":true,"integrity":"sha1-bWHeldkd/Km5oCCJrThL/49it3E="},"is-accessor-descriptor":{"version":"1.0.0","dev":true,"integrity":"sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==","requires":{"kind-of":"6.0.3"}},"is-data-descriptor":{"version":"1.0.0","dev":true,"integrity":"sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==","requires":{"kind-of":"6.0.3"}},"is-descriptor":{"version":"1.0.2","dev":true,"integrity":"sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==","requires":{"is-accessor-descriptor":"1.0.0","is-data-descriptor":"1.0.0","kind-of":"6.0.3"}},"is-number":{"version":"3.0.0","dev":true,"integrity":"sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=","requires":{"kind-of":"3.2.2"}},"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}},"source-map":{"version":"0.5.7","dev":true,"integrity":"sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="}}},"ms":{"version":"2.0.0","dev":true,"integrity":"sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="},"readable-stream":{"version":"2.3.7","dev":true,"integrity":"sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==","requires":{"core-util-is":"1.0.3","inherits":"2.0.4","isarray":"1.0.0","process-nextick-args":"2.0.1","safe-buffer":"5.1.2","string_decoder":"1.1.1","util-deprecate":"1.0.2"}},"safe-buffer":{"version":"5.1.2","dev":true,"integrity":"sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="},"string_decoder":{"version":"1.1.1","dev":true,"integrity":"sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==","requires":{"safe-buffer":"5.1.2"}},"through2":{"version":"2.0.5","dev":true,"integrity":"sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==","requires":{"readable-stream":"2.3.7","xtend":"4.0.2"}},"to-regex-range":{"version":"2.1.1","dev":true,"integrity":"sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=","requires":{"is-number":"3.0.0","repeat-string":"1.6.1"}}}},"handlebars-utils":{"version":"1.0.6","dev":true,"integrity":"sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw==","requires":{"kind-of":"6.0.3","typeof-article":"0.1.1"},"dependencies":{"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"hard-rejection":{"version":"2.1.0","dev":true,"integrity":"sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA=="},"has":{"version":"1.0.3","dev":true,"integrity":"sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==","requires":{"function-bind":"1.1.1"}},"has-bigints":{"version":"1.0.1","dev":true,"integrity":"sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="},"has-flag":{"version":"4.0.0","dev":true,"integrity":"sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="},"has-own-prop":{"version":"2.0.0","dev":true,"integrity":"sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ=="},"has-symbols":{"version":"1.0.2","dev":true,"integrity":"sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="},"has-tostringtag":{"version":"1.0.0","dev":true,"integrity":"sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==","requires":{"has-symbols":"1.0.2"}},"has-value":{"version":"1.0.0","dev":true,"integrity":"sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=","requires":{"get-value":"2.0.6","has-values":"1.0.0","isobject":"3.0.1"},"dependencies":{"is-number":{"version":"3.0.0","dev":true,"integrity":"sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=","requires":{"kind-of":"3.2.2"},"dependencies":{"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"kind-of":{"version":"4.0.0","dev":true,"integrity":"sha1-IIE989cSkosgc3hpGkUGb65y3Vc=","requires":{"is-buffer":"1.1.6"}}}},"has-values":{"version":"1.0.0","dev":true,"integrity":"sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=","requires":{"is-number":"3.0.0","kind-of":"4.0.0"}},"helper-date":{"version":"1.0.1","dev":true,"integrity":"sha512-wU3VOwwTJvGr/w5rZr3cprPHO+hIhlblTJHD6aFBrKLuNbf4lAmkawd2iK3c6NbJEvY7HAmDpqjOFSI5/+Ey2w==","requires":{"date.js":"0.3.3","handlebars-utils":"1.0.6","moment":"2.29.1"}},"helper-markdown":{"version":"1.0.0","dev":true,"integrity":"sha512-AnDqMS4ejkQK0MXze7pA9TM3pu01ZY+XXsES6gEE0RmCGk5/NIfvTn0NmItfyDOjRAzyo9z6X7YHbHX4PzIvOA==","requires":{"handlebars-utils":"1.0.6","highlight.js":"9.18.5","remarkable":"1.7.4"}},"helper-md":{"version":"0.2.2","dev":true,"integrity":"sha1-wfWdflW7riM2L9ig6XFgeuxp1B8=","requires":{"ent":"2.2.0","extend-shallow":"2.0.1","fs-exists-sync":"0.1.0","remarkable":"1.7.4"}},"highlight.js":{"version":"9.18.5","dev":true,"integrity":"sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA=="},"homedir-polyfill":{"version":"1.0.3","dev":true,"integrity":"sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==","requires":{"parse-passwd":"1.0.0"}},"hook-std":{"version":"2.0.0","dev":true,"integrity":"sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g=="},"hosted-git-info":{"version":"4.1.0","dev":true,"integrity":"sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==","requires":{"lru-cache":"6.0.0"}},"html-tag":{"version":"2.0.0","dev":true,"integrity":"sha512-XxzooSo6oBoxBEUazgjdXj7VwTn/iSTSZzTYKzYY6I916tkaYzypHxy+pbVU1h+0UQ9JlVf5XkNQyxOAiiQO1g==","requires":{"is-self-closing":"1.0.1","kind-of":"6.0.3"}},"htmlparser2":{"version":"7.2.0","dev":true,"integrity":"sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==","requires":{"domelementtype":"2.2.0","domhandler":"4.3.0","domutils":"2.8.0","entities":"3.0.1"}},"http-cache-semantics":{"version":"4.1.0","dev":true,"integrity":"sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="},"http-proxy-agent":{"version":"5.0.0","dev":true,"integrity":"sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==","requires":{"@tootallnate/once":"2.0.0","agent-base":"6.0.2","debug":"4.3.3"}},"http2-wrapper":{"version":"1.0.3","dev":true,"integrity":"sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==","requires":{"quick-lru":"5.1.1","resolve-alpn":"1.2.1"}},"https-proxy-agent":{"version":"5.0.0","dev":true,"integrity":"sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==","requires":{"agent-base":"6.0.2","debug":"4.3.3"}},"human-signals":{"version":"2.1.0","dev":true,"integrity":"sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="},"husky":{"version":"7.0.4","dev":true,"integrity":"sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ=="},"iconv-lite":{"version":"0.4.24","dev":true,"integrity":"sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==","requires":{"safer-buffer":"2.1.2"}},"ieee754":{"version":"1.2.1","dev":true,"integrity":"sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="},"ignore":{"version":"5.2.0","dev":true,"integrity":"sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ=="},"import-fresh":{"version":"3.3.0","dev":true,"integrity":"sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==","requires":{"parent-module":"1.0.1","resolve-from":"4.0.0"}},"import-from":{"version":"4.0.0","dev":true,"integrity":"sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ=="},"imurmurhash":{"version":"0.1.4","dev":true,"integrity":"sha1-khi5srkoojixPcT7a21XbyMUU+o="},"indent-string":{"version":"4.0.0","dev":true,"integrity":"sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="},"inflight":{"version":"1.0.6","dev":true,"integrity":"sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=","requires":{"once":"1.4.0","wrappy":"1.0.2"}},"info-symbol":{"version":"0.1.0","dev":true,"integrity":"sha1-J4QdcoZ920JCzWEtecEGM4gcang="},"inherits":{"version":"2.0.4","dev":true,"integrity":"sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="},"ini":{"version":"1.3.8","dev":true,"integrity":"sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="},"inquirer":{"version":"6.5.2","dev":true,"integrity":"sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==","requires":{"ansi-escapes":"3.2.0","chalk":"2.4.2","cli-cursor":"2.1.0","cli-width":"2.2.1","external-editor":"3.1.0","figures":"2.0.0","lodash":"4.17.21","mute-stream":"0.0.7","run-async":"2.4.1","rxjs":"6.6.7","string-width":"2.1.1","strip-ansi":"5.2.0","through":"2.3.8"}},"internal-slot":{"version":"1.0.3","dev":true,"integrity":"sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==","requires":{"get-intrinsic":"1.1.1","has":"1.0.3","side-channel":"1.0.4"}},"into-stream":{"version":"6.0.0","dev":true,"integrity":"sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==","requires":{"from2":"2.3.0","p-is-promise":"3.0.0"},"dependencies":{"readable-stream":{"version":"2.3.7","dev":true,"integrity":"sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==","requires":{"core-util-is":"1.0.3","inherits":"2.0.4","isarray":"1.0.0","process-nextick-args":"2.0.1","safe-buffer":"5.1.2","string_decoder":"1.1.1","util-deprecate":"1.0.2"}},"safe-buffer":{"version":"5.1.2","dev":true,"integrity":"sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="},"string_decoder":{"version":"1.1.1","dev":true,"integrity":"sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==","requires":{"safe-buffer":"5.1.2"}}}},"irregular-plurals":{"version":"3.3.0","dev":true,"integrity":"sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g=="},"is-accessor-descriptor":{"version":"0.1.6","dev":true,"integrity":"sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=","requires":{"kind-of":"3.2.2"},"dependencies":{"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"is-alphabetical":{"version":"1.0.4","dev":true,"integrity":"sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg=="},"is-alphanumerical":{"version":"1.0.4","dev":true,"integrity":"sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==","requires":{"is-alphabetical":"1.0.4","is-decimal":"1.0.4"}},"is-arrayish":{"version":"0.2.1","dev":true,"integrity":"sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="},"is-bigint":{"version":"1.0.4","dev":true,"integrity":"sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==","requires":{"has-bigints":"1.0.1"}},"is-boolean-object":{"version":"1.1.2","dev":true,"integrity":"sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==","requires":{"call-bind":"1.0.2","has-tostringtag":"1.0.0"}},"is-buffer":{"version":"1.1.6","dev":true,"integrity":"sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="},"is-builtin-module":{"version":"3.1.0","dev":true,"integrity":"sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg==","requires":{"builtin-modules":"3.2.0"}},"is-callable":{"version":"1.2.4","dev":true,"integrity":"sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="},"is-core-module":{"version":"2.8.1","dev":true,"integrity":"sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==","requires":{"has":"1.0.3"}},"is-data-descriptor":{"version":"0.1.4","dev":true,"integrity":"sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=","requires":{"kind-of":"3.2.2"},"dependencies":{"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"is-date-object":{"version":"1.0.5","dev":true,"integrity":"sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==","requires":{"has-tostringtag":"1.0.0"}},"is-decimal":{"version":"1.0.4","dev":true,"integrity":"sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw=="},"is-descriptor":{"version":"0.1.6","dev":true,"integrity":"sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==","requires":{"is-accessor-descriptor":"0.1.6","is-data-descriptor":"0.1.4","kind-of":"5.1.0"}},"is-docker":{"version":"2.2.1","dev":true,"integrity":"sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="},"is-even":{"version":"1.0.0","dev":true,"integrity":"sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY=","requires":{"is-odd":"0.1.2"},"dependencies":{"is-number":{"version":"3.0.0","dev":true,"integrity":"sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=","requires":{"kind-of":"3.2.2"}},"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"is-expression":{"version":"4.0.0","dev":true,"integrity":"sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==","requires":{"acorn":"7.4.1","object-assign":"4.1.1"}},"is-extendable":{"version":"0.1.1","dev":true,"integrity":"sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="},"is-extglob":{"version":"2.1.1","dev":true,"integrity":"sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="},"is-fullwidth-code-point":{"version":"3.0.0","dev":true,"integrity":"sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="},"is-get-set-prop":{"version":"1.0.0","dev":true,"integrity":"sha1-JzGHfk14pqae3M5rudaLB3nnYxI=","requires":{"get-set-props":"0.1.0","lowercase-keys":"1.0.1"}},"is-glob":{"version":"4.0.3","dev":true,"integrity":"sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==","requires":{"is-extglob":"2.1.1"}},"is-hexadecimal":{"version":"1.0.4","dev":true,"integrity":"sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="},"is-js-type":{"version":"2.0.0","dev":true,"integrity":"sha1-c2FwBtZZtOtHKbunR9KHgt8PfiI=","requires":{"js-types":"1.0.0"}},"is-negative-zero":{"version":"2.0.2","dev":true,"integrity":"sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA=="},"is-number":{"version":"7.0.0","dev":true,"integrity":"sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="},"is-number-object":{"version":"1.0.6","dev":true,"integrity":"sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==","requires":{"has-tostringtag":"1.0.0"}},"is-obj":{"version":"2.0.0","dev":true,"integrity":"sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="},"is-obj-prop":{"version":"1.0.0","dev":true,"integrity":"sha1-s03nnEULjXxzqyzfZ9yHWtuF+A4=","requires":{"lowercase-keys":"1.0.1","obj-props":"1.3.0"}},"is-odd":{"version":"0.1.2","dev":true,"integrity":"sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc=","requires":{"is-number":"3.0.0"}},"is-path-cwd":{"version":"2.2.0","dev":true,"integrity":"sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ=="},"is-path-inside":{"version":"3.0.3","dev":true,"integrity":"sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="},"is-plain-obj":{"version":"1.1.0","dev":true,"integrity":"sha1-caUMhCnfync8kqOQpKA7OfzVHT4="},"is-plain-object":{"version":"5.0.0","dev":true,"integrity":"sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="},"is-proto-prop":{"version":"2.0.0","dev":true,"integrity":"sha512-jl3NbQ/fGLv5Jhan4uX+Ge9ohnemqyblWVVCpAvtTQzNFvV2xhJq+esnkIbYQ9F1nITXoLfDDQLp7LBw/zzncg==","requires":{"lowercase-keys":"1.0.1","proto-props":"2.0.0"}},"is-regex":{"version":"1.1.4","dev":true,"integrity":"sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==","requires":{"call-bind":"1.0.2","has-tostringtag":"1.0.0"}},"is-self-closing":{"version":"1.0.1","dev":true,"integrity":"sha512-E+60FomW7Blv5GXTlYee2KDrnG6srxF7Xt1SjrhWUGUEsTFIqY/nq2y3DaftCsgUMdh89V07IVfhY9KIJhLezg==","requires":{"self-closing-tags":"1.0.1"}},"is-shared-array-buffer":{"version":"1.0.1","dev":true,"integrity":"sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA=="},"is-ssh":{"version":"1.3.3","dev":true,"integrity":"sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==","requires":{"protocols":"1.4.8"}},"is-stream":{"version":"2.0.1","dev":true,"integrity":"sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="},"is-string":{"version":"1.0.7","dev":true,"integrity":"sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==","requires":{"has-tostringtag":"1.0.0"}},"is-symbol":{"version":"1.0.4","dev":true,"integrity":"sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==","requires":{"has-symbols":"1.0.2"}},"is-text-path":{"version":"1.0.1","dev":true,"integrity":"sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=","requires":{"text-extensions":"1.9.0"}},"is-typedarray":{"version":"1.0.0","dev":true,"integrity":"sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="},"is-unicode-supported":{"version":"0.1.0","dev":true,"integrity":"sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="},"is-utf8":{"version":"0.2.1","dev":true,"integrity":"sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="},"is-weakref":{"version":"1.0.2","dev":true,"integrity":"sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==","requires":{"call-bind":"1.0.2"}},"is-windows":{"version":"1.0.2","dev":true,"integrity":"sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="},"is-wsl":{"version":"2.2.0","dev":true,"integrity":"sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==","requires":{"is-docker":"2.2.1"}},"isarray":{"version":"1.0.0","dev":true,"integrity":"sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="},"isexe":{"version":"2.0.0","dev":true,"integrity":"sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="},"isobject":{"version":"3.0.1","dev":true,"integrity":"sha1-TkMekrEalzFjaqH5yNHMvP2reN8="},"issue-parser":{"version":"6.0.0","dev":true,"integrity":"sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==","requires":{"lodash.capitalize":"4.2.1","lodash.escaperegexp":"4.1.2","lodash.isplainobject":"4.0.6","lodash.isstring":"4.0.1","lodash.uniqby":"4.7.0"}},"java-parser":{"version":"2.0.1","dev":true,"integrity":"sha512-IPzs8LN8drvAZKbgW1MLLsrEeW4TwSy714I6ZHlEGNV6/42S2xRU5zDn3lP6uZQakwi7nyC00T6lZvwEnBujzw==","requires":{"chevrotain":"6.5.0","lodash":"4.17.21"}},"java-properties":{"version":"1.0.2","dev":true,"integrity":"sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ=="},"jju":{"version":"1.4.0","dev":true,"integrity":"sha1-o6vicYryQaKykE+EpiWXDzia4yo="},"js-tokens":{"version":"4.0.0","dev":true,"integrity":"sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="},"js-types":{"version":"1.0.0","dev":true,"integrity":"sha1-0kLmSU7Vcq08koCfyL7X92h8vwM="},"js-yaml":{"version":"4.1.0","dev":true,"integrity":"sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==","requires":{"argparse":"2.0.1"}},"jsdoc-type-pratt-parser":{"version":"2.2.3","dev":true,"integrity":"sha512-QPyxq62Q8veBSDtDrWmqaEPjSCeknUV9dH/OAGt3q9an8qC8UQDqitQiw1NvoMskIESpoRZ6qzt4H3rlK0xo8A=="},"jsdoctypeparser":{"version":"9.0.0","dev":true,"integrity":"sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw=="},"jsesc":{"version":"2.5.2","dev":true,"integrity":"sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="},"json-buffer":{"version":"3.0.1","dev":true,"integrity":"sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="},"json-parse-better-errors":{"version":"1.0.2","dev":true,"integrity":"sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="},"json-parse-even-better-errors":{"version":"2.3.1","dev":true,"integrity":"sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="},"json-schema":{"version":"0.4.0","dev":true,"integrity":"sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="},"json-schema-migrate":{"version":"2.0.0","dev":true,"integrity":"sha512-r38SVTtojDRp4eD6WsCqiE0eNDt4v1WalBXb9cyZYw9ai5cGtBwzRNWjHzJl38w6TxFkXAIA7h+fyX3tnrAFhQ==","requires":{"ajv":"8.10.0"}},"json-schema-traverse":{"version":"0.4.1","dev":true,"integrity":"sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="},"json-stable-stringify-without-jsonify":{"version":"1.0.1","dev":true,"integrity":"sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="},"json-stringify-safe":{"version":"5.0.1","dev":true,"integrity":"sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="},"json2xml":{"version":"0.1.3","dev":true,"integrity":"sha1-mufCIL7dfGamaOJvesGC9nBOyiE="},"json5":{"version":"1.0.1","dev":true,"integrity":"sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==","requires":{"minimist":"1.2.5"}},"jsonc-eslint-parser":{"version":"1.4.1","dev":true,"integrity":"sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==","requires":{"acorn":"7.4.1","eslint-utils":"2.1.0","eslint-visitor-keys":"1.3.0","espree":"6.2.1","semver":"6.3.0"},"dependencies":{"eslint-utils":{"version":"2.1.0","dev":true,"integrity":"sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==","requires":{"eslint-visitor-keys":"1.3.0"}},"eslint-visitor-keys":{"version":"1.3.0","dev":true,"integrity":"sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="}}},"jsonfile":{"version":"6.1.0","dev":true,"integrity":"sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==","requires":{"universalify":"2.0.0"}},"jsonparse":{"version":"1.3.1","dev":true,"integrity":"sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="},"keyv":{"version":"4.1.1","dev":true,"integrity":"sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==","requires":{"json-buffer":"3.0.1"}},"kind-of":{"version":"6.0.3","dev":true,"integrity":"sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="},"kleur":{"version":"3.0.3","dev":true,"integrity":"sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="},"klona":{"version":"2.0.5","dev":true,"integrity":"sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ=="},"lazy-cache":{"version":"2.0.2","dev":true,"integrity":"sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=","requires":{"set-getter":"0.1.1"},"dependencies":{"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"leasot":{"version":"12.0.0","dev":true,"integrity":"sha512-TMe3cJTRUMpXsOFNXCig5U84wM44y84vawkl2fC7iAJif88l/b7BtTt49VrkMsivlxlqHYVu5PjuxB9sRQf39w==","requires":{"async":"3.2.3","chalk":"4.1.2","commander":"7.2.0","eol":"0.9.1","get-stdin":"8.0.0","globby":"11.1.0","json2xml":"0.1.3","lodash":"4.17.21","log-symbols":"4.1.0","strip-ansi":"6.0.1","text-table":"0.2.0"},"dependencies":{"commander":{"version":"7.2.0","dev":true,"integrity":"sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="}}},"levn":{"version":"0.4.1","dev":true,"integrity":"sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==","requires":{"prelude-ls":"1.2.1","type-check":"0.4.0"}},"lilconfig":{"version":"2.0.4","dev":true,"integrity":"sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA=="},"lines-and-columns":{"version":"1.2.4","dev":true,"integrity":"sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="},"linguist-languages":{"version":"7.15.0","dev":true,"integrity":"sha512-qkSSNDjDDycZ2Wcw+GziNBB3nNo3ddYUInM/PL8Amgwbd9RQ/BKGj2/1d6mdxKgBFnUqZuaDbkIwkE4KUwwmtQ=="},"lint-staged":{"version":"12.1.2","dev":true,"integrity":"sha512-bSMcQVqMW98HLLLR2c2tZ+vnDCnx4fd+0QJBQgN/4XkdspGRPc8DGp7UuOEBe1ApCfJ+wXXumYnJmU+wDo7j9A==","requires":{"cli-truncate":"3.1.0","colorette":"2.0.16","commander":"8.3.0","debug":"4.3.3","enquirer":"2.3.6","execa":"5.1.1","lilconfig":"2.0.4","listr2":"3.14.0","micromatch":"4.0.4","normalize-path":"3.0.0","object-inspect":"1.12.0","string-argv":"0.3.1","supports-color":"9.2.1","yaml":"1.10.2"},"dependencies":{"ansi-escapes":{"version":"4.3.2","dev":true,"integrity":"sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==","requires":{"type-fest":"0.21.3"}},"ansi-regex":{"version":"6.0.1","dev":true,"integrity":"sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="},"ansi-styles":{"version":"6.1.0","dev":true,"integrity":"sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ=="},"cli-cursor":{"version":"3.1.0","dev":true,"integrity":"sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==","requires":{"restore-cursor":"3.1.0"}},"emoji-regex":{"version":"9.2.2","dev":true,"integrity":"sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="},"is-fullwidth-code-point":{"version":"4.0.0","dev":true,"integrity":"sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="},"restore-cursor":{"version":"3.1.0","dev":true,"integrity":"sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==","requires":{"onetime":"5.1.2","signal-exit":"3.0.7"}},"rxjs":{"version":"7.5.4","dev":true,"integrity":"sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==","requires":{"tslib":"2.3.1"}},"string-width":{"version":"5.1.0","dev":true,"integrity":"sha512-7x54QnN21P+XL/v8SuNKvfgsUre6PXpN7mc77N3HlZv+f1SBRGmjxtOud2Z6FZ8DmdkD/IdjCaf9XXbnqmTZGQ==","requires":{"eastasianwidth":"0.2.0","emoji-regex":"9.2.2","strip-ansi":"7.0.1"}},"strip-ansi":{"version":"7.0.1","dev":true,"integrity":"sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==","requires":{"ansi-regex":"6.0.1"}},"supports-color":{"version":"9.2.1","dev":true,"integrity":"sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ=="},"type-fest":{"version":"0.21.3","dev":true,"integrity":"sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="}}},"liquidjs":{"version":"9.28.5","dev":true,"integrity":"sha512-K9mSxsAilSezwBtEUtaTpJy6cff3YLoBShyjNLExk5tk/rkygWBPJYWCoJIXKdKdXs/2M/WG3EXYU1ssAtSrsQ=="},"listr2":{"version":"3.14.0","dev":true,"integrity":"sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==","requires":{"cli-truncate":"2.1.0","colorette":"2.0.16","enquirer":"2.3.6","log-update":"4.0.0","p-map":"4.0.0","rfdc":"1.3.0","rxjs":"7.5.4","through":"2.3.8","wrap-ansi":"7.0.0"},"dependencies":{"cli-truncate":{"version":"2.1.0","dev":true,"integrity":"sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==","requires":{"slice-ansi":"3.0.0","string-width":"4.2.3"}},"slice-ansi":{"version":"3.0.0","dev":true,"integrity":"sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==","requires":{"ansi-styles":"4.3.0","astral-regex":"2.0.0","is-fullwidth-code-point":"3.0.0"}}}},"load-json-file":{"version":"4.0.0","dev":true,"integrity":"sha1-L19Fq5HjMhYjT9U62rZo607AmTs=","requires":{"graceful-fs":"4.2.9","parse-json":"4.0.0","pify":"3.0.0","strip-bom":"3.0.0"}},"locate-path":{"version":"5.0.0","dev":true,"integrity":"sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==","requires":{"p-locate":"4.1.0"}},"lodash":{"version":"4.17.21","dev":true,"integrity":"sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="},"lodash._reinterpolate":{"version":"3.0.0","dev":true,"integrity":"sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0="},"lodash.camelcase":{"version":"4.3.0","dev":true,"integrity":"sha1-soqmKIorn8ZRA1x3EfZathkDMaY="},"lodash.capitalize":{"version":"4.2.1","dev":true,"integrity":"sha1-+CbJtOKoUR2E46yinbBeGk87cqk="},"lodash.escaperegexp":{"version":"4.1.2","dev":true,"integrity":"sha1-ZHYsSGGAglGKw99Mz11YhtriA0c="},"lodash.get":{"version":"4.4.2","dev":true,"integrity":"sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="},"lodash.ismatch":{"version":"4.4.0","dev":true,"integrity":"sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc="},"lodash.isplainobject":{"version":"4.0.6","dev":true,"integrity":"sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="},"lodash.isstring":{"version":"4.0.1","dev":true,"integrity":"sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="},"lodash.kebabcase":{"version":"4.1.1","dev":true,"integrity":"sha1-hImxyw0p/4gZXM7KRI/21swpXDY="},"lodash.last":{"version":"3.0.0","dev":true,"integrity":"sha1-JC9mMRLdTG5jcoxgo8kJ0b2tvUw="},"lodash.map":{"version":"4.6.0","dev":true,"integrity":"sha1-dx7Hg540c9nEzeKLGTlMNWL09tM="},"lodash.merge":{"version":"4.6.2","dev":true,"integrity":"sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="},"lodash.snakecase":{"version":"4.1.1","dev":true,"integrity":"sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40="},"lodash.template":{"version":"4.5.0","dev":true,"integrity":"sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==","requires":{"lodash._reinterpolate":"3.0.0","lodash.templatesettings":"4.2.0"}},"lodash.templatesettings":{"version":"4.2.0","dev":true,"integrity":"sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==","requires":{"lodash._reinterpolate":"3.0.0"}},"lodash.uniqby":{"version":"4.7.0","dev":true,"integrity":"sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI="},"lodash.upperfirst":{"version":"4.3.1","dev":true,"integrity":"sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984="},"lodash.zip":{"version":"4.2.0","dev":true,"integrity":"sha1-7GZi5IlkCO1KtsVCo5kLcswIACA="},"lodash.zipobject":{"version":"4.1.3","dev":true,"integrity":"sha1-s5n1q6j/YqdG9peb8gshT5ZNvvg="},"log-ok":{"version":"0.1.1","dev":true,"integrity":"sha1-vqPdNqzQuKckDXhza1uXxlREozQ=","requires":{"ansi-green":"0.1.1","success-symbol":"0.1.0"}},"log-symbols":{"version":"4.1.0","dev":true,"integrity":"sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==","requires":{"chalk":"4.1.2","is-unicode-supported":"0.1.0"}},"log-update":{"version":"4.0.0","dev":true,"integrity":"sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==","requires":{"ansi-escapes":"4.3.2","cli-cursor":"3.1.0","slice-ansi":"4.0.0","wrap-ansi":"6.2.0"},"dependencies":{"slice-ansi":{"version":"4.0.0","dev":true,"integrity":"sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==","requires":{"ansi-styles":"4.3.0","astral-regex":"2.0.0","is-fullwidth-code-point":"3.0.0"}},"wrap-ansi":{"version":"6.2.0","dev":true,"integrity":"sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==","requires":{"ansi-styles":"4.3.0","string-width":"4.2.3","strip-ansi":"6.0.1"}}}},"log-utils":{"version":"0.2.1","dev":true,"integrity":"sha1-pMIXoN2aUFFdm5ICBgkas9TgMc8=","requires":{"ansi-colors":"0.2.0","error-symbol":"0.1.0","info-symbol":"0.1.0","log-ok":"0.1.1","success-symbol":"0.1.0","time-stamp":"1.1.0","warning-symbol":"0.1.0"}},"logging-helpers":{"version":"1.0.0","dev":true,"integrity":"sha512-qyIh2goLt1sOgQQrrIWuwkRjUx4NUcEqEGAcYqD8VOnOC6ItwkrVE8/tA4smGpjzyp4Svhc6RodDp9IO5ghpyA==","requires":{"isobject":"3.0.1","log-utils":"0.2.1"}},"longest":{"version":"2.0.1","dev":true,"integrity":"sha1-eB4YMpaqlPbU2RbcM10NF676I/g="},"lowercase-keys":{"version":"1.0.1","dev":true,"integrity":"sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="},"lru-cache":{"version":"6.0.0","dev":true,"integrity":"sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==","requires":{"yallist":"4.0.0"}},"lz-string":{"version":"1.4.4","dev":true,"integrity":"sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY="},"make-dir":{"version":"3.1.0","dev":true,"integrity":"sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==","requires":{"semver":"6.3.0"}},"make-error":{"version":"1.3.6","dev":true,"integrity":"sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="},"map-age-cleaner":{"version":"0.1.3","dev":true,"integrity":"sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==","requires":{"p-defer":"1.0.0"}},"map-cache":{"version":"0.2.2","dev":true,"integrity":"sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="},"map-obj":{"version":"4.3.0","dev":true,"integrity":"sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ=="},"map-visit":{"version":"1.0.0","dev":true,"integrity":"sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=","requires":{"object-visit":"1.0.1"}},"marked":{"version":"4.0.12","dev":true,"integrity":"sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ=="},"marked-terminal":{"version":"5.1.1","dev":true,"integrity":"sha512-+cKTOx9P4l7HwINYhzbrBSyzgxO2HaHKGZGuB1orZsMIgXYaJyfidT81VXRdpelW/PcHEWxywscePVgI/oUF6g==","requires":{"ansi-escapes":"5.0.0","cardinal":"2.1.1","chalk":"5.0.0","cli-table3":"0.6.1","marked":"4.0.12","node-emoji":"1.11.0","supports-hyperlinks":"2.2.0"},"dependencies":{"chalk":{"version":"5.0.0","dev":true,"integrity":"sha512-/duVOqst+luxCQRKEo4bNxinsOQtMP80ZYm7mMqzuh5PociNL0PvmHFvREJ9ueYL2TxlHjBcmLCdmocx9Vg+IQ=="}}},"mdast-util-from-markdown":{"version":"0.8.5","dev":true,"integrity":"sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==","requires":{"@types/mdast":"3.0.10","mdast-util-to-string":"2.0.0","micromark":"2.11.4","parse-entities":"2.0.0","unist-util-stringify-position":"2.0.3"}},"mdast-util-to-string":{"version":"2.0.0","dev":true,"integrity":"sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w=="},"mem":{"version":"8.1.1","dev":true,"integrity":"sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==","requires":{"map-age-cleaner":"0.1.3","mimic-fn":"3.1.0"}},"meow":{"version":"8.1.2","dev":true,"integrity":"sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==","requires":{"@types/minimist":"1.2.2","camelcase-keys":"6.2.2","decamelize-keys":"1.1.0","hard-rejection":"2.1.0","minimist-options":"4.1.0","normalize-package-data":"3.0.3","read-pkg-up":"7.0.1","redent":"3.0.0","trim-newlines":"3.0.1","type-fest":"0.18.1","yargs-parser":"20.2.9"}},"merge":{"version":"2.1.1","dev":true,"integrity":"sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w=="},"merge-stream":{"version":"2.0.0","dev":true,"integrity":"sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="},"merge2":{"version":"1.4.1","dev":true,"integrity":"sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="},"micromark":{"version":"2.11.4","dev":true,"integrity":"sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==","requires":{"debug":"4.3.3","parse-entities":"2.0.0"}},"micromatch":{"version":"4.0.4","dev":true,"integrity":"sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==","requires":{"braces":"3.0.2","picomatch":"2.3.1"}},"mime":{"version":"3.0.0","dev":true,"integrity":"sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="},"mime-db":{"version":"1.51.0","dev":true,"integrity":"sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="},"mime-types":{"version":"2.1.34","dev":true,"integrity":"sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==","requires":{"mime-db":"1.51.0"}},"mimic-fn":{"version":"2.1.0","dev":true,"integrity":"sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="},"mimic-response":{"version":"1.0.1","dev":true,"integrity":"sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="},"min-indent":{"version":"1.0.1","dev":true,"integrity":"sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="},"minimatch":{"version":"3.1.2","dev":true,"integrity":"sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==","requires":{"brace-expansion":"1.1.11"}},"minimist":{"version":"1.2.5","dev":true,"integrity":"sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="},"minimist-options":{"version":"4.1.0","dev":true,"integrity":"sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==","requires":{"arrify":"1.0.1","is-plain-obj":"1.1.0","kind-of":"6.0.3"}},"mixin-deep":{"version":"1.3.2","dev":true,"integrity":"sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==","requires":{"for-in":"1.0.2","is-extendable":"1.0.1"},"dependencies":{"is-extendable":{"version":"1.0.1","dev":true,"integrity":"sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==","requires":{"is-plain-object":"2.0.4"}}}},"modify-values":{"version":"1.0.1","dev":true,"integrity":"sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw=="},"moment":{"version":"2.29.1","dev":true,"integrity":"sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="},"ms":{"version":"2.1.2","dev":true,"integrity":"sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="},"multimap":{"version":"1.1.0","dev":true,"integrity":"sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw=="},"mute-stream":{"version":"0.0.7","dev":true,"integrity":"sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="},"mvdan-sh":{"version":"0.5.0","dev":true,"integrity":"sha512-UWbdl4LHd2fUnaEcOUFVWRdWGLkNoV12cKVIPiirYd8qM5VkCoCTXErlDubevrkEG7kGohvjRxAlTQmOqG80tw=="},"nanomatch":{"version":"1.2.13","dev":true,"integrity":"sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==","requires":{"arr-diff":"4.0.0","array-unique":"0.3.2","define-property":"2.0.2","extend-shallow":"3.0.2","fragment-cache":"0.2.1","is-windows":"1.0.2","kind-of":"6.0.3","object.pick":"1.3.0","regex-not":"1.0.2","snapdragon":"0.8.2","to-regex":"3.0.2"}},"natural-compare":{"version":"1.4.0","dev":true,"integrity":"sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc="},"natural-compare-lite":{"version":"1.4.0","dev":true,"integrity":"sha1-F7CVgZiJef3a/gIB6TG6kzyWy7Q="},"neo-async":{"version":"2.6.2","dev":true,"integrity":"sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="},"nerf-dart":{"version":"1.0.0","dev":true,"integrity":"sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo="},"node-emoji":{"version":"1.11.0","dev":true,"integrity":"sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==","requires":{"lodash":"4.17.21"}},"node-fetch":{"version":"2.6.7","dev":true,"integrity":"sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==","requires":{"whatwg-url":"5.0.0"}},"node-releases":{"version":"2.0.2","dev":true,"integrity":"sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg=="},"node-sql-parser":{"version":"4.1.1","dev":true,"integrity":"sha512-VV5ojNHj0ZOjA66DbzbJwESZmgE8OtfJamrsOq0CYxCatFeHh24uBoVP+GXo06qR4T4hzol67hZ62gVf/oZBvA==","requires":{"big-integer":"1.6.51"}},"normalize-package-data":{"version":"3.0.3","dev":true,"integrity":"sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==","requires":{"hosted-git-info":"4.1.0","is-core-module":"2.8.1","semver":"7.3.5","validate-npm-package-license":"3.0.4"}},"normalize-path":{"version":"3.0.0","dev":true,"integrity":"sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="},"normalize-url":{"version":"6.1.0","dev":true,"integrity":"sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="},"npm":{"version":"8.5.1","dev":true,"integrity":"sha512-zHrOHAatEPJ59o2JIPlhgc9LX9mb8xFrqu4kiiul4w1IGMTtKn2lqRiGIRKU0or69NSLXNmqbCP9bNJIr/wB6Q=="},"npm-run-path":{"version":"4.0.1","dev":true,"integrity":"sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==","requires":{"path-key":"3.1.1"}},"obj-props":{"version":"1.3.0","dev":true,"integrity":"sha512-k2Xkjx5wn6eC3537SWAXHzB6lkI81kS+icMKMkh4nG3w7shWG6MaWOBrNvhWVOszrtL5uxdfymQQfPUxwY+2eg=="},"object-assign":{"version":"4.1.1","dev":true,"integrity":"sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="},"object-copy":{"version":"0.1.0","dev":true,"integrity":"sha1-fn2Fi3gb18mRpBupde04EnVOmYw=","requires":{"copy-descriptor":"0.1.1","define-property":"0.2.5","kind-of":"3.2.2"}},"object-inspect":{"version":"1.12.0","dev":true,"integrity":"sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="},"object-keys":{"version":"1.1.1","dev":true,"integrity":"sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="},"object-visit":{"version":"1.0.1","dev":true,"integrity":"sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=","requires":{"isobject":"3.0.1"}},"object.assign":{"version":"4.1.2","dev":true,"integrity":"sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3","has-symbols":"1.0.2","object-keys":"1.1.1"}},"object.pick":{"version":"1.3.0","dev":true,"integrity":"sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=","requires":{"isobject":"3.0.1"}},"object.values":{"version":"1.1.5","dev":true,"integrity":"sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3","es-abstract":"1.19.1"}},"once":{"version":"1.4.0","dev":true,"integrity":"sha1-WDsap3WWHUsROsF9nFC6753Xa9E=","requires":{"wrappy":"1.0.2"}},"onetime":{"version":"5.1.2","dev":true,"integrity":"sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==","requires":{"mimic-fn":"2.1.0"}},"only-allow":{"version":"1.0.0","dev":true,"integrity":"sha512-DQazysAz1cw8JxxYVqg+wXWSm6K7smVqORwS+dtXQWPeRwjsWvRpxMRa5FPiOPxH4e05xCDv12ncAUF8JlVukw==","requires":{"boxen":"4.2.0","which-pm-runs":"1.1.0"},"dependencies":{"boxen":{"version":"4.2.0","dev":true,"integrity":"sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==","requires":{"ansi-align":"3.0.1","camelcase":"5.3.1","chalk":"3.0.0","cli-boxes":"2.2.1","string-width":"4.2.3","term-size":"2.2.1","type-fest":"0.8.1","widest-line":"3.1.0"}},"chalk":{"version":"3.0.0","dev":true,"integrity":"sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==","requires":{"ansi-styles":"4.3.0","supports-color":"7.2.0"}},"type-fest":{"version":"0.8.1","dev":true,"integrity":"sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="}}},"open":{"version":"8.4.0","dev":true,"integrity":"sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==","requires":{"define-lazy-prop":"2.0.0","is-docker":"2.2.1","is-wsl":"2.2.0"}},"open-cli":{"version":"7.0.1","dev":true,"integrity":"sha512-w//Mb5nLGTu9aIAsAehgxV+CGEkd+P3CbdoTW8y2coQ/fmGXBSrea0i4RBqGnd9prSPX1akrBYc0e3NnWM4SPA==","requires":{"file-type":"16.5.3","get-stdin":"9.0.0","meow":"10.1.2","open":"8.4.0","tempy":"1.0.1"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"camelcase":{"version":"6.3.0","dev":true,"integrity":"sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="},"camelcase-keys":{"version":"7.0.2","dev":true,"integrity":"sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==","requires":{"camelcase":"6.3.0","map-obj":"4.3.0","quick-lru":"5.1.1","type-fest":"1.4.0"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"decamelize":{"version":"5.0.1","dev":true,"integrity":"sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA=="},"find-up":{"version":"5.0.0","dev":true,"integrity":"sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==","requires":{"locate-path":"6.0.0","path-exists":"4.0.0"}},"get-stdin":{"version":"9.0.0","dev":true,"integrity":"sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA=="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"indent-string":{"version":"5.0.0","dev":true,"integrity":"sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="},"locate-path":{"version":"6.0.0","dev":true,"integrity":"sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==","requires":{"p-locate":"5.0.0"}},"meow":{"version":"10.1.2","dev":true,"integrity":"sha512-zbuAlN+V/sXlbGchNS9WTWjUzeamwMt/BApKCJi7B0QyZstZaMx0n4Unll/fg0njGtMdC9UP5SAscvOCLYdM+Q==","requires":{"@types/minimist":"1.2.2","camelcase-keys":"7.0.2","decamelize":"5.0.1","decamelize-keys":"1.1.0","hard-rejection":"2.1.0","minimist-options":"4.1.0","normalize-package-data":"3.0.3","read-pkg-up":"8.0.0","redent":"4.0.0","trim-newlines":"4.0.2","type-fest":"1.4.0","yargs-parser":"20.2.9"}},"p-limit":{"version":"3.1.0","dev":true,"integrity":"sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==","requires":{"yocto-queue":"0.1.0"}},"p-locate":{"version":"5.0.0","dev":true,"integrity":"sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==","requires":{"p-limit":"3.1.0"}},"quick-lru":{"version":"5.1.1","dev":true,"integrity":"sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="},"read-pkg":{"version":"6.0.0","dev":true,"integrity":"sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==","requires":{"@types/normalize-package-data":"2.4.1","normalize-package-data":"3.0.3","parse-json":"5.2.0","type-fest":"1.4.0"}},"read-pkg-up":{"version":"8.0.0","dev":true,"integrity":"sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==","requires":{"find-up":"5.0.0","read-pkg":"6.0.0","type-fest":"1.4.0"}},"redent":{"version":"4.0.0","dev":true,"integrity":"sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==","requires":{"indent-string":"5.0.0","strip-indent":"4.0.0"}},"strip-indent":{"version":"4.0.0","dev":true,"integrity":"sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==","requires":{"min-indent":"1.0.1"}},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"trim-newlines":{"version":"4.0.2","dev":true,"integrity":"sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew=="},"type-fest":{"version":"1.4.0","dev":true,"integrity":"sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="}}},"optionator":{"version":"0.9.1","dev":true,"integrity":"sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==","requires":{"deep-is":"0.1.4","fast-levenshtein":"2.0.6","levn":"0.4.1","prelude-ls":"1.2.1","type-check":"0.4.0","word-wrap":"1.2.3"}},"os-tmpdir":{"version":"1.0.2","dev":true,"integrity":"sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="},"p-cancelable":{"version":"2.1.1","dev":true,"integrity":"sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="},"p-defer":{"version":"1.0.0","dev":true,"integrity":"sha1-n26xgvbJqozXQwBKfU+WsZaw+ww="},"p-each-series":{"version":"2.2.0","dev":true,"integrity":"sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA=="},"p-filter":{"version":"2.1.0","dev":true,"integrity":"sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==","requires":{"p-map":"2.1.0"}},"p-is-promise":{"version":"3.0.0","dev":true,"integrity":"sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ=="},"p-limit":{"version":"2.3.0","dev":true,"integrity":"sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==","requires":{"p-try":"2.2.0"}},"p-locate":{"version":"4.1.0","dev":true,"integrity":"sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==","requires":{"p-limit":"2.3.0"}},"p-map":{"version":"4.0.0","dev":true,"integrity":"sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==","requires":{"aggregate-error":"3.1.0"}},"p-reduce":{"version":"2.1.0","dev":true,"integrity":"sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw=="},"p-retry":{"version":"4.6.1","dev":true,"integrity":"sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==","requires":{"@types/retry":"0.12.1","retry":"0.13.1"}},"p-try":{"version":"2.2.0","dev":true,"integrity":"sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="},"parent-module":{"version":"1.0.1","dev":true,"integrity":"sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==","requires":{"callsites":"3.1.0"}},"parse-author":{"version":"2.0.0","dev":true,"integrity":"sha1-00YL8d3Q367tQtp1QkLmX7aEqB8=","requires":{"author-regex":"1.0.0"}},"parse-entities":{"version":"2.0.0","dev":true,"integrity":"sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==","requires":{"character-entities":"1.2.4","character-entities-legacy":"1.1.4","character-reference-invalid":"1.1.4","is-alphanumerical":"1.0.4","is-decimal":"1.0.4","is-hexadecimal":"1.0.4"}},"parse-gitignore":{"version":"1.0.1","dev":true,"integrity":"sha512-UGyowyjtx26n65kdAMWhm6/3uy5uSrpcuH7tt+QEVudiBoVS+eqHxD5kbi9oWVRwj7sCzXqwuM+rUGw7earl6A=="},"parse-json":{"version":"5.2.0","dev":true,"integrity":"sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==","requires":{"@babel/code-frame":"7.16.7","error-ex":"1.3.2","json-parse-even-better-errors":"2.3.1","lines-and-columns":"1.2.4"}},"parse-passwd":{"version":"1.0.0","dev":true,"integrity":"sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY="},"parse-path":{"version":"4.0.3","dev":true,"integrity":"sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==","requires":{"is-ssh":"1.3.3","protocols":"1.4.8","qs":"6.10.3","query-string":"6.14.1"}},"pascalcase":{"version":"0.1.1","dev":true,"integrity":"sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="},"path-exists":{"version":"4.0.0","dev":true,"integrity":"sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="},"path-is-absolute":{"version":"1.0.1","dev":true,"integrity":"sha1-F0uSaHNVNP+8es5r9TpanhtcX18="},"path-key":{"version":"3.1.1","dev":true,"integrity":"sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="},"path-parse":{"version":"1.0.7","dev":true,"integrity":"sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="},"path-type":{"version":"4.0.0","dev":true,"integrity":"sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="},"peek-readable":{"version":"4.1.0","dev":true,"integrity":"sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="},"php-parser":{"version":"https://codeload.github.com/glayzzle/php-parser/tar.gz/27abcb2337ac6450c068ef064982dfabf77916a5","dev":true,"resolved":"https://codeload.github.com/glayzzle/php-parser/tar.gz/27abcb2337ac6450c068ef064982dfabf77916a5"},"picocolors":{"version":"1.0.0","dev":true,"integrity":"sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="},"picomatch":{"version":"2.3.1","dev":true,"integrity":"sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="},"pify":{"version":"3.0.0","dev":true,"integrity":"sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="},"pkg-conf":{"version":"2.1.0","dev":true,"integrity":"sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=","requires":{"find-up":"2.1.0","load-json-file":"4.0.0"}},"plur":{"version":"4.0.0","dev":true,"integrity":"sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==","requires":{"irregular-plurals":"3.3.0"}},"pluralize":{"version":"8.0.0","dev":true,"integrity":"sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="},"posix-character-classes":{"version":"0.1.1","dev":true,"integrity":"sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="},"prelude-ls":{"version":"1.2.1","dev":true,"integrity":"sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="},"prettier":{"version":"2.5.1","dev":true,"integrity":"sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg=="},"prettier-config-sexy-mode":{"version":"1.0.1","dev":true,"integrity":"sha512-6ZdAv6LW2vq++c5fqoh2MWrIZk9fi67o608xizEPYKPMjGUNDsqjJ/pYiJLp7yDHgGerEERc4bRQqlh8a9W6YA==","requires":{"@prettier/plugin-php":"0.17.6","@prettier/plugin-pug":"1.19.2","@prettier/plugin-ruby":"2.0.0","@prettier/plugin-xml":"1.2.0","prettier":"2.5.1","prettier-plugin-go-template":"0.0.11","prettier-plugin-ini":"0.3.1","prettier-plugin-java":"1.6.1","prettier-plugin-jsdoc":"0.3.30","prettier-plugin-organize-imports":"2.3.4","prettier-plugin-package-perfection":"1.0.6","prettier-plugin-properties":"0.1.0","prettier-plugin-sh":"0.7.1","prettier-plugin-solidity":"1.0.0-beta.19","prettier-plugin-sql":"0.4.1","tslib":"2.3.1"},"dependencies":{"acorn":{"version":"7.4.1","dev":true,"integrity":"sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="},"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"commander":{"version":"4.1.1","dev":true,"integrity":"sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="},"emoji-regex":{"version":"10.0.0","dev":true,"integrity":"sha512-KmJa8l6uHi1HrBI34udwlzZY1jOEuID/ft4d8BSSEdRyap7PwBEt910453PJa5MuGvxkLqlt4Uvhu7tttFHViw=="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"mimic-fn":{"version":"3.1.0","dev":true,"integrity":"sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ=="},"resolve-from":{"version":"4.0.0","dev":true,"integrity":"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"prettier-linter-helpers":{"version":"1.0.0","dev":true,"integrity":"sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==","requires":{"fast-diff":"1.2.0"}},"prettier-package-json":{"version":"2.6.0","dev":true,"integrity":"sha512-CS7utu4Jfm6xxCrIA4zZiOtNIwZhq0EyHnK01vmliV2QRU+L6/Ywy1tB6uUpT9Lwt5qpvRMHNApa6jxoRHfafA==","requires":{"commander":"4.1.1","cosmiconfig":"7.0.1","fs-extra":"10.0.0","glob":"7.2.0","minimatch":"3.1.2","parse-author":"2.0.0","sort-object-keys":"1.1.3","sort-order":"1.0.1"}},"prettier-plugin-go-template":{"version":"0.0.11","dev":true,"integrity":"sha512-qtgoEjvbgmcDp9TOqYNgrPrA41s6S1UMyzMqjcxdxQahTX0webWfbamyA/x3XeBFEEJmgXrRAirzJrIVzImsMg==","requires":{"prettier":"2.5.1","ulid":"2.3.0"}},"prettier-plugin-ini":{"version":"0.3.1","dev":true,"integrity":"sha512-p2MPOpkf0ebBme4n8U3TD7/uC3azlEyPgbfgU/l5SCgAvSKmXmRMSxKxonhSoQAGtdwQWcEADJbmzl+X72yN4Q==","requires":{"prettier":"2.5.1"}},"prettier-plugin-java":{"version":"1.6.1","dev":true,"integrity":"sha512-kSY17V/P88nILlILb5iMp16TVJy6Ls9Jy4zAzI4+GsEuRDZH5VqRuLd8aJS1ImWxVgRjNBmoFOjxYyxkRM0SRA==","requires":{"java-parser":"2.0.1","lodash":"4.17.21","prettier":"2.3.1"},"dependencies":{"chevrotain":{"version":"6.5.0","dev":true,"integrity":"sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==","requires":{"regexp-to-ast":"0.4.0"}},"prettier":{"version":"2.3.1","dev":true,"integrity":"sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA=="},"regexp-to-ast":{"version":"0.4.0","dev":true,"integrity":"sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw=="}}},"prettier-plugin-jsdoc":{"version":"0.3.30","dev":true,"integrity":"sha512-BTBojOMmrUA1qsWLpJN5whUfU/E72WBUQAB5AvrDkha+O8TxmqaAivnuW+87ItYGRPBFWWzj2r5iWELhBml1Ag==","requires":{"binary-search-bounds":"2.0.5","comment-parser":"1.3.0","linguist-languages":"7.15.0","mdast-util-from-markdown":"0.8.5","prettier":"2.5.1"}},"prettier-plugin-organize-imports":{"version":"2.3.4","dev":true,"integrity":"sha512-R8o23sf5iVL/U71h9SFUdhdOEPsi3nm42FD/oDYIZ2PQa4TNWWuWecxln6jlIQzpZTDMUeO1NicJP6lLn2TtRw==","requires":{"prettier":"2.5.1","typescript":"4.5.5"}},"prettier-plugin-package-perfection":{"version":"1.0.6","dev":true,"integrity":"sha512-7nxpRYdAdda6xiwrVDYDxcnxPfBu58JJPGtoJOwh2uohE6RfnUJOOjV76woXG8kO+mBptHAQtqoT6lsLRvatqA==","requires":{"prettier-package-json":"2.6.0"}},"prettier-plugin-properties":{"version":"0.1.0","dev":true,"integrity":"sha512-lObSgVaTVWSyYWxKMOzRGmvQp64S1qumu5vS91ZlMc198ay8EGUuDH+Tc019iMJXc2KNpdAYif2qAJA6mjTkgA==","requires":{"dot-properties":"1.0.1","linguist-languages":"7.15.0","prettier":"2.5.1"}},"prettier-plugin-sh":{"version":"0.7.1","dev":true,"integrity":"sha512-2MWRdGOSz0yf/z2kTKF1AqxDuH9MZD8faoDAz5ySGphxssi9oyM3Ys+jp7AfqsCXvGUDbRA4EJOlKS0yZKAW6w==","requires":{"mvdan-sh":"0.5.0","prettier":"2.5.1"}},"prettier-plugin-solidity":{"version":"1.0.0-beta.19","dev":true,"integrity":"sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g==","requires":{"@solidity-parser/parser":"0.14.1","emoji-regex":"10.0.0","escape-string-regexp":"4.0.0","prettier":"2.5.1","semver":"7.3.5","solidity-comments-extractor":"0.0.7","string-width":"4.2.3"},"dependencies":{"escape-string-regexp":{"version":"4.0.0","dev":true,"integrity":"sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="}}},"prettier-plugin-sql":{"version":"0.4.1","dev":true,"integrity":"sha512-HSBn1f1ZbFIqWm9vVJcdQl1tzdh8qYZbBNK7z3iWhDO/DXM7gZH2HnS5r99e3tYqf34WIdo2nSCWaqylo+5fPw==","requires":{"node-sql-parser":"4.1.1","prettier":"2.5.1","sql-formatter":"4.0.2"}},"pretty-format":{"version":"26.6.2","dev":true,"integrity":"sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==","requires":{"@jest/types":"26.6.2","ansi-regex":"5.0.1","ansi-styles":"4.3.0","react-is":"17.0.2"}},"process-nextick-args":{"version":"2.0.1","dev":true,"integrity":"sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="},"prompts":{"version":"2.4.2","dev":true,"integrity":"sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==","requires":{"kleur":"3.0.3","sisteransi":"1.0.5"}},"proto-props":{"version":"2.0.0","dev":true,"integrity":"sha512-2yma2tog9VaRZY2mn3Wq51uiSW4NcPYT1cQdBagwyrznrilKSZwIZ0UG3ZPL/mx+axEns0hE35T5ufOYZXEnBQ=="},"protocols":{"version":"1.4.8","dev":true,"integrity":"sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg=="},"pseudomap":{"version":"1.0.2","dev":true,"integrity":"sha1-8FKijacOYYkX7wqKw0wa5aaChrM="},"pug-error":{"version":"2.0.0","dev":true,"integrity":"sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ=="},"pug-lexer":{"version":"5.0.1","dev":true,"integrity":"sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==","requires":{"character-parser":"2.2.0","is-expression":"4.0.0","pug-error":"2.0.0"}},"pump":{"version":"3.0.0","dev":true,"integrity":"sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==","requires":{"end-of-stream":"1.4.4","once":"1.4.0"}},"punycode":{"version":"2.1.1","dev":true,"integrity":"sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="},"q":{"version":"1.5.1","dev":true,"integrity":"sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="},"qs":{"version":"6.10.3","dev":true,"integrity":"sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==","requires":{"side-channel":"1.0.4"}},"query-string":{"version":"6.14.1","dev":true,"integrity":"sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==","requires":{"decode-uri-component":"0.2.0","filter-obj":"1.1.0","split-on-first":"1.1.0","strict-uri-encode":"2.0.0"}},"queue-microtask":{"version":"1.2.3","dev":true,"integrity":"sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="},"quick-lru":{"version":"4.0.1","dev":true,"integrity":"sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g=="},"rc":{"version":"1.2.8","dev":true,"integrity":"sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==","requires":{"deep-extend":"0.6.0","ini":"1.3.8","minimist":"1.2.5","strip-json-comments":"2.0.1"}},"react-is":{"version":"17.0.2","dev":true,"integrity":"sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="},"read-pkg":{"version":"5.2.0","dev":true,"integrity":"sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==","requires":{"@types/normalize-package-data":"2.4.1","normalize-package-data":"2.5.0","parse-json":"5.2.0","type-fest":"0.6.0"},"dependencies":{"type-fest":{"version":"0.6.0","dev":true,"integrity":"sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="}}},"read-pkg-up":{"version":"7.0.1","dev":true,"integrity":"sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==","requires":{"find-up":"4.1.0","read-pkg":"5.2.0","type-fest":"0.8.1"},"dependencies":{"hosted-git-info":{"version":"2.8.9","dev":true,"integrity":"sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="},"normalize-package-data":{"version":"2.5.0","dev":true,"integrity":"sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==","requires":{"hosted-git-info":"2.8.9","resolve":"1.22.0","semver":"5.7.1","validate-npm-package-license":"3.0.4"}},"semver":{"version":"5.7.1","dev":true,"integrity":"sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="},"type-fest":{"version":"0.8.1","dev":true,"integrity":"sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="}}},"readable-stream":{"version":"3.6.0","dev":true,"integrity":"sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==","requires":{"inherits":"2.0.4","string_decoder":"1.3.0","util-deprecate":"1.0.2"}},"readable-web-to-node-stream":{"version":"3.0.2","dev":true,"integrity":"sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==","requires":{"readable-stream":"3.6.0"}},"redent":{"version":"3.0.0","dev":true,"integrity":"sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==","requires":{"indent-string":"4.0.0","strip-indent":"3.0.0"}},"redeyed":{"version":"2.1.1","dev":true,"integrity":"sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=","requires":{"esprima":"4.0.1"}},"refa":{"version":"0.9.1","dev":true,"integrity":"sha512-egU8LgFq2VXlAfUi8Jcbr5X38wEOadMFf8tCbshgcpVCYlE7k84pJOSlnvXF+muDB4igkdVMq7Z/kiNPqDT9TA==","requires":{"regexpp":"3.2.0"}},"regenerator-runtime":{"version":"0.13.9","dev":true,"integrity":"sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="},"regex-not":{"version":"1.0.2","dev":true,"integrity":"sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==","requires":{"extend-shallow":"3.0.2","safe-regex":"1.1.0"}},"regexp-ast-analysis":{"version":"0.3.0","dev":true,"integrity":"sha512-11PlbBSUxwWpdj6BdZUKfhDdV9g+cveqHB+BqBQDBD7ZermDBVgtyowUaXTvT0dO3tZYo2bDIr/GoED6X1aYSA==","requires":{"refa":"0.9.1","regexpp":"3.2.0"}},"regexp-to-ast":{"version":"0.5.0","dev":true,"integrity":"sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw=="},"regexp-tree":{"version":"0.1.24","dev":true,"integrity":"sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw=="},"regexpp":{"version":"3.2.0","dev":true,"integrity":"sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg=="},"regextras":{"version":"0.8.0","dev":true,"integrity":"sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ=="},"registry-auth-token":{"version":"4.2.1","dev":true,"integrity":"sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==","requires":{"rc":"1.2.8"}},"relative":{"version":"3.0.2","dev":true,"integrity":"sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8=","requires":{"isobject":"2.1.0"},"dependencies":{"isobject":{"version":"2.1.0","dev":true,"integrity":"sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=","requires":{"isarray":"1.0.0"}}}},"remarkable":{"version":"1.7.4","dev":true,"integrity":"sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==","requires":{"argparse":"1.0.10","autolinker":"0.28.1"}},"repeat-element":{"version":"1.1.4","dev":true,"integrity":"sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ=="},"repeat-string":{"version":"1.6.1","dev":true,"integrity":"sha1-jcrkcOHIirwtYA//Sndihtp15jc="},"req-all":{"version":"0.1.0","dev":true,"integrity":"sha1-EwBR4qzligLqy/ydRIV3pzapJzo="},"require-directory":{"version":"2.1.1","dev":true,"integrity":"sha1-jGStX9MNqxyXbiNE/+f3kqam30I="},"require-from-string":{"version":"2.0.2","dev":true,"integrity":"sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="},"requireindex":{"version":"1.2.0","dev":true,"integrity":"sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww=="},"resolve":{"version":"1.22.0","dev":true,"integrity":"sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==","requires":{"is-core-module":"2.8.1","path-parse":"1.0.7","supports-preserve-symlinks-flag":"1.0.0"}},"resolve-alpn":{"version":"1.2.1","dev":true,"integrity":"sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="},"resolve-dir":{"version":"1.0.1","dev":true,"integrity":"sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=","requires":{"expand-tilde":"2.0.2","global-modules":"1.0.0"}},"resolve-from":{"version":"5.0.0","dev":true,"integrity":"sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="},"resolve-global":{"version":"1.0.0","dev":true,"integrity":"sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==","requires":{"global-dirs":"0.1.1"}},"resolve-url":{"version":"0.2.1","dev":true,"integrity":"sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="},"responselike":{"version":"2.0.0","dev":true,"integrity":"sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==","requires":{"lowercase-keys":"2.0.0"}},"restore-cursor":{"version":"2.0.0","dev":true,"integrity":"sha1-n37ih/gv0ybU/RYpI9YhKe7g368=","requires":{"onetime":"2.0.1","signal-exit":"3.0.7"}},"ret":{"version":"0.1.15","dev":true,"integrity":"sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="},"retry":{"version":"0.13.1","dev":true,"integrity":"sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="},"reusify":{"version":"1.0.4","dev":true,"integrity":"sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="},"rfdc":{"version":"1.3.0","dev":true,"integrity":"sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="},"rimraf":{"version":"3.0.2","dev":true,"integrity":"sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==","requires":{"glob":"7.2.0"}},"run-async":{"version":"2.4.1","dev":true,"integrity":"sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="},"run-parallel":{"version":"1.2.0","dev":true,"integrity":"sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==","requires":{"queue-microtask":"1.2.3"}},"rxjs":{"version":"6.6.7","dev":true,"integrity":"sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==","requires":{"tslib":"1.14.1"}},"rxjs-report-usage":{"version":"1.0.6","dev":true,"integrity":"sha512-omv1DIv5z1kV+zDAEjaDjWSkx8w5TbFp5NZoPwUipwzYVcor/4So9ZU3bUyQ1c8lxY5Q0Es/ztWW7PGjY7to0Q==","requires":{"@babel/parser":"7.17.3","@babel/traverse":"7.17.3","@babel/types":"7.17.0","bent":"7.3.12","chalk":"4.1.2","glob":"7.2.0","prompts":"2.4.2"}},"safe-buffer":{"version":"5.2.1","dev":true,"integrity":"sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="},"safe-regex":{"version":"1.1.0","dev":true,"integrity":"sha1-QKNmnzsHfR6UPURinhV91IAjvy4=","requires":{"ret":"0.1.15"}},"safer-buffer":{"version":"2.1.2","dev":true,"integrity":"sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="},"scslre":{"version":"0.1.6","dev":true,"integrity":"sha512-JORxVRlQTfjvlOAaiQKebgFElyAm5/W8b50lgaZ0OkEnKnagJW2ufDh3xRfU75UD9z3FGIu1gL1IyR3Poa6Qmw==","requires":{"refa":"0.9.1","regexp-ast-analysis":"0.2.4","regexpp":"3.2.0"},"dependencies":{"regexp-ast-analysis":{"version":"0.2.4","dev":true,"integrity":"sha512-8L7kOZQaKPxKKAwGuUZxTQtlO3WZ+tiXy4s6G6PKL6trbOXcZoumwC3AOHHFtI/xoSbNxt7jgLvCnP1UADLWqg==","requires":{"refa":"0.9.1","regexpp":"3.2.0"}}}},"self-closing-tags":{"version":"1.0.1","dev":true,"integrity":"sha512-7t6hNbYMxM+VHXTgJmxwgZgLGktuXtVVD5AivWzNTdJBM4DBjnDKDzkf2SrNjihaArpeJYNjxkELBu1evI4lQA=="},"semantic-release":{"version":"19.0.2","dev":true,"integrity":"sha512-7tPonjZxukKECmClhsfyMKDt0GR38feIC2HxgyYaBi+9tDySBLjK/zYDLhh+m6yjnHIJa9eBTKYE7k63ZQcYbw==","requires":{"@semantic-release/commit-analyzer":"9.0.2","@semantic-release/error":"3.0.0","@semantic-release/github":"8.0.2","@semantic-release/npm":"9.0.0","@semantic-release/release-notes-generator":"10.0.3","aggregate-error":"3.1.0","cosmiconfig":"7.0.1","debug":"4.3.3","env-ci":"5.5.0","execa":"5.1.1","figures":"3.2.0","find-versions":"4.0.0","get-stream":"6.0.1","git-log-parser":"1.2.0","hook-std":"2.0.0","hosted-git-info":"4.1.0","lodash":"4.17.21","marked":"4.0.12","marked-terminal":"5.1.1","micromatch":"4.0.4","p-each-series":"2.2.0","p-reduce":"2.1.0","read-pkg-up":"7.0.1","resolve-from":"5.0.0","semver":"7.3.5","semver-diff":"3.1.1","signale":"1.4.0","yargs":"16.2.0"},"dependencies":{"normalize-package-data":{"version":"2.5.0","dev":true,"integrity":"sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==","requires":{"hosted-git-info":"2.8.9","resolve":"1.22.0","semver":"5.7.1","validate-npm-package-license":"3.0.4"}},"p-map":{"version":"2.1.0","dev":true,"integrity":"sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="},"readable-stream":{"version":"2.3.7","dev":true,"integrity":"sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==","requires":{"core-util-is":"1.0.3","inherits":"2.0.4","isarray":"1.0.0","process-nextick-args":"2.0.1","safe-buffer":"5.1.2","string_decoder":"1.1.1","util-deprecate":"1.0.2"}},"safe-buffer":{"version":"5.1.2","dev":true,"integrity":"sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="},"split2":{"version":"1.0.0","dev":true,"integrity":"sha1-UuLiIdiMdfmnP5BVbiY/+WdysxQ=","requires":{"through2":"2.0.5"}},"string_decoder":{"version":"1.1.1","dev":true,"integrity":"sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==","requires":{"safe-buffer":"5.1.2"}},"through2":{"version":"2.0.5","dev":true,"integrity":"sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==","requires":{"readable-stream":"2.3.7","xtend":"4.0.2"}},"type-fest":{"version":"1.4.0","dev":true,"integrity":"sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="}}},"semantic-release-config":{"version":"1.1.18","dev":true,"integrity":"sha512-NtEpAQ7eOJD5kdv2EqdLWuH04vzNR2DVYNqVQgDnzDqM7X2RVeszSo10mLUyY8GpTgqOQTYqRfE4MmX+RVU2gA==","requires":{"@semantic-release/changelog":"6.0.1","@semantic-release/commit-analyzer":"9.0.2","@semantic-release/exec":"6.0.3","@semantic-release/git":"10.0.1","@semantic-release/gitlab":"7.0.4","@semantic-release/npm":"9.0.0","@semantic-release/release-notes-generator":"10.0.3","conventional-changelog-emoji-config":"1.4.8","git-cz-emoji":"1.1.24","semantic-release":"19.0.2","semantic-release-gh":"0.0.7","semantic-release-npm-deprecate-old-versions":"1.3.2","semantic-release-python":"2.5.24","tslib":"2.3.1","yaml":"1.10.2"},"dependencies":{"ansi-escapes":{"version":"5.0.0","dev":true,"integrity":"sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==","requires":{"type-fest":"1.4.0"}},"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"figures":{"version":"3.2.0","dev":true,"integrity":"sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==","requires":{"escape-string-regexp":"1.0.5"}},"fs-extra":{"version":"9.1.0","dev":true,"integrity":"sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==","requires":{"at-least-node":"1.0.0","graceful-fs":"4.2.9","jsonfile":"6.1.0","universalify":"2.0.0"}},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"lowercase-keys":{"version":"2.0.0","dev":true,"integrity":"sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="},"resolve-from":{"version":"4.0.0","dev":true,"integrity":"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="},"strip-bom":{"version":"3.0.0","dev":true,"integrity":"sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="},"strip-json-comments":{"version":"2.0.1","dev":true,"integrity":"sha1-PFMZQukIwml8DsNEhYwobHygpgo="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"yargs":{"version":"16.2.0","dev":true,"integrity":"sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==","requires":{"cliui":"7.0.4","escalade":"3.1.1","get-caller-file":"2.0.5","require-directory":"2.1.1","string-width":"4.2.3","y18n":"5.0.8","yargs-parser":"20.2.9"},"dependencies":{"ansi-styles":{"version":"4.3.0","dev":true,"integrity":"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==","requires":{"color-convert":"2.0.1"}},"color-convert":{"version":"2.0.1","dev":true,"integrity":"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==","requires":{"color-name":"1.1.4"}},"color-name":{"version":"1.1.4","dev":true,"integrity":"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="}}}}},"semantic-release-gh":{"version":"0.0.7","dev":true,"integrity":"sha512-T3eQwyiMXwWMzvDIFMPVn7mzJERVRZYtNEF58bVV0SL3I9UzUkM3UAY1idJUcG3ovbz7BeerfTdIAhE2GrD/sg==","requires":{"@octokit/rest":"18.12.0","@semantic-release/error":"2.2.0","aggregate-error":"3.1.0","bottleneck":"2.19.5","debug":"4.3.3","dir-glob":"3.0.1","fs-extra":"10.0.0","globby":"11.1.0","http-proxy-agent":"5.0.0","https-proxy-agent":"5.0.0","issue-parser":"6.0.0","lodash":"4.17.21","mime":"3.0.0","p-filter":"2.1.0","p-retry":"4.6.1","semantic-release":"19.0.2","tslib":"2.3.1","url-join":"4.0.1"},"dependencies":{"@semantic-release/error":{"version":"2.2.0","dev":true,"integrity":"sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg=="},"p-map":{"version":"2.1.0","dev":true,"integrity":"sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="}}},"semantic-release-npm-deprecate-old-versions":{"version":"1.3.2","dev":true,"integrity":"sha512-N9gKDq4/Spv2W7bBwZX8KdcnP4yKeBIjJtd/OefbFGnrWgzJ/6+XDk3Tg6ctr6xlNP8sHRnCEQQea/sMNA8PuA==","requires":{"execa":"4.1.0","node-fetch":"2.6.7","semver":"7.3.5"},"dependencies":{"execa":{"version":"4.1.0","dev":true,"integrity":"sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==","requires":{"cross-spawn":"7.0.3","get-stream":"5.2.0","human-signals":"1.1.1","is-stream":"2.0.1","merge-stream":"2.0.0","npm-run-path":"4.0.1","onetime":"5.1.2","signal-exit":"3.0.7","strip-final-newline":"2.0.0"}},"get-stream":{"version":"5.2.0","dev":true,"integrity":"sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==","requires":{"pump":"3.0.0"}},"human-signals":{"version":"1.1.1","dev":true,"integrity":"sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="}}},"semantic-release-python":{"version":"2.5.24","dev":true,"integrity":"sha512-u+eAsg8DMN6CjcNALozE0nH3W/wttk5NkRXB78IITJInhy6jwN5oHRRFOIiXM0yFwwA9MY7cv8YvtW7IbALTdg==","requires":{"execa":"4.1.0","form-data":"3.0.1","fs-extra":"10.0.0","got":"11.8.3","tslib":"2.3.1","uuid":"8.3.2"},"dependencies":{"execa":{"version":"4.1.0","dev":true,"integrity":"sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==","requires":{"cross-spawn":"7.0.3","get-stream":"5.2.0","human-signals":"1.1.1","is-stream":"2.0.1","merge-stream":"2.0.0","npm-run-path":"4.0.1","onetime":"5.1.2","signal-exit":"3.0.7","strip-final-newline":"2.0.0"}},"form-data":{"version":"3.0.1","dev":true,"integrity":"sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==","requires":{"asynckit":"0.4.0","combined-stream":"1.0.8","mime-types":"2.1.34"}},"get-stream":{"version":"5.2.0","dev":true,"integrity":"sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==","requires":{"pump":"3.0.0"}},"human-signals":{"version":"1.1.1","dev":true,"integrity":"sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="},"quick-lru":{"version":"5.1.1","dev":true,"integrity":"sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="}}},"semver":{"version":"7.3.5","dev":true,"integrity":"sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==","requires":{"lru-cache":"6.0.0"}},"semver-diff":{"version":"3.1.1","dev":true,"integrity":"sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==","requires":{"semver":"6.3.0"},"dependencies":{"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"semver-regex":{"version":"3.1.3","dev":true,"integrity":"sha512-Aqi54Mk9uYTjVexLnR67rTyBusmwd04cLkHy9hNvk3+G3nT2Oyg7E0l4XVbOaNwIvQ3hHeYxGcyEy+mKreyBFQ=="},"set-getter":{"version":"0.1.1","dev":true,"integrity":"sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==","requires":{"to-object-path":"0.3.0"}},"set-value":{"version":"2.0.1","dev":true,"integrity":"sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==","requires":{"extend-shallow":"2.0.1","is-extendable":"0.1.1","is-plain-object":"2.0.4","split-string":"3.1.0"}},"shebang-command":{"version":"2.0.0","dev":true,"integrity":"sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==","requires":{"shebang-regex":"3.0.0"}},"shebang-regex":{"version":"3.0.0","dev":true,"integrity":"sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="},"shellcheck":{"version":"1.0.0","dev":true,"integrity":"sha512-CdKbWXOknBwE1wNQzAnwfLf7QNOu/yqyLSGBKoq2WuChEqfg7dnZJ1pHR2P463PbVpBRz3KGkYnXJCoQrPwtYA=="},"side-channel":{"version":"1.0.4","dev":true,"integrity":"sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==","requires":{"call-bind":"1.0.2","get-intrinsic":"1.1.1","object-inspect":"1.12.0"}},"sigmund":{"version":"1.0.1","dev":true,"integrity":"sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="},"signal-exit":{"version":"3.0.7","dev":true,"integrity":"sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="},"signale":{"version":"1.4.0","dev":true,"integrity":"sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==","requires":{"chalk":"2.4.2","figures":"2.0.0","pkg-conf":"2.1.0"},"dependencies":{"find-up":{"version":"2.1.0","dev":true,"integrity":"sha1-RdG35QbHF93UgndaK3eSCjwMV6c=","requires":{"locate-path":"2.0.0"}},"locate-path":{"version":"2.0.0","dev":true,"integrity":"sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=","requires":{"p-locate":"2.0.0","path-exists":"3.0.0"}},"p-limit":{"version":"1.3.0","dev":true,"integrity":"sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==","requires":{"p-try":"1.0.0"}},"p-locate":{"version":"2.0.0","dev":true,"integrity":"sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=","requires":{"p-limit":"1.3.0"}},"p-try":{"version":"1.0.0","dev":true,"integrity":"sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="},"parse-json":{"version":"4.0.0","dev":true,"integrity":"sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=","requires":{"error-ex":"1.3.2","json-parse-better-errors":"1.0.2"}},"path-exists":{"version":"3.0.0","dev":true,"integrity":"sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="}}},"simple-git":{"version":"2.48.0","dev":true,"integrity":"sha512-z4qtrRuaAFJS4PUd0g+xy7aN4y+RvEt/QTJpR184lhJguBA1S/LsVlvE/CM95RsYMOFJG3NGGDjqFCzKU19S/A==","requires":{"@kwsites/file-exists":"1.1.1","@kwsites/promise-deferred":"1.1.1","debug":"4.3.3"}},"sisteransi":{"version":"1.0.5","dev":true,"integrity":"sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="},"slash":{"version":"3.0.0","dev":true,"integrity":"sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="},"slice-ansi":{"version":"5.0.0","dev":true,"integrity":"sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==","requires":{"ansi-styles":"6.1.0","is-fullwidth-code-point":"4.0.0"}},"snapdragon":{"version":"0.8.2","dev":true,"integrity":"sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==","requires":{"base":"0.11.2","debug":"2.6.9","define-property":"0.2.5","extend-shallow":"2.0.1","map-cache":"0.2.2","source-map":"0.5.7","source-map-resolve":"0.5.3","use":"3.1.1"},"dependencies":{"define-property":{"version":"0.2.5","dev":true,"integrity":"sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=","requires":{"is-descriptor":"0.1.6"},"dependencies":{"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}}}},"snapdragon-node":{"version":"2.1.1","dev":true,"integrity":"sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==","requires":{"define-property":"1.0.0","isobject":"3.0.1","snapdragon-util":"3.0.1"}},"snapdragon-util":{"version":"3.0.1","dev":true,"integrity":"sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==","requires":{"kind-of":"3.2.2"}},"solidity-comments-extractor":{"version":"0.0.7","dev":true,"integrity":"sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw=="},"sort-object-keys":{"version":"1.1.3","dev":true,"integrity":"sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg=="},"sort-order":{"version":"1.0.1","dev":true,"integrity":"sha1-2CK4zbkOpqnfloxL1FmHz1SBmeY="},"source-map":{"version":"0.6.1","dev":true,"integrity":"sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="},"source-map-resolve":{"version":"0.5.3","dev":true,"integrity":"sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==","requires":{"atob":"2.1.2","decode-uri-component":"0.2.0","resolve-url":"0.2.1","source-map-url":"0.4.1","urix":"0.1.0"}},"source-map-support":{"version":"0.5.21","dev":true,"integrity":"sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==","requires":{"buffer-from":"1.1.2","source-map":"0.6.1"}},"source-map-url":{"version":"0.4.1","dev":true,"integrity":"sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw=="},"spawn-error-forwarder":{"version":"1.0.0","dev":true,"integrity":"sha1-Gv2Uc46ZmwNG17n8NzvlXgdXcCk="},"spdx-correct":{"version":"3.1.1","dev":true,"integrity":"sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==","requires":{"spdx-expression-parse":"3.0.1","spdx-license-ids":"3.0.11"}},"spdx-exceptions":{"version":"2.3.0","dev":true,"integrity":"sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="},"spdx-expression-parse":{"version":"3.0.1","dev":true,"integrity":"sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==","requires":{"spdx-exceptions":"2.3.0","spdx-license-ids":"3.0.11"}},"spdx-license-ids":{"version":"3.0.11","dev":true,"integrity":"sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g=="},"split":{"version":"1.0.1","dev":true,"integrity":"sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==","requires":{"through":"2.3.8"}},"split-on-first":{"version":"1.1.0","dev":true,"integrity":"sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="},"split-string":{"version":"3.1.0","dev":true,"integrity":"sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==","requires":{"extend-shallow":"3.0.2"},"dependencies":{"is-extendable":{"version":"1.0.1","dev":true,"integrity":"sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==","requires":{"is-plain-object":"2.0.4"}}}},"split2":{"version":"3.2.2","dev":true,"integrity":"sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==","requires":{"readable-stream":"3.6.0"}},"sprintf-js":{"version":"1.0.3","dev":true,"integrity":"sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="},"sql-formatter":{"version":"4.0.2","dev":true,"integrity":"sha512-R6u9GJRiXZLr/lDo8p56L+OyyN2QFJPCDnsyEOsbdIpsnDKL8gubYFo7lNR7Zx7hfdWT80SfkoVS0CMaF/DE2w==","requires":{"argparse":"2.0.1"}},"static-extend":{"version":"0.1.2","dev":true,"integrity":"sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=","requires":{"define-property":"0.2.5","object-copy":"0.1.0"}},"stream-combiner2":{"version":"1.1.1","dev":true,"integrity":"sha1-+02KFCDqNidk4hrUeAOXvry0HL4=","requires":{"duplexer2":"0.1.4","readable-stream":"2.3.7"}},"strict-uri-encode":{"version":"2.0.0","dev":true,"integrity":"sha1-ucczDHBChi9rFC3CdLvMWGbONUY="},"string-argv":{"version":"0.3.1","dev":true,"integrity":"sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg=="},"string-width":{"version":"4.2.3","dev":true,"integrity":"sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==","requires":{"emoji-regex":"8.0.0","is-fullwidth-code-point":"3.0.0","strip-ansi":"6.0.1"}},"string.prototype.trimend":{"version":"1.0.4","dev":true,"integrity":"sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3"}},"string.prototype.trimstart":{"version":"1.0.4","dev":true,"integrity":"sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3"}},"string_decoder":{"version":"1.3.0","dev":true,"integrity":"sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==","requires":{"safe-buffer":"5.2.1"}},"strip-ansi":{"version":"6.0.1","dev":true,"integrity":"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==","requires":{"ansi-regex":"5.0.1"}},"strip-bom":{"version":"4.0.0","dev":true,"integrity":"sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="},"strip-final-newline":{"version":"2.0.0","dev":true,"integrity":"sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="},"strip-indent":{"version":"3.0.0","dev":true,"integrity":"sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==","requires":{"min-indent":"1.0.1"}},"strip-json-comments":{"version":"3.0.1","dev":true,"integrity":"sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw=="},"striptags":{"version":"3.2.0","dev":true,"integrity":"sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw=="},"strtok3":{"version":"6.3.0","dev":true,"integrity":"sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==","requires":{"@tokenizer/token":"0.3.0","peek-readable":"4.1.0"}},"success-symbol":{"version":"0.1.0","dev":true,"integrity":"sha1-JAIuSG878c3KCUKDt2nEctO3KJc="},"supports-color":{"version":"7.2.0","dev":true,"integrity":"sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==","requires":{"has-flag":"4.0.0"}},"supports-hyperlinks":{"version":"2.2.0","dev":true,"integrity":"sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==","requires":{"has-flag":"4.0.0","supports-color":"7.2.0"}},"supports-preserve-symlinks-flag":{"version":"1.0.0","dev":true,"integrity":"sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="},"temp-dir":{"version":"2.0.0","dev":true,"integrity":"sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg=="},"tempy":{"version":"1.0.1","dev":true,"integrity":"sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==","requires":{"del":"6.0.0","is-stream":"2.0.1","temp-dir":"2.0.0","type-fest":"0.16.0","unique-string":"2.0.0"},"dependencies":{"type-fest":{"version":"0.16.0","dev":true,"integrity":"sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg=="}}},"term-size":{"version":"2.2.1","dev":true,"integrity":"sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="},"text-extensions":{"version":"1.9.0","dev":true,"integrity":"sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ=="},"text-table":{"version":"0.2.0","dev":true,"integrity":"sha1-f17oI66AUgfACvLfSoTsP8+lcLQ="},"through":{"version":"2.3.8","dev":true,"integrity":"sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="},"through2":{"version":"4.0.2","dev":true,"integrity":"sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==","requires":{"readable-stream":"3.6.0"}},"time-stamp":{"version":"1.1.0","dev":true,"integrity":"sha1-dkpaEa9QVhkhsTPztE5hhofg9cM="},"tmp":{"version":"0.0.33","dev":true,"integrity":"sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==","requires":{"os-tmpdir":"1.0.2"}},"to-fast-properties":{"version":"2.0.0","dev":true,"integrity":"sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="},"to-gfm-code-block":{"version":"0.1.1","dev":true,"integrity":"sha1-JdBFpfrlUxielje1kJANpzLYqoI="},"to-object-path":{"version":"0.3.0","dev":true,"integrity":"sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=","requires":{"kind-of":"3.2.2"}},"to-regex":{"version":"3.0.2","dev":true,"integrity":"sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==","requires":{"define-property":"2.0.2","extend-shallow":"3.0.2","regex-not":"1.0.2","safe-regex":"1.1.0"}},"to-regex-range":{"version":"5.0.1","dev":true,"integrity":"sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==","requires":{"is-number":"7.0.0"}},"token-types":{"version":"4.1.1","dev":true,"integrity":"sha512-hD+QyuUAyI2spzsI0B7gf/jJ2ggR4RjkAo37j3StuePhApJUwcWDjnHDOFdIWYSwNR28H14hpwm4EI+V1Ted1w==","requires":{"@tokenizer/token":"0.3.0","ieee754":"1.2.1"}},"toml-eslint-parser":{"version":"0.2.1","dev":true,"integrity":"sha512-lAESSx47NjCe/O9Y5dbAmP+U/EFvdxPRgA0Hp4TxCZP3Bs6hxRfLHxZvBuXvTGkVoK0DpYamPz/rCqYBzoFWVQ==","requires":{"eslint-visitor-keys":"2.1.0"}},"tr46":{"version":"0.0.3","dev":true,"integrity":"sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="},"traverse":{"version":"0.6.6","dev":true,"integrity":"sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc="},"trim-newlines":{"version":"3.0.1","dev":true,"integrity":"sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw=="},"ts-node":{"version":"9.1.1","dev":true,"integrity":"sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==","requires":{"arg":"4.1.3","create-require":"1.1.1","diff":"4.0.2","make-error":"1.3.6","source-map-support":"0.5.21","typescript":"4.5.5","yn":"3.1.1"}},"tsconfig-paths":{"version":"3.12.0","dev":true,"integrity":"sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==","requires":{"@types/json5":"0.0.29","json5":"1.0.1","minimist":"1.2.5","strip-bom":"3.0.0"}},"tslib":{"version":"2.3.1","dev":true,"integrity":"sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="},"tsutils":{"version":"3.21.0","dev":true,"integrity":"sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==","requires":{"tslib":"1.14.1","typescript":"4.5.5"}},"tsutils-etc":{"version":"1.4.1","dev":true,"integrity":"sha512-6UPYgc7OXcIW5tFxlsZF3OVSBvDInl/BkS3Xsu64YITXk7WrnWTVByKWPCThFDBp5gl5IGHOzGMdQuDCE7OL4g==","requires":{"@types/yargs":"17.0.8","tsutils":"3.21.0","typescript":"4.5.5","yargs":"17.3.1"}},"tunnel-agent":{"version":"0.6.0","dev":true,"integrity":"sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=","requires":{"safe-buffer":"5.2.1"}},"type-check":{"version":"0.4.0","dev":true,"integrity":"sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==","requires":{"prelude-ls":"1.2.1"}},"type-fest":{"version":"0.18.1","dev":true,"integrity":"sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="},"typedarray-to-buffer":{"version":"3.1.5","dev":true,"integrity":"sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==","requires":{"is-typedarray":"1.0.0"}},"typeof-article":{"version":"0.1.1","dev":true,"integrity":"sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8=","requires":{"kind-of":"3.2.2"}},"typescript":{"version":"4.5.5","dev":true,"integrity":"sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA=="},"ulid":{"version":"2.3.0","dev":true,"integrity":"sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw=="},"unbox-primitive":{"version":"1.0.1","dev":true,"integrity":"sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==","requires":{"function-bind":"1.1.1","has-bigints":"1.0.1","has-symbols":"1.0.2","which-boxed-primitive":"1.0.2"}},"union-value":{"version":"1.0.1","dev":true,"integrity":"sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==","requires":{"arr-union":"3.1.0","get-value":"2.0.6","is-extendable":"0.1.1","set-value":"2.0.1"}},"unique-string":{"version":"2.0.0","dev":true,"integrity":"sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==","requires":{"crypto-random-string":"2.0.0"}},"unist-util-stringify-position":{"version":"2.0.3","dev":true,"integrity":"sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==","requires":{"@types/unist":"2.0.6"}},"universal-user-agent":{"version":"6.0.0","dev":true,"integrity":"sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="},"universalify":{"version":"2.0.0","dev":true,"integrity":"sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="},"unset-value":{"version":"1.0.0","dev":true,"integrity":"sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=","requires":{"has-value":"0.3.1","isobject":"3.0.1"}},"uri-js":{"version":"4.4.1","dev":true,"integrity":"sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==","requires":{"punycode":"2.1.1"}},"urix":{"version":"0.1.0","dev":true,"integrity":"sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="},"url-join":{"version":"4.0.1","dev":true,"integrity":"sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="},"use":{"version":"3.1.1","dev":true,"integrity":"sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="},"util-deprecate":{"version":"1.0.2","dev":true,"integrity":"sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="},"uuid":{"version":"8.3.2","dev":true,"integrity":"sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="},"v8-compile-cache":{"version":"2.3.0","dev":true,"integrity":"sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA=="},"validate-npm-package-license":{"version":"3.0.4","dev":true,"integrity":"sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==","requires":{"spdx-correct":"3.1.1","spdx-expression-parse":"3.0.1"}},"vscode-uri":{"version":"3.0.3","dev":true,"integrity":"sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA=="},"warning-symbol":{"version":"0.1.0","dev":true,"integrity":"sha1-uzHdEbeg+dZ6su2V9Fe2WCW7rSE="},"webidl-conversions":{"version":"3.0.1","dev":true,"integrity":"sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="},"whatwg-url":{"version":"5.0.0","dev":true,"integrity":"sha1-lmRU6HZUYuN2RNNib2dCzotwll0=","requires":{"tr46":"0.0.3","webidl-conversions":"3.0.1"}},"which":{"version":"2.0.2","dev":true,"integrity":"sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==","requires":{"isexe":"2.0.0"}},"which-boxed-primitive":{"version":"1.0.2","dev":true,"integrity":"sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==","requires":{"is-bigint":"1.0.4","is-boolean-object":"1.1.2","is-number-object":"1.0.6","is-string":"1.0.7","is-symbol":"1.0.4"}},"which-pm-runs":{"version":"1.1.0","dev":true,"integrity":"sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="},"widest-line":{"version":"3.1.0","dev":true,"integrity":"sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==","requires":{"string-width":"4.2.3"}},"word-wrap":{"version":"1.2.3","dev":true,"integrity":"sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="},"wordwrap":{"version":"1.0.0","dev":true,"integrity":"sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="},"wrap-ansi":{"version":"7.0.0","dev":true,"integrity":"sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==","requires":{"ansi-styles":"4.3.0","string-width":"4.2.3","strip-ansi":"6.0.1"}},"wrappy":{"version":"1.0.2","dev":true,"integrity":"sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="},"write-file-atomic":{"version":"3.0.3","dev":true,"integrity":"sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==","requires":{"imurmurhash":"0.1.4","is-typedarray":"1.0.0","signal-exit":"3.0.7","typedarray-to-buffer":"3.1.5"}},"xdg-basedir":{"version":"4.0.0","dev":true,"integrity":"sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q=="},"xtend":{"version":"4.0.2","dev":true,"integrity":"sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="},"y18n":{"version":"5.0.8","dev":true,"integrity":"sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="},"yallist":{"version":"4.0.0","dev":true,"integrity":"sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="},"yaml":{"version":"1.10.2","dev":true,"integrity":"sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="},"yaml-eslint-parser":{"version":"0.4.1","dev":true,"integrity":"sha512-GoJ/p1EW8O2tbTbuhfxjo1XhfUFU3uX3kwvfEQoOaZjO2Lubx8POjlsSqB+18b3SxkujAdQYT9r9nURaUWNYWQ==","requires":{"eslint-visitor-keys":"2.1.0","lodash":"4.17.21","yaml":"1.10.2"}},"yargs":{"version":"17.3.1","dev":true,"integrity":"sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==","requires":{"cliui":"7.0.4","escalade":"3.1.1","get-caller-file":"2.0.5","require-directory":"2.1.1","string-width":"4.2.3","y18n":"5.0.8","yargs-parser":"21.0.0"},"dependencies":{"yargs-parser":{"version":"21.0.0","dev":true,"integrity":"sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA=="}}},"yargs-parser":{"version":"20.2.9","dev":true,"integrity":"sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="},"yarnhook":{"version":"0.5.1","dev":true,"integrity":"sha512-YPLLXO/PzsFXKvRfsOG/r60WBz8RT7VbkkQv2oHDb6o+EjX0vcUSeA3aw5el2AEWjbcg1sgemjHyCwRIvQxZWw==","requires":{"execa":"4.1.0","find-parent-dir":"0.3.1"},"dependencies":{"execa":{"version":"4.1.0","dev":true,"integrity":"sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==","requires":{"cross-spawn":"7.0.3","get-stream":"5.2.0","human-signals":"1.1.1","is-stream":"2.0.1","merge-stream":"2.0.0","npm-run-path":"4.0.1","onetime":"5.1.2","signal-exit":"3.0.7","strip-final-newline":"2.0.0"}},"get-stream":{"version":"5.2.0","dev":true,"integrity":"sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==","requires":{"pump":"3.0.0"}},"human-signals":{"version":"1.1.1","dev":true,"integrity":"sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="}}},"year":{"version":"0.2.1","dev":true,"integrity":"sha1-QIOuUgoxiyPshgN/MADLiSvfm7A="},"yn":{"version":"3.1.1","dev":true,"integrity":"sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="},"yocto-queue":{"version":"0.1.0","dev":true,"integrity":"sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="}}} \ No newline at end of file diff --git a/.config/docs/local/yarn.lock b/.config/docs/local/yarn.lock new file mode 100644 index 00000000..e9c1a73f --- /dev/null +++ b/.config/docs/local/yarn.lock @@ -0,0 +1,1246 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/eslint-parser@^7.12.16": + version "7.17.0" + resolved "7.17.0" + integrity sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA== + dependencies: + eslint-scope "^5.1.1" + eslint-visitor-keys "^2.1.0" + semver "^6.3.0" + +"@commitlint/config-conventional@15.0.0": + version "16.2.1" + resolved "16.2.1" + integrity sha512-cP9gArx7gnaj4IqmtCIcHdRjTYdRUi6lmGE+lOzGGjGe45qGOS8nyQQNvkNy2Ey2VqoSWuXXkD8zCUh6EHf1Ww== + dependencies: + conventional-changelog-conventionalcommits "^4.3.1" + +"@commitlint/config-conventional@^16.0.0": + version "16.2.1" + resolved "16.2.1" + integrity sha512-cP9gArx7gnaj4IqmtCIcHdRjTYdRUi6lmGE+lOzGGjGe45qGOS8nyQQNvkNy2Ey2VqoSWuXXkD8zCUh6EHf1Ww== + dependencies: + conventional-changelog-conventionalcommits "^4.3.1" + +"@eslint/eslintrc@^1.1.0": + version "1.1.0" + resolved "1.1.0" + integrity sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.1" + globals "^13.9.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@octokit/openapi-types@^11.2.0": + version "11.2.0" + resolved "11.2.0" + integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== + +"@prettier/plugin-php@^0.17.6": + version "0.17.6" + resolved "0.17.6" + integrity sha512-Nxkq+Gl8bGbutRV7e3/p5d+Bcftn75oH61RT8xzk44T5ET7fVP50pUdaOdvt704GSNr6wwmfBW8MhBz5IKt+fA== + dependencies: + linguist-languages "^7.5.1" + mem "^8.0.0" + php-parser "https://github.com/glayzzle/php-parser#27abcb2337ac6450c068ef064982dfabf77916a5" + +"@prettier/plugin-pug@^1.19.2": + version "1.19.2" + resolved "1.19.2" + integrity sha512-Izo+m3pb15Ym7t3J+u22kun7bQXZOj+lAqkIc5ifxwd9En9OqcCShZz7eABORmzmY276IjSfY92Ijd23nLR68Q== + dependencies: + pug-lexer "^5.0.0" + +"@prettier/plugin-ruby@^2.0.0": + version "2.0.0" + resolved "2.0.0" + integrity sha512-pA0rjTi5J7cT86XPNhXp7CpdV2Tlyj5oqDIsnQRxMu2P7LY2KJI/pyOSM8pzTH8BgRyQfe1P1NPCcTwxUnUWtQ== + dependencies: + prettier ">=2.3.0" + +"@prettier/plugin-xml@^1.0.2": + version "1.2.0" + resolved "1.2.0" + integrity sha512-bFvVAZKs59XNmntYjyefn3K4TBykS6E+d6ZW8IcylAs88ZO+TzLhp0dPpi0VKfPzq1Nb+kpDnPRTiwb4zY6NgA== + dependencies: + "@xml-tools/parser" "^1.0.11" + prettier ">=2.3" + +"@types/eslint@^7.2.13": + version "7.29.0" + resolved "7.29.0" + integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "0.0.51" + resolved "0.0.51" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + +"@types/http-cache-semantics@*": + version "4.0.1" + resolved "4.0.1" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + +"@types/istanbul-lib-coverage@*": + version "2.0.4" + resolved "2.0.4" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-coverage@^2.0.0": + version "2.0.4" + resolved "2.0.4" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "3.0.0" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "3.0.1" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@*": + version "7.0.9" + resolved "7.0.9" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + +"@types/json-schema@^7.0.9": + version "7.0.9" + resolved "7.0.9" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + +"@types/keyv@*": + version "3.1.3" + resolved "3.1.3" + integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "17.0.18" + resolved "17.0.18" + integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA== + +"@types/responselike@*": + version "1.0.0" + resolved "1.0.0" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + +"@types/unist@*": + version "2.0.6" + resolved "2.0.6" + integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== + +"@types/yargs-parser@*": + version "20.2.1" + resolved "20.2.1" + integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== + +"@types/yargs@^15.0.0": + version "17.0.8" + resolved "17.0.8" + integrity sha512-wDeUwiUmem9FzsyysEwRukaEdDNcwbROvQ9QGRKaLI6t+IltNzbn4/i4asmB10auvZGQCzSQ6t0GSczEThlUXw== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.0.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-fwCMkDimwHVeIOKeBHiZhRUfJXU8n6xW1FL9diDxAyGAFvKcH4csy0v7twivOQdQdA0KC8TDr7GGRd3L4Lv0rQ== + dependencies: + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/type-utils" "5.12.0" + "@typescript-eslint/utils" "5.12.0" + debug "^4.3.2" + functional-red-black-tree "^1.0.1" + ignore "^5.1.8" + regexpp "^3.2.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/eslint-plugin@^5.11.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-fwCMkDimwHVeIOKeBHiZhRUfJXU8n6xW1FL9diDxAyGAFvKcH4csy0v7twivOQdQdA0KC8TDr7GGRd3L4Lv0rQ== + dependencies: + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/type-utils" "5.12.0" + "@typescript-eslint/utils" "5.12.0" + debug "^4.3.2" + functional-red-black-tree "^1.0.1" + ignore "^5.1.8" + regexpp "^3.2.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@^2.27.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-iFVADWH2CmiDF+E9kFK2r474BO2JILDKw1NVD5ytqHrM3ezsfdu5uo6B+77DH0suM7iUC/yOayHNziuiI9BPbQ== + dependencies: + "@typescript-eslint/utils" "5.12.0" + +"@typescript-eslint/experimental-utils@^5.0.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-iFVADWH2CmiDF+E9kFK2r474BO2JILDKw1NVD5ytqHrM3ezsfdu5uo6B+77DH0suM7iUC/yOayHNziuiI9BPbQ== + dependencies: + "@typescript-eslint/utils" "5.12.0" + +"@typescript-eslint/parser@^5.11.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog== + dependencies: + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/typescript-estree" "5.12.0" + debug "^4.3.2" + +"@typescript-eslint/scope-manager@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ== + dependencies: + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/visitor-keys" "5.12.0" + +"@typescript-eslint/type-utils@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-9j9rli3zEBV+ae7rlbBOotJcI6zfc6SHFMdKI9M3Nc0sy458LJ79Os+TPWeBBL96J9/e36rdJOfCuyRSgFAA0Q== + dependencies: + "@typescript-eslint/utils" "5.12.0" + debug "^4.3.2" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ== + +"@typescript-eslint/typescript-estree@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ== + dependencies: + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/visitor-keys" "5.12.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-k4J2WovnMPGI4PzKgDtQdNrCnmBHpMUFy21qjX2CoPdoBcSBIMvVBr9P2YDP8jOqZOeK3ThOL6VO/sy6jtnvzw== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/typescript-estree" "5.12.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg== + dependencies: + "@typescript-eslint/types" "5.12.0" + eslint-visitor-keys "^3.0.0" + +"@washingtondc/development@latest": + version "1.0.10" + resolved "1.0.10" + integrity sha512-rO/ROg5jBNjx3HQIqsVDBWwOFD9sa6UwhsUphfwTcPCJLQ+bu0NFb3yGOpHvmbpU9AWtFS6ZcG2UHRvdPed01Q== + dependencies: + "@commitlint/cli" "15.0.0" + "@commitlint/config-conventional" "15.0.0" + commitizen "4.2.4" + cspell "5.13.1" + cz-emoji-conventional "1.0.1" + git-notify "0.2.3" + husky "7.0.4" + leasot "12.0.0" + lint-staged "12.1.2" + liquidjs "9.28.5" + only-allow "1.0.0" + open-cli "7.0.1" + shellcheck "1.0.0" + yarnhook "0.5.1" + +create-eslint-index@^1.0.0: + version "1.0.0" + resolved "1.0.0#d954372d86d5792fcd67e9f2b791b1ab162411bb" + integrity sha1-2VQ3LYbVeS/NZ+nyt5GxqxYkEbs= + dependencies: + lodash.get "^4.3.0" + +cz-emoji-conventional@1.0.1: + version "1.0.1" + resolved "1.0.1" + integrity sha512-jY+jmmbQ9n671gLWSSI34a7efDz1YPfzM3QZC5T+r8CJCCo1/myW8Ik09NV9KQ5hVSc2BBfvcq7e5IsBOZjyjg== + dependencies: + chalk "^4.1.1" + commitizen "^4.2.4" + word-wrap "^1.2.3" + +cz-emoji-conventional@^1.0.1: + version "1.0.1" + resolved "1.0.1" + integrity sha512-jY+jmmbQ9n671gLWSSI34a7efDz1YPfzM3QZC5T+r8CJCCo1/myW8Ik09NV9KQ5hVSc2BBfvcq7e5IsBOZjyjg== + dependencies: + chalk "^4.1.1" + commitizen "^4.2.4" + word-wrap "^1.2.3" + +eslint-ast-utils@^1.0.0: + version "1.1.0" + resolved "1.1.0" + integrity sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA== + dependencies: + lodash.get "^4.4.2" + lodash.zip "^4.2.0" + +eslint-config-prettier@^8.3.0: + version "8.4.0" + resolved "8.4.0" + integrity sha512-CFotdUcMY18nGRo5KGsnNxpznzhkopOcOo0InID+sgQssPrzjvsyKZPvOgymTFeHrFuC3Tzdf2YndhXtULK9Iw== + +eslint-config-strict-mode@latest: + version "1.0.44" + resolved "1.0.44" + integrity sha512-kM+KvUGpx6/Uow0d2+wCVLjVMtphht+JT6r7lpiysj51ci7PnppUQV422GDYO5D9O4q43P7H8wdsMblOGwbM+Q== + dependencies: + "@typescript-eslint/eslint-plugin" "^5.11.0" + "@typescript-eslint/parser" "^5.11.0" + eslint "^8.9.0" + eslint-config-prettier "^8.3.0" + eslint-formatter-git-log "^0.5.3" + eslint-formatter-gitlab "^3.0.0" + eslint-formatter-pretty "^4.1.0" + eslint-formatter-summary "^1.1.0" + eslint-plugin-angular "^4.1.0" + eslint-plugin-array-func "^3.1.7" + eslint-plugin-editorconfig "^3.2.0" + eslint-plugin-eslint-comments "^3.2.0" + eslint-plugin-etc "^2.0.2" + eslint-plugin-ext "^0.1.0" + eslint-plugin-filenames "^1.3.2" + eslint-plugin-fp "^2.3.0" + eslint-plugin-functional "^4.2.0" + eslint-plugin-html "^6.2.0" + eslint-plugin-import "^2.25.4" + eslint-plugin-jest "^25.7.0" + eslint-plugin-jest-async "^1.0.3" + eslint-plugin-jest-dom "^3.9.4" + eslint-plugin-jest-formatting "^3.1.0" + eslint-plugin-jsdoc "^37.9.0" + eslint-plugin-json-schema-validator "^1.2.49" + eslint-plugin-jsonc "^1.7.0" + eslint-plugin-no-constructor-bind "^2.0.4" + eslint-plugin-no-explicit-type-exports "^0.12.1" + eslint-plugin-no-secrets "^0.8.9" + eslint-plugin-no-unsanitized "^3.2.0" + eslint-plugin-no-use-extend-native "^0.5.0" + eslint-plugin-node "^11.1.0" + eslint-plugin-optimize-regex "^1.2.1" + eslint-plugin-prefer-arrow "^1.2.3" + eslint-plugin-prettier "^4.0.0" + eslint-plugin-promise "^5.2.0" + eslint-plugin-regexp "^1.5.1" + eslint-plugin-rxjs "^4.0.4" + eslint-plugin-security "^1.4.0" + eslint-plugin-sonarjs "^0.10.0" + eslint-plugin-sort-class-members "^1.14.1" + eslint-plugin-sort-keys-fix "^1.1.2" + eslint-plugin-switch-case "^1.1.2" + eslint-plugin-toml "^0.2.0" + eslint-plugin-tsdoc "^0.2.14" + eslint-plugin-typescript-sort-keys "^2.1.0" + eslint-plugin-unicorn "^37.0.1" + eslint-plugin-unused-imports "^1.1.5" + eslint-plugin-woke "^1.0.1" + eslint-plugin-yml "^0.10.1" + parse-gitignore "^1.0.1" + tslib "^2.3.1" + yaml "^1.10.2" + +eslint-etc@^5.1.0: + version "5.1.0" + resolved "5.1.0" + integrity sha512-Rmjl01h5smi5cbsFne2xpTuch2xNnwXiX2lbS4HttXUN5FwXKAwG1UEFBVGO1nC091YO/QyVahyfNPJSX2ae+g== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + tsutils "^3.17.1" + tsutils-etc "^1.4.1" + +eslint-formatter-git-log@^0.5.3: + version "0.5.3" + resolved "0.5.3" + integrity sha512-lwYPyg6fq6IQyNwLHkls1sjIXNWvY6RQx8S8hLTcgC+ldKYHd8Dc0G1qBpbQXoBFBrUmz73ZNyP1lMsAP8A33A== + dependencies: + chalk "2.4.2" + +eslint-formatter-gitlab@^3.0.0: + version "3.0.0" + resolved "3.0.0" + integrity sha512-fqZ2G45rgbrHcFunqmwuG5Qo6QAOlxEsR+KdOP08T1Xegw5tJhHh9KFWMSct8q6x8xCMUyYGHypZd342bLUttA== + dependencies: + chalk "^4.0.0" + js-yaml "^4.0.0" + +eslint-formatter-pretty@^4.1.0: + version "4.1.0" + resolved "4.1.0" + integrity sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ== + dependencies: + "@types/eslint" "^7.2.13" + ansi-escapes "^4.2.1" + chalk "^4.1.0" + eslint-rule-docs "^1.1.5" + log-symbols "^4.0.0" + plur "^4.0.0" + string-width "^4.2.0" + supports-hyperlinks "^2.0.0" + +eslint-formatter-summary@^1.1.0: + version "1.1.0" + resolved "1.1.0" + integrity sha512-teOh471ZYeEShXhBFb16X87buUjNZa2TMNn3CSf//DIKLNneqbk7u5i9hDDiIaQVvZbBXJHkgYxj8urcNKWbXw== + dependencies: + chalk "^4.1.0" + core-js "^3.9.1" + optionalDependencies: + np "^7.4.0" + +eslint-import-resolver-node@^0.3.2: + version "0.3.6" + resolved "0.3.6" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== + dependencies: + debug "^3.2.7" + resolve "^1.20.0" + +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "0.3.6" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== + dependencies: + debug "^3.2.7" + resolve "^1.20.0" + +eslint-module-utils@^2.4.1: + version "2.7.3" + resolved "2.7.3" + integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== + dependencies: + debug "^3.2.7" + find-up "^2.1.0" + +eslint-module-utils@^2.7.2: + version "2.7.3" + resolved "2.7.3" + integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== + dependencies: + debug "^3.2.7" + find-up "^2.1.0" + +eslint-plugin-angular@^4.1.0: + version "4.1.0" + resolved "4.1.0" + integrity sha512-dacledMPxVOZA3T0xcYFuvrMCy5dHxg0ZTMWUaHqSBQef3/XLyXJ9s1LNj0NikJ/dYx6OhqlnnNpKmrJhEUB+Q== + +eslint-plugin-array-func@^3.1.7: + version "3.1.7" + resolved "3.1.7" + integrity sha512-fB5TBICjHSTGToNTbCCgR8zsngpUkoCM31EMh/M/NEAyNg90i5rUuG0dnNNBML2n0BzM0nBE3sPvo2SEWf6jlA== + +eslint-plugin-editorconfig@^3.2.0: + version "3.2.0" + resolved "3.2.0" + integrity sha512-XiUg69+qgv6BekkPCjP8+2DMODzPqtLV5i0Q9FO1v40P62pfodG1vjIihVbw/338hS5W26S+8MTtXaAlrg37QQ== + dependencies: + "@typescript-eslint/eslint-plugin" "^5.0.0" + editorconfig "^0.15.0" + eslint "^8.0.1" + klona "^2.0.4" + +eslint-plugin-es@^3.0.0: + version "3.0.1" + resolved "3.0.1" + integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + +eslint-plugin-eslint-comments@^3.2.0: + version "3.2.0" + resolved "3.2.0" + integrity sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ== + dependencies: + escape-string-regexp "^1.0.5" + ignore "^5.0.5" + +eslint-plugin-etc@^2.0.2: + version "2.0.2" + resolved "2.0.2" + integrity sha512-g3b95LCdTCwZA8On9EICYL8m1NMWaiGfmNUd/ftZTeGZDXrwujKXUr+unYzqKjKFo1EbqJ31vt+Dqzrdm/sUcw== + dependencies: + "@phenomnomnominal/tsquery" "^4.0.0" + "@typescript-eslint/experimental-utils" "^5.0.0" + eslint-etc "^5.1.0" + requireindex "~1.2.0" + tslib "^2.0.0" + tsutils "^3.0.0" + +eslint-plugin-ext@^0.1.0: + version "0.1.0" + resolved "0.1.0" + integrity sha512-CbZgte+kC8u6uymkwtgDPHLgA3IRbhermH88o9VXDh4Pa1ds1QIo0ojJc+mvq5zjf3mm4GT/pTTFYZT9nQORyg== + +eslint-plugin-filenames@^1.3.2: + version "1.3.2" + resolved "1.3.2" + integrity sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w== + dependencies: + lodash.camelcase "4.3.0" + lodash.kebabcase "4.1.1" + lodash.snakecase "4.1.1" + lodash.upperfirst "4.3.1" + +eslint-plugin-fp@^2.3.0: + version "2.3.0" + resolved "2.3.0#376d2a108710e981980bdc3875e3b9920da0489c" + integrity sha1-N20qEIcQ6YGYC9w4deO5kg2gSJw= + dependencies: + create-eslint-index "^1.0.0" + eslint-ast-utils "^1.0.0" + lodash "^4.13.1" + req-all "^0.1.0" + +eslint-plugin-functional@^4.2.0: + version "4.2.0" + resolved "4.2.0" + integrity sha512-3v1DuKQTGwJo93UQ5SKzEjvJTaMGfznzwgGjWEBhLXxJfOMhcW7O6QUO1pmb5aLou9hoh7r31lkPvWmbIbIbew== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + deepmerge-ts "^2.0.1" + escape-string-regexp "^4.0.0" + +eslint-plugin-html@^6.2.0: + version "6.2.0" + resolved "6.2.0" + integrity sha512-vi3NW0E8AJombTvt8beMwkL1R/fdRWl4QSNRNMhVQKWm36/X0KF0unGNAY4mqUF06mnwVWZcIcerrCnfn9025g== + dependencies: + htmlparser2 "^7.1.2" + +eslint-plugin-import@^2.25.4: + version "2.25.4" + resolved "2.25.4" + integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== + dependencies: + array-includes "^3.1.4" + array.prototype.flat "^1.2.5" + debug "^2.6.9" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.6" + eslint-module-utils "^2.7.2" + has "^1.0.3" + is-core-module "^2.8.0" + is-glob "^4.0.3" + minimatch "^3.0.4" + object.values "^1.1.5" + resolve "^1.20.0" + tsconfig-paths "^3.12.0" + +eslint-plugin-jest-async@^1.0.3: + version "1.0.3" + resolved "1.0.3#69196702d8af85655b21a789a6fd75ca0a37800f" + integrity sha1-aRlnAtivhWVbIaeJpv11ygo3gA8= + dependencies: + requireindex "~1.1.0" + +eslint-plugin-jest-dom@^3.9.4: + version "3.9.4" + resolved "3.9.4" + integrity sha512-VRkaALGIhyxinnewZFHe2WJsRWp3TONpXysVXK1IUNJHCpJAIM9yRrI7fQ8i5F6UYE7+DAnvNhSSJZesLTonug== + dependencies: + "@babel/runtime" "^7.16.3" + "@testing-library/dom" "^7.31.2" + requireindex "^1.2.0" + +eslint-plugin-jest-formatting@^3.1.0: + version "3.1.0" + resolved "3.1.0" + integrity sha512-XyysraZ1JSgGbLSDxjj5HzKKh0glgWf+7CkqxbTqb7zEhW7X2WHo5SBQ8cGhnszKN+2Lj3/oevBlHNbHezoc/A== + +eslint-plugin-jest@^25.7.0: + version "25.7.0" + resolved "25.7.0" + integrity sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + +eslint-plugin-jsdoc@^37.9.0: + version "37.9.4" + resolved "37.9.4" + integrity sha512-VxCyGgUNNnj2T4bb1OqltkbsPp3ehRzR5onIfh6zGrAvISmvgX/sbxUlh3YyGqWtjOTSBCURdKdmelSXEIHnlA== + dependencies: + "@es-joy/jsdoccomment" "~0.20.1" + comment-parser "1.3.0" + debug "^4.3.3" + escape-string-regexp "^4.0.0" + esquery "^1.4.0" + regextras "^0.8.0" + semver "^7.3.5" + spdx-expression-parse "^3.0.1" + +eslint-plugin-json-schema-validator@^1.2.49: + version "1.2.49" + resolved "1.2.49" + integrity sha512-Ib7PVrho5a08OI05VyVEkwb2u1jDWeDoHsIngaZFVi4OsnLObcW6gb3xO6VSYHUDvC5CTQans7vVjLUslcCMiA== + dependencies: + ajv "^8.0.0" + debug "^4.3.1" + eslint-utils "^3.0.0" + json-schema-migrate "^2.0.0" + jsonc-eslint-parser "^1.0.0" + minimatch "^3.0.4" + toml-eslint-parser "^0.2.1" + tunnel-agent "^0.6.0" + yaml-eslint-parser "^0.4.0" + +eslint-plugin-jsonc@^1.7.0: + version "1.7.0" + resolved "1.7.0" + integrity sha512-pb3CAD9B0zhv3r9Bg9AdzswL50I3mbIq1ys+tNeuaDeibFlweo84SBNm22oqaFx/Dka+YZw2SLukAkQlJzSHMQ== + dependencies: + eslint-utils "^2.1.0 || ^3.0.0" + jsonc-eslint-parser "^1.4.1" + natural-compare "^1.4.0" + +eslint-plugin-no-constructor-bind@^2.0.4: + version "2.0.4" + resolved "2.0.4" + integrity sha512-r0CGAE5SrRYt1OdACNiZGiOcBbFslKIPnMrFo3kPmX3iKZOm8HRD2eIbqhlc9lSSiBWcPZxXErXnroqgt+dKBg== + dependencies: + requireindex "~1.2.0" + +eslint-plugin-no-explicit-type-exports@^0.12.1: + version "0.12.1" + resolved "0.12.1" + integrity sha512-m1v/f+LYVygCY735KfCovkoXYPbZH5zxEj/tuLOnMwX/qbJEJoRb9evul88Ois5HidvKbiMdMg/tXU55Ki++jg== + dependencies: + "@typescript-eslint/experimental-utils" "^2.27.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.1" + +eslint-plugin-no-secrets@^0.8.9: + version "0.8.9" + resolved "0.8.9" + integrity sha512-CqaBxXrImABCtxMWspAnm8d5UKkpNylC7zqVveb+fJHEvsSiNGJlSWzdSIvBUnW1XhJXkzifNIZQC08rEII5Ng== + +eslint-plugin-no-unsanitized@^3.2.0: + version "3.2.0" + resolved "3.2.0" + integrity sha512-92opuXbjWmXcod94EyCKhp36V1QHLM/ArAST2ssgKOojALne0eZvSPfrg4oyr0EwTXvy0RJNe/Tkm33VkDUrKQ== + +eslint-plugin-no-use-extend-native@^0.5.0: + version "0.5.0" + resolved "0.5.0" + integrity sha512-dBNjs8hor8rJgeXLH4HTut5eD3RGWf9JUsadIfuL7UosVQ/dnvOKwxEcRrXrFxrMZ8llUVWT+hOimxJABsAUzQ== + dependencies: + is-get-set-prop "^1.0.0" + is-js-type "^2.0.0" + is-obj-prop "^1.0.0" + is-proto-prop "^2.0.0" + +eslint-plugin-node@^11.1.0: + version "11.1.0" + resolved "11.1.0" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== + dependencies: + eslint-plugin-es "^3.0.0" + eslint-utils "^2.0.0" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-optimize-regex@^1.2.1: + version "1.2.1" + resolved "1.2.1" + integrity sha512-fUaU7Tj1G/KSTDTABJw4Wp427Rl7RPl9ViYTu1Jrv36fJw4DFhd4elPdXiuYtdPsNsvzn9GcVlKEssGIVjw0UQ== + dependencies: + regexp-tree "^0.1.21" + +eslint-plugin-prefer-arrow@^1.2.3: + version "1.2.3" + resolved "1.2.3" + integrity sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ== + +eslint-plugin-prettier@^4.0.0: + version "4.0.0" + resolved "4.0.0" + integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-promise@^5.2.0: + version "5.2.0" + resolved "5.2.0" + integrity sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw== + +eslint-plugin-regexp@^1.5.1: + version "1.5.1" + resolved "1.5.1" + integrity sha512-5v0rQIi54m2KycQHqmOAHrZhvI56GHmI2acr6zEffAqfeifTtobAEapv9Uf4o8//lGvwVkHKyjLoSbBNEFcfOA== + dependencies: + comment-parser "^1.1.2" + eslint-utils "^3.0.0" + grapheme-splitter "^1.0.4" + jsdoctypeparser "^9.0.0" + refa "^0.9.0" + regexp-ast-analysis "^0.3.0" + regexpp "^3.2.0" + scslre "^0.1.6" + +eslint-plugin-rxjs@^4.0.4: + version "4.0.4" + resolved "4.0.4" + integrity sha512-7JopYQRqS5TYBNXioTLtS+W6+IKC1rsC7q8Dtou8E5gMuPxuEFqxU1x2161bwadaK3+h6sR+xiGjEhiE6JwjUA== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + common-tags "^1.8.0" + decamelize "^5.0.0" + eslint-etc "^5.1.0" + requireindex "~1.2.0" + rxjs-report-usage "^1.0.4" + tslib "^2.0.0" + tsutils "^3.0.0" + tsutils-etc "^1.4.1" + +eslint-plugin-security@^1.4.0: + version "1.4.0" + resolved "1.4.0" + integrity sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA== + dependencies: + safe-regex "^1.1.0" + +eslint-plugin-sonarjs@^0.10.0: + version "0.10.0" + resolved "0.10.0" + integrity sha512-FBRIBmWQh2UAfuLSnuYEfmle33jIup9hfkR0X8pkfjeCKNpHUG8qyZI63ahs3aw8CJrv47QJ9ccdK3ZxKH016A== + +eslint-plugin-sort-class-members@^1.14.1: + version "1.14.1" + resolved "1.14.1" + integrity sha512-/Q/cm3h4N9DBNYvJMQMhluucSmr3Yydr9U0BgGcXUQe/rgWdXKSymZ5Ewcf4vmAG0bbTmAYmekuMnYYrqlu9Rg== + +eslint-plugin-sort-keys-fix@^1.1.2: + version "1.1.2" + resolved "1.1.2" + integrity sha512-DNPHFGCA0/hZIsfODbeLZqaGY/+q3vgtshF85r+YWDNCQ2apd9PNs/zL6ttKm0nD1IFwvxyg3YOTI7FHl4unrw== + dependencies: + espree "^6.1.2" + esutils "^2.0.2" + natural-compare "^1.4.0" + requireindex "~1.2.0" + +eslint-plugin-switch-case@^1.1.2: + version "1.1.2" + resolved "1.1.2#a622db0c4c440828526b6f26f000d71e74850032" + integrity sha1-piLbDExECChSa28m8ADXHnSFADI= + dependencies: + lodash.last "^3.0.0" + lodash.zipobject "^4.0.0" + +eslint-plugin-toml@^0.2.0: + version "0.2.0" + resolved "0.2.0" + integrity sha512-Y4eGb9q7i0i+UyZAXl3QAqcxkds2X7tfctXPllL7X+PpBXD/3wiqq1RNeoiLmHI9evpO1BjyuakffJZuGM7ElA== + dependencies: + debug "^4.1.1" + lodash "^4.17.19" + toml-eslint-parser "^0.2.1" + +eslint-plugin-tsdoc@^0.2.14: + version "0.2.14" + resolved "0.2.14" + integrity sha512-fJ3fnZRsdIoBZgzkQjv8vAj6NeeOoFkTfgosj6mKsFjX70QV256sA/wq+y/R2+OL4L8E79VVaVWrPeZnKNe8Ng== + dependencies: + "@microsoft/tsdoc" "0.13.2" + "@microsoft/tsdoc-config" "0.15.2" + +eslint-plugin-typescript-sort-keys@^2.1.0: + version "2.1.0" + resolved "2.1.0" + integrity sha512-ET7ABypdz19m47QnKynzNfWPi4CTNQ5jQQC1X5d0gojIwblkbGiCa5IilsqzBTmqxZ0yXDqKBO/GBkBFQCOFsg== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + json-schema "^0.4.0" + natural-compare-lite "^1.4.0" + +eslint-plugin-unicorn@^37.0.1: + version "37.0.1" + resolved "37.0.1" + integrity sha512-E1jq5u9ojnadisJcPi+hMXTGSiIzkIUMDvWsBudsCGXvKUB2aNSU2TcfyW2/jAS5A4ryBXfzxLykMxX1EdluSQ== + dependencies: + "@babel/helper-validator-identifier" "^7.14.9" + ci-info "^3.2.0" + clean-regexp "^1.0.0" + eslint-template-visitor "^2.3.2" + eslint-utils "^3.0.0" + esquery "^1.4.0" + indent-string "4" + is-builtin-module "^3.1.0" + lodash "^4.17.21" + pluralize "^8.0.0" + read-pkg-up "^7.0.1" + regexp-tree "^0.1.23" + safe-regex "^2.1.1" + semver "^7.3.5" + strip-indent "^3.0.0" + +eslint-plugin-unused-imports@^1.1.5: + version "1.1.5" + resolved "1.1.5" + integrity sha512-TeV8l8zkLQrq9LBeYFCQmYVIXMjfHgdRQLw7dEZp4ZB3PeR10Y5Uif11heCsHRmhdRIYMoewr1d9ouUHLbLHew== + dependencies: + eslint-rule-composer "^0.3.0" + +eslint-plugin-woke@^1.0.1: + version "1.0.1" + resolved "1.0.1" + integrity sha512-cDRyZMNJkrbycju6OKhGnF29j0kbzV9AnLk3IPz6Kn0vWrSf8RyUdS5at97o/CJ8SEN2e6furQch1o/0LIrfMg== + dependencies: + requireindex "~1.1.0" + +eslint-plugin-yml@^0.10.1: + version "0.10.1" + resolved "0.10.1" + integrity sha512-af0WgO3qaH+RW6jv1s6RzXKlg2NZLisN95lqGUf1KqBT6rEJyGSCpM49QYaSTvzmMaB/gcdbrnAfNoYwUn0Yig== + dependencies: + debug "^4.1.1" + lodash "^4.17.19" + natural-compare "^1.4.0" + yaml-eslint-parser "^0.4.0" + +eslint-rule-composer@^0.3.0: + version "0.3.0" + resolved "0.3.0" + integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg== + +eslint-rule-docs@^1.1.5: + version "1.1.231" + resolved "1.1.231" + integrity sha512-egHz9A1WG7b8CS0x1P6P/Rj5FqZOjray/VjpJa14tMZalfRKvpE2ONJ3plCM7+PcinmU4tcmbPLv0VtwzSdLVA== + +eslint-scope@^5.1.1: + version "7.1.1" + resolved "7.1.1" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-scope@^7.1.1: + version "7.1.1" + resolved "7.1.1" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-template-visitor@^2.3.2: + version "2.3.2" + resolved "2.3.2" + integrity sha512-3ydhqFpuV7x1M9EK52BPNj6V0Kwu0KKkcIAfpUhwHbR8ocRln/oUHgfxQupY8O1h4Qv/POHDumb/BwwNfxbtnA== + dependencies: + "@babel/core" "^7.12.16" + "@babel/eslint-parser" "^7.12.16" + eslint-visitor-keys "^2.0.0" + esquery "^1.3.1" + multimap "^1.1.0" + +eslint-utils@^2.0.0: + version "3.0.0" + resolved "3.0.0" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-utils@^2.1.0: + version "3.0.0" + resolved "3.0.0" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +"eslint-utils@^2.1.0 || ^3.0.0": + version "3.0.0" + resolved "3.0.0" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "3.0.0" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^1.3.0: + version "3.3.0" + resolved "3.3.0" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint-visitor-keys@^2.0.0: + version "3.3.0" + resolved "3.3.0" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint-visitor-keys@^2.1.0: + version "3.3.0" + resolved "3.3.0" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint-visitor-keys@^3.0.0: + version "3.3.0" + resolved "3.3.0" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "3.3.0" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.0.1: + version "8.9.0" + resolved "8.9.0" + integrity sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q== + dependencies: + "@eslint/eslintrc" "^1.1.0" + "@humanwhocodes/config-array" "^0.9.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.6.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +eslint@^8.9.0: + version "8.9.0" + resolved "8.9.0" + integrity sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q== + dependencies: + "@eslint/eslintrc" "^1.1.0" + "@humanwhocodes/config-array" "^0.9.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.6.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +git-cz-emoji@latest: + version "1.1.24" + resolved "1.1.24" + integrity sha512-m9XItxRoL6k/NCAeWubMJW2m3r29V6JkQSpZk5vtFY6IVJ8S4l6l7AVreOsRemKoWiVzxtb2I4xBbJNt1vlrNw== + dependencies: + chalk "^4.1.2" + commitizen "^4.2.4" + tslib "^2.3.1" + word-wrap "^1.2.3" + +glob@^7.2.0: + version "7.2.0" + resolved "7.2.0" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +handlebars-helpers@^0.10.0: + version "0.10.0" + resolved "0.10.0" + integrity sha512-QiyhQz58u/DbuV41VnfpE0nhy6YCH4vB514ajysV8SoKmP+DxU+pR+fahVyNECHj+jiwEN2VrvxD/34/yHaLUg== + dependencies: + arr-flatten "^1.1.0" + array-sort "^0.1.4" + create-frame "^1.0.0" + define-property "^1.0.0" + "falsey" "^0.3.2" + for-in "^1.0.2" + for-own "^1.0.0" + get-object "^0.2.0" + get-value "^2.0.6" + handlebars "^4.0.11" + handlebars-helper-create-frame "^0.1.0" + handlebars-utils "^1.0.6" + has-value "^1.0.0" + helper-date "^1.0.1" + helper-markdown "^1.0.0" + helper-md "^0.2.2" + html-tag "^2.0.0" + is-even "^1.0.0" + is-glob "^4.0.0" + is-number "^4.0.0" + kind-of "^6.0.0" + lazy-cache "^2.0.2" + logging-helpers "^1.0.0" + micromatch "^3.1.4" + relative "^3.0.2" + striptags "^3.1.0" + to-gfm-code-block "^0.1.1" + year "^0.2.1" + +jsonc-eslint-parser@^1.0.0: + version "1.4.1" + resolved "1.4.1" + integrity sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg== + dependencies: + acorn "^7.4.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^1.3.0" + espree "^6.0.0" + semver "^6.3.0" + +jsonc-eslint-parser@^1.4.1: + version "1.4.1" + resolved "1.4.1" + integrity sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg== + dependencies: + acorn "^7.4.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^1.3.0" + espree "^6.0.0" + semver "^6.3.0" + +prettier-config-sexy-mode@latest: + version "1.0.1" + resolved "1.0.1" + integrity sha512-6ZdAv6LW2vq++c5fqoh2MWrIZk9fi67o608xizEPYKPMjGUNDsqjJ/pYiJLp7yDHgGerEERc4bRQqlh8a9W6YA== + dependencies: + "@prettier/plugin-php" "^0.17.6" + "@prettier/plugin-pug" "^1.19.2" + "@prettier/plugin-ruby" "^2.0.0" + "@prettier/plugin-xml" "^1.0.2" + prettier "^2.4.1" + prettier-plugin-go-template "^0.0.11" + prettier-plugin-ini "^0.3.1" + prettier-plugin-java "^1.6.1" + prettier-plugin-jsdoc "^0.3.30" + prettier-plugin-organize-imports "^2.3.4" + prettier-plugin-package-perfection "^1.0.1" + prettier-plugin-properties "^0.1.0" + prettier-plugin-sh "^0.7.1" + prettier-plugin-solidity "^1.0.0-beta.19" + prettier-plugin-sql "^0.4.1" + tslib "^2.3.1" + +prettier-plugin-go-template@^0.0.11: + version "0.0.11" + resolved "0.0.11" + integrity sha512-qtgoEjvbgmcDp9TOqYNgrPrA41s6S1UMyzMqjcxdxQahTX0webWfbamyA/x3XeBFEEJmgXrRAirzJrIVzImsMg== + dependencies: + ulid "^2.3.0" + +prettier-plugin-ini@^0.3.1: + version "0.3.1" + resolved "0.3.1" + integrity sha512-p2MPOpkf0ebBme4n8U3TD7/uC3azlEyPgbfgU/l5SCgAvSKmXmRMSxKxonhSoQAGtdwQWcEADJbmzl+X72yN4Q== + dependencies: + prettier ">=2.0" + +prettier-plugin-java@^1.6.1: + version "1.6.1" + resolved "1.6.1" + integrity sha512-kSY17V/P88nILlILb5iMp16TVJy6Ls9Jy4zAzI4+GsEuRDZH5VqRuLd8aJS1ImWxVgRjNBmoFOjxYyxkRM0SRA== + dependencies: + java-parser "2.0.1" + lodash "4.17.21" + prettier "2.3.1" + +prettier-plugin-jsdoc@^0.3.30: + version "0.3.30" + resolved "0.3.30" + integrity sha512-BTBojOMmrUA1qsWLpJN5whUfU/E72WBUQAB5AvrDkha+O8TxmqaAivnuW+87ItYGRPBFWWzj2r5iWELhBml1Ag== + dependencies: + binary-search-bounds "^2.0.5" + comment-parser "^1.1.4" + linguist-languages "^7.13.0" + mdast-util-from-markdown "^0.8.5" + +prettier-plugin-organize-imports@^2.3.4: + version "2.3.4" + resolved "2.3.4" + integrity sha512-R8o23sf5iVL/U71h9SFUdhdOEPsi3nm42FD/oDYIZ2PQa4TNWWuWecxln6jlIQzpZTDMUeO1NicJP6lLn2TtRw== + +prettier-plugin-package-perfection@^1.0.1: + version "1.0.6" + resolved "1.0.6" + integrity sha512-7nxpRYdAdda6xiwrVDYDxcnxPfBu58JJPGtoJOwh2uohE6RfnUJOOjV76woXG8kO+mBptHAQtqoT6lsLRvatqA== + dependencies: + prettier-package-json "2.6.0" + +prettier-plugin-properties@^0.1.0: + version "0.1.0" + resolved "0.1.0" + integrity sha512-lObSgVaTVWSyYWxKMOzRGmvQp64S1qumu5vS91ZlMc198ay8EGUuDH+Tc019iMJXc2KNpdAYif2qAJA6mjTkgA== + dependencies: + dot-properties "^1.0.0" + linguist-languages "^7.9.0" + +prettier-plugin-sh@^0.7.1: + version "0.7.1" + resolved "0.7.1" + integrity sha512-2MWRdGOSz0yf/z2kTKF1AqxDuH9MZD8faoDAz5ySGphxssi9oyM3Ys+jp7AfqsCXvGUDbRA4EJOlKS0yZKAW6w== + dependencies: + mvdan-sh "^0.5.0" + +prettier-plugin-solidity@^1.0.0-beta.19: + version "1.0.0-beta.19" + resolved "1.0.0-beta.19" + integrity sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g== + dependencies: + "@solidity-parser/parser" "^0.14.0" + emoji-regex "^10.0.0" + escape-string-regexp "^4.0.0" + semver "^7.3.5" + solidity-comments-extractor "^0.0.7" + string-width "^4.2.3" + +prettier-plugin-sql@^0.4.1: + version "0.4.1" + resolved "0.4.1" + integrity sha512-HSBn1f1ZbFIqWm9vVJcdQl1tzdh8qYZbBNK7z3iWhDO/DXM7gZH2HnS5r99e3tYqf34WIdo2nSCWaqylo+5fPw== + dependencies: + node-sql-parser "^4.0.0" + sql-formatter "^4.0.2" + +semantic-release-config@latest: + version "1.1.18" + resolved "1.1.18" + integrity sha512-NtEpAQ7eOJD5kdv2EqdLWuH04vzNR2DVYNqVQgDnzDqM7X2RVeszSo10mLUyY8GpTgqOQTYqRfE4MmX+RVU2gA== + dependencies: + "@semantic-release/changelog" "^6.0.1" + "@semantic-release/commit-analyzer" "^9.0.2" + "@semantic-release/exec" "^6.0.3" + "@semantic-release/git" "^10.0.1" + "@semantic-release/gitlab" "^7.0.4" + "@semantic-release/npm" "^9.0.0" + "@semantic-release/release-notes-generator" "^10.0.3" + conventional-changelog-emoji-config latest + git-cz-emoji latest + semantic-release "^19.0.2" + semantic-release-gh latest + semantic-release-npm-deprecate-old-versions "^1.3.2" + semantic-release-python latest + tslib "^2.3.1" + yaml "^1.10.2" + +toml-eslint-parser@^0.2.1: + version "0.2.1" + resolved "0.2.1" + integrity sha512-lAESSx47NjCe/O9Y5dbAmP+U/EFvdxPRgA0Hp4TxCZP3Bs6hxRfLHxZvBuXvTGkVoK0DpYamPz/rCqYBzoFWVQ== + dependencies: + eslint-visitor-keys "^2.0.0" + +typescript@^4.5.5: + version "4.5.5" + resolved "4.5.5" + integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== + +yaml-eslint-parser@^0.4.0: + version "0.4.1" + resolved "0.4.1" + integrity sha512-GoJ/p1EW8O2tbTbuhfxjo1XhfUFU3uX3kwvfEQoOaZjO2Lubx8POjlsSqB+18b3SxkujAdQYT9r9nURaUWNYWQ== + dependencies: + eslint-visitor-keys "^2.1.0" + lodash "^4.17.20" + yaml "^1.10.0" diff --git a/.config/docs/readme-playbook/architecture.md b/.config/docs/readme-playbook/architecture.md new file mode 100644 index 00000000..00e43a2f --- /dev/null +++ b/.config/docs/readme-playbook/architecture.md @@ -0,0 +1,3 @@ +## Architecture + +You can find a high-level overview of what each folder and file does in the [ARCHITECTURE.md](docs/ARCHITECTURE.md) file. diff --git a/.config/docs/readme-playbook/dependencies.md b/.config/docs/readme-playbook/dependencies.md new file mode 100644 index 00000000..cc95fa59 --- /dev/null +++ b/.config/docs/readme-playbook/dependencies.md @@ -0,0 +1,36 @@ +## Requirements + +- **[Python >=3.7](https://www.python.org/)** +- **[Ansible >=2.9](https://www.ansible.com/)** +- Ansible controller should be a macOS/Linux environment (WSL/Docker can be used on Windows) + +### Host Requirements + +There are Python and Ansible package requirements need to be installed by running the following command (or equivalent) in the root of this repository: + +``` +pip3 install -r .config/requirements.txt +ansible-galaxy install requirements.yml +``` + +#### Easier Method of Installing the Host Requirements + +You can also run `bash start.sh` if you do not mind development dependencies being installed as well. This method will even handle installing Python 3 and Ansible. + +### Operating System + +**This playbook is built and tested to run on fresh installs of Windows, Mac OS X, Ubuntu, Fedora, Debian, CentOS, Archlinux, and Qubes**. It may still be possible to run the playbook on your current machine. However, installing the playbook on a fresh install is the only thing we actively support. That said, if you come across an issue with an environment that already has configurations and software present, please do not hesitate to [open an issue]({{ repository.gitlab }}{{ repository.location.issue.gitlab }}). + +### Connection + +SSH (or WinRM in the case of Windows) and Python should be available on the target systems you would like to provision. If you are attempting to provision a Windows machine, you can ensure that WinRM is enabled and configured so that you can remotely provision the Windows target by running the following command with PowerShell: + +```powershell +Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://install.doctor/windows-client')) +``` + +### MAS on Mac OS X + +We use [mas](https://github.com/mas-cli/mas) to install apps from the App Store in some of our roles. Sadly, automatically signing into the App Store is not possible on OS X 10.13+ via mas. This is because [mas no longer supports login functionality on OS X 10.13+](https://github.com/mas-cli/mas/issues/164). + +There is another caveat with mas. In order to install an application using mas, the application has to have already been added via the App Store GUI. This means that the first time around you will have to install the apps via the App Store GUI so they are associated with your App Store account. diff --git a/.config/docs/readme-playbook/introduction.md b/.config/docs/readme-playbook/introduction.md new file mode 100644 index 00000000..51ad7093 --- /dev/null +++ b/.config/docs/readme-playbook/introduction.md @@ -0,0 +1,16 @@ +## Introduction + +Welcome to a new way of doing things. Born out of complete paranoia and a relentless pursuit of the best of GitHub Awesome lists, Gas Station aims to add the capability of being able to completely wipe whole networks and restore them on a regular basis. It takes a unique approach to network provisioning because it supports desktop provisioning as a first-class citizen. By default, without much configuration, it is meant to provision and maintain the state of a network that includes development workstations and servers. One type of user that might benefit from this project is a web developer who wants to start saving the state of their desktop as code. Another type of user is one who wants to start hosting RAM-intensive web applications in their home-lab environment to save huge amounts on cloud costs. This project is also meant to be maintainable by a single person. Granted, if you look through our eco-system you will see we are well-equipped for supporting entire teams as well. + +Gas Station a collection of Ansible playbooks, configurations, scripts, and roles meant to provision computers and networks with the "best of GitHub". By leveraging Ansible, you can provision your whole network relatively fast in the event of a disaster or scheduled network reset. This project is also intended to increase the security of your network by allowing you to frequently wipe, reinstall, and re-provision your network, bringing it back to its original state. This is done by backing up container storage volumes (like database files and Docker volumes) to encrypted S3 buckets, storing configurations in encrypted git repositories, and leveraging GitHub-sourced power tools to make the job easy-peasy. + +This project started when a certain somebody changed their desktop wallpaper to an _cute_ picture of a cat 🐱 when, all of a sudden, their computer meowed. Well, it actually started before that but no one believes someone who claims that time travelers hacked them on a regular basis. *Tip: If you are stuck in spiritual darkness involving time travelers, save yourself some headaches by adopting an other-people first mentality that may include volunteering, tithing, and surrendering to Jesus Christ.* Anyway, enough preaching! + +Gas Station is: + +- Highly configurable - most roles come with optional variables that you can configure to change the behavior of the role +- Highly configured - in-depth research is done to ensure each software component is configured with bash completions, plugins that are well-received by the community, and integrated with other software used in the playbook +- Compatible with all major operating systems (i.e. Windows, Mac OS X, Ubuntu, Fedora, CentOS, Debian, and even Archlinux) +- The product of a team of experts +- An amazing way to learn about developer tools that many would consider to be "the best of GitHub" +- Open to new ideas - feel free to [open an issue]({{ repository.gitlab }}{{ repository.location.issues.gitlab }}) or [contribute]({{ repository.github }}{{ repository.location.contributing.github }}) with a [pull request]({{ repository.github }}{{ repository.location.issues.github }})! diff --git a/.config/docs/readme-playbook/managing-environments.md b/.config/docs/readme-playbook/managing-environments.md new file mode 100644 index 00000000..01501438 --- /dev/null +++ b/.config/docs/readme-playbook/managing-environments.md @@ -0,0 +1,17 @@ +## Managing Environments + +We accomplish managing different environments by symlinking all the folders that should be unique to each network environment (e.g. `host_vars/`, `group_vars/`, `inventories/`, `files/vpn/`, and `files/ssh/`). In the `environments/` folder, you will see multiple folders. In our case, `environments/dev/` contains sensible configurations for testing the playbook and its' roles. The production environment is a seperate git submodule that links to a private git repository that contains our Ansible-vaulted API keys and passwords. When you are ready to set up your production configurations, you can use this method of storing your environment-specific folders in the `environments/` folder as well. But if you are just starting off, you do not have to worry about this since, by default, this playbook is configured to run with the settings included in the `/environments/dev/` folder. + +### Switching Between Environments + +If you already have the project bootstrapped (i.e. already ran `bash .config/scripts/start.sh`), you can switch environments with an interactive prompt by running: + +```shell +task ansible:playbook:environment +``` + +Alternatively, you can run the following if you would like to bypass the prompt: + +```shell +task ansible:playbook:environment -- environmentName +``` diff --git a/.config/docs/readme-playbook/philosophy.md b/.config/docs/readme-playbook/philosophy.md new file mode 100644 index 00000000..b6300ba3 --- /dev/null +++ b/.config/docs/readme-playbook/philosophy.md @@ -0,0 +1,20 @@ +## Philosophy + +The philosophy of this project basically boils down to "**_automate everything_**" and include the best development tools that might be useful without over-bloating the machine with services. Automating everything should include tasks like automatically accepting software terms in advance or pre-populating Portainer with certificates of all the Docker hosts you would like to control. One problem we face is that there are so many great tools offered on GitHub. A lot of research has to go into what to include and what to pass on. The decision of whether or not to include a piece of software in the default playbook basically boils down to: + +- **Project popularity** - If one project has 10k stars and a similar alternative has 500 stars then 9 times of out 10 the more popular project is selected. +- **Last commit date** - We prefer software that is being actively maintained, for obvious reasons. +- **Cross platform** - Our playbook supports the majority of popular operating systems so we opt for cross-platform software. However, in some cases, we will include software that has limited cross-platform support like Xcode (which is only available on Mac OS X). If a piece of software is too good to pass up, it is added and only installed on the system(s) that support it. +- **Usefulness** - If a tool could potentially improve developer effectiveness then we are more likely to include it. +- **System Impact** - Software that can be run with a small RAM footprint and software that does not need a service to load on boot is much more likely to be included. + +One of the goals of this project is to be able to re-provision a network with the click of a button. This might not be feasible since consumer-grade hardware usually does not include features like IPMI (which is a feature included in high-end motherboards that lets you control the power state remotely). However, we aim to reduce the amount of interaction required when re-provisioning an entire network down to the bare minimum. In the worst case scenario, you will have to reformat, reinstall the operating system, and ensure that OpenSSH is running (or WinRM in the case of Windows) on each of the computers in your network. However, the long term goal is to allow the user to reformat and reinstall the operating system used as your Ansible host using an automated USB installer and then automatically re-provision everything else on the network by utilizing IPMI. + +You might ask, "But how can I retain application-level configurations?" We currently handle this by: + +- Pre-defining dotfiles in a customizable Git repository +- Backing up to encrypted S3 buckets +- Syncing files to private git repositories +- Utilizing tools that synchronize settings like [mackup](https://github.com/lra/mackup) or [macprefs](https://github.com/clintmod/macprefs) in the case of macOS + +However, we intentionally keep this synchronization to a minimum (i.e. only back up what is necessary). After all, one of the goals of this project is to be able to regularly flush the bad stuff off a system. By keeping what we back up to a minimum, we reduce the attack surface. diff --git a/.config/docs/readme-playbook/quick-description.md b/.config/docs/readme-playbook/quick-description.md new file mode 100644 index 00000000..b3edeff4 --- /dev/null +++ b/.config/docs/readme-playbook/quick-description.md @@ -0,0 +1 @@ +>

**A no-stone-unturned Ansible playbook you can use to set up the ultimate home lab or on-premise addition to your cloud!**



diff --git a/.config/docs/readme-playbook/quick-start.md b/.config/docs/readme-playbook/quick-start.md new file mode 100644 index 00000000..b8c901bb --- /dev/null +++ b/.config/docs/readme-playbook/quick-start.md @@ -0,0 +1,35 @@ +## Quick Start + +The easiest way to run the entire playbook, outlined in the `main.yml` file, is to run the appropriate command listed below. These commands will run the playbook on the machine you run the command on. This is probably the best way to get your feet wet before you decide to give us a ⭐ and customize the playbook for your own needs. Ideally, this command should be run on the machine that you plan on running Ansible with to provision the other computers on your network. It is only guaranteed to work on fresh installs so testing it out with [Vagrant](https://www.vagrantup.com/) is highly encouraged. + +### Vagrant (Recommended) + +To test it out with Vagrant, you can run the following commands which will open up an interactive dialog where you can pick which operating system and virtualization provider you wish to test the installation with: + +```shell +bash start.sh && task ansible:test:vagrant +``` + +### macOS/Linux + +```shell +curl -sSL https://install.doctor/quickstart > ./setup.sh && bash ./setup.sh +``` + +### Windows + +In an administrative PowerShell session, run: + +```powershell +iex ((New-Object System.Net.WebClient).DownloadString('https://install.doctor/windows-quickstart')) +``` + +### Qubes + +Our playbooks include a specially crafted playbook for Qubes. It will load your VMs with sensible defaults. For more details, check out the [Qubes playbook](https://gitlab.com/megabyte-labs/gas-station/-/blob/master/playbooks/qubes.yml) and [Qubes variables](https://gitlab.com/megabyte-labs/gas-station/-/blob/master/environments/prod/group_vars/qubes). Perhaps most importantly, the "quickstart" [the inventory file](https://gitlab.com/megabyte-labs/gas-station/-/blob/master/environments/prod/inventories/quickstart.yml) details the VM structure that the provisioning script adds to the target system. + +To setup Qubes, run the following on a fresh install in dom0: + +```shell +qvm-run --pass-io sys-firewall "curl -sSL https://install.doctor/qubes" > ./setup.sh && bash ./setup.sh +``` \ No newline at end of file diff --git a/.config/docs/readme-playbook/software.md b/.config/docs/readme-playbook/software.md new file mode 100644 index 00000000..8f581a51 --- /dev/null +++ b/.config/docs/readme-playbook/software.md @@ -0,0 +1,82 @@ +## Software + +This project breaks down software into a role (found in the subdirectories of the `roles/` folder) if the software requires anything other than being added to the `PATH` variable. Below is a quick description of what each role does. Browsing through this list, along with the conditions laid out in `main.yml`, you will be able to get a better picture of what software will be installed by the default `main.yml` playbook. + +### Role-Based Software + +{{ role_var_chart }} + +We encourage you to browse through the repositories that are linked to in the table above to learn about the configuration options they support. Some of the roles are included as roles because they support configurations that rely on user-specific variables like API keys. + +### Binaries + +A lot of the nifty software we install by default does not require any configuration other than being added to the `PATH` or being installed with an installer like `brew`. For this kind of software that requires no configuration, we list the software we would like installed by the playbook as a variable in `group_vars/` or `host_vars/` as an array of keys assigned to the `software` variable ([example here](environments/prod/group_vars/desktop/vars.yml)). With those keys, we install the software using the [`professormanhattan.genericinstaller`](https://galaxy.ansible.com/professormanhattan/genericinstaller) role which determines how to install the binaries by looking up the keys against the `software_package` object ([example here](environments/prod/group_vars/all/software.yml)). + +**NOTE:** The binary packages listed in these charts will attempt to install using the system package manager and then from source if the option is available before resorting to less desirable methods like downloading the binary from GitHub releases. The order of installation method preference that the [`professormanhattan.genericinstaller`](https://galaxy.ansible.com/professormanhattan/genericinstaller) role attempts to use is defined in the `INSERT_VARIABLE_NAME` variable. The default order is: + +1. System package managers +2. Compiling from source (via Go, Rust, etc.) +3. Installing via Homebrew +4. Downloading the pre-compiled assets from GitHub releases + +For your convienience, we have split the long list of single binary based software into two lists - one for CLIs and one for Applications: + +#### Binary Desktop Applications + +{{ binaryapp_var_chart }} + +#### Binary CLIs / TUIs + +{{ binarycli_var_chart }} + +### NPM Packages + +NPM provides a huge catalog of useful CLIs and libraries so we also include a useful and interesting default set of NPM-hosted CLIs for hosts in the `desktop` group ([defined here](environments/prod/group_vars/desktop/npm-packages.yml), for example): + +{{ npm_var_chart }} + +### Python Packages + +In a similar fashion to the NPM packages, we include a great set of default Python packages that are included by default for the `desktop` group ([defined here](environments/prod/group_vars/desktop/pip-packages.yml)): + +{{ pypi_var_chart }} + +### Ruby Gems + +A handful of Ruby gems are also installed on targets in the `desktop` group ([defined here](environments/prod/group_vars/desktop/ruby-gems.yml)): + +{{ gem_var_chart }} + +### Visual Studio Code Extensions + +A considerable amount of effort has gone into researching and finding the "best" VS Code extensions. They are [defined here](environments/prod/group_vars/desktop/vscode-extensions.yml) and Gas Station also installs a good baseline configuration which includes settings for these extensions: + +{{ vscode_var_chart }} + +### Chrome Extensions + +To reduce the amount of time it takes to configure Chromium-based browsers like Brave, Chromium, and Chrome, we also include the capability of automatically installing Chromium-based browser extensions (via a variable [defined here](environments/prod/group_vars/desktop/chrome-extensions.yml)): + +{{ chrome_var_chart }} + +### Firefox Add-Ons + +Below you can find the Firefox extensions that the base configuration of this playbook will automatically install: + +{{ firefox_var_chart }} + +### Homebrew Formulae (macOS and Linux only) + +Although most of the `brew` installs are handled by the [Binaries](#binaries) installer, some `brew` packages are also installed using [this configuration](environments/prod/group_vars/desktop/homebrew.yml). The default Homebrew formulae include: + +{{ brew_var_chart }} + +### Homebrew Casks (macOS only) + +On macOS, some software is installed using Homebrew casks. These include: + +{{ cask_var_chart }} + +### Go, Rust, and System-Specific Packages + +Go packages, Rust crates, and system-specific packages like `.deb` and `.rpm` bundles are all handled by the [`professormanhattan.genericinstaller`](https://galaxy.ansible.com/professormanhattan/genericinstaller) role described above in the [Binaries](#binaries) section. There are also ways of installing Go and Rust packages directly by using configuration options provided by their corresponding roles outlined in the [Roles](#roles) section. diff --git a/.config/docs/readme-playbook/subheader.md b/.config/docs/readme-playbook/subheader.md new file mode 100644 index 00000000..89b04fe9 --- /dev/null +++ b/.config/docs/readme-playbook/subheader.md @@ -0,0 +1,38 @@ +
+ + Homepage + + + Contributing + + + Slack + + + Gitter + + + GitHub + + + GitLab + +
+
+
+ + Version: {{ pkg.version }} + + + Build status + + + E2E test status + + + Documentation + + + License: {{ license }} + +
diff --git a/.config/docs/readme-playbook/supported-os.md b/.config/docs/readme-playbook/supported-os.md new file mode 100644 index 00000000..b84f4b2f --- /dev/null +++ b/.config/docs/readme-playbook/supported-os.md @@ -0,0 +1,5 @@ +## Supported Operating Systems + +The following chart shows the operating systems that have been tested for compatibility using the `environments/dev/` environment. This chart is automatically generated using the Ansible Molecule tests you can view in the `molecule/default/` folder. We currently have logic in place to automatically handle the testing of Windows, Mac OS X, Ubuntu, Fedora, CentOS, Debian, Archlinux, and, of course, Qubes. If your operating system is not listed but is a variant of one of the systems we test (i.e. a Debian-flavored system or a RedHat-flavored system) then it might still work. + +{{ compatibility_matrix }} diff --git a/.config/docs/readme-playbook/web-apps.md b/.config/docs/readme-playbook/web-apps.md new file mode 100644 index 00000000..7cc9c71b --- /dev/null +++ b/.config/docs/readme-playbook/web-apps.md @@ -0,0 +1,35 @@ +## Web Applications + +This playbook does a bit more than just install software. It also optionally sets up web applications too. If you choose to deploy the default Gas Station web applications on your network, you should probably do it on a computer/server that has a lot of RAM (e.g. 64GB+). + +Although a production environment will always be more stable and performant if it is hosted with a major cloud provider, sometimes it makes more sense to locally host web applications. Some applications have abnormally large RAM requirements that could potentially cost thousands per month to host with a legit cloud provider. + +We use Kubernetes as the provider for the majority of the applications. It is a production-grade system and although there is a steeper learning curve it is well worth it. Each application we install is packaged as a Helm chart. All of the data is backed up regularly to an encrypted cloud S3 bucket of your choice. + +### Helm Charts + +The available Helm charts that this playbook completely handles the set up for are listed below. + +{{ helm_var_chart }} + +### Host Applications + +By default, on each computer provisioned using the default settings of Gas Station, several apps are installed on each host. Docker Compose is used to manage the deployment. The default apps include: + +{{ hostapp_var_chart }} + +You can, of course, disable deploying these apps. However, we include them because they have a small footprint and include useful features. You can also customize the list of apps you wish to include on each host. + +#### HTPC + +We do not maintain any of the host applications except the ones listed above. However, we do provide the capability of marking a computer being provisioned as an HTPC. Doing this will include a suite of web applications with powerful auto-downloading, organizing, tagging, and media-serving capabilities. Since most people will probably be stepping outside the confines of the law for this, it is not recommended. If you still want to experiment then you can find descriptions of the applications below. The applications are intended to be hosted on a single computer via Docker Compose. The backend for Kodi is included but you should still use the regular installation method for Plex and the front-end of Kodi to view your media collection. + +{{ htpc_var_chart }} + +### Online Services + +Certain parts of the stack rely on cloud-based service providers. All of the providers can be used for free. The providers are generally chosen because their settings need to persist or the functionality that they provide would benefit from a security-hardened SaaS offering. + +You can, of course, swap these services out for alternatives. However, our scripts integrate these specific services so if you want to swap them out then some leg work will be necessary. + +{{ saas_var_chart }} diff --git a/.config/docs/readme-role/dependencies.md b/.config/docs/readme-role/dependencies.md new file mode 100644 index 00000000..8b7df1d2 --- /dev/null +++ b/.config/docs/readme-role/dependencies.md @@ -0,0 +1,24 @@ +## Dependencies + +Most of our roles rely on [Ansible Galaxy]({{ profile_link.galaxy }}) collections. Some of our projects are also dependent on other roles and collections that are published on Ansible Galaxy. Before you run this role, you will need to install the collection and role dependencies, as well as the Python requirements, by running: + +```yaml +if type poetry &> /dev/null; then poetry install --no-root; else pip3 install -r .config/assets/requirements.txt; fi +ansible-galaxy install -r requirements.yml +``` + +Alternatively, you can simply run `bash .config/scripts/start.sh` if you are new to Ansible and do not mind the development requirements also being installed. This is the easy way of making sure that everything works properly. + +### Python + +Although the only tool necessary to run this play on a standard machine is Ansible (a Python package), we include several other Python dependencies that are required for specialized use cases and development. The table below details these packages: + +{{ python_role_dependencies }} + +### Galaxy Roles + +Although most of our roles do not have dependencies, there are some cases where another role has to be installed before the logic can continue. At the beginning of the play, the Ansible Galaxy role dependencies listed in `meta/main.yml` will run. These dependencies are configured to only run once per playbook. If you include more than one of our roles in your playbook that have dependencies in common then the dependency installation will be skipped after the first run. Some of our roles also utilize helper roles directly from the task files which helps keep our [main playbook (Gas Station)]({{ repository.playbooks }}) DRY. + +The `requirements.yml` file contains a full list of the Ansible Galaxy dependencies required by this role (i.e. `meta/main.yml` role dependencies, helper roles, collections, etc.). For your convenience, a list of the role dependencies along with quick descriptions is below: + +{{ role_dependencies }} diff --git a/.config/docs/readme-role/example.md b/.config/docs/readme-role/example.md new file mode 100644 index 00000000..e18d1018 --- /dev/null +++ b/.config/docs/readme-role/example.md @@ -0,0 +1,15 @@ +## Example Playbook + +With the dependencies installed, all you have to do is add the role to your main playbook. The role handles the `become` behavior so you can simply add the role to your playbook without having to worry about commands that should not be run as root: + +```lang-yml +- hosts: all + roles: + - {{ galaxy_info.namespace }}.{{ galaxy_info.role_name }} +``` + +If you are incorporating this role into a pre-existing playbook, then it might be prudent to copy the requirements outlined in `pyproject.toml` and `requirements.yml` to their corresponding files in the root of your playbook so you only have to worry about installing one set of requirements during future use. Note that the dependencies in `pyproject.toml` can be moved to the more traditional `requirements.txt`, if that is what you are currently using to track Python dependencies. + +### Real World Example + +You can find an example of a playbook that incorporates this role in our main playbook (a.k.a. [Gas Station]({{ repository.project.playbooks }})). The playbook is an excellent example for someone learning how to use Ansible. It also incorporates a lot of well-thought out build tools that more advanced Ansible users can appreciate. And people who could care less about Ansible can also benefit from it because it allows you to more or less turn your computer (and network) into the ultimate development enivornment. The bottom line is that it is an awesome project that developers should know about! diff --git a/.config/docs/readme-role/overview.md b/.config/docs/readme-role/overview.md new file mode 100644 index 00000000..3ea87cb3 --- /dev/null +++ b/.config/docs/readme-role/overview.md @@ -0,0 +1,3 @@ +## Overview + +{{ alternative_description }}. {{ overview }} diff --git a/.config/docs/readme-role/quick-description.md b/.config/docs/readme-role/quick-description.md new file mode 100644 index 00000000..2494036e --- /dev/null +++ b/.config/docs/readme-role/quick-description.md @@ -0,0 +1,3 @@ +>

**{{ subheader_description }}**


+ + diff --git a/.config/docs/readme-role/quick-start.md b/.config/docs/readme-role/quick-start.md new file mode 100644 index 00000000..f516f1c2 --- /dev/null +++ b/.config/docs/readme-role/quick-start.md @@ -0,0 +1,19 @@ +## Quick Start + +Looking to install {{ name }} without having to deal with [Ansible](https://www.ansible.com/)? Simply run the following command that correlates to your operating system: + +**Linux/macOS:** + +```shell +curl -sS {{ link.installdoctor }}/{{ galaxy_info.role_name }} | bash +``` + +**Windows:** + +```powershell +Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://install.doctor/{{ galaxy_info.role_name }}?os=win')) +``` + +**Important Note:** _Before running the commands above you should probably directly access the URL to make sure the code is legit. We already know it is safe but, before running any script on your computer, you should inspect it._ + +You can also check out **[Install Doctor]({{ link.installdoctor }})**. It is an app we created that can install any Ansible role with a one-liner. It has some other nifty features too like the ability to install binaries on-the-fly without requiring a password. However, if you would like to incorporate this role into an Ansible playbook (and customize settings) then please continue reading below. diff --git a/.config/docs/readme-role/subheader.md b/.config/docs/readme-role/subheader.md new file mode 100644 index 00000000..8178009a --- /dev/null +++ b/.config/docs/readme-role/subheader.md @@ -0,0 +1,53 @@ +
+ + Homepage + + + Contributing + + + Slack + + + Gitter + + + GitHub + + + GitLab + +
+
+
+ + Ansible Galaxy role: {{ profile.galaxy }}.{{ galaxy_info.role_name }} + + + Version: {{ pkg.version }} + + + Build status + + + Windows 11 test status + + + macOS test status + + + Linux Molecule test status + + + Ansible Galaxy quality score + + + Ansible Galaxy download count + + + Documentation + + + License: {{ license }} + +
diff --git a/.config/docs/readme-role/supported-os.md b/.config/docs/readme-role/supported-os.md new file mode 100644 index 00000000..e6d2b552 --- /dev/null +++ b/.config/docs/readme-role/supported-os.md @@ -0,0 +1,9 @@ +## Supported Operating Systems + +The chart below shows the operating systems that we have tested this role on. It is automatically generated using the Ansible Molecule tests located in the `molecule/` folder. There is CI logic in place to automatically handle the testing of Windows, macOS, Ubuntu, Fedora, CentOS, Debian, and Archlinux. If your operating system is not listed but is a variant of one of the systems we test (i.e. a Debian-flavored system or a RedHat-flavored system) then it is possible that the role will still work. + +{{ compatibility_matrix }} + +**_What does idempotent mean?_** Idempotent means that if you run this role twice in row then there will be no changes to the system the second time around. + +We spent a lot of time perfecting our CI configurations and build tools. If you are interested in learning more about how we perfected our process then you might find our [Ansible common files](https://gitlab.com/megabyte-labs/common/ansible) and [Ansible documentation](https://gitlab.com/megabyte-labs/documentation/ansible) repositories interesting. See the [CONTRIBUTING.md](docs/CONTRIBUTING.md) guide for more details. diff --git a/.config/docs/variables.json b/.config/docs/variables.json new file mode 100644 index 00000000..c3fcea92 --- /dev/null +++ b/.config/docs/variables.json @@ -0,0 +1,592 @@ +{ + "SPACE": "", + "alt_badge_style": "flat-square", + "author": { + "email": "brian@megabyte.space", + "name": "Brian Zalewski" + }, + "autodoc_actions_description": "", + "autodoc_tags_description": "", + "autodoc_todo_description": "", + "autodoc_variables_description": "", + "badge_style": "for-the-badge", + "blueprint_requirements": [ + ["Variable Name", "Variable Description"], + [ + "`description`", + "Short description of the role, worded in such a way that it makes sense by itself and with 'An Ansible role that ' prepended to it" + ], + ["`group`", "This should always be set to 'ansible' for Ansible roles"], + [ + "`name`", + "This should be the official name for the product that the role installs/configures. It is used in the title of the repository and throughout the documentation to refer to the product." + ], + [ + "`overview`", + "This variable should be a description of what the role installs. You can usually find a good description by Googling, \"What is Android Studio,\" for example if you were populating this variable for the [Android Studio role]({{ repository.group.ansible_roles }}/androidstudio). This text is shown at the top of the README, right below the header section and before the table of contents. Whenever possible, key products/terms should be linked to using markdown. You can see an example of us hyperlinking in this variable by checking out the [Android Studio role]({{ repository.group.ansible_roles }}/androidstudio). The idea is to make it as easy as possible for our users to figure out exactly what the role does." + ], + ["`repository.github`", "The HTTPS URL of the GitHub mirror"], + ["`repository.gitlab`", "The HTTPS URL of the GitLab repository"], + [ + "`slug`", + "This should generally be the ending slug of the GitHub mirror. It is used for things like filling in the package.json name." + ], + ["`subgroup`", "This should always be set to 'role' for Ansible roles"], + ["`title`", "The title of the README.md"] + ], + "commit_help_url": "https://megabyte.space/docs/contributing/commit-guidelines", + "company": "Megabyte LLC", + "copyright": "2020-2021", + "description_emojis": "👨🏻‍💻 🩺", + "docker_label_authors": "Brian Zalewski ", + "docs": { + "header_description_post": "", + "header_description_pre": "", + "header_title_post": "", + "header_title_pre": "", + "link": "https://megabyte.space/docs/common/ansible" + }, + "downloadLinks": { + "fedora": "https://download.fedoraproject.org/pub/fedora/linux/releases/35/Workstation/x86_64/iso/Fedora-Workstation-Live-x86_64-35-1.2.iso", + "kali": "https://cdimage.kali.org/kali-2022.1/kali-linux-2022.1-installer-amd64.iso", + "qubes": "https://ftp.qubes-os.org/iso/Qubes-R4.1.0-x86_64.iso", + "tails": "https://ftp.osuosl.org/pub/tails/stable/tails-amd64-4.29/tails-amd64-4.29.iso", + "ubuntu": "https://mirror.arizona.edu/ubuntu-releases/21.10/ubuntu-21.10-desktop-amd64.iso", + "windows": "https://software.download.prss.microsoft.com/db/Win11_English_x64.iso?t=c15e0cba-9c8d-4984-b30f-43c42425733d&e=1650582458&h=960d57d2c6a0243e32d3106c0d8f82387966ddf9a9bfce82f89e66866457c014" + }, + "email": { + "help": "help@megabyte.space" + }, + "emoji_beginnings": ["🚀 ", "🔥👉 ", "👉 ", "😉 ", "🆓 ", "🐴 ", "👀 ", "🎉 ", "", "", "", "", "", "", "", ""], + "emoji_endings": [" 🚀", " 🔥🔥🔥", " 👏", " 😉", " 🐙", " 🐴", " 👀", " 🎟", " 🎉🎉", "", "", "", "", "", "", ""], + "github_prefix": "", + "gitlab_pipelines": [ + { + "active": true, + "cron": "0 5 1 * *", + "description": "Monthly Repository Update", + "ref": "synchronize", + "variable": { + "REPOSITORY_UPDATE": true + } + } + ], + "gomodProxy": true, + "group": "ansible", + "groups": { + "angular": ["app", "website"], + "ansible": ["playbook", "role"], + "docker": ["ansible-molecule", "app", "ci-pipeline", "codeclimate", "software"], + "go": ["cli", "library"], + "npm": ["app", "cli", "config", "library", "plugin"], + "packer": ["desktop", "server"], + "python": ["cli", "library"] + }, + "homebrew": { + "folder": "Formula", + "name": "homebrew-tap", + "owner": "installdoc" + }, + "hostapp_var_chart": [ + ["App", "Description", "GitHub            "], + [ + "**[Authelia](https://www.authelia.com/)**", + "An authentication portal that supports SSO and 2FA (_[Homepage](https://www.authelia.com/) | [Documentation](https://www.authelia.com/docs/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/authelia/authelia?style=social)](https://github.com/authelia/authelia)" + ], + [ + "**[Homer](https://github.com/bastienwirtz/homer)**", + "A very simple homepage which is customized by the playbook to automatically include links to the Docker containers you choose to host on the computer (_[Demo](https://homer-demo.netlify.app/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/bastienwirtz/homer?style=social)](https://github.com/bastienwirtz/homer)" + ], + [ + "**[Portainer](https://www.portainer.io/)**", + "A Docker management tool (_[Homepage](https://www.portainer.io/) | [Documentation](https://docs.portainer.io/) | [Demo](https://github.com/portainer/portainer#demo)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/portainer/portainer?style=social)](https://github.com/portainer/portainer)" + ], + [ + "**[Serve](https://github.com/vercel/serve)**", + "Simple interface for viewing files located or symlinked to in the `/var/www/` folder of the machine", + "[![GitHub Repo stars](https://img.shields.io/github/stars/vercel/serve?style=social)](https://github.com/vercel/serve)" + ] + ], + "htpc_var_chart": [ + ["App", "Description", "GitHub            "], + [ + "**[WireGuard](https://docs.linuxserver.io/images/docker-wireguard)**", + "Dedicated WireGuard VPN for the HTPC applications which is configured in *our docker-compose.yml* file to be used as the internet connection for all the containers (_[Homepage](https://www.wireguard.com/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/linuxserver/docker-wireguard?style=social)](https://github.com/linuxserver/docker-wireguard)" + ], + [ + "**[Bazarr](https://docs.linuxserver.io/images/docker-bazarr)**", + "Manages and automatically downloads subtitles (_[Homepage](https://www.bazarr.media/) | [Documentation](https://wiki.bazarr.media/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/morpheus65535/bazarr?style=social)](https://github.com/morpheus65535/bazarr)" + ], + [ + "**[Heimdall](https://docs.linuxserver.io/images/docker-heimdall)**", + "Simple start page for all the HTPC apps (_[Homepage](https://heimdall.site/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/linuxserver/Heimdall?style=social)](https://github.com/linuxserver/Heimdall)" + ], + [ + "**[Jackett](https://docs.linuxserver.io/images/docker-jackett)**", + "Request proxy server for Radarr and Sonarr which helps speed things up", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Jackett/Jackett?style=social)](https://github.com/Jackett/Jackett)" + ], + [ + "**[Kodi Headless](https://hub.docker.com/r/linuxserver/kodi-headless)**", + "Backend for Kodi used to host a centralized database for Kodi instances (_[Homepage](https://kodi.tv/) | [Documentation](https://kodi.wiki/view/Main_Page)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/xbmc/xbmc?style=social)](https://github.com/xbmc/xbmc)" + ], + [ + "**[Lidarr](https://docs.linuxserver.io/images/docker-lidarr)**", + "Music collection manager that automatically downloads from BitTorrent and Usenet (_[Homepage](https://lidarr.audio/) | [Documentation](https://wiki.servarr.com/en/lidarr)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Lidarr/Lidarr?style=social)](https://github.com/Lidarr/Lidarr)" + ], + [ + "**[NZBGet](https://docs.linuxserver.io/images/docker-nzbget)**", + "NZBGet is a Usenet download manager used to download from NewsGroups which are supposedly more secure than torrents. **NOTE: Viruses are still prevalent on both NewsGroups and torrents - make sure you don't run anything with admin / sudo privileges.** (_[Homepage](https://nzbget.net/) | [Documentation](https://nzbget.net/documentation)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/nzbget/nzbget?style=social)](https://github.com/nzbget/nzbget)" + ], + [ + "**[Ombi](https://docs.linuxserver.io/images/docker-ombi)**", + "Plex media request and user management system which can be used to allow users who use your HTPC server to request movies, TV shows, and other media (_[Homepage](https://ombi.io/) | [Documentation](https://docs.ombi.app/) | [Demo](https://app.ombi.io/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Ombi-app/Ombi?style=social)](https://github.com/Ombi-app/Ombi)" + ], + [ + "**[Organizr](https://docs.linuxserver.io/images/docker-htpcmanager)**", + "Front end for HTPC web applications with a full-featured user interface that is full of eye candy (_[Homepage](https://organizr.app/) | [Documentation](https://docs.organizr.app/) | [Demo](https://docs.organizr.app/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/causefx/Organizr?style=social)](https://github.com/causefx/Organizr)" + ], + [ + "**[Radarr](https://docs.linuxserver.io/images/docker-radarr)**", + "Automatic movie downloader that can even be configured to download lists including the Top 250 IMBD movies (_[Homepage](https://radarr.video/) | [Documentation](https://wiki.servarr.com/radarr)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Radarr/Radarr?style=social)](https://github.com/Radarr/Radarr)" + ], + [ + "**[Sonarr](https://docs.linuxserver.io/images/docker-sonarr)**", + "Automatic TV show downloader with tons of ways to easily and automatically download TV shows (_[Homepage](https://sonarr.tv/) | [Documentation](https://wiki.servarr.com/en/sonarr)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Sonarr/Sonarr?style=social)](https://github.com/Sonarr/Sonarr)" + ], + [ + "**[Tautulli](https://docs.linuxserver.io/images/docker-tautulli)**", + "Metrics and monitoring dashboard for Plex (_[Homepage](https://tautulli.com/) | [Documentation](https://wiki.bazarr.media/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Tautulli/Tautulli?style=social)](https://github.com/Tautulli/Tautulli)" + ], + [ + "**[Transmission](https://docs.linuxserver.io/images/docker-transmission)**", + "BitTorrent client that can be used in conjunction with or as an alternative to using NewsGroups via NZBGet (_[Homepage](https://transmissionbt.com/) | [Documentation](https://github.com/transmission/transmission/blob/main/docs/README.md)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/transmission/transmission?style=social)](https://github.com/transmission/transmission)" + ] + ], + "idPost": "megabyte.space", + "json_top_keys": "", + "license": "MIT", + "link": { + "chat": "https://app.slack.com/client/T01ABCG4NK1/C01NN74H0LW/details/", + "docker_role": "https://github.com/ProfessorManhattan/ansible-docker", + "docs": "https://megabyte.space/docs", + "gitter": "https://gitter.im/megabyte-labs/community", + "home": "https://megabyte.space", + "installdoctor": "https://install.doctor", + "mod_ansible_autodoc": "https://pypi.org/project/mod-ansible-autodoc/", + "privacy": "https://megabyte.space/privacy", + "shield": "https://shields.io", + "terms": "https://megabyte.space/terms" + }, + "misc": { + "appnest": "https://github.com/andreasbm/readme", + "husky": "https://www.npmjs.com/package/husky", + "tinypng": "https://tinypng.com/" + }, + "molecule_descriptions": [ + ["Scenario", "Description"], + ["`default`", "Uses VirtualBox to run tests for all platforms in parallel."], + ["`docker`", "Uses Docker to run tests for all Linux platforms and versions in parallel."], + [ + "`docker-snap`", + "The same as the `docker` scenario except it excludes platforms that have trouble installing snap packages on Docker." + ], + ["`archlinux-desktop`", "Runs the test on the latest version of Archlinux desktop using VirtualBox."], + ["`centos-desktop`", "Runs the test on the latest version of CentOS desktop using VirtualBox."], + ["`debian-desktop`", "Runs the test on the latest version of Debian desktop using VirtualBox."], + ["`fedora-desktop`", "Runs the test on the latest version of Fedora desktop using VirtualBox."], + ["`macos-desktop`", "Runs the test on the latest version of macOS desktop using VirtualBox."], + ["`ubuntu-desktop`", "Runs the test on the latest version of Ubuntu desktop using VirtualBox."], + ["`windows-desktop`", "Runs the test on the latest version of Windows desktop using VirtualBox."], + ["`ci-docker-archlinux`", "Uses Docker to test Archlinux."], + ["`ci-docker-centos`", "Uses Docker to test multiple versions of CentOS."], + ["`ci-docker-debian`", "Uses Docker to test multiple versions of Debian."], + [ + "`ci-docker-debian-snap`", + "Uses Docker to test Debian just like `ci-docker-debian` except it excludes versions that cannot install snap packages." + ], + ["`ci-docker-fedora`", "Uses Docker to test multiple versions of Fedora."], + ["`ci-docker-ubuntu`", "Uses Docker to test multiple versions of Ubuntu."] + ], + "name": "[[ package.json .blueprint.name - See CONTRIBUTING.md ]]", + "newProjectTemplates": { + "angular-app": { + "group": "angular", + "subgroup": "app", + "title": "Angular App" + }, + "angular-website": { + "group": "angular", + "subgroup": "website", + "title": "Angular Website" + }, + "ansible-role": { + "group": "ansible", + "subgroup": "role", + "title": "Ansible Role" + }, + "docker-app": { + "group": "docker", + "subgroup": "app", + "title": "Dockerfile (App)" + }, + "docker-ci-pipeline": { + "group": "docker", + "subgroup": "ci-pipeline", + "title": "CI/CD Pipeline Dockerfile" + }, + "docker-codeclimate": { + "group": "docker", + "subgroup": "codeclimate", + "title": "CodeClimate Engine / Linter Dockerfile" + }, + "docker-docker-compose": { + "group": "docker", + "subgroup": "docker-compose", + "title": "Docker Compose" + }, + "go-cli": { + "group": "go", + "subgroup": "cli", + "title": "Go CLI" + }, + "go-library": { + "group": "go", + "subgroup": "library", + "title": "Go Library" + }, + "misc": { + "group": "misc", + "subgroup": "misc", + "title": "Miscellaneous project" + }, + "npm-app": { + "group": "npm", + "subgroup": "app", + "title": "Node.js App" + }, + "npm-cli": { + "group": "npm", + "subgroup": "cli", + "title": "Node.js CLI" + }, + "npm-configs": { + "group": "npm", + "subgroup": "configs", + "title": "NPM Config Package" + }, + "npm-library": { + "group": "npm", + "subgroup": "library", + "title": "Node.js Library" + }, + "npm-plugin": { + "group": "npm", + "subgroup": "plugin", + "title": "NPM Plugin" + }, + "npm-web-component": { + "group": "npm", + "subgroup": "web-component", + "title": "Web Component" + }, + "packer-desktop": { + "group": "packer", + "subgroup": "desktop", + "title": "Packer (Desktop)" + }, + "packer-server": { + "group": "packer", + "subgroup": "server", + "title": "Packer (Server)" + }, + "python-cli": { + "group": "python", + "subgroup": "cli", + "title": "Python CLI" + }, + "python-library": { + "group": "python", + "subgroup": "library", + "title": "Python Library" + }, + "website": { + "group": "npm", + "subgroup": "website", + "title": "Website" + } + }, + "npm_publish_config_access": "public", + "npm_standard_version_prerelease": "git add --all", + "npm_type": "module", + "organization": "Megabyte Labs", + "overview": "[[ This is a new repository without the details filled in yet. Look for the section about blueprint data in the CONTRIBUTING.md to set up the project. ]]", + "playbook_path": "megabyte-labs/gas-station", + "profile": { + "dockerHubUser": "professormanhattan", + "dockerhub": "megabytelabs", + "galaxy": "professormanhattan", + "github": "ProfessorManhattan", + "githubOrg": "megabyte-labs", + "linkedin": "blzalewski", + "npmjs": "thisismyfirstday", + "npmjs_organization": "installdoc", + "opencollective": "megabytelabs", + "patreon": "ProfessorManhattan", + "pypi": "ProfessorManhattan", + "replit": "ProfessorMegaby", + "stackblitz": "ProfessorManhattan", + "twitter": "MegabyteLabs", + "vagrant": "ProfessorManhattan" + }, + "profile_link": { + "dockerhub": "https://hub.docker.com/u", + "galaxy": "https://galaxy.ansible.com", + "github": "https://github.com", + "linkedin": "https://www.linkedin.com/in/", + "npmjs": "https://www.npmjs.com/~", + "opencollective": "https://opencollective.com", + "patreon": "https://www.patreon.com", + "pypi": "https://pypi.org/user", + "replit": "https://repl.it/@", + "stackblitz": "https://stackblitz.com/@", + "twitter": "MegabyteLabs", + "vagrant": "https://app.vagrantup.com" + }, + "python_role_dependencies": [ + ["Package", "Description", "Required"], + [ + "ansible", + "A configuration management system that can remotely configure computers", + "
✔️
" + ], + [ + "docker", + "Enables the capability of provisioning Docker containers with Ansible", + "
✔️
" + ], + [ + "python-vagrant", + "Required for provisioning Vagrant VMs", + "
✔️
" + ], + [ + "pywinrm", + "Required for provisioning Windows machines that are using WinRM", + "
✔️
" + ], + [ + "ansible-lint", + "Linting tool for Ansible files", + "" + ], + [ + "ansibler", + "Custom tool used to generate advanced documentation (e.g. it generates the compatibility chart and some other charts)", + "" + ], + [ + "black", + "Python file auto-formatter included in case project utilizes Python test scripts", + "" + ], + [ + "blocklint", + "Linting tool that prevents certain words from entering the code base", + "" + ], + [ + "flake8", + "Python linter that reports Python syntax and style errors", + "" + ], + [ + "mod-ansible-autodoc", + "Custom fork of [ansible-autodoc](https://pypi.org/project/ansible-autodoc/0.5.1.1/) which allows us to auto-generate documentation based on comments in the role's YAML files", + "" + ], + [ + "molecule", + "Test framework for Ansible", + "" + ], + [ + "molecule-docker", + "Molecule plugin for provisioning Docker containers", + "" + ], + [ + "molecule-vagrant", + "Molecule plugin for provisioning Vagrant VMs", + "" + ], + [ + "pre-commit-hooks", + "Suite of tools useful for linting", + "" + ], + [ + "proselint", + "Linter used to generate English-language improvements (used to improve documentation)", + "" + ], + [ + "yamllint", + "Linter for YAML files that ensures proper syntax and styling is used", + "" + ] + ], + "redditApplicationId": "O3UxD7HlPpcN88gpEkPIXQ", + "redditUsername": "tsgangster", + "repository": { + "github": "", + "gitlab": "", + "gitlabBaseUrl": "https://gitlab.com/megabyte-labs", + "group": { + "ansible_roles": "https://gitlab.com/megabyte-labs/ansible-roles", + "ansible_roles_path": "megabyte-labs/ansible-roles", + "apps": "https://gitlab.com/megabyte-labs/apps", + "apps_path": "megabyte-labs/apps", + "ci": "https://gitlab.com/megabyte-labs/ci", + "ci_path": "megabyte-labs/ci", + "cloud": "https://gitlab.com/megabyte-labs/cloud", + "cloud_path": "megabyte-labs/cloud", + "common": "https://gitlab.com/megabyte-labs/common", + "common_path": "megabyte-labs/common", + "cryptocurrency": "https://gitlab.com/megabyte-labs/cryptocurrency", + "cryptocurrency_path": "megabyte-labs/cryptocurrency", + "docker_compose": "https://gitlab.com/megabyte-labs/docker-compose", + "docker_compose_path": "megabyte-labs/docker-compose", + "dockerfile": "https://gitlab.com/megabyte-labs/docker", + "dockerfile_path": "megabyte-labs/docker", + "documentation": "https://gitlab.com/megabyte-labs/documentation", + "documentation_path": "megabyte-labs/documentation", + "go": "https://gitlab.com/megabyte-labs/go", + "go_path": "megabyte-labs/go", + "kubernetes": "https://gitlab.com/megabyte-labs/kubernetes", + "kubernetes_path": "megabyte-labs/kubernetes_path", + "npm": "https://gitlab.com/megabyte-labs/npm", + "npm_path": "megabyte-labs/npm", + "packer": "https://gitlab.com/megabyte-labs/packer", + "packer_path": "megabyte-labs/packer", + "python": "https://gitlab.com/megabyte-labs/python", + "python_cli_path": "megabyte-labs/python/cli", + "python_path": "megabyte-labs/python", + "software": "https://gitlab.com/megabyte-labs/software", + "software_path": "megabyte-labs/software", + "web_components": "https://gitlab.com/megabyte-labs/web-components", + "web_components_path": "megabyte-labs/web-components" + }, + "location": { + "commits": { + "github": "/commits/master", + "gitlab": "/-/commits/master", + "gitlab_e2e": "/-/commits/e2e" + }, + "conduct": { + "github": "/blob/master/docs/CODE_OF_CONDUCT.md", + "gitlab": "/-/blob/master/docs/CODE_OF_CONDUCT.md" + }, + "contributing": { + "github": "/blob/master/docs/CONTRIBUTING.md", + "gitlab": "/-/blob/master/docs/CONTRIBUTING.md" + }, + "demo": { + "github": "/raw/master/docs/demo.gif", + "gitlab": "/-/raw/master/docs/demo.gif" + }, + "issues": { + "github": "/issues", + "gitlab": "/-/issues" + }, + "license": { + "github": "/blob/master/LICENSE", + "gitlab": "/-/blob/master/LICENSE" + }, + "logo": { + "github": "/raw/master/logo.png", + "gitlab": "/-/raw/master/logo.png" + }, + "readme": { + "github": "/blob/master/README.md", + "gitlab": "/-/blob/master/README.md" + } + }, + "namespace": "", + "prefix": "ansible-", + "project": { + "assets": "https://gitlab.com/megabyte-labs/assets/-/raw/master", + "autodoc": "https://github.com/AndresBott/ansible-autodoc", + "playbooks": "https://github.com/ProfessorManhattan/Gas-Station", + "wrangler": "https://gitlab.com/megabyte-labs/wrangler" + } + }, + "saas_var_chart": [ + ["Service", "Description", "Price"], + [ + "**[CloudFlare](https://www.cloudflare.com/)**", + "CloudFlare is a DNS provider, edge network, and much more. Some day it might be able to replace all the services in this list but until then CloudFlare is the preferred provider for anything it offers a product for. In our configurations, CloudFlare is used for DNS, encrypted tunnels via [cloudflared](https://github.com/cloudflare/cloudflared), [CloudFlare WARP](https://1.1.1.1/), and [CloudFlare Teams](https://blog.cloudflare.com/introducing-cloudflare-for-teams/). On top of that, CloudFlare provides some other great features that can be utilized to make lightning-fast web apps. (_[Documentation](https://developers.cloudflare.com/docs/)_)", + "**Free** for the services we integrate" + ], + [ + "**[Digital Ocean](https://m.do.co/c/751743d45e36)**", + "Digital Ocean is a cloud hosting provider. Anytime CloudFlare's offerings are not enough to satisfy requirements, Digital Ocean is used. The service has a clean and simple web UI, a wide variety of CLIs/SDKs available on GitHub, and the company has been around since 2011. Digital Ocean is primarily used by our stack to host Kubernetes, S3 buckets, and cheap virtual private servers. (_[Documentation](https://docs.digitalocean.com/)_)", + "**~$40/month** for a Kubernetes cluster, S3 bucket, and a general-purpose VPS" + ], + [ + "**[Wasabi](https://wasabi.com/)**", + "Wasabi is the cheapest S3 bucket provider available. It is used as a secondary backup for any data that is backed up / saved to an S3 bucket. (_[Documentation](https://wasabi.com/help/docs/)_)", + "**$5.99/month** for S3 bucket" + ], + [ + "**[Ory](https://www.ory.sh/)**", + "Ory is the only identity platform that can scale indefinitely and is based entirely on open source. Ory is leveraged to provide a feature-rich and programmable single sign-on platform. It includes support for hardware-based tokens. (_[Documentation](https://www.ory.sh/docs/welcome)_)", + "**Free** for the developer edition" + ], + [ + "**[Proton](https://proton.me/)**", + "Proton Mail is an end-to-end encrypted email service founded in 2013 in Geneva, Switzerland. Proton Mail and ProtonVPN are used in our stack to provide secure e-mail and configure VPN profiles using ProtonVPN's unique security features. With the Business plan, you can get custom domain branded e-mail and enough VPN connections to configure your router / VPN profiles on each of your devices. (_[Documentation](https://proton.me/support)_)", + "**$12.99/month** for the Business edition" + ], + [ + "**[GMail](https://mail.google.com)**", + "GMail is a free e-mail service offered by Google. In some cases, we leverage GMail's SMTP capabilities to send notification e-mails. (_[Documentation](https://support.google.com/mail/?hl=en#topic=7065107)_)", + "**Free**" + ] + ], + "scriptsBuild": "task donothing", + "scriptsHelp": "task --menu", + "scriptsPrepare": "npm run start && (test -f Taskfile.yml && task common:husky) || true", + "scriptsReplaceThis": "\"", + "scriptsReplaceWith": "\\\"", + "scriptsStart": "bash start.sh", + "scriptsTest": "task donothing", + "sharp_instructions": [], + "slackNotificationChannel": "#misc", + "slackNotificationIcon": "https://gitlab.com/megabyte-labs/misc/assets/-/raw/master/logo/megabytelabs-color-icon-350x350.png", + "slackNotificationUsername": "Megabyte Labs Release Notification Bot", + "sponsorship": { + "author": "Brian Zalewski", + "text": "I create open source projects out of love. Although I have a job, shelter, and as much fast food as I can handle, it would still be pretty cool to be appreciated by the community for something I have spent a lot of time and money on. Please consider sponsoring me! Who knows? Maybe I will be able to quit my job and publish open source full time." + }, + "subgroup": "[[ Needs to be setup. Set this in the common repository for this type of project ]]", + "teamsNotificationColor": "#1DA1F2", + "teamsNotificationIcon": "https://gitlab.com/megabyte-labs/misc/assets/-/raw/master/logo/megabytelabs-color-icon-350x350.png", + "title": "[[ package.json .blueprint.title - See CONTRIBUTING.md ]]", + "version": "0.0.1" +} diff --git a/.config/flake8.toml b/.config/flake8.toml new file mode 100644 index 00000000..e965bcd2 --- /dev/null +++ b/.config/flake8.toml @@ -0,0 +1,4 @@ +[flake8] +exclude = .autodoc, .cache, .common, .config, .git, .github, .gitlab, .husky, .modules, .npm, .pnpm-store, .shared, .task, .venv, .vscode, build, dist, node_modules, roles, venv +ignore = E402 +max-line-length = 120 diff --git a/.config/hadolint.yml b/.config/hadolint.yml new file mode 100644 index 00000000..a668998c --- /dev/null +++ b/.config/hadolint.yml @@ -0,0 +1,8 @@ +--- +ignored: + # Last USER should not be root + - DL3002 + # Do not use sudo + - DL3004 + # Do not use `latest` images + - DL3007 diff --git a/.config/hbs.cjs b/.config/hbs.cjs new file mode 100644 index 00000000..c997dd6e --- /dev/null +++ b/.config/hbs.cjs @@ -0,0 +1,70 @@ +const fs = require('fs') +const { execSync } = require('child_process') + +function getTaskIncludeKey(path) { + return path + .replace('.config/taskfiles/', '') + .replace('local/', '') + .replace('/Taskfile-', ':') + .replace('/Taskfile.yml', '') + .replace('Taskfile-', '') + .replace('.yml', '') +} + +module.exports.register = function (Handlebars) { + /** + * Import [handlebars-helpers](https://github.com/helpers/handlebars-helpers) + */ + require('handlebars-helpers')({ + handlebars: Handlebars + }) + + /** + * Used to generate the includes: section of the main Taskfile.yml + * in the root of every repository + */ + Handlebars.registerHelper('bodegaIncludes', (pattern, options) => { + const readdir = Handlebars.helpers.readdir + const files = readdir('.config/taskfiles/') + const tasks = Handlebars.helpers.each([...files, './local'], { + fn: (file) => { + if (fs.lstatSync(file).isDirectory()) { + return readdir(file).filter((taskfile) => taskfile.match(/.*Taskfile.*.yml/gu)) + } else { + return [] + } + } + }) + + return tasks + .replaceAll('.config/taskfiles/', ',.config/taskfiles/') + .replaceAll('local/', ',local/') + .split(',') + .map((path) => ({ + key: getTaskIncludeKey(path), + taskPath: './' + path, + optional: path.includes('local/Taskfile-') + })) + .filter((x) => !!x.key) + .sort((a, b) => a.key.localeCompare(b.key)) + }) + + /** + * Used for returning input from synchronous commands (i.e. bash commands) + */ + Handlebars.registerHelper('execSync', function (input, options) { + const output = execSync(input) + + return output + }) + + /** + * Used for generating Homebrew resource stanzas for Python packages. + * For more information, see: https://github.com/tdsmith/homebrew-pypi-poet + */ + Handlebars.registerHelper('poet', function (input, options) { + const formulae = execSync('poetry run poet -f ' + input) + + return formulae + }) +} diff --git a/.config/husky/.gitignore b/.config/husky/.gitignore new file mode 100644 index 00000000..31354ec1 --- /dev/null +++ b/.config/husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.config/husky/commit-msg b/.config/husky/commit-msg new file mode 100755 index 00000000..b232317d --- /dev/null +++ b/.config/husky/commit-msg @@ -0,0 +1,34 @@ +#!/bin/sh +# shellcheck disable=SC1090,SC1091,SC2016 + +# @file .config/husky/commit-msg +# @brief A git hook script for the `commit-msg` hook +# @arg $1 Path to a temporary file that contains the commit message written by the developer (e.g. .git/COMMIT_EDITMSG) + +[ -f .config/log ] && chmod +x .config/log +if [ -f "$(dirname "$0")/_/husky.sh" ]; then + . "$(dirname "$0")/_/husky.sh" + + # Attempt to register Bodega/Task from common places if it is not in PATH + if ! type task > /dev/null; then + PATH="$PATH:$HOME/.local/go/bin:$HOME/.local/bin:$HOME/bin:$HOME/go/bin:$HOME/.asdf/shims" + if ! type task > /dev/null; then + for DOTFILE in .profile .bashrc .bash_profile .zshrc; do + . "$HOME/$DOTFILE" + if type task > /dev/null; then + break + fi + done + fi + fi + + # Show warning if Bodega/Task is still not registered/installed, else proceed with hook + if ! type task > /dev/null; then + .config/log warn 'Bodega `task` does not appear to be installed or is not registered in the `PATH` variable - please manually include it' + .config/log info 'Get Bodega here -> `https://github.com/megabyte-labs/Bodega`' + else + task git:hook:commit-msg -- "$1" + fi +else + .config/log warn 'Husky pre-commit hooks are currently not properly setup.' +fi diff --git a/.config/husky/post-checkout b/.config/husky/post-checkout new file mode 100755 index 00000000..2f85e157 --- /dev/null +++ b/.config/husky/post-checkout @@ -0,0 +1,36 @@ +#!/bin/sh +# shellcheck disable=SC1090,SC1091,SC2016 + +# @file .config/husky/post-checkout +# @brief A git hook script for the `post-checkout` hook +# @arg $1 The ref of the previous HEAD (e.g. f693bc50756b490f7ad067eb455338b634d01036) +# @arg $2 The ref of the new HEAD +# @arg $3 Equal to 1 if changing branches + +[ -f .config/log ] && chmod +x .config/log +if [ -f "$(dirname "$0")/_/husky.sh" ]; then + . "$(dirname "$0")/_/husky.sh" + + # Attempt to register Bodega/Task from common places if it is not in PATH + if ! type task > /dev/null; then + PATH="$PATH:$HOME/.local/go/bin:$HOME/.local/bin:$HOME/bin:$HOME/go/bin:$HOME/.asdf/shims" + if ! type task > /dev/null; then + for DOTFILE in .profile .bashrc .bash_profile .zshrc; do + . "$HOME/$DOTFILE" + if type task > /dev/null; then + break + fi + done + fi + fi + + # Show warning if Bodega/Task is still not registered/installed, else proceed with hook + if ! type task > /dev/null; then + .config/log warn 'Bodega `task` does not appear to be installed or is not registered in the `PATH` variable - please manually include it' + .config/log info 'Get Bodega here -> `https://github.com/megabyte-labs/Bodega`' + else + task git:hook:post-checkout + fi +else + .config/log warn 'Husky pre-commit hooks are currently not properly setup.' +fi diff --git a/.config/husky/post-commit b/.config/husky/post-commit new file mode 100755 index 00000000..4e1f9cb3 --- /dev/null +++ b/.config/husky/post-commit @@ -0,0 +1,34 @@ +#!/bin/sh +# shellcheck disable=SC1090,SC1091,SC2016 + +# @file .config/husky/post-commit +# @brief A git hook script for the `post-commit` hook. There are no parameters but you can easily get the +# last commit by running `git log -1 HEAD`. Generally, this script is used for notifications or something similar. + +[ -f .config/log ] && chmod +x .config/log +if [ -f "$(dirname "$0")/_/husky.sh" ]; then + . "$(dirname "$0")/_/husky.sh" + + # Attempt to register Bodega/Task from common places if it is not in PATH + if ! type task > /dev/null; then + PATH="$PATH:$HOME/.local/go/bin:$HOME/.local/bin:$HOME/bin:$HOME/go/bin:$HOME/.asdf/shims" + if ! type task > /dev/null; then + for DOTFILE in .profile .bashrc .bash_profile .zshrc; do + . "$HOME/$DOTFILE" + if type task > /dev/null; then + break + fi + done + fi + fi + + # Show warning if Bodega/Task is still not registered/installed, else proceed with hook + if ! type task > /dev/null; then + .config/log warn 'Bodega `task` does not appear to be installed or is not registered in the `PATH` variable - please manually include it' + .config/log info 'Get Bodega here -> `https://github.com/megabyte-labs/Bodega`' + else + task git:hook:post-commit + fi +else + .config/log warn 'Husky pre-commit hooks are currently not properly setup.' +fi diff --git a/.config/husky/post-merge b/.config/husky/post-merge new file mode 100755 index 00000000..76fd55f6 --- /dev/null +++ b/.config/husky/post-merge @@ -0,0 +1,34 @@ +#!/bin/sh +# shellcheck disable=SC1090,SC1091,SC2016 + +# @file .config/husky/post-merge +# @brief A git hook script for the `post-merge` hook +# @arg $1 A status flag specifying whether or not the merge being done was a squash merge + +[ -f .config/log ] && chmod +x .config/log +if [ -f "$(dirname "$0")/_/husky.sh" ]; then + . "$(dirname "$0")/_/husky.sh" + + # Attempt to register Bodega/Task from common places if it is not in PATH + if ! type task > /dev/null; then + PATH="$PATH:$HOME/.local/go/bin:$HOME/.local/bin:$HOME/bin:$HOME/go/bin:$HOME/.asdf/shims" + if ! type task > /dev/null; then + for DOTFILE in .profile .bashrc .bash_profile .zshrc; do + . "$HOME/$DOTFILE" + if type task > /dev/null; then + break + fi + done + fi + fi + + # Show warning if Bodega/Task is still not registered/installed, else proceed with hook + if ! type task > /dev/null; then + .config/log warn 'Bodega `task` does not appear to be installed or is not registered in the `PATH` variable - please manually include it' + .config/log info 'Get Bodega here -> `https://github.com/megabyte-labs/Bodega`' + else + task git:hook:post-merge + fi +else + .config/log warn 'Husky pre-commit hooks are currently not properly setup.' +fi diff --git a/.config/husky/post-rewrite b/.config/husky/post-rewrite new file mode 100755 index 00000000..3f44f1a7 --- /dev/null +++ b/.config/husky/post-rewrite @@ -0,0 +1,35 @@ +#!/bin/sh +# shellcheck disable=SC1090,SC1091,SC2016 + +# @file .config/husky/post-rewrite +# @brief A git hook script for the `post-rewrite` hook. It is called when running commands +# that rewrite commits (e.g. git pull origin master --rebase) +# @arg $1 Denotes the command it was invoked by: currently one of amend or rebase + +[ -f .config/log ] && chmod +x .config/log +if [ -f "$(dirname "$0")/_/husky.sh" ]; then + . "$(dirname "$0")/_/husky.sh" + + # Attempt to register Bodega/Task from common places if it is not in PATH + if ! type task > /dev/null; then + PATH="$PATH:$HOME/.local/go/bin:$HOME/.local/bin:$HOME/bin:$HOME/go/bin:$HOME/.asdf/shims" + if ! type task > /dev/null; then + for DOTFILE in .profile .bashrc .bash_profile .zshrc; do + . "$HOME/$DOTFILE" + if type task > /dev/null; then + break + fi + done + fi + fi + + # Show warning if Bodega/Task is still not registered/installed, else proceed with hook + if ! type task > /dev/null; then + .config/log warn 'Bodega `task` does not appear to be installed or is not registered in the `PATH` variable - please manually include it' + .config/log info 'Get Bodega here -> `https://github.com/megabyte-labs/Bodega`' + else + task git:hook:post-rewrite + fi +else + .config/log warn 'Husky pre-commit hooks are currently not properly setup.' +fi diff --git a/.config/husky/pre-commit b/.config/husky/pre-commit new file mode 100755 index 00000000..50daca34 --- /dev/null +++ b/.config/husky/pre-commit @@ -0,0 +1,49 @@ +#!/bin/sh +# shellcheck disable=SC1090,SC1091,SC2016 + +# @file .config/husky/pre-commit +# @brief A git hook script for the `pre-commit` hook + +[ -f .config/log ] && chmod +x .config/log +if [ -f "$(dirname "$0")/_/husky.sh" ]; then + . "$(dirname "$0")/_/husky.sh" + + # Attempt to register Bodega/Task from common places if it is not in PATH + if ! type task > /dev/null; then + PATH="$PATH:$HOME/.local/go/bin:$HOME/.local/bin:$HOME/bin:$HOME/go/bin:$HOME/.asdf/shims" + if ! type task > /dev/null; then + for DOTFILE in .profile .bashrc .bash_profile .zshrc; do + . "$HOME/$DOTFILE" + if type task > /dev/null; then + break + fi + done + fi + fi + + # Show warning if Bodega/Task is still not registered/installed, else proceed with hook + if ! type task > /dev/null; then + .config/log warn 'Bodega `task` does not appear to be installed or is not registered in the `PATH` variable - please manually include it' + .config/log info 'Get Bodega here -> `https://github.com/megabyte-labs/Bodega`' + else + .config/log info "Performing various pre-commit tasks on staged files (like autofixing, detecting private keys, etc.)" + + STAGED_FILES=$(git diff --cached --name-only) + for FILE in "$STAGED_FILES"; do + if [ -f "$1" ]; then + task git:hook:pre-commit -- "$FILE" & + fi + done + wait + + .config/log info 'Linting and fixing the staged files with `lint-staged`' + task lint:lint-staged + + .config/log info 'Reporting possible spelling errors in the staged files with `cspell`' + task lint:spelling + + .config/log success 'Pre-commit validation complete!' + fi +else + .config/log warn 'Husky pre-commit hooks are currently not properly setup.' +fi diff --git a/.config/husky/pre-push b/.config/husky/pre-push new file mode 100755 index 00000000..591b5378 --- /dev/null +++ b/.config/husky/pre-push @@ -0,0 +1,35 @@ +#!/bin/sh +# shellcheck disable=SC1090,SC1091,SC2016 + +# @file .config/husky/pre-push +# @brief A git hook script for the `pre-push` hook +# @arg $1 The name of the remote (e.g. origin) +# @arg $2 The location of the remote (e.g. git@gitlab.com:megabyte-labs/common/angular.git) + +[ -f .config/log ] && chmod +x .config/log +if [ -f "$(dirname "$0")/_/husky.sh" ]; then + . "$(dirname "$0")/_/husky.sh" + + # Attempt to register Bodega/Task from common places if it is not in PATH + if ! type task > /dev/null; then + PATH="$PATH:$HOME/.local/go/bin:$HOME/.local/bin:$HOME/bin:$HOME/go/bin:$HOME/.asdf/shims" + if ! type task > /dev/null; then + for DOTFILE in .profile .bashrc .bash_profile .zshrc; do + . "$HOME/$DOTFILE" + if type task > /dev/null; then + break + fi + done + fi + fi + + # Show warning if Bodega/Task is still not registered/installed, else proceed with hook + if ! type task > /dev/null; then + .config/log warn 'Bodega `task` does not appear to be installed or is not registered in the `PATH` variable - please manually include it' + .config/log info 'Get Bodega here -> `https://github.com/megabyte-labs/Bodega`' + else + task git:hook:pre-push -- "$1 $2" + fi +else + .config/log warn 'Husky pre-commit hooks are currently not properly setup.' +fi diff --git a/.config/husky/prepare-commit-msg b/.config/husky/prepare-commit-msg new file mode 100755 index 00000000..04922185 --- /dev/null +++ b/.config/husky/prepare-commit-msg @@ -0,0 +1,28 @@ +#!/bin/sh +# shellcheck disable=SC1091,SC2016 + +# @file .config/husky/prepare-commit-msg +# @brief A git hook script for the `prepare-commit-msg` hook. Add the `-n` flag to bypass. +# @arg $1 string The full path to the MERGE_MSG +# @arg $2 string The type of the `prepare-commit-msg` event. For a `git pull origin master` +# command, the event type is 'merge'. + +[ -f .config/log ] && chmod +x .config/log +if [ -f "$(dirname "$0")/_/husky.sh" ]; then + . "$(dirname "$0")/_/husky.sh" + if [ "$2" != 'merge' ]; then + .config/log info 'This git hook is configured to run even when --no-verify is used. In order to bypass this prompt, use the -n flag instead.' + .config/log info 'Opening a `git commit` dialog' + if ! type pnpx > /dev/null && type npm > /dev/null; then + npm install -g pnpm + elif ! type pnpx > /dev/null; then + .config/log error '`pnpm` or `npm` must be installed' + fi + if ! type git-cz &> /dev/null; then + pnpm install -g commitizen + fi + exec < /dev/tty && (git-cz --hook || true) + fi +else + .config/log warn 'Husky pre-commit hooks are currently not properly setup.' +fi diff --git a/.config/log b/.config/log new file mode 100755 index 00000000..37566fbd --- /dev/null +++ b/.config/log @@ -0,0 +1,393 @@ +#!/usr/bin/env bash + +# @file .config/log +# @brief Logger / prompt library that logs pretty console messages and provides several prompt methods +# @description +# This file contains several functions that log content in different formats. It also provides an +# interface for [Gum](https://github.com/charmbracelet/gum) based prompts. The available methods include: +# +# * `choose` - Prompt user with choices +# * `confirm` - Fancy Yes/No confirmation prompt +# * `error` - Logs an error message +# * `filter` - Filterable list of choices (with choices passed in as a line-return seperated file) +# * `info` - Logs a regular message +# * `input` - Prompt for a text input +# * `md` - Render a markdown file with [Glow](https://github.com/charmbracelet/glow) +# * `password` - Prompt for text that is masked by the prompt +# * `prompt` - Log a description for a prompt that follows +# * `spin` - Show a spinner while background job completes +# * `star` - Logs a message with a star icon at the beginning +# * `start` - Log a job start message +# * `success` - Logs a success message +# * `warn` - Logs a warning message +# * `write` - Multi-line input prompt +# +# If the `docker` environment variable is not set, the script / library will ensure both Gum and Glow are installed. + +# @description Installs glow (a markdown renderer) from GitHub releases +# @example installGlow +installGlow() { + # TODO: Add support for other architecture types + if [ -d '/Applications' ] && [ -d '/Library' ] && [ -d '/Users' ]; then + GLOW_DOWNLOAD_URL="https://github.com/charmbracelet/glow/releases/download/v1.4.1/glow_1.4.1_Darwin_x86_64.tar.gz" + elif [ -f '/etc/ubuntu-release' ] || [ -f '/etc/debian_version' ] || [ -f '/etc/redhat-release' ] || [ -f '/etc/SuSE-release' ] || [ -f '/etc/arch-release' ] || [ -f '/etc/alpine-release' ]; then + GLOW_DOWNLOAD_URL="https://github.com/charmbracelet/glow/releases/download/v1.4.1/glow_1.4.1_linux_x86_64.tar.gz" + fi + if type curl &> /dev/null; then + if { [ -d '/Applications' ] && [ -d '/Library' ] && [ -d '/Users' ]; } || [ -f '/etc/ubuntu-release' ] || [ -f '/etc/debian_version' ] || [ -f '/etc/redhat-release' ] || [ -f '/etc/SuSE-release' ] || [ -f '/etc/arch-release' ] || [ -f '/etc/alpine-release' ]; then + TMP="$(mktemp)" + TMP_DIR="$(dirname "$TMP")" + curl -sSL "$GLOW_DOWNLOAD_URL" > "$TMP" + tar -xzf "$TMP" -C "$TMP_DIR" + if [ -n "$HOME" ]; then + if mkdir -p "$HOME/.local/bin" && mv "$TMP_DIR/glow" "$HOME/.local/bin/glow"; then + GLOW_PATH="$HOME/.local/bin/glow" + else + GLOW_PATH="$(dirname "${BASH_SOURCE[0]}")/glow" + mv "$TMP_DIR/gum" "$GLOW_PATH" + fi + chmod +x "$GLOW_PATH" + else + echo "WARNING: The HOME environment variable is not set! (Glow)" + fi + else + echo "WARNING: Unable to detect system type. (Glow)" + fi + fi +} + +# @description Installs gum (a logging CLI) from GitHub releases +# @example installGum +installGum() { + # TODO: Add support for other architecture types + if [ -d '/Applications' ] && [ -d '/Library' ] && [ -d '/Users' ]; then + GUM_DOWNLOAD_URL="https://github.com/charmbracelet/gum/releases/download/v0.4.0/gum_0.4.0_Darwin_x86_64.tar.gz" + elif [ -f '/etc/ubuntu-release' ] || [ -f '/etc/debian_version' ] || [ -f '/etc/redhat-release' ] || [ -f '/etc/SuSE-release' ] || [ -f '/etc/arch-release' ] || [ -f '/etc/alpine-release' ]; then + GUM_DOWNLOAD_URL="https://github.com/charmbracelet/gum/releases/download/v0.4.0/gum_0.4.0_linux_x86_64.tar.gz" + fi + if type curl &> /dev/null; then + if { [ -d '/Applications' ] && [ -d '/Library' ] && [ -d '/Users' ]; } || [ -f '/etc/ubuntu-release' ] || [ -f '/etc/debian_version' ] || [ -f '/etc/redhat-release' ] || [ -f '/etc/SuSE-release' ] || [ -f '/etc/arch-release' ] || [ -f '/etc/alpine-release' ]; then + TMP="$(mktemp)" + TMP_DIR="$(dirname "$TMP")" + curl -sSL "$GUM_DOWNLOAD_URL" > "$TMP" + tar -xzf "$TMP" -C "$TMP_DIR" + if [ -n "$HOME" ]; then + if mkdir -p "$HOME/.local/bin" && mv "$TMP_DIR/gum" "$HOME/.local/bin/gum"; then + GUM_PATH="$HOME/.local/bin/gum" + else + GUM_PATH="$(dirname "${BASH_SOURCE[0]}")/gum" + mv "$TMP_DIR/gum" "$GLOW_PATH" + fi + chmod +x "$GUM_PATH" + else + echo "WARNING: The HOME environment variable is not set! (Gum)" + fi + else + echo "WARNING: Unable to detect system type. (Gum)" + fi + fi +} + +# @description Configure the logger to use echo or gum +if [ "${container:=}" != 'docker' ]; then + # Acquire gum's path or attempt to install it + if type gum &> /dev/null; then + GUM_PATH="$(which gum)" + elif [ -f "$HOME/.local/bin/gum" ]; then + GUM_PATH="$HOME/.local/bin/gum" + elif [ -f "$(dirname "${BASH_SOURCE[0]}")/gum" ]; then + GUM_PATH="$(dirname "${BASH_SOURCE[0]}")/gum" + elif type brew &> /dev/null; then + brew install gum + GUM_PATH="$(which gum)" + else + installGum + fi + + # If gum's path was set, then turn on enhanced logging + if [ -n "$GUM_PATH" ]; then + chmod +x "$GUM_PATH" + ENHANCED_LOGGING=true + fi +fi + +# @description Disable logging for Semantic Release because it tries to parse it as JSON +if [ -n "$SEMANTIC_RELEASE" ]; then + NO_LOGGING=true +fi + +# @description Logs using Node.js +# @example logger info "An informative log" +logger() { + if [ "$1" == 'error' ]; then + "$GUM_PATH" style --border="thick" "$("$GUM_PATH" style --foreground="#ff0000" "✖") $("$GUM_PATH" style --bold --background="#ff0000" --foreground="#ffffff" " ERROR ") $("$GUM_PATH" style --bold "$(format "$2")")" + elif [ "$1" == 'info' ]; then + "$GUM_PATH" style " $("$GUM_PATH" style --foreground="#00ffff" "○") $2" + elif [ "$1" == 'md' ]; then + # @description Ensure glow is installed + if [ "${container:=}" != 'docker' ]; then + if type glow &> /dev/null; then + GLOW_PATH="$(which glow)" + elif [ -f "$HOME/.local/bin/glow" ]; then + GLOW_PATH="$HOME/.local/bin/glow" + elif [ -f "$(dirname "${BASH_SOURCE[0]}")/glow" ]; then + GLOW_PATH="$(dirname "${BASH_SOURCE[0]}")/glow" + elif type brew &> /dev/null; then + brew install glow + GLOW_PATH="$(which glow)" + else + installGlow + fi + + if [ -n "$GLOW_PATH" ]; then + chmod +x "$GLOW_PATH" + ENHANCED_LOGGING=true + fi + fi + "$GLOW_PATH" "$2" + elif [ "$1" == 'prompt' ]; then + "$GUM_PATH" style " $("$GUM_PATH" style --foreground="#00008b" "▶") $("$GUM_PATH" style --bold "$(format "$2")")" + elif [ "$1" == 'star' ]; then + "$GUM_PATH" style " $("$GUM_PATH" style --foreground="#d1d100" "◆") $("$GUM_PATH" style --bold --underline "$(format "$2")")" + elif [ "$1" == 'start' ]; then + "$GUM_PATH" style " $("$GUM_PATH" style --foreground="#00ff00" "▶") $("$GUM_PATH" style --bold "$(format "$2")")" + elif [ "$1" == 'success' ]; then + "$GUM_PATH" style "$("$GUM_PATH" style --foreground="#00ff00" "✔") $("$GUM_PATH" style --bold "$(format "$2")")" + elif [ "$1" == 'warn' ]; then + "$GUM_PATH" style " $("$GUM_PATH" style --foreground="#d1d100" "◆") $("$GUM_PATH" style --bold --background="#ffff00" --foreground="#000000" " WARNING ") $("$GUM_PATH" style --bold --italic "$(format "$2")")" + else + echo "WARNING: Unknown log type" + echo "$2" + fi +} + +format() { + # shellcheck disable=SC2001,SC2016 + ANSI_STR="$(echo "$1" | sed 's/^\([^`]*\)`\([^`]*\)`/\1\\u001b[47;1;30m \2 \\e[0;39m/')" + if [[ $ANSI_STR == *'`'*'`'* ]]; then + ANSI_STR="$(format "$ANSI_STR")" + fi + echo -e "$ANSI_STR" +} + +# @description Display prompt that allows you to choose between options +# @example RESPONSE="$(.config/log choose "file.png" "another-file.jpg")" +choose() { + if type gum &> /dev/null; then + CHOOSE_ARGS="gum choose" + for CURRENT_VAR in "$@"; do + CHOOSE_ARGS="$CHOOSE_ARGS \"$CURRENT_VAR\"" + done + eval $CHOOSE_ARGS + else + echo "ERROR: gum is not installed!" + fi +} + +# @description Display a confirmation prompt that returns an exit code if "No" is selected +# @example RESPONSE="$(.config/log confirm "Are you sure?" "Yeah" "Naa")" +confirm() { + if type gum &> /dev/null; then + GUM_OPTS="" + if [ -n "$2" ]; then + # shellcheck disable=SC089 + GUM_OPTS="$GUM_OPTS --affirmative=""'$2'" + fi + if [ -n "$3" ]; then + GUM_OPTS="$GUM_OPTS --negative=""'$3'" + fi + if [ -n "$1" ]; then + if [ -n "$GUM_OPTS" ]; then + gum confirm "$1" "$GUM_OPTS" + else + gum confirm "$1" + fi + else + gum confirm + fi + else + echo "ERROR: gum is not installed!" + fi +} + +# @description Logs an error message +# @example .config/log error "Something happened!" +error() { + if [ -z "$NO_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then + logger error "$1" + else + echo -e "\e[1;41m ERROR \e[0m $(format "$1")\e[0;39m" + fi + fi +} + +# @description Display a filterable prompt that is populated with options from a text file +# @example echo Strawberry >> flavors.text && echo Banana >> flavors.text && RESPONSE="$(.config/log filter flavors.txt)" +filter() { + if type gum &> /dev/null; then + TMP="$(mktemp)" + gum filter < "$1" + else + echo "ERROR: gum is not installed!" + fi +} + +# @description Logs an info message +# @example .config/log info "Here is some information" +info() { + if [ -z "$NO_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then + logger info "$1" + else + echo -e "\e[1;46m INFO \e[0m $(format "$1")\e[0;39m" + fi + fi +} + +# @description Displays an input with masked characters +# @example INPUT="$(.config/log input 'Enter the value..')" +input() { + if type gum &> /dev/null; then + if [ -n "$1" ]; then + gum input --placeholder="$1" + else + gum input + fi + else + echo "ERROR: gum is not installed!" + fi +} + +# @description Logs a message written in markdown +# @example .config/log md "[styled_link](https://google.com)" +# @example .config/log md mymarkdown/file.md +md() { + if [ ! -f "$1" ]; then + echo "ERROR: A markdown file must be passed in as the parameter" && exit 1 + fi + if [ -n "$ENHANCED_LOGGING" ]; then + logger md "$1" + fi +} + +# @description Displays an input with masked characters +# @example PASSWORD="$(.config/log password 'Enter the Ansible vault password')" +password() { + if type gum &> /dev/null; then + if [ -n "$1" ]; then + gum input --password --placeholder="$1" + else + gum input --password + fi + else + echo "ERROR: gum is not installed!" + fi +} + +# @description Logs a message that describes a prompt +# @example .config/log prompt "Enter text into the following prompt" +prompt() { + if [ -z "$NO_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then + logger prompt "$1" + else + echo -e "\e[1;104m PROMPT \e[0m $(format "$1")\e[0;39m" + fi + fi +} + +# @description Display a spinner that stays until a command is completed +# @example .config/log spin "brew install yq" "Installing yq..")" +spin() { + if type gum &> /dev/null; then + if [ -n "$1" ] && [ -n "$2" ]; then + gum spin --title="$2" "$1" + elif [ -n "$1" ]; then + gum spin "$1" + else + gum input + fi + else + echo "ERROR: gum is not installed!" + fi +} + +# @description Logs a message that starts with a star emoji +# @example .config/log star "Congratulations" +star() { + if [ -z "$NO_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then + logger star "$1" + else + echo -e "\e[1;104m LINK \e[0m $(format "$1")\e[0;39m" + fi + fi +} + +# @description Logs a message at the beginning of a task +# @example .config/log start "Starting the process.." +start() { + if [ -z "$NO_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then + logger start "$1" + else + echo -e "\e[1;46m START \e[0m $(format "$1")\e[0;39m" + fi + fi +} + +# @description Logs a success message +# @example .config/log success "Yay!" +success() { + if [ -z "$NO_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then + logger success "$1" + else + echo -e "\e[1;42m SUCCESS \e[0m $(format "$1")\e[0;39m" + fi + fi +} + +# @description Logs a warning message +# @example .config/log warn "Just so you know.." +warn() { + if [ -z "$NO_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then + logger warn "$1" + else + echo -e "\e[1;43m WARNING \e[0m $(format "$1")\e[0;39m" + fi + fi +} + +# @description Displays a multi-line prompt for text input +# @example .config/log write "Write something..")" +write() { + if type gum &> /dev/null; then + if [ -n "$1" ]; then + gum write --placeholder="$1" + else + gum write + fi + else + echo "ERROR: gum is not installed!" + fi +} + +if [ -n "$1" ] && [ -n "$2" ]; then + # Public functions that require at least two parameters to be used + if [ "$1" == 'warn' ] || [ "$1" == 'success' ] || [ "$1" == 'star' ] || [ "$1" == 'info' ] \ + || [ "$1" == 'error' ] || [ "$1" == 'md' ] || [ "$1" == 'write' ] || [ "$1" == 'start' ] \ + || [ "$1" == 'spin' ] || [ "$1" == 'prompt' ] || [ "$1" == 'filter' ] || [ "$1" == 'input' ] \ + || [ "$1" == 'confirm' ] || [ "$1" == 'password' ]; then + "$1" "$2" + elif [[ "$1" == 'choose' ]]; then + "$@" + fi +elif [ -n "$1" ]; then + # Public functions that can run with only one argument passed to .config/log (i.e. `.config/log password`) + if [ "$1" == 'write' ] || [ "$1" == 'password' ] || [ "$1" == 'confirm' ] || [ "$1" == 'input' ]; then + "$1" + fi +fi diff --git a/.config/molecule/config.yml b/.config/molecule/config.yml new file mode 100644 index 00000000..15ae68e3 --- /dev/null +++ b/.config/molecule/config.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: shell + command: | + if type task > /dev/null; then + task ansible:test:molecule:dependencies + else + ansible-galaxy install --ignore-errors -r requirements.yml + fi +provisioner: + name: ansible + options: + vvv: true + playbooks: + converge: ../converge.yml + prepare: ../../.config/molecule/prepare.yml + docker: + create: ../../.config/molecule/docker.create.yml + destroy: ../../.config/molecule/docker.destroy.yml + gce: + create: ../../.config/molecule/gce.create.yml + destroy: ../../.config/molecule/gce.destroy.yml + vagrant: + create: ../../.config/molecule/vagrant.create.yml + destroy: ../../.config/molecule/vagrant.destroy.yml +verifier: + name: ansible diff --git a/.config/molecule/docker.create.yml b/.config/molecule/docker.create.yml new file mode 100644 index 00000000..319a98a9 --- /dev/null +++ b/.config/molecule/docker.create.yml @@ -0,0 +1,179 @@ +--- +# yamllint disable rule:line-length +- name: Update platforms + hosts: localhost + tasks: + - name: Filtering platforms list using the group defined in the MOLECULE_GROUP environment variable + set_fact: + molecule_yml: "{{ molecule_yml | combine({'platforms': (molecule_yml.platforms | selectattr('groups', 'contains', lookup('env', 'MOLECULE_GROUP')))}) }}" + when: ansible_env.MOLECULE_GROUP is defined + +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: '{{ molecule_no_log }}' + vars: + molecule_labels: + owner: molecule + tasks: + - name: Log into a Docker registry + community.docker.docker_login: + username: '{{ item.registry.credentials.username }}' + password: '{{ item.registry.credentials.password }}' + email: '{{ item.registry.credentials.email | default(omit) }}' + registry: '{{ item.registry.url }}' + docker_host: "{{ item.docker_host | default(lookup('env', 'DOCKER_HOST') or 'unix://var/run/docker.sock') }}" + cacert_path: "{{ item.cacert_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/ca.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + cert_path: "{{ item.cert_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/cert.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + key_path: "{{ item.key_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/key.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + tls_verify: "{{ item.tls_verify | default(lookup('env', 'DOCKER_TLS_VERIFY')) or false }}" + with_items: '{{ molecule_yml.platforms }}' + when: + - item.registry is defined + - item.registry.credentials is defined + - item.registry.credentials.username is defined + no_log: true + + - name: Check presence of custom Dockerfiles + ansible.builtin.stat: + path: "{{ molecule_scenario_directory + '/' + (item.dockerfile | default( 'Dockerfile.j2')) }}" + loop: '{{ molecule_yml.platforms }}' + register: dockerfile_stats + + - name: Create Dockerfiles from image names + ansible.builtin.template: + # when using embedded playbooks the dockerfile is alonside them + src: >- + {%- if dockerfile_stats.results[i].stat.exists -%} + {{ molecule_scenario_directory + '/' + (item.dockerfile | default( 'Dockerfile.j2')) }} + {%- else -%} + {{ playbook_dir + '/Dockerfile.j2' }} + {%- endif -%} + dest: "{{ molecule_ephemeral_directory }}/Dockerfile_{{ item.image | regex_replace('[^a-zA-Z0-9_]', '_') }}" + mode: '0600' + loop: '{{ molecule_yml.platforms }}' + loop_control: + index_var: i + when: not item.pre_build_image | default(false) + register: platforms + + - name: Discover local Docker images + community.docker.docker_image_info: + name: 'molecule_local/{{ item.item.name }}' + docker_host: "{{ item.item.docker_host | default(lookup('env', 'DOCKER_HOST') or 'unix://var/run/docker.sock') }}" + cacert_path: "{{ item.cacert_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/ca.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + cert_path: "{{ item.cert_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/cert.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + key_path: "{{ item.key_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/key.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + tls_verify: "{{ item.tls_verify | default(lookup('env', 'DOCKER_TLS_VERIFY')) or false }}" + with_items: '{{ platforms.results }}' + when: + - not item.pre_build_image | default(false) + register: docker_images + + - name: Build an Ansible compatible image (new) # noqa: no-handler + when: + - platforms.changed or docker_images.results | map(attribute='images') | select('equalto', []) | list | count >= 0 + - not item.item.pre_build_image | default(false) + community.docker.docker_image: + build: + path: '{{ molecule_ephemeral_directory }}' + dockerfile: '{{ item.invocation.module_args.dest }}' + pull: '{{ item.item.pull | default(true) }}' + network: '{{ item.item.network_mode | default(omit) }}' + args: '{{ item.item.buildargs | default(omit) }}' + name: 'molecule_local/{{ item.item.image }}' + docker_host: "{{ item.item.docker_host | default(lookup('env', 'DOCKER_HOST') or 'unix://var/run/docker.sock') }}" + cacert_path: "{{ item.cacert_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/ca.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + cert_path: "{{ item.cert_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/cert.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + key_path: "{{ item.key_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/key.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + tls_verify: "{{ item.tls_verify | default(lookup('env', 'DOCKER_TLS_VERIFY')) or false }}" + force_source: '{{ item.item.force | default(true) }}' + source: build + with_items: '{{ platforms.results }}' + loop_control: + label: 'molecule_local/{{ item.item.image }}' + no_log: false + register: result + until: result is not failed + retries: 3 + delay: 30 + + - name: Create docker network(s) + ansible.builtin.include_tasks: tasks/create_network.yml + with_items: '{{ molecule_yml.platforms | molecule_get_docker_networks(molecule_labels) }}' + loop_control: + label: '{{ item.name }}' + no_log: false + + - name: Determine the CMD directives + ansible.builtin.set_fact: + command_directives_dict: >- + {{ command_directives_dict | default({}) | + combine({ item.name: item.command | default('bash -c "while true; do sleep 10000; done"') }) + }} + with_items: '{{ molecule_yml.platforms }}' + when: item.override_command | default(true) + + - name: Create molecule instance(s) + community.docker.docker_container: + name: '{{ item.name }}' + docker_host: "{{ item.docker_host | default(lookup('env', 'DOCKER_HOST') or 'unix://var/run/docker.sock') }}" + cacert_path: "{{ item.cacert_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/ca.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + cert_path: "{{ item.cert_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/cert.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + key_path: "{{ item.key_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/key.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + tls_verify: "{{ item.tls_verify | default(lookup('env', 'DOCKER_TLS_VERIFY')) or false }}" + hostname: '{{ item.hostname | default(item.name) }}' + image: "{{ item.pre_build_image | default(false) | ternary('', 'molecule_local/') }}{{ item.image }}" + pull: '{{ item.pull | default(omit) }}' + memory: '{{ item.memory | default(omit) }}' + memory_swap: '{{ item.memory_swap | default(omit) }}' + state: started + recreate: false + log_driver: json-file + command: '{{ (command_directives_dict | default({}))[item.name] | default(omit) }}' + command_handling: "{{ item.command_handling | default('compatibility') }}" + user: '{{ item.user | default(omit) }}' + pid_mode: '{{ item.pid_mode | default(omit) }}' + privileged: '{{ item.privileged | default(omit) }}' + security_opts: '{{ item.security_opts | default(omit) }}' + devices: '{{ item.devices | default(omit) }}' + links: '{{ item.links | default(omit) }}' + volumes: '{{ item.volumes | default(omit) }}' + mounts: '{{ item.mounts | default(omit) }}' + tmpfs: '{{ item.tmpfs | default(omit) }}' + capabilities: '{{ item.capabilities | default(omit) }}' + sysctls: '{{ item.sysctls | default(omit) }}' + exposed_ports: '{{ item.exposed_ports | default(omit) }}' + published_ports: '{{ item.published_ports | default(omit) }}' + ulimits: '{{ item.ulimits | default(omit) }}' + networks: '{{ item.networks | default(omit) }}' + network_mode: '{{ item.network_mode | default(omit) }}' + networks_cli_compatible: '{{ item.networks_cli_compatible | default(true) }}' + purge_networks: '{{ item.purge_networks | default(omit) }}' + dns_servers: '{{ item.dns_servers | default(omit) }}' + etc_hosts: '{{ item.etc_hosts | default(omit) }}' + env: '{{ item.env | default(omit) }}' + restart_policy: '{{ item.restart_policy | default(omit) }}' + restart_retries: '{{ item.restart_retries | default(omit) }}' + tty: '{{ item.tty | default(omit) }}' + labels: '{{ molecule_labels | combine(item.labels | default({})) }}' + container_default_behavior: "{{ item.container_default_behavior | default('compatibility' + if ansible_version.full is version_compare('2.10', '>=') else omit) }}" + stop_signal: '{{ item.stop_signal | default(omit) }}' + kill_signal: '{{ item.kill_signal | default(omit) }}' + register: server + with_items: '{{ molecule_yml.platforms }}' + loop_control: + label: '{{ item.name }}' + no_log: false + async: 7200 + poll: 0 + + - name: Wait for instance(s) creation to complete + ansible.builtin.async_status: + jid: '{{ item.ansible_job_id }}' + register: docker_jobs + until: docker_jobs.finished + retries: 300 + with_items: '{{ server.results }}' diff --git a/.config/molecule/docker.destroy.yml b/.config/molecule/docker.destroy.yml new file mode 100644 index 00000000..556d99d8 --- /dev/null +++ b/.config/molecule/docker.destroy.yml @@ -0,0 +1,53 @@ +--- +# yamllint disable rule:line-length +- name: Update platforms + hosts: localhost + tasks: + - name: Filtering platforms list using the group defined in the MOLECULE_GROUP environment variable + set_fact: + molecule_yml: "{{ molecule_yml | combine({'platforms': (molecule_yml.platforms | selectattr('groups', 'contains', lookup('env', 'MOLECULE_GROUP')))}) }}" + when: ansible_env.MOLECULE_GROUP is defined + +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: '{{ molecule_no_log }}' + tasks: + - name: Destroy molecule instance(s) + community.docker.docker_container: + name: '{{ item.name }}' + docker_host: "{{ item.docker_host | default(lookup('env', 'DOCKER_HOST') or 'unix://var/run/docker.sock') }}" + cacert_path: "{{ item.cacert_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/ca.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + cert_path: "{{ item.cert_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/cert.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + key_path: "{{ item.key_path | default((lookup('env', 'DOCKER_CERT_PATH') + '/key.pem') if lookup('env', 'DOCKER_CERT_PATH') else omit) }}" + tls_verify: "{{ item.tls_verify | default(lookup('env', 'DOCKER_TLS_VERIFY')) or false }}" + state: absent + force_kill: '{{ item.force_kill | default(true) }}' + keep_volumes: '{{ item.keep_volumes | default(true) }}' + container_default_behavior: "{{ item.container_default_behavior | default('compatibility' if + ansible_version.full is version_compare('2.10', '>=') else omit) }}" + register: server + loop: '{{ molecule_yml.platforms }}' + loop_control: + label: '{{ item.name }}' + no_log: false + async: 7200 + poll: 0 + + - name: Wait for instance(s) deletion to complete + ansible.builtin.async_status: + jid: '{{ item.ansible_job_id }}' + register: docker_jobs + until: docker_jobs.finished + retries: 300 + loop: '{{ server.results }}' + loop_control: + label: '{{ item.item.name }}' + + - name: Delete docker networks(s) + include_tasks: tasks/delete_network.yml + loop: '{{ molecule_yml.platforms | molecule_get_docker_networks() }}' + loop_control: + label: '{{ item.name }}' + no_log: false diff --git a/.config/molecule/files/windows_auth.py b/.config/molecule/files/windows_auth.py new file mode 100644 index 00000000..25204299 --- /dev/null +++ b/.config/molecule/files/windows_auth.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python + +# Copyright 2015 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import base64 +import copy +import datetime +import json +import time +import argparse + +# PyCrypto library: https://pypi.python.org/pypi/pycrypto +from Crypto.Cipher import PKCS1_OAEP +from Crypto.PublicKey import RSA +from Crypto.Util.number import long_to_bytes + +# Google API Client Library for Python: +# https://developers.google.com/api-client-library/python/start/get_started +import google.auth +from googleapiclient.discovery import build + + +def GetCompute(): + """Get a compute object for communicating with the Compute Engine API.""" + credentials, project = google.auth.default() + compute = build("compute", "v1", credentials=credentials) + return compute + + +def GetInstance(compute, instance, zone, project): + """Get the data for a Google Compute Engine instance.""" + cmd = compute.instances().get(instance=instance, project=project, zone=zone) + return cmd.execute() + + +def GetKey(): + """Get an RSA key for encryption.""" + # This uses the PyCrypto library + key = RSA.generate(2048) + return key + + +def GetModulusExponentInBase64(key): + """Return the public modulus and exponent for the key in bas64 encoding.""" + mod = long_to_bytes(key.n) + exp = long_to_bytes(key.e) + + modulus = base64.b64encode(mod) + exponent = base64.b64encode(exp) + + return modulus, exponent + + +def GetExpirationTimeString(): + """Return an RFC3339 UTC timestamp for 5 minutes from now.""" + utc_now = datetime.datetime.utcnow() + # These metadata entries are one-time-use, so the expiration time does + # not need to be very far in the future. In fact, one minute would + # generally be sufficient. Five minutes allows for minor variations + # between the time on the client and the time on the server. + expire_time = utc_now + datetime.timedelta(minutes=5) + return expire_time.strftime("%Y-%m-%dT%H:%M:%SZ") + + +def GetJsonString(user, modulus, exponent, email): + """Return the JSON string object that represents the windows-keys entry.""" + + converted_modulus = modulus.decode("utf-8") + converted_exponent = exponent.decode("utf-8") + + expire = GetExpirationTimeString() + data = { + "userName": user, + "modulus": converted_modulus, + "exponent": converted_exponent, + "email": email, + "expireOn": expire, + } + + return json.dumps(data) + + +def UpdateWindowsKeys(old_metadata, metadata_entry): + """Return updated metadata contents with the new windows-keys entry.""" + # Simply overwrites the "windows-keys" metadata entry. Production code may + # want to append new lines to the metadata value and remove any expired + # entries. + new_metadata = copy.deepcopy(old_metadata) + new_metadata["items"] = [{"key": "windows-keys", "value": metadata_entry}] + return new_metadata + + +def UpdateInstanceMetadata(compute, instance, zone, project, new_metadata): + """Update the instance metadata.""" + cmd = compute.instances().setMetadata( + instance=instance, project=project, zone=zone, body=new_metadata + ) + return cmd.execute() + + +def GetSerialPortFourOutput(compute, instance, zone, project): + """Get the output from serial port 4 from the instance.""" + # Encrypted passwords are printed to COM4 on the windows server: + port = 4 + cmd = compute.instances().getSerialPortOutput( + instance=instance, project=project, zone=zone, port=port + ) + output = cmd.execute() + return output["contents"] + + +def GetEncryptedPasswordFromSerialPort(serial_port_output, modulus): + """Find and return the correct encrypted password, based on the modulus.""" + # In production code, this may need to be run multiple times if the output + # does not yet contain the correct entry. + + converted_modulus = modulus.decode("utf-8") + + output = serial_port_output.split("\n") + for line in reversed(output): + try: + entry = json.loads(line) + if converted_modulus == entry["modulus"]: + return entry["encryptedPassword"] + except ValueError: + pass + + +def DecryptPassword(encrypted_password, key): + """Decrypt a base64 encoded encrypted password using the provided key.""" + + decoded_password = base64.b64decode(encrypted_password) + cipher = PKCS1_OAEP.new(key) + password = cipher.decrypt(decoded_password) + return password + + +def Arguments(): + # Create the parser + args = argparse.ArgumentParser(description="List the content of a folder") + + # Add the arguments + args.add_argument( + "--instance", metavar="instance", type=str, help="compute instance name" + ) + + args.add_argument("--zone", metavar="zone", type=str, help="compute zone") + + args.add_argument("--project", metavar="project", type=str, help="gcp project") + + args.add_argument("--username", metavar="username", type=str, help="username") + + args.add_argument("--email", metavar="email", type=str, help="email") + + # return arguments + return args.parse_args() + + +def main(): + config_args = Arguments() + + # Setup + compute = GetCompute() + key = GetKey() + modulus, exponent = GetModulusExponentInBase64(key) + + # Get existing metadata + instance_ref = GetInstance( + compute, config_args.instance, config_args.zone, config_args.project + ) + old_metadata = instance_ref["metadata"] + # Create and set new metadata + metadata_entry = GetJsonString( + config_args.username, modulus, exponent, config_args.email + ) + new_metadata = UpdateWindowsKeys(old_metadata, metadata_entry) + + # Get Serial output BEFORE the modification + serial_port_output = GetSerialPortFourOutput( + compute, config_args.instance, config_args.zone, config_args.project + ) + + UpdateInstanceMetadata( + compute, + config_args.instance, + config_args.zone, + config_args.project, + new_metadata, + ) + + # Get and decrypt password from serial port output + # Monitor changes from output to get the encrypted password as soon as it's generated, will wait for 30 seconds + i = 0 + new_serial_port_output = serial_port_output + while i <= 20 and serial_port_output == new_serial_port_output: + new_serial_port_output = GetSerialPortFourOutput( + compute, config_args.instance, config_args.zone, config_args.project + ) + i += 1 + time.sleep(3) + + enc_password = GetEncryptedPasswordFromSerialPort(new_serial_port_output, modulus) + + password = DecryptPassword(enc_password, key) + converted_password = password.decode("utf-8") + + # Display only the password + print(format(converted_password)) + + +if __name__ == "__main__": + main() diff --git a/.config/molecule/filter_plugins/get_docker_networks.py b/.config/molecule/filter_plugins/get_docker_networks.py new file mode 100644 index 00000000..cd57dac3 --- /dev/null +++ b/.config/molecule/filter_plugins/get_docker_networks.py @@ -0,0 +1,37 @@ +"""Embedded ansible filter used by Molecule Docker driver create playbook.""" + + +def get_docker_networks(data, labels={}): + """Get list of docker networks.""" + network_list = [] + network_names = [] + for platform in data: + if "docker_networks" in platform: + for docker_network in platform["docker_networks"]: + if "labels" not in docker_network: + docker_network["labels"] = {} + for key in labels: + docker_network["labels"][key] = labels[key] + + if "name" in docker_network: + network_list.append(docker_network) + network_names.append(docker_network["name"]) + + # If a network name is defined for a platform but is not defined in + # docker_networks, add it to the network list. + if "networks" in platform: + for network in platform["networks"]: + if "name" in network: + name = network["name"] + if name not in network_names: + network_list.append({"name": name, "labels": labels}) + return network_list + + +class FilterModule(object): + """Core Molecule filter plugins.""" + + def filters(self): + return { + "molecule_get_docker_networks": get_docker_networks, + } diff --git a/.config/molecule/gce.create.yml b/.config/molecule/gce.create.yml new file mode 100644 index 00000000..56a65b0a --- /dev/null +++ b/.config/molecule/gce.create.yml @@ -0,0 +1,39 @@ +--- +# yamllint disable rule:line-length +- name: Update platforms + hosts: localhost + tasks: + - name: Filtering platforms list using the group defined in the MOLECULE_GROUP environment variable + set_fact: + molecule_yml: "{{ molecule_yml | combine({'platforms': (molecule_yml.platforms | selectattr('groups', 'contains', lookup('env', 'MOLECULE_GROUP')))}) }}" + when: ansible_env.MOLECULE_GROUP is defined + +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: '{{ molecule_no_log }}' + vars: + ssh_identity_file: "{{ lookup('env', 'MOLECULE_EPHEMERAL_DIRECTORY') }}/ssh_key" + gcp_project_id: "{{ molecule_yml.driver.project_id | default(lookup('env', 'GCE_PROJECT_ID')) }}" + tasks: + - name: Make sure the group contains only Linux hosts or Windows hosts + ansible.builtin.assert: + that: + - molecule_yml.driver.instance_os_type | lower == "linux" or + molecule_yml.driver.instance_os_type | lower == "windows" + fail_msg: instance_os_type is possible only to specify linux or windows either + + - name: Include create_linux_instance tasks + ansible.builtin.include_tasks: tasks/create_linux_instance.yml + when: + - molecule_yml.driver.instance_os_type | lower == "linux" + + - name: Include create_windows_instance tasks + ansible.builtin.include_tasks: tasks/create_windows_instance.yml + when: + - molecule_yml.driver.instance_os_type | lower == "windows" + + handlers: + - name: Import main handler tasks + ansible.builtin.import_tasks: handlers/main.yml diff --git a/.config/molecule/gce.destroy.yml b/.config/molecule/gce.destroy.yml new file mode 100644 index 00000000..daba9f70 --- /dev/null +++ b/.config/molecule/gce.destroy.yml @@ -0,0 +1,46 @@ +--- +# yamllint disable rule:line-length +- name: Update platforms + hosts: localhost + tasks: + - name: Filtering platforms list using the group defined in the MOLECULE_GROUP environment variable + set_fact: + molecule_yml: "{{ molecule_yml | combine({'platforms': (molecule_yml.platforms | selectattr('groups', 'contains', lookup('env', 'MOLECULE_GROUP')))}) }}" + when: ansible_env.MOLECULE_GROUP is defined + +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: '{{ molecule_no_log }}' + tasks: + - name: Destroy molecule instance(s) + google.cloud.gcp_compute_instance: + name: '{{ item.name }}' + state: absent + zone: "{{ item.zone | default(molecule_yml.driver.region + '-b') }}" + project: "{{ molecule_yml.driver.project_id | default(lookup('env', 'GCE_PROJECT_ID')) }}" + scopes: "{{ molecule_yml.driver.scopes | default(['https://www.googleapis.com/auth/compute'], True) }}" + service_account_email: '{{ molecule_yml.driver.service_account_email | default (omit, true) }}' + service_account_file: '{{ molecule_yml.driver.service_account_file | default (omit, true) }}' + auth_kind: '{{ molecule_yml.driver.auth_kind | default(omit, true) }}' + register: async_results + loop: '{{ molecule_yml.platforms }}' + async: 7200 + poll: 0 + notify: + - Wipe out instance config + - Dump instance config + + - name: Wait for instance(s) deletion to complete + ansible.builtin.async_status: + jid: '{{ item.ansible_job_id }}' + register: server + until: server.finished + retries: 300 + delay: 10 + loop: '{{ async_results.results }}' + + handlers: + - name: Import main handler tasks + ansible.builtin.import_tasks: handlers/main.yml diff --git a/.config/molecule/handlers/main.yml b/.config/molecule/handlers/main.yml new file mode 100644 index 00000000..8f760e4f --- /dev/null +++ b/.config/molecule/handlers/main.yml @@ -0,0 +1,51 @@ +--- +# yamllint disable rule:line-length +- name: Populate instance config dict Linux + ansible.builtin.set_fact: + instance_conf_dict: + instance: '{{ instance_info.name }}' + + address: '{{ instance_info.networkInterfaces.0.accessConfigs.0.natIP if molecule_yml.driver.external_access else instance_info.networkInterfaces.0.networkIP }}' + user: "{{ lookup('env','USER') }}" + port: '22' + identity_file: '{{ ssh_identity_file }}' + instance_os_type: '{{ molecule_yml.driver.instance_os_type }}' + + loop: '{{ server.results }}' + loop_control: + loop_var: instance_info + no_log: true + register: instance_conf_dict + +- name: Populate instance config dict Windows + ansible.builtin.set_fact: + instance_conf_dict: + instance: '{{ instance_info.name }}' + + address: '{{ instance_info.networkInterfaces.0.accessConfigs.0.natIP if molecule_yml.driver.external_access else instance_info.networkInterfaces.0.networkIP }}' + user: molecule_usr + password: '{{ instance_info.password }}' + port: '{{ instance_info.winrm_port | default(5986) }}' + winrm_transport: "{{ molecule_yml.driver.winrm_transport | default('ntlm') }}" + winrm_server_cert_validation: "{{ molecule_yml.driver.winrm_server_cert_validation | default('ignore') }}" + instance_os_type: '{{ molecule_yml.driver.instance_os_type }}' + + loop: '{{ win_instances }}' + loop_control: + loop_var: instance_info + no_log: true + register: instance_conf_dict + +- name: Wipe out instance config + ansible.builtin.set_fact: + instance_conf: {} + +- name: Convert instance config dict to a list + ansible.builtin.set_fact: + instance_conf: "{{ instance_conf_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + +- name: Dump instance config + ansible.builtin.copy: + content: '{{ instance_conf }}' + dest: '{{ molecule_instance_config }}' + mode: '0600' diff --git a/.config/molecule/prepare.yml b/.config/molecule/prepare.yml new file mode 100644 index 00000000..b0dbb51c --- /dev/null +++ b/.config/molecule/prepare.yml @@ -0,0 +1,20 @@ +--- +- name: Prepare + hosts: "{{ lookup('env', 'MOLECULE_GROUP') | default('all', true) }}" + gather_facts: false + tasks: + - become: true + changed_when: false + name: Bootstrap Python for Ansible + raw: | + command -v python3 python || ( + command -v apk >/dev/null && sudo apk add --no-progress --update python3 || + (test -e /usr/bin/dnf && sudo dnf install -y python3) || + (test -e /usr/bin/apt && (apt -y update && apt install -y python-minimal)) || + (test -e /usr/bin/yum && sudo yum -y -qq install python3) || + (test -e /usr/sbin/pkg && sudo env ASSUME_ALWAYS_YES=yes pkg update && sudo env ASSUME_ALWAYS_YES=yes pkg install python3) || + (test -e /usr/sbin/pkg_add && sudo /usr/sbin/pkg_add -U -I -x python%3.7) || + echo "Warning: Python not boostrapped due to unknown platform." + ) + when: + - ansible_connection != 'winrm' diff --git a/.config/molecule/tasks/create_linux_instance.yml b/.config/molecule/tasks/create_linux_instance.yml new file mode 100644 index 00000000..4b3acdae --- /dev/null +++ b/.config/molecule/tasks/create_linux_instance.yml @@ -0,0 +1,64 @@ +--- +# yamllint disable rule:line-length +- name: create ssh keypair + community.crypto.openssh_keypair: + comment: "{{ lookup('env','USER') }} user for Molecule" + path: '{{ ssh_identity_file }}' + register: keypair + +- name: create molecule Linux instance(s) + google.cloud.gcp_compute_instance: + state: present + name: '{{ item.name }}' + machine_type: "{{ item.machine_type | default('n1-standard-1') }}" + metadata: + ssh-keys: "{{ lookup('env','USER') }}:{{ keypair.public_key }}" + scheduling: + preemptible: '{{ item.preemptible | default(false) }}' + disks: + - auto_delete: true + boot: true + initialize_params: + disk_size_gb: '{{ item.disk_size_gb | default(omit) }}' + source_image: "{{ item.image | default('projects/debian-cloud/global/images/family/debian-10') }}" + source_image_encryption_key: + raw_key: '{{ item.image_encryption_key | default(omit) }}' + network_interfaces: + - network: + selfLink: "https://www.googleapis.com/compute/v1/projects/{{ molecule_yml.driver.vpc_host_project | default(gcp_project_id) }}/global/networks/{{ molecule_yml.driver.network_name | default('default') }}" + subnetwork: + selfLink: "https://compute.googleapis.com/compute/v1/projects/{{ molecule_yml.driver.vpc_host_project | default(gcp_project_id) }}/regions/{{ molecule_yml.driver.region }}/subnetworks/{{ molecule_yml.driver.subnetwork_name | default('default') }}" + access_configs: "{{ [{'name': 'instance_ip', 'type': 'ONE_TO_ONE_NAT'}] if molecule_yml.driver.external_access else [] }}" + zone: "{{ item.zone | default(molecule_yml.driver.region + '-b') }}" + project: '{{ gcp_project_id }}' + scopes: "{{ molecule_yml.driver.scopes | default(['https://www.googleapis.com/auth/compute'], True) }}" + service_account_email: '{{ molecule_yml.driver.service_account_email | default (omit, true) }}' + service_account_file: '{{ molecule_yml.driver.service_account_file | default (omit, true) }}' + auth_kind: '{{ molecule_yml.driver.auth_kind | default(omit, true) }}' + register: async_results + loop: '{{ molecule_yml.platforms }}' + loop_control: + pause: 3 + async: 7200 + poll: 0 + +- name: Wait for instance(s) creation to complete + ansible.builtin.async_status: + jid: '{{ item.ansible_job_id }}' + loop: '{{ async_results.results }}' + register: server + until: server.finished + retries: 300 + delay: 10 + notify: + - Populate instance config dict Linux + - Convert instance config dict to a list + - Dump instance config + +- name: Wait for SSH + ansible.builtin.wait_for: + port: 22 + host: '{{ item.networkInterfaces.0.accessConfigs.0.natIP if molecule_yml.driver.external_access else item.networkInterfaces.0.networkIP }}' + search_regex: SSH + delay: 10 + loop: '{{ server.results }}' diff --git a/.config/molecule/tasks/create_windows_instance.yml b/.config/molecule/tasks/create_windows_instance.yml new file mode 100644 index 00000000..437b5411 --- /dev/null +++ b/.config/molecule/tasks/create_windows_instance.yml @@ -0,0 +1,65 @@ +--- +# yamllint disable rule:line-length +- name: create ssh keypair + community.crypto.openssh_keypair: + comment: "{{ lookup('env','USER') }} user for Molecule" + path: '{{ ssh_identity_file }}' + register: keypair + +- name: create molecule Linux instance(s) + google.cloud.gcp_compute_instance: + state: present + name: '{{ item.name }}' + machine_type: "{{ item.machine_type | default('n1-standard-1') }}" + metadata: + ssh-keys: "{{ lookup('env','USER') }}:{{ keypair.public_key }}" + scheduling: + preemptible: '{{ item.preemptible | default(false) }}' + disks: + - auto_delete: true + boot: true + initialize_params: + disk_size_gb: '{{ item.disk_size_gb | default(omit) }}' + source_image: "{{ item.image | default('projects/debian-cloud/global/images/family/debian-10') }}" + source_image_encryption_key: + raw_key: '{{ item.image_encryption_key | default(omit) }}' + network_interfaces: + - network: + selfLink: "https://www.googleapis.com/compute/v1/projects/{{ molecule_yml.driver.vpc_host_project | default(gcp_project_id) }}/global/networks/{{ molecule_yml.driver.network_name | default('default') }}" + subnetwork: + selfLink: "https://compute.googleapis.com/compute/v1/projects/{{ molecule_yml.driver.vpc_host_project | default(gcp_project_id) }}/regions/{{ molecule_yml.driver.region }}/subnetworks/{{ molecule_yml.driver.subnetwork_name | default('default') }}" + access_configs: "{{ [{'name': 'instance_ip', 'type': 'ONE_TO_ONE_NAT'}] if molecule_yml.driver.external_access else [] }}" + zone: "{{ item.zone | default(molecule_yml.driver.region + '-b') }}" + project: '{{ gcp_project_id }}' + scopes: "{{ molecule_yml.driver.scopes | default(['https://www.googleapis.com/auth/compute'], True) }}" + service_account_email: '{{ molecule_yml.driver.service_account_email | default (omit, true) }}' + service_account_file: '{{ molecule_yml.driver.service_account_file | default (omit, true) }}' + auth_kind: '{{ molecule_yml.driver.auth_kind | default(omit, true) }}' + register: async_results + loop: '{{ molecule_yml.platforms }}' + loop_control: + pause: 3 + async: 7200 + poll: 0 + +- name: Wait for instance(s) creation to complete + ansible.builtin.async_status: + jid: '{{ item.ansible_job_id }}' + loop: '{{ async_results.results }}' + register: server + until: server.finished + retries: 300 + delay: 10 + notify: + - Populate instance config dict Linux + - Convert instance config dict to a list + - Dump instance config + +- name: Wait for SSH + ansible.builtin.wait_for: + port: 22 + + host: "{{ item.networkInterfaces.0.accessConfigs.0.natIP if molecule_yml.driver.external_access else (item.name + '.' + item.zone + '.' + molecule_yml.driver.project_id) }}" + search_regex: SSH + delay: 10 + loop: '{{ server.results }}' diff --git a/.config/molecule/vagrant.create.yml b/.config/molecule/vagrant.create.yml new file mode 100644 index 00000000..cb677d02 --- /dev/null +++ b/.config/molecule/vagrant.create.yml @@ -0,0 +1,63 @@ +--- +# yamllint disable rule:line-length +- name: Update platforms + hosts: localhost + tasks: + - name: Filtering platforms list using the group defined in the MOLECULE_GROUP environment variable + set_fact: + molecule_yml: "{{ molecule_yml | combine({'platforms': (molecule_yml.platforms | selectattr('groups', 'contains', lookup('env', 'MOLECULE_GROUP')))}) }}" + when: ansible_env.MOLECULE_GROUP is defined + +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: '{{ molecule_no_log }}' + tasks: + - name: Create molecule instance(s) + vagrant: + instance_name: '{{ item.name }}' + instance_interfaces: '{{ item.interfaces | default(omit) }}' + instance_raw_config_args: '{{ item.instance_raw_config_args | default(omit) }}' + config_options: '{{ item.config_options | default(omit) }}' + platform_box: '{{ item.box | default("generic/alpine310") }}' + platform_box_version: '{{ item.box_version | default(omit) }}' + platform_box_url: '{{ item.box_url | default(omit) }}' + provider_name: '{{ molecule_yml.driver.provider.name | default(omit, true) }}' + provider_memory: '{{ item.memory | default(omit) }}' + provider_cpus: '{{ item.cpus | default(omit) }}' + provider_options: '{{ item.provider_options | default(omit) }}' + provider_raw_config_args: '{{ item.provider_raw_config_args | default(omit) }}' + provider_override_args: '{{ item.provider_override_args | default(omit) }}' + provision: '{{ item.provision | default(omit) }}' + state: up + register: server + with_items: '{{ molecule_yml.platforms }}' + loop_control: + label: '{{ item.name }}' + no_log: false + + - name: Run tasks if there were changes while creating the molecule instance(s) + when: server.changed | bool + block: + - name: Populate instance config dict + set_fact: + instance_conf_dict: + instance: '{{ item.Host }}' + address: '{{ item.HostName }}' + user: '{{ item.User }}' + port: '{{ item.Port }}' + identity_file: '{{ item.IdentityFile }}' + + with_items: '{{ server.results }}' + register: instance_config_dict + + - name: Convert instance config dict to a list + set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + + - name: Dump instance config + copy: + content: '{{ instance_conf | to_json | from_json | to_yaml }}' + dest: '{{ molecule_instance_config }}' + mode: 0600 diff --git a/.config/molecule/vagrant.destroy.yml b/.config/molecule/vagrant.destroy.yml new file mode 100644 index 00000000..fa60bb3b --- /dev/null +++ b/.config/molecule/vagrant.destroy.yml @@ -0,0 +1,43 @@ +--- +# yamllint disable rule:line-length +- name: Update platforms + hosts: localhost + tasks: + - name: Filtering platforms list using the group defined in the MOLECULE_GROUP environment variable + set_fact: + molecule_yml: "{{ molecule_yml | combine({'platforms': (molecule_yml.platforms | selectattr('groups', 'contains', lookup('env', 'MOLECULE_GROUP')))}) }}" + when: ansible_env.MOLECULE_GROUP is defined + +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: '{{ molecule_no_log }}' + tasks: + - name: Destroy molecule instance(s) + vagrant: + instance_name: '{{ item.name }}' + platform_box: '{{ item.box | default(omit) }}' + provider_name: '{{ molecule_yml.driver.provider.name | default(omit, true) }}' + provider_options: '{{ item.provider_options | default(omit) }}' + provider_raw_config_args: '{{ item.provider_raw_config_args | default(omit) }}' + force_stop: '{{ item.force_stop | default(true) }}' + state: destroy + register: server + with_items: '{{ molecule_yml.platforms }}' + loop_control: + label: '{{ item.name }}' + no_log: false + + - name: Populate instance config + set_fact: + instance_conf: {} + + - name: Dump instance config # noqa 503 + copy: + content: | + # Molecule managed + {{ instance_conf | to_json | from_json | to_yaml }} + dest: '{{ molecule_instance_config }}' + mode: 0600 + when: server.changed | bool diff --git a/.config/nodemon.json b/.config/nodemon.json new file mode 100644 index 00000000..d879e11a --- /dev/null +++ b/.config/nodemon.json @@ -0,0 +1,4 @@ +{ + "exec": "task project:livereload", + "ext": "py,yml" +} diff --git a/.config/package-lock.json b/.config/package-lock.json new file mode 100644 index 00000000..45c8fdb4 --- /dev/null +++ b/.config/package-lock.json @@ -0,0 +1 @@ +{"name":"@installdoc/ansible-gas-station","preserveSymlinks":false,"version":"0.0.1","lockfileVersion":1,"dependencies":{"@ampproject/remapping":{"version":"2.1.2","dev":true,"integrity":"sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==","requires":{"@jridgewell/trace-mapping":"0.3.4"}},"@babel/code-frame":{"version":"7.16.7","dev":true,"integrity":"sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==","requires":{"@babel/highlight":"7.16.10"}},"@babel/compat-data":{"version":"7.17.0","dev":true,"integrity":"sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng=="},"@babel/core":{"version":"7.17.5","dev":true,"integrity":"sha512-/BBMw4EvjmyquN5O+t5eh0+YqB3XXJkYD2cjKpYtWOfFy4lQ4UozNSmxAcWT8r2XtZs0ewG+zrfsqeR15i1ajA==","requires":{"@ampproject/remapping":"2.1.2","@babel/code-frame":"7.16.7","@babel/generator":"7.17.3","@babel/helper-compilation-targets":"7.16.7","@babel/helper-module-transforms":"7.16.7","@babel/helpers":"7.17.2","@babel/parser":"7.17.3","@babel/template":"7.16.7","@babel/traverse":"7.17.3","@babel/types":"7.17.0","convert-source-map":"1.8.0","debug":"4.3.3","gensync":"1.0.0-beta.2","json5":"2.2.0","semver":"6.3.0"}},"@babel/eslint-parser":{"version":"7.17.0","dev":true,"integrity":"sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==","requires":{"@babel/core":"7.17.5","eslint":"8.9.0","eslint-scope":"5.1.1","eslint-visitor-keys":"2.1.0","semver":"6.3.0"}},"@babel/generator":{"version":"7.17.3","dev":true,"integrity":"sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==","requires":{"@babel/types":"7.17.0","jsesc":"2.5.2","source-map":"0.5.7"}},"@babel/helper-compilation-targets":{"version":"7.16.7","dev":true,"integrity":"sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==","requires":{"@babel/compat-data":"7.17.0","@babel/core":"7.17.5","@babel/helper-validator-option":"7.16.7","browserslist":"4.19.2","semver":"6.3.0"}},"@babel/helper-environment-visitor":{"version":"7.16.7","dev":true,"integrity":"sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-function-name":{"version":"7.16.7","dev":true,"integrity":"sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA==","requires":{"@babel/helper-get-function-arity":"7.16.7","@babel/template":"7.16.7","@babel/types":"7.17.0"}},"@babel/helper-get-function-arity":{"version":"7.16.7","dev":true,"integrity":"sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-hoist-variables":{"version":"7.16.7","dev":true,"integrity":"sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-module-imports":{"version":"7.16.7","dev":true,"integrity":"sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-module-transforms":{"version":"7.16.7","dev":true,"integrity":"sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==","requires":{"@babel/helper-environment-visitor":"7.16.7","@babel/helper-module-imports":"7.16.7","@babel/helper-simple-access":"7.16.7","@babel/helper-split-export-declaration":"7.16.7","@babel/helper-validator-identifier":"7.16.7","@babel/template":"7.16.7","@babel/traverse":"7.17.3","@babel/types":"7.17.0"}},"@babel/helper-simple-access":{"version":"7.16.7","dev":true,"integrity":"sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-split-export-declaration":{"version":"7.16.7","dev":true,"integrity":"sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==","requires":{"@babel/types":"7.17.0"}},"@babel/helper-validator-identifier":{"version":"7.16.7","dev":true,"integrity":"sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="},"@babel/helper-validator-option":{"version":"7.16.7","dev":true,"integrity":"sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ=="},"@babel/helpers":{"version":"7.17.2","dev":true,"integrity":"sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==","requires":{"@babel/template":"7.16.7","@babel/traverse":"7.17.3","@babel/types":"7.17.0"}},"@babel/highlight":{"version":"7.16.10","dev":true,"integrity":"sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==","requires":{"@babel/helper-validator-identifier":"7.16.7","chalk":"2.4.2","js-tokens":"4.0.0"}},"@babel/parser":{"version":"7.17.3","dev":true,"integrity":"sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA=="},"@babel/runtime":{"version":"7.17.2","dev":true,"integrity":"sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==","requires":{"regenerator-runtime":"0.13.9"}},"@babel/runtime-corejs3":{"version":"7.17.2","dev":true,"integrity":"sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==","requires":{"core-js-pure":"3.21.1","regenerator-runtime":"0.13.9"}},"@babel/template":{"version":"7.16.7","dev":true,"integrity":"sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==","requires":{"@babel/code-frame":"7.16.7","@babel/parser":"7.17.3","@babel/types":"7.17.0"}},"@babel/traverse":{"version":"7.17.3","dev":true,"integrity":"sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==","requires":{"@babel/code-frame":"7.16.7","@babel/generator":"7.17.3","@babel/helper-environment-visitor":"7.16.7","@babel/helper-function-name":"7.16.7","@babel/helper-hoist-variables":"7.16.7","@babel/helper-split-export-declaration":"7.16.7","@babel/parser":"7.17.3","@babel/types":"7.17.0","debug":"4.3.3","globals":"11.12.0"},"dependencies":{"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}}}},"@babel/types":{"version":"7.17.0","dev":true,"integrity":"sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==","requires":{"@babel/helper-validator-identifier":"7.16.7","to-fast-properties":"2.0.0"}},"@commitlint/cli":{"version":"15.0.0","dev":true,"integrity":"sha512-Y5xmDCweytqzo4N4lOI2YRiuX35xTjcs8n5hUceBH8eyK0YbwtgWX50BJOH2XbkwEmII9blNhlBog6AdQsqicg==","requires":{"@commitlint/format":"15.0.0","@commitlint/lint":"15.0.0","@commitlint/load":"15.0.0","@commitlint/read":"15.0.0","@commitlint/types":"15.0.0","lodash":"4.17.21","resolve-from":"5.0.0","resolve-global":"1.0.0","yargs":"17.3.1"}},"@commitlint/config-conventional":{"version":"16.2.1","dev":true,"integrity":"sha512-cP9gArx7gnaj4IqmtCIcHdRjTYdRUi6lmGE+lOzGGjGe45qGOS8nyQQNvkNy2Ey2VqoSWuXXkD8zCUh6EHf1Ww==","requires":{"conventional-changelog-conventionalcommits":"4.6.3"}},"@commitlint/ensure":{"version":"15.0.0","dev":true,"integrity":"sha512-7DV4iNIald3vycwaWBNGk5FbonaNzOlU8nBe5m5AgU2dIeNKuXwLm+zzJzG27j0Ho56rgz//3F6RIvmsoxY9ZA==","requires":{"@commitlint/types":"15.0.0","lodash":"4.17.21"}},"@commitlint/execute-rule":{"version":"15.0.0","dev":true,"integrity":"sha512-pyE4ApxjbWhb1TXz5vRiGwI2ssdMMgZbaaheZq1/7WC0xRnqnIhE1yUC1D2q20qPtvkZPstTYvMiRVtF+DvjUg=="},"@commitlint/format":{"version":"15.0.0","dev":true,"integrity":"sha512-bPhAfqwRhPk92WiuY0ktEJNpRRHSCd+Eg1MdhGyL9Bl3U25E5zvuInA+dNctnzZiOBSH/37ZaD0eOKCpQE6acg==","requires":{"@commitlint/types":"15.0.0","chalk":"4.1.2"}},"@commitlint/is-ignored":{"version":"15.0.0","dev":true,"integrity":"sha512-edtnkf2QZ/7e/YCJDgn1WDw9wfF1WfOitW5YEoSOb4SxjJEb/oE87kxNPZ2j8mnDMuunspcMfGHeg6fRlwaEWg==","requires":{"@commitlint/types":"15.0.0","semver":"7.3.5"}},"@commitlint/lint":{"version":"15.0.0","dev":true,"integrity":"sha512-hUi2+Im/2dJ5FBvWnodypTkg+5haCgsDzB0fyMApWLUA1IucYUAqRCQCW5em1Mhk9Crw1pd5YzFNikhIclkqCw==","requires":{"@commitlint/is-ignored":"15.0.0","@commitlint/parse":"15.0.0","@commitlint/rules":"15.0.0","@commitlint/types":"15.0.0"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"@commitlint/load":{"version":"15.0.0","dev":true,"integrity":"sha512-Ak1YPeOhvxmY3ioe0o6m1yLGvUAYb4BdfGgShU8jiTCmU3Mnmms0Xh/kfQz8AybhezCC3AmVTyBLaBZxOHR8kg==","requires":{"@commitlint/execute-rule":"15.0.0","@commitlint/resolve-extends":"15.0.0","@commitlint/types":"15.0.0","@endemolshinegroup/cosmiconfig-typescript-loader":"3.0.2","chalk":"4.1.2","cosmiconfig":"7.0.1","lodash":"4.17.21","resolve-from":"5.0.0","typescript":"4.5.5"},"dependencies":{"resolve-from":{"version":"4.0.0","dev":true,"integrity":"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="}}},"@commitlint/message":{"version":"15.0.0","dev":true,"integrity":"sha512-L8euabzboKavPuDJsdIYAY2wx97LbiGEYsckMo6NmV8pOun50c8hQx6ouXFSAx4pp+mX9yUGmMiVqfrk2LKDJQ=="},"@commitlint/parse":{"version":"15.0.0","dev":true,"integrity":"sha512-7fweM67tZfBNS7zw1KTuuT5K2u9nGytUJqFqT/1Ln3Na9cBCsoAqR47mfsNOTlRCgGwakm4xiQ7BpS2gN0OGuw==","requires":{"@commitlint/types":"15.0.0","conventional-changelog-angular":"5.0.13","conventional-commits-parser":"3.2.4"}},"@commitlint/read":{"version":"15.0.0","dev":true,"integrity":"sha512-5yI1o2HKZFVe7RTjL7IhuhHMKar/MDNY34vEHqqz9gMI7BK/rdP8uVb4Di1efl2V0UPnwID0nPKWESjQ8Ti0gw==","requires":{"@commitlint/top-level":"15.0.0","@commitlint/types":"15.0.0","fs-extra":"10.0.0","git-raw-commits":"2.0.11"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"find-up":{"version":"5.0.0","dev":true,"integrity":"sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==","requires":{"locate-path":"6.0.0","path-exists":"4.0.0"}},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"locate-path":{"version":"6.0.0","dev":true,"integrity":"sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==","requires":{"p-locate":"5.0.0"}},"p-limit":{"version":"3.1.0","dev":true,"integrity":"sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==","requires":{"yocto-queue":"0.1.0"}},"p-locate":{"version":"5.0.0","dev":true,"integrity":"sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==","requires":{"p-limit":"3.1.0"}},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"@commitlint/resolve-extends":{"version":"15.0.0","dev":true,"integrity":"sha512-7apfRJjgJsKja7lHsPfEFixKjA/fk/UeD3owkOw1174yYu4u8xBDLSeU3IinGPdMuF9m245eX8wo7vLUy+EBSg==","requires":{"import-fresh":"3.3.0","lodash":"4.17.21","resolve-from":"5.0.0","resolve-global":"1.0.0"}},"@commitlint/rules":{"version":"15.0.0","dev":true,"integrity":"sha512-SqXfp6QUlwBS+0IZm4FEA/NmmAwcFQIkG3B05BtemOVWXQdZ8j1vV6hDwvA9oMPCmUSrrGpHOtZK7HaHhng2yA==","requires":{"@commitlint/ensure":"15.0.0","@commitlint/message":"15.0.0","@commitlint/to-lines":"15.0.0","@commitlint/types":"15.0.0","execa":"5.1.1"}},"@commitlint/to-lines":{"version":"15.0.0","dev":true,"integrity":"sha512-mY3MNA9ujPqVpiJjTYG9MDsYCobue5PJFO0MfcIzS1mCVvngH8ZFTPAh1fT5t+t1h876boS88+9WgqjRvbYItw=="},"@commitlint/top-level":{"version":"15.0.0","dev":true,"integrity":"sha512-7Gz3t7xcuuUw1d1Nou6YLaztzp2Em+qZ6YdCzrqYc+aquca3Vt0O696nuiBDU/oE+tls4Hx2CNpAbWhTgEwB5A==","requires":{"find-up":"5.0.0"}},"@commitlint/types":{"version":"15.0.0","dev":true,"integrity":"sha512-OMSLX+QJnyNoTwws54ULv9sOvuw9GdVezln76oyUd4YbMMJyaav62aSXDuCdWyL2sm9hTkSzyEi52PNaIj/vqw==","requires":{"chalk":"4.1.2"}},"@cspell/cspell-bundled-dicts":{"version":"5.18.5","dev":true,"integrity":"sha512-jFvwF8bb8HUYqMUPQiGZUHAf8zfriZRagzoCW8w4NLLJB1IZNGlQvQCQskQG9cYtOmKAYHCbOwm8SjA9FKwQow==","requires":{"@cspell/dict-ada":"2.0.0","@cspell/dict-aws":"2.0.0","@cspell/dict-bash":"2.0.1","@cspell/dict-companies":"2.0.2","@cspell/dict-cpp":"2.0.0","@cspell/dict-cryptocurrencies":"2.0.0","@cspell/dict-csharp":"2.0.1","@cspell/dict-css":"2.0.0","@cspell/dict-dart":"1.1.0","@cspell/dict-django":"2.0.0","@cspell/dict-dotnet":"2.0.0","@cspell/dict-elixir":"2.0.0","@cspell/dict-en-gb":"1.1.33","@cspell/dict-en_us":"2.1.7","@cspell/dict-filetypes":"2.0.1","@cspell/dict-fonts":"2.0.0","@cspell/dict-fullstack":"2.0.4","@cspell/dict-golang":"2.0.0","@cspell/dict-haskell":"2.0.0","@cspell/dict-html":"3.0.0","@cspell/dict-html-symbol-entities":"2.0.0","@cspell/dict-java":"2.0.0","@cspell/dict-latex":"2.0.0","@cspell/dict-lorem-ipsum":"2.0.0","@cspell/dict-lua":"2.0.0","@cspell/dict-node":"2.0.0","@cspell/dict-npm":"2.0.1","@cspell/dict-php":"2.0.0","@cspell/dict-powershell":"2.0.0","@cspell/dict-public-licenses":"1.0.4","@cspell/dict-python":"2.0.6","@cspell/dict-r":"1.0.2","@cspell/dict-ruby":"2.0.0","@cspell/dict-rust":"2.0.0","@cspell/dict-scala":"2.0.0","@cspell/dict-software-terms":"2.1.0","@cspell/dict-swift":"1.0.2","@cspell/dict-typescript":"2.0.0","@cspell/dict-vue":"2.0.2"}},"@cspell/cspell-pipe":{"version":"5.18.5","dev":true,"integrity":"sha512-U/4e4Zm7Mm23SuJu6b49+9Do/2aS+c9sPQa1Z9ZZqHQ4BqswJagk5oZ0V45BjYJ/0acHSRpIxbndpVJ01cjf8A=="},"@cspell/cspell-types":{"version":"5.18.5","dev":true,"integrity":"sha512-yvDFCUa1CbjBuMkFCh+yUAAaG6VW5WXoewzLwhMFsMV1GZmkbftOcvZq0YuZviNsjdBViDH0dhKdlzwC953upg=="},"@cspell/dict-ada":{"version":"2.0.0","dev":true,"integrity":"sha512-4gfJEYXVwz6IN2LBaT6QoUV4pqaR35i0z0u9O684vLuVczvNJIHa4vNaSEFBr9d6xxncUyqstgP9P73ajJjh9A=="},"@cspell/dict-aws":{"version":"2.0.0","dev":true,"integrity":"sha512-NKz7pDZ7pwj/b33i3f4WLpC1rOOUMmENwYgftxU+giU2YBeKM2wZbMTSEIzsrel56r0UlQYmdIVlP/B4nnVaoQ=="},"@cspell/dict-bash":{"version":"2.0.1","dev":true,"integrity":"sha512-pBx3T/5w7fPF8XD5cx3NwtRFvNpQYmYqzM043NKP2hDmlx4uFwbH599Lvt5mwCMZKfIoRXaNUQvq7se2gstQjw=="},"@cspell/dict-companies":{"version":"2.0.2","dev":true,"integrity":"sha512-LPKwBMAWRz+p1R8q+TV6E1sGOOTvxJOaJeXNN++CZQ7i6JMn5Rf+BSxagwkeK6z3o9vIC5ZE4AcQ5BMkvyjqGw=="},"@cspell/dict-cpp":{"version":"2.0.0","dev":true,"integrity":"sha512-EflHLs2pHEEXZM6jPfTGR/KHZKQtJlvzqgkg1zaA1YKv5HQNw9Wy5KVPGEV2bjPcFsZJO3xXjO1KBZcoOPjPmA=="},"@cspell/dict-cryptocurrencies":{"version":"2.0.0","dev":true,"integrity":"sha512-nREysmmfOp7L2YCRAUufQahwD5/Punzb5AZ6eyg4zUamdRWHgBFphb5/9h2flt1vgdUfhc6hZcML21Ci7iXjaA=="},"@cspell/dict-csharp":{"version":"2.0.1","dev":true,"integrity":"sha512-ZzAr+WRP2FUtXHZtfhe8f3j9vPjH+5i44Hcr5JqbWxmqciGoTbWBPQXwu9y+J4mbdC69HSWRrVGkNJ8rQk8pSw=="},"@cspell/dict-css":{"version":"2.0.0","dev":true,"integrity":"sha512-MrFyswFHnPh4H0u6IlV4eHy+ZCUrrHzeL161LyTOqCvaKpbZavMgNYXzZqTF9xafO0iLgwKrl+Gkclu1KVBg0Q=="},"@cspell/dict-dart":{"version":"1.1.0","dev":true,"integrity":"sha512-bBqZINm+RVjMgUrAhRzv/xx3jc3dkIqO0higPbsK+63IAtMNY3EiQnEO4eapbU+qAhyvICY9hZQZXy5Ux4p+Pw=="},"@cspell/dict-django":{"version":"2.0.0","dev":true,"integrity":"sha512-GkJdJv6cmzrKcmq2/oxTXjKF5uv71r4eTqnFmgPbNBW1t+G4VYpzOf0QrVQrhx2RC4DdW5XfcTf+iS0FxHOTmw=="},"@cspell/dict-dotnet":{"version":"2.0.0","dev":true,"integrity":"sha512-WOHfjwMuLbo76khDsDa1lJvP/dXcwXVwonWwfUFRt82BL/GtyMalh1HEtCWwKDuK/9f8PCEt/EZMkHT3D5ZV3w=="},"@cspell/dict-elixir":{"version":"2.0.0","dev":true,"integrity":"sha512-NeDObcqiYuqWRrzMAQLZDSrZlChTEZwTA2zHdI2nPtpeDl4FQcTz2BHP8zVt6Lj6G2QHJmNGmQtSmDguX86NYA=="},"@cspell/dict-en-gb":{"version":"1.1.33","dev":true,"integrity":"sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g=="},"@cspell/dict-en_us":{"version":"2.1.7","dev":true,"integrity":"sha512-7IeAHZjXiWSIKFx/3CIlY6misvg2KyJ2KO3tSVSKuAlC3UXHGVOcbcY0kQ95IJeKbB6Ot6aW/Aaw73Nzhuurrg=="},"@cspell/dict-filetypes":{"version":"2.0.1","dev":true,"integrity":"sha512-bQ7K3U/3hKO2lpQjObf0veNP/n50qk5CVezSwApMBckf/sAVvDTR1RGAvYdr+vdQnkdQrk6wYmhbshXi0sLDVg=="},"@cspell/dict-fonts":{"version":"2.0.0","dev":true,"integrity":"sha512-AgkTalphfDPtKFPYmEExDcj8rRCh86xlOSXco8tehOEkYVYbksOk9XH0YVH34RFpy93YBd2nnVGLgyGVwagcPw=="},"@cspell/dict-fullstack":{"version":"2.0.4","dev":true,"integrity":"sha512-+JtYO58QAXnetRN+MGVzI8YbkbFTLpYfl/Cw/tmNqy7U1IDVC4sTXQ2pZvbbeKQWFHBqYvBs0YASV+mTouXYBw=="},"@cspell/dict-golang":{"version":"2.0.0","dev":true,"integrity":"sha512-rUeZJR/S/ZjAsOURtxsAO6xDQhL0IzF458ScahaeOqe0zVL3tx7tCLikCgT92NWPs3BNqmsZGqYSDbn/1KsSIA=="},"@cspell/dict-haskell":{"version":"2.0.0","dev":true,"integrity":"sha512-cjX1Br+gSWqtcmJD/IMHz1UoP3pUaKIIKy/JfhEs7ANtRt6hhfEKe9dl2kQzDkkKt4pXol+YgdYxL/sVc/nLgQ=="},"@cspell/dict-html":{"version":"3.0.0","dev":true,"integrity":"sha512-VzZs/UtyRe4spdaH5SWakik+K3vB2fTyW3kdgGQbzjPGHyb5OXI5fmxQcX0yaSv5RkL0igVROHhu2ARUudoTpw=="},"@cspell/dict-html-symbol-entities":{"version":"2.0.0","dev":true,"integrity":"sha512-71S5wGCe7dq6C+zGDwsEAe5msub/irrLi6SExeG11a/EkpA3RKAEheDGPk0hOY4+vOcIFHaApxOjLTtgQfYWfA=="},"@cspell/dict-java":{"version":"2.0.0","dev":true,"integrity":"sha512-9f5LDATlAiXRGqxLxgqbOLlQxuMW2zcN7tBgxwtN+4u90vM03ZUOR/gKIuDV/y0ZuAiWBIjA73cjk8DJ13Q1eA=="},"@cspell/dict-latex":{"version":"2.0.0","dev":true,"integrity":"sha512-H6RRwbHhQ9ARoO1R57SDqB+q/J5jUDdVnkdfukJkA+HNlJBhCcDuzGOIJqr+GBkJYDkF3obZ3LEOk2lUfT+Eyg=="},"@cspell/dict-lorem-ipsum":{"version":"2.0.0","dev":true,"integrity":"sha512-jKogAKtqvgPMleL6usyj3rZ0m8sVUR6drrD+wMnWSfdx1BmUyTsYiuh/mPEfLAebaYHELWSLQG3rDZRvV9Riqg=="},"@cspell/dict-lua":{"version":"2.0.0","dev":true,"integrity":"sha512-7WUEBEspSKtsq104WdIys1+DLqAxpJPzw74Py1TuE3fI5GvlzeSZkRFP2ya54GB2lCO4C3mq4M8EnitpibVDfw=="},"@cspell/dict-node":{"version":"2.0.0","dev":true,"integrity":"sha512-tPPl3liJORa/l6AoYqh/7rjoM7bdtaIXnIN6ox7CE0flZcBS5rWOB6mzEY3rpu/XJX0pjbBiIoqrolDkVl1RTQ=="},"@cspell/dict-npm":{"version":"2.0.1","dev":true,"integrity":"sha512-LRaJFSQfI0BIbbksPFE6fUjAyRFZRcknfOnYC/5c1wB/vsKH6KsqxTeCWNmHTYrk4KdBLZROhsHJXQIoqVTd4w=="},"@cspell/dict-php":{"version":"2.0.0","dev":true,"integrity":"sha512-29WgU77eTO985LvMHwPi1pcpfopfCWfTdffDyqya0JIfOSaFUrlYKzGPkE4mRxcz2G3hXsaM0SRvBNdIRwEdUg=="},"@cspell/dict-powershell":{"version":"2.0.0","dev":true,"integrity":"sha512-6uvEhLiGmG3u9TFkM1TYcky6aL9Yk7Sk3KJwoTYBaQJY2KqrprgyQtW6yxIw9oU52VRHlq3KKvSAA9Q26+SIkQ=="},"@cspell/dict-public-licenses":{"version":"1.0.4","dev":true,"integrity":"sha512-h4xULfVEDUeWyvp1OO19pcGDqWcBEQ7WGMp3QBHyYpjsamlzsyYYjCRSY2ZvpM7wruDmywSRFmRHJ/+uNFT7nA=="},"@cspell/dict-python":{"version":"2.0.6","dev":true,"integrity":"sha512-54ICgMRiGwavorg8UJC38Fwx8tW8WKj8pimJmFUd0F/ImQ8wmeg4VrmyMach5MZVUaw1qUe2aP5uSyqA15Q0mg=="},"@cspell/dict-r":{"version":"1.0.2","dev":true,"integrity":"sha512-Rp3d4sgD6izW9TW5yVI3D//3HTl9oOGBuzTvXRdoHksVPRvzIu2liVhj8MnQ3XIRe5Kc6IhLBAm6izuV2BpGwQ=="},"@cspell/dict-ruby":{"version":"2.0.0","dev":true,"integrity":"sha512-ux73GEIZrApxIG/BDnpdxWE7r9TY3n+3HFAEp+LDJjSjpwpn2VXopd7GsjwsvmlAv5F3Jch8tzgzujFZkvqdoA=="},"@cspell/dict-rust":{"version":"2.0.0","dev":true,"integrity":"sha512-EWlQivTKXMU3TTcq/Pi6KPKTQADknasQ700UrxRPzxhwQ4sKVZ88GDu6VZJlsbFUz8Vko289KS6wjiox/7WpmQ=="},"@cspell/dict-scala":{"version":"2.0.0","dev":true,"integrity":"sha512-MUwA2YKpqaQOSR4V1/CVGRNk8Ii5kf6I8Ch+4/BhRZRQXuwWbi21rDRYWPqdQWps7VNzAbbMA+PQDWsD5YY38g=="},"@cspell/dict-software-terms":{"version":"2.1.0","dev":true,"integrity":"sha512-R9vfnNqp+cUqILsK3wofGvMrerr6biq+pIY1ayobLf4vUU8Wo4lK+DwRBUd7mHOu1GjXGM/scU54BP19BcYoTw=="},"@cspell/dict-swift":{"version":"1.0.2","dev":true,"integrity":"sha512-IrMcRO7AYB2qU5cj4ttZyEbd04DRNOG6Iha106qGGmn4P096m+Y7lOnSLJx/rZbD/cAT3Z/7i465Lr1J93j7yg=="},"@cspell/dict-typescript":{"version":"2.0.0","dev":true,"integrity":"sha512-WFBahxsnD2y4Os14tE5Zxh31Ggn4DzGOAu3UoxYl1lLLxaszx4RH7LmAeFuznySboiaBeRBbpfJOjQA796O6VQ=="},"@cspell/dict-vue":{"version":"2.0.2","dev":true,"integrity":"sha512-/MB0RS0Gn01s4pgmjy0FvsLfr3RRMrRphEuvTRserNcM8XVtoIVAtrjig/Gg0DPwDrN8Clm0L1j7iQay6S8D0g=="},"@endemolshinegroup/cosmiconfig-typescript-loader":{"version":"3.0.2","dev":true,"integrity":"sha512-QRVtqJuS1mcT56oHpVegkKBlgtWjXw/gHNWO3eL9oyB5Sc7HBoc2OLG/nYpVfT/Jejvo3NUrD0Udk7XgoyDKkA==","requires":{"cosmiconfig":"7.0.1","lodash.get":"4.4.2","make-error":"1.3.6","ts-node":"9.1.1","tslib":"2.3.1"}},"@es-joy/jsdoccomment":{"version":"0.20.1","dev":true,"integrity":"sha512-oeJK41dcdqkvdZy/HctKklJNkt/jh+av3PZARrZEl+fs/8HaHeeYoAvEwOV0u5I6bArTF17JEsTZMY359e/nfQ==","requires":{"comment-parser":"1.3.0","esquery":"1.4.0","jsdoc-type-pratt-parser":"2.2.3"}},"@eslint/eslintrc":{"version":"1.1.0","dev":true,"integrity":"sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg==","requires":{"ajv":"6.12.6","debug":"4.3.3","espree":"9.3.1","globals":"13.12.1","ignore":"4.0.6","import-fresh":"3.3.0","js-yaml":"4.1.0","minimatch":"3.1.2","strip-json-comments":"3.1.1"},"dependencies":{"ignore":{"version":"4.0.6","dev":true,"integrity":"sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg=="}}},"@gitmoji/commit-types":{"version":"1.1.5","dev":true,"integrity":"sha512-8D3FZMRY+gtYpTcHG1SOGmm9CFqxNh6rI9xDoCydxHxnWgqInbdF3nk9gibW5gXA58Hf2cVcJaLEcGOKLRAtmw=="},"@gitmoji/parser-opts":{"version":"1.3.0","dev":true,"integrity":"sha512-NqfoW12eXMdDJz3g3NHnopEgDoqdY1YshO6XxIVPOqotGTDcjD1sL28AVKskbYveGmHBPdHieRRv6M9Iq7dt4w=="},"@humanwhocodes/config-array":{"version":"0.9.3","dev":true,"integrity":"sha512-3xSMlXHh03hCcCmFc0rbKp3Ivt2PFEJnQUJDDMTJQ2wkECZWdq4GePs2ctc5H8zV+cHPaq8k2vU8mrQjA6iHdQ==","requires":{"@humanwhocodes/object-schema":"1.2.1","debug":"4.3.3","minimatch":"3.1.2"}},"@humanwhocodes/object-schema":{"version":"1.2.1","dev":true,"integrity":"sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="},"@jest/types":{"version":"26.6.2","dev":true,"integrity":"sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==","requires":{"@types/istanbul-lib-coverage":"2.0.4","@types/istanbul-reports":"3.0.1","@types/node":"17.0.18","@types/yargs":"15.0.14","chalk":"4.1.2"}},"@jridgewell/resolve-uri":{"version":"3.0.5","dev":true,"integrity":"sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew=="},"@jridgewell/sourcemap-codec":{"version":"1.4.11","dev":true,"integrity":"sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg=="},"@jridgewell/trace-mapping":{"version":"0.3.4","dev":true,"integrity":"sha512-vFv9ttIedivx0ux3QSjhgtCVjPZd5l46ZOMDSCwnH1yUO2e964gO8LZGyv2QkqcgR6TnBU1v+1IFqmeoG+0UJQ==","requires":{"@jridgewell/resolve-uri":"3.0.5","@jridgewell/sourcemap-codec":"1.4.11"}},"@kwsites/file-exists":{"version":"1.1.1","dev":true,"integrity":"sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==","requires":{"debug":"4.3.3"}},"@kwsites/promise-deferred":{"version":"1.1.1","dev":true,"integrity":"sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="},"@microsoft/tsdoc":{"version":"0.13.2","dev":true,"integrity":"sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg=="},"@microsoft/tsdoc-config":{"version":"0.15.2","dev":true,"integrity":"sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==","requires":{"@microsoft/tsdoc":"0.13.2","ajv":"6.12.6","jju":"1.4.0","resolve":"1.19.0"}},"@nodelib/fs.scandir":{"version":"2.1.5","dev":true,"integrity":"sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==","requires":{"@nodelib/fs.stat":"2.0.5","run-parallel":"1.2.0"}},"@nodelib/fs.stat":{"version":"2.0.5","dev":true,"integrity":"sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="},"@nodelib/fs.walk":{"version":"1.2.8","dev":true,"integrity":"sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==","requires":{"@nodelib/fs.scandir":"2.1.5","fastq":"1.13.0"}},"@octokit/auth-token":{"version":"2.5.0","dev":true,"integrity":"sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==","requires":{"@octokit/types":"6.34.0"}},"@octokit/core":{"version":"3.5.1","dev":true,"integrity":"sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==","requires":{"@octokit/auth-token":"2.5.0","@octokit/graphql":"4.8.0","@octokit/request":"5.6.3","@octokit/request-error":"2.1.0","@octokit/types":"6.34.0","before-after-hook":"2.2.2","universal-user-agent":"6.0.0"}},"@octokit/endpoint":{"version":"6.0.12","dev":true,"integrity":"sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==","requires":{"@octokit/types":"6.34.0","is-plain-object":"5.0.0","universal-user-agent":"6.0.0"}},"@octokit/graphql":{"version":"4.8.0","dev":true,"integrity":"sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==","requires":{"@octokit/request":"5.6.3","@octokit/types":"6.34.0","universal-user-agent":"6.0.0"}},"@octokit/openapi-types":{"version":"11.2.0","dev":true,"integrity":"sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA=="},"@octokit/plugin-paginate-rest":{"version":"2.17.0","dev":true,"integrity":"sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==","requires":{"@octokit/core":"3.5.1","@octokit/types":"6.34.0"}},"@octokit/plugin-request-log":{"version":"1.0.4","dev":true,"integrity":"sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==","requires":{"@octokit/core":"3.5.1"}},"@octokit/plugin-rest-endpoint-methods":{"version":"5.13.0","dev":true,"integrity":"sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==","requires":{"@octokit/core":"3.5.1","@octokit/types":"6.34.0","deprecation":"2.3.1"}},"@octokit/request":{"version":"5.6.3","dev":true,"integrity":"sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==","requires":{"@octokit/endpoint":"6.0.12","@octokit/request-error":"2.1.0","@octokit/types":"6.34.0","is-plain-object":"5.0.0","node-fetch":"2.6.7","universal-user-agent":"6.0.0"}},"@octokit/request-error":{"version":"2.1.0","dev":true,"integrity":"sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==","requires":{"@octokit/types":"6.34.0","deprecation":"2.3.1","once":"1.4.0"}},"@octokit/rest":{"version":"18.12.0","dev":true,"integrity":"sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==","requires":{"@octokit/core":"3.5.1","@octokit/plugin-paginate-rest":"2.17.0","@octokit/plugin-request-log":"1.0.4","@octokit/plugin-rest-endpoint-methods":"5.13.0"}},"@octokit/types":{"version":"6.34.0","dev":true,"integrity":"sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==","requires":{"@octokit/openapi-types":"11.2.0"}},"@phenomnomnominal/tsquery":{"version":"4.2.0","dev":true,"integrity":"sha512-hR2U3uVcrrdkuG30ItQ+uFDs4ncZAybxWG0OjTE8ptPzVoU7GVeXpy+vMU8zX9EbmjGeITPw/su5HjYQyAH8bA==","requires":{"esquery":"1.4.0","typescript":"4.5.5"}},"@prettier/plugin-php":{"version":"0.17.6","dev":true,"integrity":"sha512-Nxkq+Gl8bGbutRV7e3/p5d+Bcftn75oH61RT8xzk44T5ET7fVP50pUdaOdvt704GSNr6wwmfBW8MhBz5IKt+fA==","requires":{"linguist-languages":"7.15.0","mem":"8.1.1","php-parser":"3.0.2","prettier":"2.5.1"}},"@prettier/plugin-pug":{"version":"1.19.2","dev":true,"integrity":"sha512-Izo+m3pb15Ym7t3J+u22kun7bQXZOj+lAqkIc5ifxwd9En9OqcCShZz7eABORmzmY276IjSfY92Ijd23nLR68Q==","requires":{"prettier":"2.5.1","pug-lexer":"5.0.1"}},"@prettier/plugin-ruby":{"version":"2.0.0","dev":true,"integrity":"sha512-pA0rjTi5J7cT86XPNhXp7CpdV2Tlyj5oqDIsnQRxMu2P7LY2KJI/pyOSM8pzTH8BgRyQfe1P1NPCcTwxUnUWtQ==","requires":{"prettier":"2.5.1"}},"@prettier/plugin-xml":{"version":"1.2.0","dev":true,"integrity":"sha512-bFvVAZKs59XNmntYjyefn3K4TBykS6E+d6ZW8IcylAs88ZO+TzLhp0dPpi0VKfPzq1Nb+kpDnPRTiwb4zY6NgA==","requires":{"@xml-tools/parser":"1.0.11","prettier":"2.5.1"}},"@semantic-release/changelog":{"version":"6.0.1","dev":true,"integrity":"sha512-FT+tAGdWHr0RCM3EpWegWnvXJ05LQtBkQUaQRIExONoXjVjLuOILNm4DEKNaV+GAQyJjbLRVs57ti//GypH6PA==","requires":{"@semantic-release/error":"3.0.0","aggregate-error":"3.1.0","fs-extra":"9.1.0","lodash":"4.17.21","semantic-release":"19.0.2"}},"@semantic-release/commit-analyzer":{"version":"9.0.2","dev":true,"integrity":"sha512-E+dr6L+xIHZkX4zNMe6Rnwg4YQrWNXK+rNsvwOPpdFppvZO1olE2fIgWhv89TkQErygevbjsZFSIxp+u6w2e5g==","requires":{"conventional-changelog-angular":"5.0.13","conventional-commits-filter":"2.0.7","conventional-commits-parser":"3.2.4","debug":"4.3.3","import-from":"4.0.0","lodash":"4.17.21","micromatch":"4.0.4","semantic-release":"19.0.2"}},"@semantic-release/error":{"version":"3.0.0","dev":true,"integrity":"sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw=="},"@semantic-release/exec":{"version":"6.0.3","dev":true,"integrity":"sha512-bxAq8vLOw76aV89vxxICecEa8jfaWwYITw6X74zzlO0mc/Bgieqx9kBRz9z96pHectiTAtsCwsQcUyLYWnp3VQ==","requires":{"@semantic-release/error":"3.0.0","aggregate-error":"3.1.0","debug":"4.3.3","execa":"5.1.1","lodash":"4.17.21","parse-json":"5.2.0","semantic-release":"19.0.2"}},"@semantic-release/git":{"version":"10.0.1","dev":true,"integrity":"sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==","requires":{"@semantic-release/error":"3.0.0","aggregate-error":"3.1.0","debug":"4.3.3","dir-glob":"3.0.1","execa":"5.1.1","lodash":"4.17.21","micromatch":"4.0.4","p-reduce":"2.1.0","semantic-release":"19.0.2"}},"@semantic-release/github":{"version":"8.0.2","dev":true,"integrity":"sha512-wIbfhOeuxlYzMTjtSAa2xgr54n7ZuPAS2gadyTWBpUt2PNAPgla7A6XxCXJnaKPgfVF0iFfSk3B+KlVKk6ByVg==","requires":{"@octokit/rest":"18.12.0","@semantic-release/error":"2.2.0","aggregate-error":"3.1.0","bottleneck":"2.19.5","debug":"4.3.3","dir-glob":"3.0.1","fs-extra":"10.0.0","globby":"11.1.0","http-proxy-agent":"5.0.0","https-proxy-agent":"5.0.0","issue-parser":"6.0.0","lodash":"4.17.21","mime":"3.0.0","p-filter":"2.1.0","p-retry":"4.6.1","semantic-release":"19.0.2","url-join":"4.0.1"},"dependencies":{"@semantic-release/error":{"version":"2.2.0","dev":true,"integrity":"sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg=="}}},"@semantic-release/gitlab":{"version":"7.0.4","dev":true,"integrity":"sha512-TL6kT526+ir/uehMFdTlJNXUj+p+SjPAYUkit6lh5Rs8kxeHQ01bgmpYLQlc94ZDpy9x2Tzcb/NRwKojkmLG4A==","requires":{"@semantic-release/error":"3.0.0","aggregate-error":"3.1.0","debug":"4.3.3","dir-glob":"3.0.1","escape-string-regexp":"3.0.0","form-data":"4.0.0","fs-extra":"10.0.0","globby":"11.1.0","got":"11.8.3","lodash":"4.17.21","parse-path":"4.0.3","semantic-release":"19.0.2","url-join":"4.0.1"},"dependencies":{"escape-string-regexp":{"version":"3.0.0","dev":true,"integrity":"sha512-11dXIUC3umvzEViLP117d0KN6LJzZxh5+9F4E/7WLAAw7GrHk8NpUR+g9iJi/pe9C0py4F8rs0hreyRCwlAuZg=="},"get-stream":{"version":"5.2.0","dev":true,"integrity":"sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==","requires":{"pump":"3.0.0"}},"quick-lru":{"version":"5.1.1","dev":true,"integrity":"sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="}}},"@semantic-release/npm":{"version":"9.0.0","dev":true,"integrity":"sha512-hj2jqayS2SPUmFtCMCOQMX975uMDfRoymj1HvMSwYdaoI6hVZvhrTFPBgJeM85O0C+G3IFviAUar5gel/1VGDQ==","requires":{"@semantic-release/error":"3.0.0","aggregate-error":"3.1.0","execa":"5.1.1","fs-extra":"10.0.0","lodash":"4.17.21","nerf-dart":"1.0.0","normalize-url":"6.1.0","npm":"8.5.1","rc":"1.2.8","read-pkg":"5.2.0","registry-auth-token":"4.2.1","semantic-release":"19.0.2","semver":"7.3.5","tempy":"1.0.1"},"dependencies":{"hosted-git-info":{"version":"2.8.9","dev":true,"integrity":"sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="},"normalize-package-data":{"version":"2.5.0","dev":true,"integrity":"sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==","requires":{"hosted-git-info":"2.8.9","resolve":"1.22.0","semver":"5.7.1","validate-npm-package-license":"3.0.4"}},"type-fest":{"version":"0.6.0","dev":true,"integrity":"sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="}}},"@semantic-release/release-notes-generator":{"version":"10.0.3","dev":true,"integrity":"sha512-k4x4VhIKneOWoBGHkx0qZogNjCldLPRiAjnIpMnlUh6PtaWXp/T+C9U7/TaNDDtgDa5HMbHl4WlREdxHio6/3w==","requires":{"conventional-changelog-angular":"5.0.13","conventional-changelog-writer":"5.0.1","conventional-commits-filter":"2.0.7","conventional-commits-parser":"3.2.4","debug":"4.3.3","get-stream":"6.0.1","import-from":"4.0.0","into-stream":"6.0.0","lodash":"4.17.21","read-pkg-up":"7.0.1","semantic-release":"19.0.2"},"dependencies":{"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"@sindresorhus/is":{"version":"4.4.0","dev":true,"integrity":"sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ=="},"@solidity-parser/parser":{"version":"0.14.1","dev":true,"integrity":"sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw==","requires":{"antlr4ts":"0.5.0-alpha.4"}},"@szmarczak/http-timer":{"version":"4.0.6","dev":true,"integrity":"sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==","requires":{"defer-to-connect":"2.0.1"}},"@testing-library/dom":{"version":"7.31.2","dev":true,"integrity":"sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==","requires":{"@babel/code-frame":"7.16.7","@babel/runtime":"7.17.2","@types/aria-query":"4.2.2","aria-query":"4.2.2","chalk":"4.1.2","dom-accessibility-api":"0.5.11","lz-string":"1.4.4","pretty-format":"26.6.2"}},"@tokenizer/token":{"version":"0.3.0","dev":true,"integrity":"sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="},"@tootallnate/once":{"version":"2.0.0","dev":true,"integrity":"sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="},"@types/aria-query":{"version":"4.2.2","dev":true,"integrity":"sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig=="},"@types/cacheable-request":{"version":"6.0.2","dev":true,"integrity":"sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==","requires":{"@types/http-cache-semantics":"4.0.1","@types/keyv":"3.1.3","@types/node":"17.0.18","@types/responselike":"1.0.0"}},"@types/eslint":{"version":"7.29.0","dev":true,"integrity":"sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==","requires":{"@types/estree":"0.0.51","@types/json-schema":"7.0.9"}},"@types/estree":{"version":"0.0.51","dev":true,"integrity":"sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ=="},"@types/http-cache-semantics":{"version":"4.0.1","dev":true,"integrity":"sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="},"@types/istanbul-lib-coverage":{"version":"2.0.4","dev":true,"integrity":"sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g=="},"@types/istanbul-lib-report":{"version":"3.0.0","dev":true,"integrity":"sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==","requires":{"@types/istanbul-lib-coverage":"2.0.4"}},"@types/istanbul-reports":{"version":"3.0.1","dev":true,"integrity":"sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==","requires":{"@types/istanbul-lib-report":"3.0.0"}},"@types/json-schema":{"version":"7.0.9","dev":true,"integrity":"sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ=="},"@types/json5":{"version":"0.0.29","dev":true,"integrity":"sha1-7ihweulOEdK4J7y+UnC86n8+ce4="},"@types/keyv":{"version":"3.1.3","dev":true,"integrity":"sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==","requires":{"@types/node":"17.0.18"}},"@types/mdast":{"version":"3.0.10","dev":true,"integrity":"sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==","requires":{"@types/unist":"2.0.6"}},"@types/minimist":{"version":"1.2.2","dev":true,"integrity":"sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ=="},"@types/node":{"version":"17.0.18","dev":true,"integrity":"sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA=="},"@types/normalize-package-data":{"version":"2.4.1","dev":true,"integrity":"sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw=="},"@types/parse-json":{"version":"4.0.0","dev":true,"integrity":"sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA=="},"@types/responselike":{"version":"1.0.0","dev":true,"integrity":"sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==","requires":{"@types/node":"17.0.18"}},"@types/retry":{"version":"0.12.1","dev":true,"integrity":"sha512-xoDlM2S4ortawSWORYqsdU+2rxdh4LRW9ytc3zmT37RIKQh6IHyKwwtKhKis9ah8ol07DCkZxPt8BBvPjC6v4g=="},"@types/unist":{"version":"2.0.6","dev":true,"integrity":"sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ=="},"@types/yargs":{"version":"17.0.8","dev":true,"integrity":"sha512-wDeUwiUmem9FzsyysEwRukaEdDNcwbROvQ9QGRKaLI6t+IltNzbn4/i4asmB10auvZGQCzSQ6t0GSczEThlUXw==","requires":{"@types/yargs-parser":"20.2.1"}},"@types/yargs-parser":{"version":"20.2.1","dev":true,"integrity":"sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="},"@typescript-eslint/eslint-plugin":{"version":"5.12.0","dev":true,"integrity":"sha512-fwCMkDimwHVeIOKeBHiZhRUfJXU8n6xW1FL9diDxAyGAFvKcH4csy0v7twivOQdQdA0KC8TDr7GGRd3L4Lv0rQ==","requires":{"@typescript-eslint/parser":"5.12.0","@typescript-eslint/scope-manager":"5.12.0","@typescript-eslint/type-utils":"5.12.0","@typescript-eslint/utils":"5.12.0","debug":"4.3.3","eslint":"8.9.0","functional-red-black-tree":"1.0.1","ignore":"5.2.0","regexpp":"3.2.0","semver":"7.3.5","tsutils":"3.21.0","typescript":"4.5.5"},"dependencies":{"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"@typescript-eslint/experimental-utils":{"version":"5.12.0","dev":true,"integrity":"sha512-iFVADWH2CmiDF+E9kFK2r474BO2JILDKw1NVD5ytqHrM3ezsfdu5uo6B+77DH0suM7iUC/yOayHNziuiI9BPbQ==","requires":{"@typescript-eslint/utils":"5.12.0","eslint":"8.9.0"}},"@typescript-eslint/parser":{"version":"5.12.0","dev":true,"integrity":"sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog==","requires":{"@typescript-eslint/scope-manager":"5.12.0","@typescript-eslint/types":"5.12.0","@typescript-eslint/typescript-estree":"5.12.0","debug":"4.3.3","eslint":"8.9.0","typescript":"4.5.5"},"dependencies":{"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"@typescript-eslint/scope-manager":{"version":"5.12.0","dev":true,"integrity":"sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ==","requires":{"@typescript-eslint/types":"5.12.0","@typescript-eslint/visitor-keys":"5.12.0"}},"@typescript-eslint/type-utils":{"version":"5.12.0","dev":true,"integrity":"sha512-9j9rli3zEBV+ae7rlbBOotJcI6zfc6SHFMdKI9M3Nc0sy458LJ79Os+TPWeBBL96J9/e36rdJOfCuyRSgFAA0Q==","requires":{"@typescript-eslint/utils":"5.12.0","debug":"4.3.3","eslint":"8.9.0","tsutils":"3.21.0","typescript":"4.5.5"}},"@typescript-eslint/types":{"version":"5.12.0","dev":true,"integrity":"sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ=="},"@typescript-eslint/typescript-estree":{"version":"5.12.0","dev":true,"integrity":"sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ==","requires":{"@typescript-eslint/types":"5.12.0","@typescript-eslint/visitor-keys":"5.12.0","debug":"4.3.3","globby":"11.1.0","is-glob":"4.0.3","semver":"7.3.5","tsutils":"3.21.0","typescript":"4.5.5"}},"@typescript-eslint/utils":{"version":"5.12.0","dev":true,"integrity":"sha512-k4J2WovnMPGI4PzKgDtQdNrCnmBHpMUFy21qjX2CoPdoBcSBIMvVBr9P2YDP8jOqZOeK3ThOL6VO/sy6jtnvzw==","requires":{"@types/json-schema":"7.0.9","@typescript-eslint/scope-manager":"5.12.0","@typescript-eslint/types":"5.12.0","@typescript-eslint/typescript-estree":"5.12.0","eslint":"8.9.0","eslint-scope":"5.1.1","eslint-utils":"3.0.0"}},"@typescript-eslint/visitor-keys":{"version":"5.12.0","dev":true,"integrity":"sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg==","requires":{"@typescript-eslint/types":"5.12.0","eslint-visitor-keys":"3.3.0"}},"@washingtondc/development":{"version":"1.0.10","dev":true,"integrity":"sha512-rO/ROg5jBNjx3HQIqsVDBWwOFD9sa6UwhsUphfwTcPCJLQ+bu0NFb3yGOpHvmbpU9AWtFS6ZcG2UHRvdPed01Q==","requires":{"@commitlint/cli":"15.0.0","@commitlint/config-conventional":"15.0.0","commitizen":"4.2.4","cspell":"5.13.1","cz-emoji-conventional":"1.0.1","git-notify":"0.2.3","husky":"7.0.4","leasot":"12.0.0","lint-staged":"12.1.2","liquidjs":"9.28.5","only-allow":"1.0.0","open-cli":"7.0.1","shellcheck":"1.0.0","typescript":"4.5.5","yarnhook":"0.5.1"},"dependencies":{"@commitlint/config-conventional":{"version":"15.0.0","dev":true,"integrity":"sha512-eZBRL8Lk3hMNHp1wUMYj0qrZQEsST1ai7KHR8J1IDD9aHgT7L2giciibuQ+Og7vxVhR5WtYDvh9xirXFVPaSkQ==","requires":{"conventional-changelog-conventionalcommits":"4.6.3"}},"glob":{"version":"7.1.4","dev":true,"integrity":"sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==","requires":{"fs.realpath":"1.0.0","inflight":"1.0.6","inherits":"2.0.4","minimatch":"3.1.2","once":"1.4.0","path-is-absolute":"1.0.1"}}}},"@xml-tools/parser":{"version":"1.0.11","dev":true,"integrity":"sha512-aKqQ077XnR+oQtHJlrAflaZaL7qZsulWc/i/ZEooar5JiWj1eLt0+Wg28cpa+XLney107wXqneC+oG1IZvxkTA==","requires":{"chevrotain":"7.1.1"}},"JSONStream":{"version":"1.3.5","dev":true,"integrity":"sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==","requires":{"jsonparse":"1.3.1","through":"2.3.8"}},"acorn":{"version":"8.7.0","dev":true,"integrity":"sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="},"acorn-jsx":{"version":"5.3.2","dev":true,"integrity":"sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==","requires":{"acorn":"8.7.0"}},"agent-base":{"version":"6.0.2","dev":true,"integrity":"sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==","requires":{"debug":"4.3.3"}},"aggregate-error":{"version":"3.1.0","dev":true,"integrity":"sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==","requires":{"clean-stack":"2.2.0","indent-string":"4.0.0"}},"ajv":{"version":"6.12.6","dev":true,"integrity":"sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==","requires":{"fast-deep-equal":"3.1.3","fast-json-stable-stringify":"2.1.0","json-schema-traverse":"0.4.1","uri-js":"4.4.1"}},"ansi-align":{"version":"3.0.1","dev":true,"integrity":"sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==","requires":{"string-width":"4.2.3"}},"ansi-bgblack":{"version":"0.1.1","dev":true,"integrity":"sha1-poulAHiHcBtqr74/oNrf36juPKI=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgblue":{"version":"0.1.1","dev":true,"integrity":"sha1-Z73ATtybm1J4lp2hlt6j11yMNhM=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgcyan":{"version":"0.1.1","dev":true,"integrity":"sha1-WEiUJWAL3p9VBwaN2Wnr/bUP52g=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bggreen":{"version":"0.1.1","dev":true,"integrity":"sha1-TjGRJIUplD9DIelr8THRwTgWr0k=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgmagenta":{"version":"0.1.1","dev":true,"integrity":"sha1-myhDLAduqpmUGGcqPvvhk5HCx6E=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgred":{"version":"0.1.1","dev":true,"integrity":"sha1-p2+Sg4OCukMpCmwXeEJPmE1vEEE=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgwhite":{"version":"0.1.1","dev":true,"integrity":"sha1-ZQRlE3elim7OzQMxmU5IAljhG6g=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bgyellow":{"version":"0.1.1","dev":true,"integrity":"sha1-w/4usIzUdmSAKeaHTRWgs49h1E8=","requires":{"ansi-wrap":"0.1.0"}},"ansi-black":{"version":"0.1.1","dev":true,"integrity":"sha1-9hheiJNgslRaHsUMC/Bj/EMDJFM=","requires":{"ansi-wrap":"0.1.0"}},"ansi-blue":{"version":"0.1.1","dev":true,"integrity":"sha1-FbgEmQ6S/JyoxUds6PaZd3wh7b8=","requires":{"ansi-wrap":"0.1.0"}},"ansi-bold":{"version":"0.1.1","dev":true,"integrity":"sha1-PmOVCvWswq4uZw5vZ96xFdGl9QU=","requires":{"ansi-wrap":"0.1.0"}},"ansi-colors":{"version":"4.1.1","dev":true,"integrity":"sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA=="},"ansi-cyan":{"version":"0.1.1","dev":true,"integrity":"sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=","requires":{"ansi-wrap":"0.1.0"}},"ansi-dim":{"version":"0.1.1","dev":true,"integrity":"sha1-QN5MYDqoCG2Oeoa4/5mNXDbu/Ww=","requires":{"ansi-wrap":"0.1.0"}},"ansi-escapes":{"version":"3.2.0","dev":true,"integrity":"sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ=="},"ansi-gray":{"version":"0.1.1","dev":true,"integrity":"sha1-KWLPVOyXksSFEKPetSRDaGHvclE=","requires":{"ansi-wrap":"0.1.0"}},"ansi-green":{"version":"0.1.1","dev":true,"integrity":"sha1-il2al55FjVfEDjNYCzc5C44Q0Pc=","requires":{"ansi-wrap":"0.1.0"}},"ansi-grey":{"version":"0.1.1","dev":true,"integrity":"sha1-WdmLasK6GfilF5jphT+6eDOaM8E=","requires":{"ansi-wrap":"0.1.0"}},"ansi-hidden":{"version":"0.1.1","dev":true,"integrity":"sha1-7WpMSY0rt8uyidvyqNHcyFZ/rg8=","requires":{"ansi-wrap":"0.1.0"}},"ansi-inverse":{"version":"0.1.1","dev":true,"integrity":"sha1-tq9Fgm/oJr+1KKbHmIV5Q1XM0mk=","requires":{"ansi-wrap":"0.1.0"}},"ansi-italic":{"version":"0.1.1","dev":true,"integrity":"sha1-EEdDRj9iXBQqA2c5z4XtpoiYbyM=","requires":{"ansi-wrap":"0.1.0"}},"ansi-magenta":{"version":"0.1.1","dev":true,"integrity":"sha1-BjtboW+z8j4c/aKwfAqJ3hHkMK4=","requires":{"ansi-wrap":"0.1.0"}},"ansi-red":{"version":"0.1.1","dev":true,"integrity":"sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=","requires":{"ansi-wrap":"0.1.0"}},"ansi-regex":{"version":"5.0.1","dev":true,"integrity":"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="},"ansi-reset":{"version":"0.1.1","dev":true,"integrity":"sha1-5+cSksPH3c1NYu9KbHwFmAkRw7c=","requires":{"ansi-wrap":"0.1.0"}},"ansi-strikethrough":{"version":"0.1.1","dev":true,"integrity":"sha1-2Eh3FAss/wfRyT685pkE9oiF5Wg=","requires":{"ansi-wrap":"0.1.0"}},"ansi-styles":{"version":"4.3.0","dev":true,"integrity":"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==","requires":{"color-convert":"2.0.1"}},"ansi-underline":{"version":"0.1.1","dev":true,"integrity":"sha1-38kg9Ml7WXfqFi34/7mIMIqqcaQ=","requires":{"ansi-wrap":"0.1.0"}},"ansi-white":{"version":"0.1.1","dev":true,"integrity":"sha1-nHe3wZPF7pkuYBHTbsTJIbRXiUQ=","requires":{"ansi-wrap":"0.1.0"}},"ansi-wrap":{"version":"0.1.0","dev":true,"integrity":"sha1-qCJQ3bABXponyoLoLqYDu/pF768="},"ansi-yellow":{"version":"0.1.1","dev":true,"integrity":"sha1-y5NW8vRscy8OMZnmEClVp32oPB0=","requires":{"ansi-wrap":"0.1.0"}},"ansicolors":{"version":"0.3.2","dev":true,"integrity":"sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk="},"antlr4ts":{"version":"0.5.0-alpha.4","dev":true,"integrity":"sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ=="},"arg":{"version":"4.1.3","dev":true,"integrity":"sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="},"argparse":{"version":"2.0.1","dev":true,"integrity":"sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="},"argv-formatter":{"version":"1.0.0","dev":true,"integrity":"sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk="},"aria-query":{"version":"4.2.2","dev":true,"integrity":"sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==","requires":{"@babel/runtime":"7.17.2","@babel/runtime-corejs3":"7.17.2"}},"arr-diff":{"version":"4.0.0","dev":true,"integrity":"sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA="},"arr-flatten":{"version":"1.1.0","dev":true,"integrity":"sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="},"arr-union":{"version":"3.1.0","dev":true,"integrity":"sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ="},"array-ify":{"version":"1.0.0","dev":true,"integrity":"sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4="},"array-includes":{"version":"3.1.4","dev":true,"integrity":"sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3","es-abstract":"1.19.1","get-intrinsic":"1.1.1","is-string":"1.0.7"}},"array-sort":{"version":"0.1.4","dev":true,"integrity":"sha512-BNcM+RXxndPxiZ2rd76k6nyQLRZr2/B/sdi8pQ+Joafr5AH279L40dfokSUTp8O+AaqYjXWhblBWa2st2nc4fQ==","requires":{"default-compare":"1.0.0","get-value":"2.0.6","kind-of":"5.1.0"},"dependencies":{"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}},"array-timsort":{"version":"1.0.3","dev":true,"integrity":"sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ=="},"array-union":{"version":"2.1.0","dev":true,"integrity":"sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="},"array-unique":{"version":"0.3.2","dev":true,"integrity":"sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg="},"array.prototype.flat":{"version":"1.2.5","dev":true,"integrity":"sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3","es-abstract":"1.19.1"}},"arrify":{"version":"1.0.1","dev":true,"integrity":"sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="},"assign-symbols":{"version":"1.0.0","dev":true,"integrity":"sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c="},"astral-regex":{"version":"2.0.0","dev":true,"integrity":"sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="},"async":{"version":"3.2.3","dev":true,"integrity":"sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g=="},"asynckit":{"version":"0.4.0","dev":true,"integrity":"sha1-x57Zf380y48robyXkLzDZkdLS3k="},"at-least-node":{"version":"1.0.0","dev":true,"integrity":"sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="},"atob":{"version":"2.1.2","dev":true,"integrity":"sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="},"author-regex":{"version":"1.0.0","dev":true,"integrity":"sha1-0IiFvmubv5Q5/gh8dihyRfCoFFA="},"autolinker":{"version":"0.28.1","dev":true,"integrity":"sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=","requires":{"gulp-header":"1.8.12"}},"balanced-match":{"version":"1.0.2","dev":true,"integrity":"sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="},"base":{"version":"0.11.2","dev":true,"integrity":"sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==","requires":{"cache-base":"1.0.1","class-utils":"0.3.6","component-emitter":"1.3.0","define-property":"1.0.0","isobject":"3.0.1","mixin-deep":"1.3.2","pascalcase":"0.1.1"}},"before-after-hook":{"version":"2.2.2","dev":true,"integrity":"sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ=="},"bent":{"version":"7.3.12","dev":true,"integrity":"sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w==","requires":{"bytesish":"0.4.4","caseless":"0.12.0","is-stream":"2.0.1"}},"big-integer":{"version":"1.6.51","dev":true,"integrity":"sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg=="},"binary-search-bounds":{"version":"2.0.5","dev":true,"integrity":"sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA=="},"bottleneck":{"version":"2.19.5","dev":true,"integrity":"sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw=="},"boxen":{"version":"5.1.2","dev":true,"integrity":"sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==","requires":{"ansi-align":"3.0.1","camelcase":"6.3.0","chalk":"4.1.2","cli-boxes":"2.2.1","string-width":"4.2.3","type-fest":"0.20.2","widest-line":"3.1.0","wrap-ansi":"7.0.0"}},"brace-expansion":{"version":"1.1.11","dev":true,"integrity":"sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==","requires":{"balanced-match":"1.0.2","concat-map":"0.0.1"}},"braces":{"version":"3.0.2","dev":true,"integrity":"sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==","requires":{"fill-range":"7.0.1"}},"browserslist":{"version":"4.19.2","dev":true,"integrity":"sha512-97XU1CTZ5TwU9Qy/Taj+RtiI6SQM1WIhZ9osT7EY0oO2aWXGABZT2OZeRL+6PfaQsiiMIjjwIoYFPq4APgspgQ==","requires":{"caniuse-lite":"1.0.30001312","electron-to-chromium":"1.4.71","escalade":"3.1.1","node-releases":"2.0.2","picocolors":"1.0.0"}},"buffer-from":{"version":"1.1.2","dev":true,"integrity":"sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="},"builtin-modules":{"version":"3.2.0","dev":true,"integrity":"sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA=="},"bytesish":{"version":"0.4.4","dev":true,"integrity":"sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ=="},"cache-base":{"version":"1.0.1","dev":true,"integrity":"sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==","requires":{"collection-visit":"1.0.0","component-emitter":"1.3.0","get-value":"2.0.6","has-value":"1.0.0","isobject":"3.0.1","set-value":"2.0.1","to-object-path":"0.3.0","union-value":"1.0.1","unset-value":"1.0.0"},"dependencies":{"isobject":{"version":"2.1.0","dev":true,"integrity":"sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=","requires":{"isarray":"1.0.0"}}}},"cacheable-lookup":{"version":"5.0.4","dev":true,"integrity":"sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="},"cacheable-request":{"version":"7.0.2","dev":true,"integrity":"sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==","requires":{"clone-response":"1.0.2","get-stream":"5.2.0","http-cache-semantics":"4.1.0","keyv":"4.1.1","lowercase-keys":"2.0.0","normalize-url":"6.1.0","responselike":"2.0.0"}},"cachedir":{"version":"2.2.0","dev":true,"integrity":"sha512-VvxA0xhNqIIfg0V9AmJkDg91DaJwryutH5rVEZAhcNi4iJFj9f+QxmAjgK1LT9I8OgToX27fypX6/MeCXVbBjQ=="},"call-bind":{"version":"1.0.2","dev":true,"integrity":"sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==","requires":{"function-bind":"1.1.1","get-intrinsic":"1.1.1"}},"callsites":{"version":"3.1.0","dev":true,"integrity":"sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="},"camelcase":{"version":"5.3.1","dev":true,"integrity":"sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="},"camelcase-keys":{"version":"6.2.2","dev":true,"integrity":"sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==","requires":{"camelcase":"5.3.1","map-obj":"4.3.0","quick-lru":"4.0.1"}},"caniuse-lite":{"version":"1.0.30001312","dev":true,"integrity":"sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ=="},"cardinal":{"version":"2.1.1","dev":true,"integrity":"sha1-fMEFXYItISlU0HsIXeolHMe8VQU=","requires":{"ansicolors":"0.3.2","redeyed":"2.1.1"}},"caseless":{"version":"0.12.0","dev":true,"integrity":"sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="},"chalk":{"version":"4.1.2","dev":true,"integrity":"sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==","requires":{"ansi-styles":"4.3.0","supports-color":"7.2.0"}},"character-entities":{"version":"1.2.4","dev":true,"integrity":"sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw=="},"character-entities-legacy":{"version":"1.1.4","dev":true,"integrity":"sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA=="},"character-parser":{"version":"2.2.0","dev":true,"integrity":"sha1-x84o821LzZdE5f/CxfzeHHMmH8A=","requires":{"is-regex":"1.1.4"}},"character-reference-invalid":{"version":"1.1.4","dev":true,"integrity":"sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg=="},"chardet":{"version":"0.7.0","dev":true,"integrity":"sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="},"chevrotain":{"version":"7.1.1","dev":true,"integrity":"sha512-wy3mC1x4ye+O+QkEinVJkPf5u2vsrDIYW9G7ZuwFl6v/Yu0LwUuT2POsb+NUWApebyxfkQq6+yDfRExbnI5rcw==","requires":{"regexp-to-ast":"0.5.0"}},"ci-info":{"version":"3.3.0","dev":true,"integrity":"sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw=="},"class-utils":{"version":"0.3.6","dev":true,"integrity":"sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==","requires":{"arr-union":"3.1.0","define-property":"0.2.5","isobject":"3.0.1","static-extend":"0.1.2"},"dependencies":{"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}},"clean-regexp":{"version":"1.0.0","dev":true,"integrity":"sha1-jffHquUf02h06PjQW5GAvBGj/tc=","requires":{"escape-string-regexp":"1.0.5"}},"clean-stack":{"version":"2.2.0","dev":true,"integrity":"sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="},"clear-module":{"version":"4.1.2","dev":true,"integrity":"sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==","requires":{"parent-module":"2.0.0","resolve-from":"5.0.0"}},"cli-boxes":{"version":"2.2.1","dev":true,"integrity":"sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw=="},"cli-cursor":{"version":"2.1.0","dev":true,"integrity":"sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=","requires":{"restore-cursor":"2.0.0"}},"cli-table3":{"version":"0.6.1","dev":true,"integrity":"sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA==","requires":{"string-width":"4.2.3"}},"cli-truncate":{"version":"3.1.0","dev":true,"integrity":"sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==","requires":{"slice-ansi":"5.0.0","string-width":"5.1.0"}},"cli-width":{"version":"2.2.1","dev":true,"integrity":"sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw=="},"cliui":{"version":"7.0.4","dev":true,"integrity":"sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==","requires":{"string-width":"4.2.3","strip-ansi":"6.0.1","wrap-ansi":"7.0.0"}},"clone-response":{"version":"1.0.2","dev":true,"integrity":"sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=","requires":{"mimic-response":"1.0.1"}},"collection-visit":{"version":"1.0.0","dev":true,"integrity":"sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=","requires":{"map-visit":"1.0.0","object-visit":"1.0.1"}},"color-convert":{"version":"2.0.1","dev":true,"integrity":"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==","requires":{"color-name":"1.1.4"}},"color-name":{"version":"1.1.4","dev":true,"integrity":"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="},"colorette":{"version":"2.0.16","dev":true,"integrity":"sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g=="},"combined-stream":{"version":"1.0.8","dev":true,"integrity":"sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==","requires":{"delayed-stream":"1.0.0"}},"commander":{"version":"8.3.0","dev":true,"integrity":"sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="},"comment-json":{"version":"4.2.2","dev":true,"integrity":"sha512-H8T+kl3nZesZu41zO2oNXIJWojNeK3mHxCLrsBNu6feksBXsgb+PtYz5daP5P86A0F3sz3840KVYehr04enISQ==","requires":{"array-timsort":"1.0.3","core-util-is":"1.0.3","esprima":"4.0.1","has-own-prop":"2.0.0","repeat-string":"1.6.1"}},"comment-parser":{"version":"1.3.0","dev":true,"integrity":"sha512-hRpmWIKgzd81vn0ydoWoyPoALEOnF4wt8yKD35Ib1D6XC2siLiYaiqfGkYrunuKdsXGwpBpHU3+9r+RVw2NZfA=="},"commitizen":{"version":"4.2.4","dev":true,"integrity":"sha512-LlZChbDzg3Ir3O2S7jSo/cgWp5/QwylQVr59K4xayVq8S4/RdKzSyJkghAiZZHfhh5t4pxunUoyeg0ml1q/7aw==","requires":{"cachedir":"2.2.0","cz-conventional-changelog":"3.2.0","dedent":"0.7.0","detect-indent":"6.0.0","find-node-modules":"2.1.2","find-root":"1.1.0","fs-extra":"8.1.0","glob":"7.1.4","inquirer":"6.5.2","is-utf8":"0.2.1","lodash":"4.17.21","minimist":"1.2.5","strip-bom":"4.0.0","strip-json-comments":"3.0.1"},"dependencies":{"ansi-regex":{"version":"3.0.0","dev":true,"integrity":"sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="},"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"fs-extra":{"version":"8.1.0","dev":true,"integrity":"sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==","requires":{"graceful-fs":"4.2.9","jsonfile":"4.0.0","universalify":"0.1.2"}},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"is-fullwidth-code-point":{"version":"2.0.0","dev":true,"integrity":"sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="},"jsonfile":{"version":"4.0.0","dev":true,"integrity":"sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss="},"mimic-fn":{"version":"1.2.0","dev":true,"integrity":"sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="},"onetime":{"version":"2.0.1","dev":true,"integrity":"sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=","requires":{"mimic-fn":"1.2.0"}},"string-width":{"version":"2.1.1","dev":true,"integrity":"sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==","requires":{"is-fullwidth-code-point":"2.0.0","strip-ansi":"4.0.0"},"dependencies":{"strip-ansi":{"version":"4.0.0","dev":true,"integrity":"sha1-qEeQIusaw2iocTibY1JixQXuNo8=","requires":{"ansi-regex":"3.0.0"}}}},"strip-ansi":{"version":"5.2.0","dev":true,"integrity":"sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==","requires":{"ansi-regex":"4.1.0"},"dependencies":{"ansi-regex":{"version":"4.1.0","dev":true,"integrity":"sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="}}},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="},"universalify":{"version":"0.1.2","dev":true,"integrity":"sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="},"which":{"version":"1.3.1","dev":true,"integrity":"sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==","requires":{"isexe":"2.0.0"}}}},"common-tags":{"version":"1.8.2","dev":true,"integrity":"sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="},"compare-func":{"version":"2.0.0","dev":true,"integrity":"sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==","requires":{"array-ify":"1.0.0","dot-prop":"5.3.0"}},"component-emitter":{"version":"1.3.0","dev":true,"integrity":"sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="},"concat-map":{"version":"0.0.1","dev":true,"integrity":"sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="},"concat-with-sourcemaps":{"version":"1.1.0","dev":true,"integrity":"sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==","requires":{"source-map":"0.6.1"}},"configstore":{"version":"5.0.1","dev":true,"integrity":"sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==","requires":{"dot-prop":"5.3.0","graceful-fs":"4.2.9","make-dir":"3.1.0","unique-string":"2.0.0","write-file-atomic":"3.0.3","xdg-basedir":"4.0.0"}},"conventional-changelog-angular":{"version":"5.0.13","dev":true,"integrity":"sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==","requires":{"compare-func":"2.0.0","q":"1.5.1"}},"conventional-changelog-conventionalcommits":{"version":"4.6.3","dev":true,"integrity":"sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==","requires":{"compare-func":"2.0.0","lodash":"4.17.21","q":"1.5.1"}},"conventional-changelog-emoji-config":{"version":"1.4.8","dev":true,"integrity":"sha512-YssHA4Xcbha2h+tbq25f3apS6AsLSQSQJVPcgic5i09DNfiOnWhxdBIn/GcmoT+TS7+DlzCMufE2j6dMiuYOWg==","requires":{"conventional-changelog-gitmoji-config":"1.4.3","conventional-changelog-writer":"5.0.1","conventional-commits-parser":"3.2.4","cosmiconfig":"7.0.1","git-cz-emoji":"1.1.24","semantic-release":"19.0.2","tslib":"2.3.1"},"dependencies":{"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"conventional-changelog-gitmoji-config":{"version":"1.4.3","dev":true,"integrity":"sha512-8HqbhNFuya85ICZvXv1ck2a2B7KlSVPGEFtK+Jar/qCUuq+E0PiINBJbpYcHOfsEucDRW4W4AUMQH7ap6+0DdQ==","requires":{"@gitmoji/commit-types":"1.1.5","@gitmoji/parser-opts":"1.3.0","cosmiconfig":"7.0.1"}},"conventional-changelog-writer":{"version":"5.0.1","dev":true,"integrity":"sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==","requires":{"conventional-commits-filter":"2.0.7","dateformat":"3.0.3","handlebars":"4.7.7","json-stringify-safe":"5.0.1","lodash":"4.17.21","meow":"8.1.2","semver":"6.3.0","split":"1.0.1","through2":"4.0.2"}},"conventional-commit-types":{"version":"3.0.0","dev":true,"integrity":"sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg=="},"conventional-commits-filter":{"version":"2.0.7","dev":true,"integrity":"sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==","requires":{"lodash.ismatch":"4.4.0","modify-values":"1.0.1"}},"conventional-commits-parser":{"version":"3.2.4","dev":true,"integrity":"sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==","requires":{"JSONStream":"1.3.5","is-text-path":"1.0.1","lodash":"4.17.21","meow":"8.1.2","split2":"3.2.2","through2":"4.0.2"}},"convert-source-map":{"version":"1.8.0","dev":true,"integrity":"sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==","requires":{"safe-buffer":"5.1.2"}},"copy-descriptor":{"version":"0.1.1","dev":true,"integrity":"sha1-Z29us8OZl8LuGsOpJP1hJHSPV40="},"core-js":{"version":"3.21.1","dev":true,"integrity":"sha512-FRq5b/VMrWlrmCzwRrpDYNxyHP9BcAZC+xHJaqTgIE5091ZV1NTmyh0sGOg5XqpnHvR0svdy0sv1gWA1zmhxig=="},"core-js-pure":{"version":"3.21.1","dev":true,"integrity":"sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ=="},"core-util-is":{"version":"1.0.3","dev":true,"integrity":"sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="},"cosmiconfig":{"version":"7.0.1","dev":true,"integrity":"sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==","requires":{"@types/parse-json":"4.0.0","import-fresh":"3.3.0","parse-json":"5.2.0","path-type":"4.0.0","yaml":"1.10.2"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"create-eslint-index":{"version":"1.0.0","dev":true,"integrity":"sha1-2VQ3LYbVeS/NZ+nyt5GxqxYkEbs=","requires":{"lodash.get":"4.4.2"}},"create-frame":{"version":"1.0.0","dev":true,"integrity":"sha1-i5XyaR4ySbYIBEPjPQutn49pdao=","requires":{"define-property":"0.2.5","extend-shallow":"2.0.1","isobject":"3.0.1","lazy-cache":"2.0.2"},"dependencies":{"define-property":{"version":"0.2.5","dev":true,"integrity":"sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=","requires":{"is-descriptor":"0.1.6"}},"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}},"create-require":{"version":"1.1.1","dev":true,"integrity":"sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="},"cross-spawn":{"version":"7.0.3","dev":true,"integrity":"sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==","requires":{"path-key":"3.1.1","shebang-command":"2.0.0","which":"2.0.2"}},"crypto-random-string":{"version":"2.0.0","dev":true,"integrity":"sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="},"cspell":{"version":"5.13.1","dev":true,"integrity":"sha512-uH0JpQEdMmc5peRXCUuF6Vdp8Bn5mD9YdzUb3Gxd/korSHjxf2nW+c6humaSfW2sHGf67fsyKwlde5jT2HHTyw==","requires":{"chalk":"4.1.2","commander":"8.3.0","comment-json":"4.2.2","cspell-gitignore":"5.18.5","cspell-glob":"5.18.5","cspell-lib":"5.18.5","fast-json-stable-stringify":"2.1.0","file-entry-cache":"6.0.1","fs-extra":"10.0.0","get-stdin":"8.0.0","glob":"7.2.0","imurmurhash":"0.1.4","strip-ansi":"6.0.1","vscode-uri":"3.0.3"},"dependencies":{"find-up":{"version":"5.0.0","dev":true,"integrity":"sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==","requires":{"locate-path":"6.0.0","path-exists":"4.0.0"}},"locate-path":{"version":"6.0.0","dev":true,"integrity":"sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==","requires":{"p-locate":"5.0.0"}},"p-limit":{"version":"3.1.0","dev":true,"integrity":"sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==","requires":{"yocto-queue":"0.1.0"}},"p-locate":{"version":"5.0.0","dev":true,"integrity":"sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==","requires":{"p-limit":"3.1.0"}},"parent-module":{"version":"2.0.0","dev":true,"integrity":"sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==","requires":{"callsites":"3.1.0"}},"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"cspell-gitignore":{"version":"5.18.5","dev":true,"integrity":"sha512-YKYFYMswkia0Uc5CMOapLwo8OKRfP+QbqyODTJ7IJACT7KCOSEj7A3B250LR2mWyvThyIUB+2c7+6ePdFCnh2g==","requires":{"cspell-glob":"5.18.5","find-up":"5.0.0"}},"cspell-glob":{"version":"5.18.5","dev":true,"integrity":"sha512-Tr/wMHpJ5zvD4qV4d5is1WJ6OQZSQSjiWoLCQ8pslpltGJhjYXPh3W9A8n4Ghr4AUUJNLKEQyCX+Z1kcA3hgOQ==","requires":{"micromatch":"4.0.4"}},"cspell-io":{"version":"5.18.5","dev":true,"integrity":"sha512-Ar2shXmKtLP935Linv+162xY6SNqIrwLI3rBRXs0/KnD/YdcLJQB0iBgFqvfvg7TcPg+EZOf9Oc6EvTLg2eprg=="},"cspell-lib":{"version":"5.18.5","dev":true,"integrity":"sha512-yrUk3MbRXy/YGNIcLfURDnw4fRiXcbHo9K5B6IhwYfHKc3VM6QgvEQ0ce44uzZ+AEZzWuQ++GbhUih+bSJ87DQ==","requires":{"@cspell/cspell-bundled-dicts":"5.18.5","@cspell/cspell-types":"5.18.5","clear-module":"4.1.2","comment-json":"4.2.2","configstore":"5.0.1","cosmiconfig":"7.0.1","cspell-glob":"5.18.5","cspell-io":"5.18.5","cspell-trie-lib":"5.18.5","fast-equals":"3.0.0","find-up":"5.0.0","fs-extra":"10.0.0","gensequence":"3.1.1","import-fresh":"3.3.0","resolve-from":"5.0.0","resolve-global":"1.0.0","vscode-uri":"3.0.3"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"cspell-trie-lib":{"version":"5.18.5","dev":true,"integrity":"sha512-FifImmkcArPYiE8fLXcbB/yS15QyWwvHw/gpCPEkcuJMJH2gxC+HOE909JnBsyPyjCaX5gHWiIf7ePjdXlWsDg==","requires":{"@cspell/cspell-pipe":"5.18.5","fs-extra":"10.0.0","gensequence":"3.1.1"}},"cz-conventional-changelog":{"version":"3.2.0","dev":true,"integrity":"sha512-yAYxeGpVi27hqIilG1nh4A9Bnx4J3Ov+eXy4koL3drrR+IO9GaWPsKjik20ht608Asqi8TQPf0mczhEeyAtMzg==","requires":{"chalk":"2.4.2","commitizen":"4.2.4","conventional-commit-types":"3.0.0","lodash.map":"4.6.0","longest":"2.0.1","word-wrap":"1.2.3"}},"cz-emoji-conventional":{"version":"1.0.1","dev":true,"integrity":"sha512-jY+jmmbQ9n671gLWSSI34a7efDz1YPfzM3QZC5T+r8CJCCo1/myW8Ik09NV9KQ5hVSc2BBfvcq7e5IsBOZjyjg==","requires":{"chalk":"4.1.2","commitizen":"4.2.4","word-wrap":"1.2.3"},"dependencies":{"ansi-regex":{"version":"3.0.0","dev":true,"integrity":"sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="},"fs-extra":{"version":"8.1.0","dev":true,"integrity":"sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==","requires":{"graceful-fs":"4.2.9","jsonfile":"4.0.0","universalify":"0.1.2"}},"glob":{"version":"7.1.4","dev":true,"integrity":"sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==","requires":{"fs.realpath":"1.0.0","inflight":"1.0.6","inherits":"2.0.4","minimatch":"3.1.2","once":"1.4.0","path-is-absolute":"1.0.1"}},"is-fullwidth-code-point":{"version":"2.0.0","dev":true,"integrity":"sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="},"jsonfile":{"version":"4.0.0","dev":true,"integrity":"sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss="},"mimic-fn":{"version":"1.2.0","dev":true,"integrity":"sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="},"onetime":{"version":"2.0.1","dev":true,"integrity":"sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=","requires":{"mimic-fn":"1.2.0"}},"string-width":{"version":"2.1.1","dev":true,"integrity":"sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==","requires":{"is-fullwidth-code-point":"2.0.0","strip-ansi":"4.0.0"},"dependencies":{"strip-ansi":{"version":"4.0.0","dev":true,"integrity":"sha1-qEeQIusaw2iocTibY1JixQXuNo8=","requires":{"ansi-regex":"3.0.0"}}}},"strip-ansi":{"version":"5.2.0","dev":true,"integrity":"sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==","requires":{"ansi-regex":"4.1.0"},"dependencies":{"ansi-regex":{"version":"4.1.0","dev":true,"integrity":"sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="}}},"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="},"universalify":{"version":"0.1.2","dev":true,"integrity":"sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="},"which":{"version":"1.3.1","dev":true,"integrity":"sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==","requires":{"isexe":"2.0.0"}}}},"dargs":{"version":"7.0.0","dev":true,"integrity":"sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg=="},"date.js":{"version":"0.3.3","dev":true,"integrity":"sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw==","requires":{"debug":"3.1.0"}},"dateformat":{"version":"3.0.3","dev":true,"integrity":"sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q=="},"debug":{"version":"4.3.3","dev":true,"integrity":"sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==","requires":{"ms":"2.1.2"}},"decamelize":{"version":"1.2.0","dev":true,"integrity":"sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="},"decamelize-keys":{"version":"1.1.0","dev":true,"integrity":"sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=","requires":{"decamelize":"1.2.0","map-obj":"1.0.1"},"dependencies":{"map-obj":{"version":"1.0.1","dev":true,"integrity":"sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0="}}},"decode-uri-component":{"version":"0.2.0","dev":true,"integrity":"sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="},"decompress-response":{"version":"6.0.0","dev":true,"integrity":"sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==","requires":{"mimic-response":"3.1.0"},"dependencies":{"mimic-response":{"version":"3.1.0","dev":true,"integrity":"sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="}}},"dedent":{"version":"0.7.0","dev":true,"integrity":"sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw="},"deep-extend":{"version":"0.6.0","dev":true,"integrity":"sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="},"deep-is":{"version":"0.1.4","dev":true,"integrity":"sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="},"deepmerge-ts":{"version":"2.0.1","dev":true,"integrity":"sha512-7xeG0xMleW+gyrtUsdOeR6tCLwkyYDh3koIuvc8TxBcDh3WlaBQiEbFwEzk8clKomJZMhmoyxo7Y9CRrrrLVlg==","requires":{"is-plain-object":"5.0.0"}},"default-compare":{"version":"1.0.0","dev":true,"integrity":"sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==","requires":{"kind-of":"5.1.0"}},"defer-to-connect":{"version":"2.0.1","dev":true,"integrity":"sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="},"define-lazy-prop":{"version":"2.0.0","dev":true,"integrity":"sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="},"define-properties":{"version":"1.1.3","dev":true,"integrity":"sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==","requires":{"object-keys":"1.1.1"}},"define-property":{"version":"1.0.0","dev":true,"integrity":"sha1-dp66rz9KY6rTr56NMEybvnm/sOY=","requires":{"is-descriptor":"1.0.2"},"dependencies":{"is-accessor-descriptor":{"version":"1.0.0","dev":true,"integrity":"sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==","requires":{"kind-of":"6.0.3"}},"is-data-descriptor":{"version":"1.0.0","dev":true,"integrity":"sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==","requires":{"kind-of":"6.0.3"}},"is-descriptor":{"version":"1.0.2","dev":true,"integrity":"sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==","requires":{"is-accessor-descriptor":"1.0.0","is-data-descriptor":"1.0.0","kind-of":"6.0.3"}}}},"del":{"version":"6.0.0","dev":true,"integrity":"sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ==","requires":{"globby":"11.1.0","graceful-fs":"4.2.9","is-glob":"4.0.3","is-path-cwd":"2.2.0","is-path-inside":"3.0.3","p-map":"4.0.0","rimraf":"3.0.2","slash":"3.0.0"}},"delayed-stream":{"version":"1.0.0","dev":true,"integrity":"sha1-3zrhmayt+31ECqrgsp4icrJOxhk="},"deprecation":{"version":"2.3.1","dev":true,"integrity":"sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="},"detect-file":{"version":"1.0.0","dev":true,"integrity":"sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc="},"detect-indent":{"version":"6.0.0","dev":true,"integrity":"sha512-oSyFlqaTHCItVRGK5RmrmjB+CmaMOW7IaNA/kdxqhoa6d17j/5ce9O9eWXmV/KEdRwqpQA+Vqe8a8Bsybu4YnA=="},"detect-newline":{"version":"3.1.0","dev":true,"integrity":"sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA=="},"diff":{"version":"4.0.2","dev":true,"integrity":"sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="},"dir-glob":{"version":"3.0.1","dev":true,"integrity":"sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==","requires":{"path-type":"4.0.0"}},"doctrine":{"version":"3.0.0","dev":true,"integrity":"sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==","requires":{"esutils":"2.0.3"}},"dom-accessibility-api":{"version":"0.5.11","dev":true,"integrity":"sha512-7X6GvzjYf4yTdRKuCVScV+aA9Fvh5r8WzWrXBH9w82ZWB/eYDMGCnazoC/YAqAzUJWHzLOnZqr46K3iEyUhUvw=="},"dom-serializer":{"version":"1.3.2","dev":true,"integrity":"sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==","requires":{"domelementtype":"2.2.0","domhandler":"4.3.0","entities":"2.2.0"}},"domelementtype":{"version":"2.2.0","dev":true,"integrity":"sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="},"domhandler":{"version":"4.3.0","dev":true,"integrity":"sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g==","requires":{"domelementtype":"2.2.0"}},"domutils":{"version":"2.8.0","dev":true,"integrity":"sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==","requires":{"dom-serializer":"1.3.2","domelementtype":"2.2.0","domhandler":"4.3.0"},"dependencies":{"entities":{"version":"2.2.0","dev":true,"integrity":"sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="}}},"dot-prop":{"version":"5.3.0","dev":true,"integrity":"sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==","requires":{"is-obj":"2.0.0"}},"dot-properties":{"version":"1.0.1","dev":true,"integrity":"sha512-cjIHHKlf2dPINJ5Io3lPocWvWmthXn3ztqyHVzUfufRiCiPECb0oiEqEGbEGaunFZtcMvwgUcxP9CTpLG4KCsA=="},"duplexer2":{"version":"0.1.4","dev":true,"integrity":"sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=","requires":{"readable-stream":"2.3.7"}},"eastasianwidth":{"version":"0.2.0","dev":true,"integrity":"sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="},"editorconfig":{"version":"0.15.3","dev":true,"integrity":"sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==","requires":{"commander":"2.20.3","lru-cache":"4.1.5","semver":"5.7.1","sigmund":"1.0.1"}},"electron-to-chromium":{"version":"1.4.71","dev":true,"integrity":"sha512-Hk61vXXKRb2cd3znPE9F+2pLWdIOmP7GjiTj45y6L3W/lO+hSnUSUhq+6lEaERWBdZOHbk2s3YV5c9xVl3boVw=="},"emoji-regex":{"version":"8.0.0","dev":true,"integrity":"sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="},"end-of-stream":{"version":"1.4.4","dev":true,"integrity":"sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==","requires":{"once":"1.4.0"}},"enquirer":{"version":"2.3.6","dev":true,"integrity":"sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==","requires":{"ansi-colors":"4.1.1"}},"ent":{"version":"2.2.0","dev":true,"integrity":"sha1-6WQhkyWiHQX0RGai9obtbOX13R0="},"entities":{"version":"3.0.1","dev":true,"integrity":"sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q=="},"env-ci":{"version":"5.5.0","dev":true,"integrity":"sha512-o0JdWIbOLP+WJKIUt36hz1ImQQFuN92nhsfTkHHap+J8CiI8WgGpH/a9jEGHh4/TU5BUUGjlnKXNoDb57+ne+A==","requires":{"execa":"5.1.1","fromentries":"1.3.2","java-properties":"1.0.2"}},"eol":{"version":"0.9.1","dev":true,"integrity":"sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg=="},"error-ex":{"version":"1.3.2","dev":true,"integrity":"sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==","requires":{"is-arrayish":"0.2.1"}},"error-symbol":{"version":"0.1.0","dev":true,"integrity":"sha1-Ck2uN9YA0VopukU9jvkg8YRDM/Y="},"es-abstract":{"version":"1.19.1","dev":true,"integrity":"sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==","requires":{"call-bind":"1.0.2","es-to-primitive":"1.2.1","function-bind":"1.1.1","get-intrinsic":"1.1.1","get-symbol-description":"1.0.0","has":"1.0.3","has-symbols":"1.0.2","internal-slot":"1.0.3","is-callable":"1.2.4","is-negative-zero":"2.0.2","is-regex":"1.1.4","is-shared-array-buffer":"1.0.1","is-string":"1.0.7","is-weakref":"1.0.2","object-inspect":"1.12.0","object-keys":"1.1.1","object.assign":"4.1.2","string.prototype.trimend":"1.0.4","string.prototype.trimstart":"1.0.4","unbox-primitive":"1.0.1"}},"es-to-primitive":{"version":"1.2.1","dev":true,"integrity":"sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==","requires":{"is-callable":"1.2.4","is-date-object":"1.0.5","is-symbol":"1.0.4"}},"escalade":{"version":"3.1.1","dev":true,"integrity":"sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="},"escape-string-regexp":{"version":"1.0.5","dev":true,"integrity":"sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="},"eslint":{"version":"8.9.0","dev":true,"integrity":"sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q==","requires":{"@eslint/eslintrc":"1.1.0","@humanwhocodes/config-array":"0.9.3","ajv":"6.12.6","chalk":"4.1.2","cross-spawn":"7.0.3","debug":"4.3.3","doctrine":"3.0.0","escape-string-regexp":"4.0.0","eslint-scope":"7.1.1","eslint-utils":"3.0.0","eslint-visitor-keys":"3.3.0","espree":"9.3.1","esquery":"1.4.0","esutils":"2.0.3","fast-deep-equal":"3.1.3","file-entry-cache":"6.0.1","functional-red-black-tree":"1.0.1","glob-parent":"6.0.2","globals":"13.12.1","ignore":"5.2.0","import-fresh":"3.3.0","imurmurhash":"0.1.4","is-glob":"4.0.3","js-yaml":"4.1.0","json-stable-stringify-without-jsonify":"1.0.1","levn":"0.4.1","lodash.merge":"4.6.2","minimatch":"3.1.2","natural-compare":"1.4.0","optionator":"0.9.1","regexpp":"3.2.0","strip-ansi":"6.0.1","strip-json-comments":"3.1.1","text-table":"0.2.0","v8-compile-cache":"2.3.0"},"dependencies":{"eslint-scope":{"version":"7.1.1","dev":true,"integrity":"sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==","requires":{"esrecurse":"4.3.0","estraverse":"5.3.0"}},"estraverse":{"version":"5.3.0","dev":true,"integrity":"sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="},"glob-parent":{"version":"6.0.2","dev":true,"integrity":"sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==","requires":{"is-glob":"4.0.3"}}}},"eslint-ast-utils":{"version":"1.1.0","dev":true,"integrity":"sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA==","requires":{"lodash.get":"4.4.2","lodash.zip":"4.2.0"}},"eslint-config-prettier":{"version":"8.3.0","dev":true,"integrity":"sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==","requires":{"eslint":"8.9.0"}},"eslint-config-strict-mode":{"version":"1.0.41","dev":true,"integrity":"sha512-dodnJvuUF3LEwF+7GJai6PMBB/Qqgd2CYJqwD1TCRkkS72zYZ2vJWkMfpQ/KPUH4kbXRnsiZUAlBOY5/c7ThFA==","requires":{"@typescript-eslint/eslint-plugin":"5.12.0","@typescript-eslint/parser":"5.12.0","eslint":"8.9.0","eslint-config-prettier":"8.3.0","eslint-formatter-git-log":"0.5.3","eslint-formatter-gitlab":"3.0.0","eslint-formatter-pretty":"4.1.0","eslint-formatter-summary":"1.1.0","eslint-plugin-angular":"4.1.0","eslint-plugin-array-func":"3.1.7","eslint-plugin-editorconfig":"3.2.0","eslint-plugin-eslint-comments":"3.2.0","eslint-plugin-etc":"2.0.2","eslint-plugin-ext":"0.1.0","eslint-plugin-filenames":"1.3.2","eslint-plugin-fp":"2.3.0","eslint-plugin-functional":"4.2.0","eslint-plugin-html":"6.2.0","eslint-plugin-import":"2.25.4","eslint-plugin-jest":"25.7.0","eslint-plugin-jest-async":"1.0.3","eslint-plugin-jest-dom":"3.9.4","eslint-plugin-jest-formatting":"3.1.0","eslint-plugin-jsdoc":"37.9.4","eslint-plugin-json-schema-validator":"1.2.49","eslint-plugin-jsonc":"1.7.0","eslint-plugin-no-constructor-bind":"2.0.4","eslint-plugin-no-explicit-type-exports":"0.12.1","eslint-plugin-no-secrets":"0.8.9","eslint-plugin-no-unsanitized":"3.2.0","eslint-plugin-no-use-extend-native":"0.5.0","eslint-plugin-node":"11.1.0","eslint-plugin-optimize-regex":"1.2.1","eslint-plugin-prefer-arrow":"1.2.3","eslint-plugin-prettier":"4.0.0","eslint-plugin-promise":"5.2.0","eslint-plugin-regexp":"1.5.1","eslint-plugin-rxjs":"4.0.4","eslint-plugin-security":"1.4.0","eslint-plugin-sonarjs":"0.10.0","eslint-plugin-sort-class-members":"1.14.1","eslint-plugin-sort-keys-fix":"1.1.2","eslint-plugin-switch-case":"1.1.2","eslint-plugin-toml":"0.2.0","eslint-plugin-tsdoc":"0.2.14","eslint-plugin-typescript-sort-keys":"2.1.0","eslint-plugin-unicorn":"37.0.1","eslint-plugin-unused-imports":"1.1.5","eslint-plugin-woke":"1.0.1","eslint-plugin-yml":"0.10.1","parse-gitignore":"1.0.1","tslib":"2.3.1","yaml":"1.10.2"},"dependencies":{"ansi-escapes":{"version":"4.3.2","dev":true,"integrity":"sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==","requires":{"type-fest":"0.21.3"}},"commander":{"version":"2.20.3","dev":true,"integrity":"sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="},"decamelize":{"version":"5.0.1","dev":true,"integrity":"sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA=="},"escape-string-regexp":{"version":"4.0.0","dev":true,"integrity":"sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="},"find-up":{"version":"2.1.0","dev":true,"integrity":"sha1-RdG35QbHF93UgndaK3eSCjwMV6c=","requires":{"locate-path":"2.0.0"}},"hosted-git-info":{"version":"2.8.9","dev":true,"integrity":"sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="},"locate-path":{"version":"2.0.0","dev":true,"integrity":"sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=","requires":{"p-locate":"2.0.0","path-exists":"3.0.0"}},"normalize-package-data":{"version":"2.5.0","dev":true,"integrity":"sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==","requires":{"hosted-git-info":"2.8.9","resolve":"1.22.0","semver":"5.7.1","validate-npm-package-license":"3.0.4"}},"p-limit":{"version":"1.3.0","dev":true,"integrity":"sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==","requires":{"p-try":"1.0.0"}},"p-locate":{"version":"2.0.0","dev":true,"integrity":"sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=","requires":{"p-limit":"1.3.0"}},"p-try":{"version":"1.0.0","dev":true,"integrity":"sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="},"path-exists":{"version":"3.0.0","dev":true,"integrity":"sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="},"resolve-from":{"version":"4.0.0","dev":true,"integrity":"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="},"source-map":{"version":"0.5.7","dev":true,"integrity":"sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="},"strip-bom":{"version":"3.0.0","dev":true,"integrity":"sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="},"strip-json-comments":{"version":"3.1.1","dev":true,"integrity":"sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="},"type-fest":{"version":"0.20.2","dev":true,"integrity":"sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="},"yargs-parser":{"version":"21.0.0","dev":true,"integrity":"sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA=="}}},"eslint-etc":{"version":"5.1.0","dev":true,"integrity":"sha512-Rmjl01h5smi5cbsFne2xpTuch2xNnwXiX2lbS4HttXUN5FwXKAwG1UEFBVGO1nC091YO/QyVahyfNPJSX2ae+g==","requires":{"@typescript-eslint/experimental-utils":"5.12.0","eslint":"8.9.0","tsutils":"3.21.0","tsutils-etc":"1.4.1","typescript":"4.5.5"}},"eslint-formatter-git-log":{"version":"0.5.3","dev":true,"integrity":"sha512-lwYPyg6fq6IQyNwLHkls1sjIXNWvY6RQx8S8hLTcgC+ldKYHd8Dc0G1qBpbQXoBFBrUmz73ZNyP1lMsAP8A33A==","requires":{"chalk":"2.4.2","eslint":"8.9.0"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"eslint-formatter-gitlab":{"version":"3.0.0","dev":true,"integrity":"sha512-fqZ2G45rgbrHcFunqmwuG5Qo6QAOlxEsR+KdOP08T1Xegw5tJhHh9KFWMSct8q6x8xCMUyYGHypZd342bLUttA==","requires":{"chalk":"4.1.2","eslint":"8.9.0","js-yaml":"4.1.0"}},"eslint-formatter-pretty":{"version":"4.1.0","dev":true,"integrity":"sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ==","requires":{"@types/eslint":"7.29.0","ansi-escapes":"4.3.2","chalk":"4.1.2","eslint-rule-docs":"1.1.231","log-symbols":"4.1.0","plur":"4.0.0","string-width":"4.2.3","supports-hyperlinks":"2.2.0"},"dependencies":{"type-fest":{"version":"0.21.3","dev":true,"integrity":"sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="}}},"eslint-formatter-summary":{"version":"1.1.0","dev":true,"integrity":"sha512-teOh471ZYeEShXhBFb16X87buUjNZa2TMNn3CSf//DIKLNneqbk7u5i9hDDiIaQVvZbBXJHkgYxj8urcNKWbXw==","requires":{"chalk":"4.1.2","core-js":"3.21.1"}},"eslint-import-resolver-node":{"version":"0.3.6","dev":true,"integrity":"sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==","requires":{"debug":"3.2.7","resolve":"1.22.0"},"dependencies":{"debug":{"version":"3.2.7","dev":true,"integrity":"sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==","requires":{"ms":"2.1.3"}},"ms":{"version":"2.1.3","dev":true,"integrity":"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="}}},"eslint-module-utils":{"version":"2.7.3","dev":true,"integrity":"sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==","requires":{"debug":"3.2.7","find-up":"2.1.0"},"dependencies":{"debug":{"version":"3.2.7","dev":true,"integrity":"sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==","requires":{"ms":"2.1.3"}},"ms":{"version":"2.1.3","dev":true,"integrity":"sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="}}},"eslint-plugin-angular":{"version":"4.1.0","dev":true,"integrity":"sha512-dacledMPxVOZA3T0xcYFuvrMCy5dHxg0ZTMWUaHqSBQef3/XLyXJ9s1LNj0NikJ/dYx6OhqlnnNpKmrJhEUB+Q=="},"eslint-plugin-array-func":{"version":"3.1.7","dev":true,"integrity":"sha512-fB5TBICjHSTGToNTbCCgR8zsngpUkoCM31EMh/M/NEAyNg90i5rUuG0dnNNBML2n0BzM0nBE3sPvo2SEWf6jlA==","requires":{"eslint":"8.9.0"}},"eslint-plugin-editorconfig":{"version":"3.2.0","dev":true,"integrity":"sha512-XiUg69+qgv6BekkPCjP8+2DMODzPqtLV5i0Q9FO1v40P62pfodG1vjIihVbw/338hS5W26S+8MTtXaAlrg37QQ==","requires":{"@typescript-eslint/eslint-plugin":"5.12.0","editorconfig":"0.15.3","eslint":"8.9.0","klona":"2.0.5"},"dependencies":{"lru-cache":{"version":"4.1.5","dev":true,"integrity":"sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==","requires":{"pseudomap":"1.0.2","yallist":"2.1.2"}},"semver":{"version":"5.7.1","dev":true,"integrity":"sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="},"yallist":{"version":"2.1.2","dev":true,"integrity":"sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="}}},"eslint-plugin-es":{"version":"3.0.1","dev":true,"integrity":"sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==","requires":{"eslint":"8.9.0","eslint-utils":"2.1.0","regexpp":"3.2.0"}},"eslint-plugin-eslint-comments":{"version":"3.2.0","dev":true,"integrity":"sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==","requires":{"escape-string-regexp":"1.0.5","eslint":"8.9.0","ignore":"5.2.0"}},"eslint-plugin-etc":{"version":"2.0.2","dev":true,"integrity":"sha512-g3b95LCdTCwZA8On9EICYL8m1NMWaiGfmNUd/ftZTeGZDXrwujKXUr+unYzqKjKFo1EbqJ31vt+Dqzrdm/sUcw==","requires":{"@phenomnomnominal/tsquery":"4.2.0","@typescript-eslint/experimental-utils":"5.12.0","eslint":"8.9.0","eslint-etc":"5.1.0","requireindex":"1.2.0","tslib":"2.3.1","tsutils":"3.21.0","typescript":"4.5.5"},"dependencies":{"estraverse":{"version":"5.3.0","dev":true,"integrity":"sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="},"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-ext":{"version":"0.1.0","dev":true,"integrity":"sha512-CbZgte+kC8u6uymkwtgDPHLgA3IRbhermH88o9VXDh4Pa1ds1QIo0ojJc+mvq5zjf3mm4GT/pTTFYZT9nQORyg=="},"eslint-plugin-filenames":{"version":"1.3.2","dev":true,"integrity":"sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==","requires":{"eslint":"8.9.0","lodash.camelcase":"4.3.0","lodash.kebabcase":"4.1.1","lodash.snakecase":"4.1.1","lodash.upperfirst":"4.3.1"}},"eslint-plugin-fp":{"version":"2.3.0","dev":true,"integrity":"sha1-N20qEIcQ6YGYC9w4deO5kg2gSJw=","requires":{"create-eslint-index":"1.0.0","eslint":"8.9.0","eslint-ast-utils":"1.1.0","lodash":"4.17.21","req-all":"0.1.0"}},"eslint-plugin-functional":{"version":"4.2.0","dev":true,"integrity":"sha512-3v1DuKQTGwJo93UQ5SKzEjvJTaMGfznzwgGjWEBhLXxJfOMhcW7O6QUO1pmb5aLou9hoh7r31lkPvWmbIbIbew==","requires":{"@typescript-eslint/experimental-utils":"5.12.0","deepmerge-ts":"2.0.1","escape-string-regexp":"4.0.0","eslint":"8.9.0","typescript":"4.5.5"},"dependencies":{"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-html":{"version":"6.2.0","dev":true,"integrity":"sha512-vi3NW0E8AJombTvt8beMwkL1R/fdRWl4QSNRNMhVQKWm36/X0KF0unGNAY4mqUF06mnwVWZcIcerrCnfn9025g==","requires":{"htmlparser2":"7.2.0"}},"eslint-plugin-import":{"version":"2.25.4","dev":true,"integrity":"sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA==","requires":{"array-includes":"3.1.4","array.prototype.flat":"1.2.5","debug":"2.6.9","doctrine":"2.1.0","eslint":"8.9.0","eslint-import-resolver-node":"0.3.6","eslint-module-utils":"2.7.3","has":"1.0.3","is-core-module":"2.8.1","is-glob":"4.0.3","minimatch":"3.1.2","object.values":"1.1.5","resolve":"1.22.0","tsconfig-paths":"3.12.0"},"dependencies":{"debug":{"version":"2.6.9","dev":true,"integrity":"sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==","requires":{"ms":"2.0.0"}},"doctrine":{"version":"2.1.0","dev":true,"integrity":"sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==","requires":{"esutils":"2.0.3"}},"ms":{"version":"2.0.0","dev":true,"integrity":"sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="}}},"eslint-plugin-jest":{"version":"25.7.0","dev":true,"integrity":"sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==","requires":{"@typescript-eslint/eslint-plugin":"5.12.0","@typescript-eslint/experimental-utils":"5.12.0","eslint":"8.9.0"},"dependencies":{"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-jest-async":{"version":"1.0.3","dev":true,"integrity":"sha1-aRlnAtivhWVbIaeJpv11ygo3gA8=","requires":{"requireindex":"1.1.0"},"dependencies":{"requireindex":{"version":"1.1.0","dev":true,"integrity":"sha1-5UBLgVV+91225JxacgBIk/4D4WI="}}},"eslint-plugin-jest-dom":{"version":"3.9.4","dev":true,"integrity":"sha512-VRkaALGIhyxinnewZFHe2WJsRWp3TONpXysVXK1IUNJHCpJAIM9yRrI7fQ8i5F6UYE7+DAnvNhSSJZesLTonug==","requires":{"@babel/runtime":"7.17.2","@testing-library/dom":"7.31.2","eslint":"8.9.0","requireindex":"1.2.0"},"dependencies":{"@types/yargs":{"version":"15.0.14","dev":true,"integrity":"sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==","requires":{"@types/yargs-parser":"20.2.1"}},"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"eslint-plugin-jest-formatting":{"version":"3.1.0","dev":true,"integrity":"sha512-XyysraZ1JSgGbLSDxjj5HzKKh0glgWf+7CkqxbTqb7zEhW7X2WHo5SBQ8cGhnszKN+2Lj3/oevBlHNbHezoc/A==","requires":{"eslint":"8.9.0"}},"eslint-plugin-jsdoc":{"version":"37.9.4","dev":true,"integrity":"sha512-VxCyGgUNNnj2T4bb1OqltkbsPp3ehRzR5onIfh6zGrAvISmvgX/sbxUlh3YyGqWtjOTSBCURdKdmelSXEIHnlA==","requires":{"@es-joy/jsdoccomment":"0.20.1","comment-parser":"1.3.0","debug":"4.3.3","escape-string-regexp":"4.0.0","eslint":"8.9.0","esquery":"1.4.0","regextras":"0.8.0","semver":"7.3.5","spdx-expression-parse":"3.0.1"},"dependencies":{"estraverse":{"version":"5.3.0","dev":true,"integrity":"sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="}}},"eslint-plugin-json-schema-validator":{"version":"1.2.49","dev":true,"integrity":"sha512-Ib7PVrho5a08OI05VyVEkwb2u1jDWeDoHsIngaZFVi4OsnLObcW6gb3xO6VSYHUDvC5CTQans7vVjLUslcCMiA==","requires":{"ajv":"8.10.0","debug":"4.3.3","eslint":"8.9.0","eslint-utils":"3.0.0","json-schema-migrate":"2.0.0","jsonc-eslint-parser":"1.4.1","minimatch":"3.1.2","toml-eslint-parser":"0.2.1","tunnel-agent":"0.6.0","yaml-eslint-parser":"0.4.1"},"dependencies":{"acorn":{"version":"7.4.1","dev":true,"integrity":"sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="},"ajv":{"version":"8.10.0","dev":true,"integrity":"sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==","requires":{"fast-deep-equal":"3.1.3","json-schema-traverse":"1.0.0","require-from-string":"2.0.2","uri-js":"4.4.1"}},"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="},"espree":{"version":"6.2.1","dev":true,"integrity":"sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==","requires":{"acorn":"7.4.1","acorn-jsx":"5.3.2","eslint-visitor-keys":"1.3.0"}},"json-schema-traverse":{"version":"1.0.0","dev":true,"integrity":"sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="},"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"eslint-plugin-jsonc":{"version":"1.7.0","dev":true,"integrity":"sha512-pb3CAD9B0zhv3r9Bg9AdzswL50I3mbIq1ys+tNeuaDeibFlweo84SBNm22oqaFx/Dka+YZw2SLukAkQlJzSHMQ==","requires":{"eslint":"8.9.0","eslint-utils":"3.0.0","jsonc-eslint-parser":"1.4.1","natural-compare":"1.4.0"},"dependencies":{"acorn":{"version":"7.4.1","dev":true,"integrity":"sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="},"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="},"espree":{"version":"6.2.1","dev":true,"integrity":"sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==","requires":{"acorn":"7.4.1","acorn-jsx":"5.3.2","eslint-visitor-keys":"1.3.0"}},"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"eslint-plugin-no-constructor-bind":{"version":"2.0.4","dev":true,"integrity":"sha512-r0CGAE5SrRYt1OdACNiZGiOcBbFslKIPnMrFo3kPmX3iKZOm8HRD2eIbqhlc9lSSiBWcPZxXErXnroqgt+dKBg==","requires":{"requireindex":"1.2.0"}},"eslint-plugin-no-explicit-type-exports":{"version":"0.12.1","dev":true,"integrity":"sha512-m1v/f+LYVygCY735KfCovkoXYPbZH5zxEj/tuLOnMwX/qbJEJoRb9evul88Ois5HidvKbiMdMg/tXU55Ki++jg==","requires":{"@typescript-eslint/experimental-utils":"2.34.0","@typescript-eslint/parser":"5.12.0","eslint":"8.9.0","eslint-import-resolver-node":"0.3.6","eslint-module-utils":"2.7.3"},"dependencies":{"@typescript-eslint/experimental-utils":{"version":"2.34.0","dev":true,"integrity":"sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==","requires":{"@types/json-schema":"7.0.9","@typescript-eslint/typescript-estree":"2.34.0","eslint":"8.9.0","eslint-scope":"5.1.1","eslint-utils":"2.1.0"}},"@typescript-eslint/typescript-estree":{"version":"2.34.0","dev":true,"integrity":"sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==","requires":{"debug":"4.3.3","eslint-visitor-keys":"1.3.0","glob":"7.2.0","is-glob":"4.0.3","lodash":"4.17.21","semver":"7.3.5","tsutils":"3.21.0","typescript":"4.5.5"}},"eslint-utils":{"version":"2.1.0","dev":true,"integrity":"sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==","requires":{"eslint-visitor-keys":"1.3.0"}},"eslint-visitor-keys":{"version":"1.3.0","dev":true,"integrity":"sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="},"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-no-secrets":{"version":"0.8.9","dev":true,"integrity":"sha512-CqaBxXrImABCtxMWspAnm8d5UKkpNylC7zqVveb+fJHEvsSiNGJlSWzdSIvBUnW1XhJXkzifNIZQC08rEII5Ng==","requires":{"eslint":"8.9.0"}},"eslint-plugin-no-unsanitized":{"version":"3.2.0","dev":true,"integrity":"sha512-92opuXbjWmXcod94EyCKhp36V1QHLM/ArAST2ssgKOojALne0eZvSPfrg4oyr0EwTXvy0RJNe/Tkm33VkDUrKQ==","requires":{"eslint":"8.9.0"}},"eslint-plugin-no-use-extend-native":{"version":"0.5.0","dev":true,"integrity":"sha512-dBNjs8hor8rJgeXLH4HTut5eD3RGWf9JUsadIfuL7UosVQ/dnvOKwxEcRrXrFxrMZ8llUVWT+hOimxJABsAUzQ==","requires":{"is-get-set-prop":"1.0.0","is-js-type":"2.0.0","is-obj-prop":"1.0.0","is-proto-prop":"2.0.0"}},"eslint-plugin-node":{"version":"11.1.0","dev":true,"integrity":"sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==","requires":{"eslint":"8.9.0","eslint-plugin-es":"3.0.1","eslint-utils":"2.1.0","ignore":"5.2.0","minimatch":"3.1.2","resolve":"1.22.0","semver":"6.3.0"},"dependencies":{"eslint-utils":{"version":"2.1.0","dev":true,"integrity":"sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==","requires":{"eslint-visitor-keys":"1.3.0"}},"eslint-visitor-keys":{"version":"1.3.0","dev":true,"integrity":"sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="},"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"eslint-plugin-optimize-regex":{"version":"1.2.1","dev":true,"integrity":"sha512-fUaU7Tj1G/KSTDTABJw4Wp427Rl7RPl9ViYTu1Jrv36fJw4DFhd4elPdXiuYtdPsNsvzn9GcVlKEssGIVjw0UQ==","requires":{"regexp-tree":"0.1.24"}},"eslint-plugin-prefer-arrow":{"version":"1.2.3","dev":true,"integrity":"sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==","requires":{"eslint":"8.9.0"}},"eslint-plugin-prettier":{"version":"4.0.0","dev":true,"integrity":"sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==","requires":{"eslint":"8.9.0","eslint-config-prettier":"8.3.0","prettier-linter-helpers":"1.0.0"}},"eslint-plugin-promise":{"version":"5.2.0","dev":true,"integrity":"sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw==","requires":{"eslint":"8.9.0"}},"eslint-plugin-regexp":{"version":"1.5.1","dev":true,"integrity":"sha512-5v0rQIi54m2KycQHqmOAHrZhvI56GHmI2acr6zEffAqfeifTtobAEapv9Uf4o8//lGvwVkHKyjLoSbBNEFcfOA==","requires":{"comment-parser":"1.3.0","eslint":"8.9.0","eslint-utils":"3.0.0","grapheme-splitter":"1.0.4","jsdoctypeparser":"9.0.0","refa":"0.9.1","regexp-ast-analysis":"0.3.0","regexpp":"3.2.0","scslre":"0.1.6"},"dependencies":{"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="}}},"eslint-plugin-rxjs":{"version":"4.0.4","dev":true,"integrity":"sha512-7JopYQRqS5TYBNXioTLtS+W6+IKC1rsC7q8Dtou8E5gMuPxuEFqxU1x2161bwadaK3+h6sR+xiGjEhiE6JwjUA==","requires":{"@typescript-eslint/experimental-utils":"5.12.0","common-tags":"1.8.2","decamelize":"5.0.1","eslint":"8.9.0","eslint-etc":"5.1.0","requireindex":"1.2.0","rxjs-report-usage":"1.0.6","tslib":"2.3.1","tsutils":"3.21.0","tsutils-etc":"1.4.1","typescript":"4.5.5"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"globals":{"version":"11.12.0","dev":true,"integrity":"sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-security":{"version":"1.4.0","dev":true,"integrity":"sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA==","requires":{"safe-regex":"1.1.0"}},"eslint-plugin-sonarjs":{"version":"0.10.0","dev":true,"integrity":"sha512-FBRIBmWQh2UAfuLSnuYEfmle33jIup9hfkR0X8pkfjeCKNpHUG8qyZI63ahs3aw8CJrv47QJ9ccdK3ZxKH016A==","requires":{"eslint":"8.9.0"}},"eslint-plugin-sort-class-members":{"version":"1.14.1","dev":true,"integrity":"sha512-/Q/cm3h4N9DBNYvJMQMhluucSmr3Yydr9U0BgGcXUQe/rgWdXKSymZ5Ewcf4vmAG0bbTmAYmekuMnYYrqlu9Rg==","requires":{"eslint":"8.9.0"}},"eslint-plugin-sort-keys-fix":{"version":"1.1.2","dev":true,"integrity":"sha512-DNPHFGCA0/hZIsfODbeLZqaGY/+q3vgtshF85r+YWDNCQ2apd9PNs/zL6ttKm0nD1IFwvxyg3YOTI7FHl4unrw==","requires":{"espree":"6.2.1","esutils":"2.0.3","natural-compare":"1.4.0","requireindex":"1.2.0"},"dependencies":{"acorn":{"version":"7.4.1","dev":true,"integrity":"sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="},"eslint-visitor-keys":{"version":"1.3.0","dev":true,"integrity":"sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="},"espree":{"version":"6.2.1","dev":true,"integrity":"sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==","requires":{"acorn":"7.4.1","acorn-jsx":"5.3.2","eslint-visitor-keys":"1.3.0"}}}},"eslint-plugin-switch-case":{"version":"1.1.2","dev":true,"integrity":"sha1-piLbDExECChSa28m8ADXHnSFADI=","requires":{"lodash.last":"3.0.0","lodash.zipobject":"4.1.3"}},"eslint-plugin-toml":{"version":"0.2.0","dev":true,"integrity":"sha512-Y4eGb9q7i0i+UyZAXl3QAqcxkds2X7tfctXPllL7X+PpBXD/3wiqq1RNeoiLmHI9evpO1BjyuakffJZuGM7ElA==","requires":{"debug":"4.3.3","eslint":"8.9.0","lodash":"4.17.21","toml-eslint-parser":"0.2.1"},"dependencies":{"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="}}},"eslint-plugin-tsdoc":{"version":"0.2.14","dev":true,"integrity":"sha512-fJ3fnZRsdIoBZgzkQjv8vAj6NeeOoFkTfgosj6mKsFjX70QV256sA/wq+y/R2+OL4L8E79VVaVWrPeZnKNe8Ng==","requires":{"@microsoft/tsdoc":"0.13.2","@microsoft/tsdoc-config":"0.15.2"},"dependencies":{"resolve":{"version":"1.19.0","dev":true,"integrity":"sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==","requires":{"is-core-module":"2.8.1","path-parse":"1.0.7"}}}},"eslint-plugin-typescript-sort-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-ET7ABypdz19m47QnKynzNfWPi4CTNQ5jQQC1X5d0gojIwblkbGiCa5IilsqzBTmqxZ0yXDqKBO/GBkBFQCOFsg==","requires":{"@typescript-eslint/experimental-utils":"5.12.0","@typescript-eslint/parser":"5.12.0","eslint":"8.9.0","json-schema":"0.4.0","natural-compare-lite":"1.4.0","typescript":"4.5.5"},"dependencies":{"tslib":{"version":"1.14.1","dev":true,"integrity":"sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="}}},"eslint-plugin-unicorn":{"version":"37.0.1","dev":true,"integrity":"sha512-E1jq5u9ojnadisJcPi+hMXTGSiIzkIUMDvWsBudsCGXvKUB2aNSU2TcfyW2/jAS5A4ryBXfzxLykMxX1EdluSQ==","requires":{"@babel/helper-validator-identifier":"7.16.7","ci-info":"3.3.0","clean-regexp":"1.0.0","eslint":"8.9.0","eslint-template-visitor":"2.3.2","eslint-utils":"3.0.0","esquery":"1.4.0","indent-string":"4.0.0","is-builtin-module":"3.1.0","lodash":"4.17.21","pluralize":"8.0.0","read-pkg-up":"7.0.1","regexp-tree":"0.1.24","safe-regex":"2.1.1","semver":"7.3.5","strip-indent":"3.0.0"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="},"globals":{"version":"11.12.0","dev":true,"integrity":"sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"json5":{"version":"2.2.0","dev":true,"integrity":"sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==","requires":{"minimist":"1.2.5"}},"safe-buffer":{"version":"5.1.2","dev":true,"integrity":"sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="},"safe-regex":{"version":"2.1.1","dev":true,"integrity":"sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==","requires":{"regexp-tree":"0.1.24"}},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"type-fest":{"version":"0.8.1","dev":true,"integrity":"sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="}}},"eslint-plugin-unused-imports":{"version":"1.1.5","dev":true,"integrity":"sha512-TeV8l8zkLQrq9LBeYFCQmYVIXMjfHgdRQLw7dEZp4ZB3PeR10Y5Uif11heCsHRmhdRIYMoewr1d9ouUHLbLHew==","requires":{"@typescript-eslint/eslint-plugin":"5.12.0","eslint":"8.9.0","eslint-rule-composer":"0.3.0"}},"eslint-plugin-woke":{"version":"1.0.1","dev":true,"integrity":"sha512-cDRyZMNJkrbycju6OKhGnF29j0kbzV9AnLk3IPz6Kn0vWrSf8RyUdS5at97o/CJ8SEN2e6furQch1o/0LIrfMg==","requires":{"eslint":"8.9.0","requireindex":"1.1.0"},"dependencies":{"requireindex":{"version":"1.1.0","dev":true,"integrity":"sha1-5UBLgVV+91225JxacgBIk/4D4WI="}}},"eslint-plugin-yml":{"version":"0.10.1","dev":true,"integrity":"sha512-af0WgO3qaH+RW6jv1s6RzXKlg2NZLisN95lqGUf1KqBT6rEJyGSCpM49QYaSTvzmMaB/gcdbrnAfNoYwUn0Yig==","requires":{"debug":"4.3.3","eslint":"8.9.0","lodash":"4.17.21","natural-compare":"1.4.0","yaml-eslint-parser":"0.4.1"},"dependencies":{"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="}}},"eslint-rule-composer":{"version":"0.3.0","dev":true,"integrity":"sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg=="},"eslint-rule-docs":{"version":"1.1.231","dev":true,"integrity":"sha512-egHz9A1WG7b8CS0x1P6P/Rj5FqZOjray/VjpJa14tMZalfRKvpE2ONJ3plCM7+PcinmU4tcmbPLv0VtwzSdLVA=="},"eslint-scope":{"version":"5.1.1","dev":true,"integrity":"sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==","requires":{"esrecurse":"4.3.0","estraverse":"4.3.0"}},"eslint-template-visitor":{"version":"2.3.2","dev":true,"integrity":"sha512-3ydhqFpuV7x1M9EK52BPNj6V0Kwu0KKkcIAfpUhwHbR8ocRln/oUHgfxQupY8O1h4Qv/POHDumb/BwwNfxbtnA==","requires":{"@babel/core":"7.17.5","@babel/eslint-parser":"7.17.0","eslint":"8.9.0","eslint-visitor-keys":"2.1.0","esquery":"1.4.0","multimap":"1.1.0"},"dependencies":{"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"eslint-utils":{"version":"3.0.0","dev":true,"integrity":"sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==","requires":{"eslint":"8.9.0","eslint-visitor-keys":"2.1.0"},"dependencies":{"eslint-visitor-keys":{"version":"2.1.0","dev":true,"integrity":"sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="}}},"eslint-visitor-keys":{"version":"3.3.0","dev":true,"integrity":"sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA=="},"espree":{"version":"9.3.1","dev":true,"integrity":"sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==","requires":{"acorn":"8.7.0","acorn-jsx":"5.3.2","eslint-visitor-keys":"3.3.0"}},"esprima":{"version":"4.0.1","dev":true,"integrity":"sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="},"esquery":{"version":"1.4.0","dev":true,"integrity":"sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==","requires":{"estraverse":"5.3.0"}},"esrecurse":{"version":"4.3.0","dev":true,"integrity":"sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==","requires":{"estraverse":"5.3.0"},"dependencies":{"estraverse":{"version":"5.3.0","dev":true,"integrity":"sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="}}},"estraverse":{"version":"4.3.0","dev":true,"integrity":"sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="},"esutils":{"version":"2.0.3","dev":true,"integrity":"sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="},"execa":{"version":"5.1.1","dev":true,"integrity":"sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==","requires":{"cross-spawn":"7.0.3","get-stream":"6.0.1","human-signals":"2.1.0","is-stream":"2.0.1","merge-stream":"2.0.0","npm-run-path":"4.0.1","onetime":"5.1.2","signal-exit":"3.0.7","strip-final-newline":"2.0.0"}},"expand-brackets":{"version":"2.1.4","dev":true,"integrity":"sha1-t3c14xXOMPa27/D4OwQVGiJEliI=","requires":{"debug":"2.6.9","define-property":"0.2.5","extend-shallow":"2.0.1","posix-character-classes":"0.1.1","regex-not":"1.0.2","snapdragon":"0.8.2","to-regex":"3.0.2"},"dependencies":{"define-property":{"version":"0.2.5","dev":true,"integrity":"sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=","requires":{"is-descriptor":"0.1.6"}}}},"expand-tilde":{"version":"2.0.2","dev":true,"integrity":"sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=","requires":{"homedir-polyfill":"1.0.3"}},"extend-shallow":{"version":"2.0.1","dev":true,"integrity":"sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=","requires":{"is-extendable":"0.1.1"}},"external-editor":{"version":"3.1.0","dev":true,"integrity":"sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==","requires":{"chardet":"0.7.0","iconv-lite":"0.4.24","tmp":"0.0.33"}},"extglob":{"version":"2.0.4","dev":true,"integrity":"sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==","requires":{"array-unique":"0.3.2","define-property":"1.0.0","expand-brackets":"2.1.4","extend-shallow":"2.0.1","fragment-cache":"0.2.1","regex-not":"1.0.2","snapdragon":"0.8.2","to-regex":"3.0.2"},"dependencies":{"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}},"falsey":{"version":"0.3.2","dev":true,"integrity":"sha512-lxEuefF5MBIVDmE6XeqCdM4BWk1+vYmGZtkbKZ/VFcg6uBBw6fXNEbWmxCjDdQlFc9hy450nkiWwM3VAW6G1qg==","requires":{"kind-of":"5.1.0"},"dependencies":{"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}},"fast-deep-equal":{"version":"3.1.3","dev":true,"integrity":"sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="},"fast-diff":{"version":"1.2.0","dev":true,"integrity":"sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w=="},"fast-equals":{"version":"3.0.0","dev":true,"integrity":"sha512-Af7nSOpf7617idrFg0MJY6x7yVDPoO80aSwtKTC0afT8B/SsmvTpA+2a+uPLmhVF5IHmY5NPuBAA3dJrp55rJA=="},"fast-glob":{"version":"3.2.11","dev":true,"integrity":"sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==","requires":{"@nodelib/fs.stat":"2.0.5","@nodelib/fs.walk":"1.2.8","glob-parent":"5.1.2","merge2":"1.4.1","micromatch":"4.0.4"}},"fast-json-stable-stringify":{"version":"2.1.0","dev":true,"integrity":"sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="},"fast-levenshtein":{"version":"2.0.6","dev":true,"integrity":"sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="},"fastq":{"version":"1.13.0","dev":true,"integrity":"sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==","requires":{"reusify":"1.0.4"}},"figures":{"version":"2.0.0","dev":true,"integrity":"sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=","requires":{"escape-string-regexp":"1.0.5"}},"file-entry-cache":{"version":"6.0.1","dev":true,"integrity":"sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==","requires":{"flat-cache":"3.0.4"}},"file-type":{"version":"16.5.3","dev":true,"integrity":"sha512-uVsl7iFhHSOY4bEONLlTK47iAHtNsFHWP5YE4xJfZ4rnX7S1Q3wce09XgqSC7E/xh8Ncv/be1lNoyprlUH/x6A==","requires":{"readable-web-to-node-stream":"3.0.2","strtok3":"6.3.0","token-types":"4.1.1"}},"fill-range":{"version":"7.0.1","dev":true,"integrity":"sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==","requires":{"to-regex-range":"5.0.1"}},"filter-obj":{"version":"1.1.0","dev":true,"integrity":"sha1-mzERErxsYSehbgFsbF1/GeCAXFs="},"find-node-modules":{"version":"2.1.2","dev":true,"integrity":"sha512-x+3P4mbtRPlSiVE1Qco0Z4YLU8WFiFcuWTf3m75OV9Uzcfs2Bg+O9N+r/K0AnmINBW06KpfqKwYJbFlFq4qNug==","requires":{"findup-sync":"4.0.0","merge":"2.1.1"}},"find-parent-dir":{"version":"0.3.1","dev":true,"integrity":"sha512-o4UcykWV/XN9wm+jMEtWLPlV8RXCZnMhQI6F6OdHeSez7iiJWePw8ijOlskJZMsaQoGR/b7dH6lO02HhaTN7+A=="},"find-root":{"version":"1.1.0","dev":true,"integrity":"sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="},"find-up":{"version":"4.1.0","dev":true,"integrity":"sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==","requires":{"locate-path":"5.0.0","path-exists":"4.0.0"}},"find-versions":{"version":"4.0.0","dev":true,"integrity":"sha512-wgpWy002tA+wgmO27buH/9KzyEOQnKsG/R0yrcjPT9BOFm0zRBVQbZ95nRGXWMywS8YR5knRbpohio0bcJABxQ==","requires":{"semver-regex":"3.1.3"}},"findup-sync":{"version":"4.0.0","dev":true,"integrity":"sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==","requires":{"detect-file":"1.0.0","is-glob":"4.0.3","micromatch":"4.0.4","resolve-dir":"1.0.1"}},"flat-cache":{"version":"3.0.4","dev":true,"integrity":"sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==","requires":{"flatted":"3.2.5","rimraf":"3.0.2"}},"flatted":{"version":"3.2.5","dev":true,"integrity":"sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg=="},"for-in":{"version":"1.0.2","dev":true,"integrity":"sha1-gQaNKVqBQuwKxybG4iAMMPttXoA="},"for-own":{"version":"1.0.0","dev":true,"integrity":"sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=","requires":{"for-in":"1.0.2"}},"form-data":{"version":"4.0.0","dev":true,"integrity":"sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==","requires":{"asynckit":"0.4.0","combined-stream":"1.0.8","mime-types":"2.1.34"}},"fragment-cache":{"version":"0.2.1","dev":true,"integrity":"sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=","requires":{"map-cache":"0.2.2"}},"from2":{"version":"2.3.0","dev":true,"integrity":"sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=","requires":{"inherits":"2.0.4","readable-stream":"2.3.7"}},"fromentries":{"version":"1.3.2","dev":true,"integrity":"sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg=="},"fs-exists-sync":{"version":"0.1.0","dev":true,"integrity":"sha1-mC1ok6+RjnLQjeyehnP/K1qNat0="},"fs-extra":{"version":"10.0.0","dev":true,"integrity":"sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==","requires":{"graceful-fs":"4.2.9","jsonfile":"6.1.0","universalify":"2.0.0"}},"fs.realpath":{"version":"1.0.0","dev":true,"integrity":"sha1-FQStJSMVjKpA20onh8sBQRmU6k8="},"function-bind":{"version":"1.1.1","dev":true,"integrity":"sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="},"functional-red-black-tree":{"version":"1.0.1","dev":true,"integrity":"sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="},"gensequence":{"version":"3.1.1","dev":true,"integrity":"sha512-ys3h0hiteRwmY6BsvSttPmkhC0vEQHPJduANBRtH/dlDPZ0UBIb/dXy80IcckXyuQ6LKg+PloRqvGER9IS7F7g=="},"gensync":{"version":"1.0.0-beta.2","dev":true,"integrity":"sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="},"get-caller-file":{"version":"2.0.5","dev":true,"integrity":"sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="},"get-intrinsic":{"version":"1.1.1","dev":true,"integrity":"sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==","requires":{"function-bind":"1.1.1","has":"1.0.3","has-symbols":"1.0.2"}},"get-object":{"version":"0.2.0","dev":true,"integrity":"sha1-2S/31RkMZFMM2gVD2sY6PUf+jAw=","requires":{"is-number":"2.1.0","isobject":"0.2.0"},"dependencies":{"is-number":{"version":"2.1.0","dev":true,"integrity":"sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=","requires":{"kind-of":"3.2.2"}},"isobject":{"version":"0.2.0","dev":true,"integrity":"sha1-o0MhkvObkQtfAsyYlIeDbscKqF4="},"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"get-set-props":{"version":"0.1.0","dev":true,"integrity":"sha1-mYR1wXhEVobQsyJG2l3428++jqM="},"get-stdin":{"version":"8.0.0","dev":true,"integrity":"sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg=="},"get-stream":{"version":"6.0.1","dev":true,"integrity":"sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="},"get-symbol-description":{"version":"1.0.0","dev":true,"integrity":"sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==","requires":{"call-bind":"1.0.2","get-intrinsic":"1.1.1"}},"get-value":{"version":"2.0.6","dev":true,"integrity":"sha1-3BXKHGcjh8p2vTesCjlbogQqLCg="},"git-cz-emoji":{"version":"1.1.24","dev":true,"integrity":"sha512-m9XItxRoL6k/NCAeWubMJW2m3r29V6JkQSpZk5vtFY6IVJ8S4l6l7AVreOsRemKoWiVzxtb2I4xBbJNt1vlrNw==","requires":{"chalk":"4.1.2","commitizen":"4.2.4","tslib":"2.3.1","word-wrap":"1.2.3"},"dependencies":{"ansi-regex":{"version":"3.0.0","dev":true,"integrity":"sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="},"fs-extra":{"version":"8.1.0","dev":true,"integrity":"sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==","requires":{"graceful-fs":"4.2.9","jsonfile":"4.0.0","universalify":"0.1.2"}},"glob":{"version":"7.1.4","dev":true,"integrity":"sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==","requires":{"fs.realpath":"1.0.0","inflight":"1.0.6","inherits":"2.0.4","minimatch":"3.1.2","once":"1.4.0","path-is-absolute":"1.0.1"}},"is-fullwidth-code-point":{"version":"2.0.0","dev":true,"integrity":"sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="},"jsonfile":{"version":"4.0.0","dev":true,"integrity":"sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss="},"mimic-fn":{"version":"1.2.0","dev":true,"integrity":"sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ=="},"onetime":{"version":"2.0.1","dev":true,"integrity":"sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=","requires":{"mimic-fn":"1.2.0"}},"string-width":{"version":"2.1.1","dev":true,"integrity":"sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==","requires":{"is-fullwidth-code-point":"2.0.0","strip-ansi":"4.0.0"},"dependencies":{"strip-ansi":{"version":"4.0.0","dev":true,"integrity":"sha1-qEeQIusaw2iocTibY1JixQXuNo8=","requires":{"ansi-regex":"3.0.0"}}}},"strip-ansi":{"version":"5.2.0","dev":true,"integrity":"sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==","requires":{"ansi-regex":"4.1.0"},"dependencies":{"ansi-regex":{"version":"4.1.0","dev":true,"integrity":"sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="}}},"universalify":{"version":"0.1.2","dev":true,"integrity":"sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="},"which":{"version":"1.3.1","dev":true,"integrity":"sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==","requires":{"isexe":"2.0.0"}}}},"git-log-parser":{"version":"1.2.0","dev":true,"integrity":"sha1-LmpMGxP8AAKCB7p5WnrDFme5/Uo=","requires":{"argv-formatter":"1.0.0","spawn-error-forwarder":"1.0.0","split2":"1.0.0","stream-combiner2":"1.1.1","through2":"2.0.5","traverse":"0.6.6"}},"git-notify":{"version":"0.2.3","dev":true,"integrity":"sha512-fnUsm66BY6yQptuvnnQYseRKBFIO0l/IbLJJ01FuIZvCsFmRboWeaAT4BMIfdnaBC1F+63t4qM6ytej/y8gEGQ==","requires":{"boxen":"5.1.2","chalk":"4.1.2","detect-newline":"3.1.0","git-raw-commits":"2.0.11","meow":"9.0.0","simple-git":"2.48.0"},"dependencies":{"camelcase":{"version":"6.3.0","dev":true,"integrity":"sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="},"meow":{"version":"9.0.0","dev":true,"integrity":"sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==","requires":{"@types/minimist":"1.2.2","camelcase-keys":"6.2.2","decamelize":"1.2.0","decamelize-keys":"1.1.0","hard-rejection":"2.1.0","minimist-options":"4.1.0","normalize-package-data":"3.0.3","read-pkg-up":"7.0.1","redent":"3.0.0","trim-newlines":"3.0.1","type-fest":"0.18.1","yargs-parser":"20.2.9"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"camelcase":{"version":"5.3.1","dev":true,"integrity":"sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"type-fest":{"version":"0.18.1","dev":true,"integrity":"sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="}}},"type-fest":{"version":"0.20.2","dev":true,"integrity":"sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="}}},"git-raw-commits":{"version":"2.0.11","dev":true,"integrity":"sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==","requires":{"dargs":"7.0.0","lodash":"4.17.21","meow":"8.1.2","split2":"3.2.2","through2":"4.0.2"}},"glob":{"version":"7.2.0","dev":true,"integrity":"sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==","requires":{"fs.realpath":"1.0.0","inflight":"1.0.6","inherits":"2.0.4","minimatch":"3.1.2","once":"1.4.0","path-is-absolute":"1.0.1"}},"glob-parent":{"version":"5.1.2","dev":true,"integrity":"sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==","requires":{"is-glob":"4.0.3"}},"global-dirs":{"version":"0.1.1","dev":true,"integrity":"sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=","requires":{"ini":"1.3.8"}},"global-modules":{"version":"1.0.0","dev":true,"integrity":"sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==","requires":{"global-prefix":"1.0.2","is-windows":"1.0.2","resolve-dir":"1.0.1"}},"global-prefix":{"version":"1.0.2","dev":true,"integrity":"sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=","requires":{"expand-tilde":"2.0.2","homedir-polyfill":"1.0.3","ini":"1.3.8","is-windows":"1.0.2","which":"1.3.1"}},"globals":{"version":"13.12.1","dev":true,"integrity":"sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==","requires":{"type-fest":"0.20.2"}},"globby":{"version":"11.1.0","dev":true,"integrity":"sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==","requires":{"array-union":"2.1.0","dir-glob":"3.0.1","fast-glob":"3.2.11","ignore":"5.2.0","merge2":"1.4.1","slash":"3.0.0"}},"got":{"version":"11.8.3","dev":true,"integrity":"sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==","requires":{"@sindresorhus/is":"4.4.0","@szmarczak/http-timer":"4.0.6","@types/cacheable-request":"6.0.2","@types/responselike":"1.0.0","cacheable-lookup":"5.0.4","cacheable-request":"7.0.2","decompress-response":"6.0.0","http2-wrapper":"1.0.3","lowercase-keys":"2.0.0","p-cancelable":"2.1.1","responselike":"2.0.0"}},"graceful-fs":{"version":"4.2.9","dev":true,"integrity":"sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ=="},"grapheme-splitter":{"version":"1.0.4","dev":true,"integrity":"sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="},"gulp-header":{"version":"1.8.12","dev":true,"integrity":"sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==","requires":{"concat-with-sourcemaps":"1.1.0","lodash.template":"4.5.0","through2":"2.0.5"}},"handlebars":{"version":"4.7.7","dev":true,"integrity":"sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==","requires":{"minimist":"1.2.5","neo-async":"2.6.2","source-map":"0.6.1","wordwrap":"1.0.0"}},"handlebars-helper-create-frame":{"version":"0.1.0","dev":true,"integrity":"sha1-iqUdEK62QI/MZgXUDXc1YohIegM=","requires":{"create-frame":"1.0.0","isobject":"3.0.1"}},"handlebars-helpers":{"version":"0.10.0","dev":true,"integrity":"sha512-QiyhQz58u/DbuV41VnfpE0nhy6YCH4vB514ajysV8SoKmP+DxU+pR+fahVyNECHj+jiwEN2VrvxD/34/yHaLUg==","requires":{"arr-flatten":"1.1.0","array-sort":"0.1.4","create-frame":"1.0.0","define-property":"1.0.0","falsey":"0.3.2","for-in":"1.0.2","for-own":"1.0.0","get-object":"0.2.0","get-value":"2.0.6","handlebars":"4.7.7","handlebars-helper-create-frame":"0.1.0","handlebars-utils":"1.0.6","has-value":"1.0.0","helper-date":"1.0.1","helper-markdown":"1.0.0","helper-md":"0.2.2","html-tag":"2.0.0","is-even":"1.0.0","is-glob":"4.0.3","is-number":"4.0.0","kind-of":"6.0.3","lazy-cache":"2.0.2","logging-helpers":"1.0.0","micromatch":"3.1.10","relative":"3.0.2","striptags":"3.2.0","to-gfm-code-block":"0.1.1","year":"0.2.1"},"dependencies":{"ansi-colors":{"version":"0.2.0","dev":true,"integrity":"sha1-csMd4qDZoszQysMMyYI+6y9kNLU=","requires":{"ansi-bgblack":"0.1.1","ansi-bgblue":"0.1.1","ansi-bgcyan":"0.1.1","ansi-bggreen":"0.1.1","ansi-bgmagenta":"0.1.1","ansi-bgred":"0.1.1","ansi-bgwhite":"0.1.1","ansi-bgyellow":"0.1.1","ansi-black":"0.1.1","ansi-blue":"0.1.1","ansi-bold":"0.1.1","ansi-cyan":"0.1.1","ansi-dim":"0.1.1","ansi-gray":"0.1.1","ansi-green":"0.1.1","ansi-grey":"0.1.1","ansi-hidden":"0.1.1","ansi-inverse":"0.1.1","ansi-italic":"0.1.1","ansi-magenta":"0.1.1","ansi-red":"0.1.1","ansi-reset":"0.1.1","ansi-strikethrough":"0.1.1","ansi-underline":"0.1.1","ansi-white":"0.1.1","ansi-yellow":"0.1.1","lazy-cache":"2.0.2"}},"argparse":{"version":"1.0.10","dev":true,"integrity":"sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==","requires":{"sprintf-js":"1.0.3"}},"braces":{"version":"2.3.2","dev":true,"integrity":"sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==","requires":{"arr-flatten":"1.1.0","array-unique":"0.3.2","extend-shallow":"2.0.1","fill-range":"4.0.0","isobject":"3.0.1","repeat-element":"1.1.4","snapdragon":"0.8.2","snapdragon-node":"2.1.1","split-string":"3.1.0","to-regex":"3.0.2"}},"debug":{"version":"3.1.0","dev":true,"integrity":"sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==","requires":{"ms":"2.0.0"}},"fill-range":{"version":"4.0.0","dev":true,"integrity":"sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=","requires":{"extend-shallow":"2.0.1","is-number":"3.0.0","repeat-string":"1.6.1","to-regex-range":"2.1.1"}},"is-number":{"version":"4.0.0","dev":true,"integrity":"sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ=="},"is-plain-object":{"version":"2.0.4","dev":true,"integrity":"sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==","requires":{"isobject":"3.0.1"}},"micromatch":{"version":"3.1.10","dev":true,"integrity":"sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==","requires":{"arr-diff":"4.0.0","array-unique":"0.3.2","braces":"2.3.2","define-property":"2.0.2","extend-shallow":"3.0.2","extglob":"2.0.4","fragment-cache":"0.2.1","kind-of":"6.0.3","nanomatch":"1.2.13","object.pick":"1.3.0","regex-not":"1.0.2","snapdragon":"0.8.2","to-regex":"3.0.2"},"dependencies":{"debug":{"version":"2.6.9","dev":true,"integrity":"sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==","requires":{"ms":"2.0.0"}},"define-property":{"version":"2.0.2","dev":true,"integrity":"sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==","requires":{"is-descriptor":"1.0.2","isobject":"3.0.1"}},"extend-shallow":{"version":"3.0.2","dev":true,"integrity":"sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=","requires":{"assign-symbols":"1.0.0","is-extendable":"1.0.1"},"dependencies":{"is-extendable":{"version":"1.0.1","dev":true,"integrity":"sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==","requires":{"is-plain-object":"2.0.4"}}}},"has-value":{"version":"0.3.1","dev":true,"integrity":"sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=","requires":{"get-value":"2.0.6","has-values":"0.1.4","isobject":"2.1.0"}},"has-values":{"version":"0.1.4","dev":true,"integrity":"sha1-bWHeldkd/Km5oCCJrThL/49it3E="},"is-accessor-descriptor":{"version":"1.0.0","dev":true,"integrity":"sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==","requires":{"kind-of":"6.0.3"}},"is-data-descriptor":{"version":"1.0.0","dev":true,"integrity":"sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==","requires":{"kind-of":"6.0.3"}},"is-descriptor":{"version":"1.0.2","dev":true,"integrity":"sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==","requires":{"is-accessor-descriptor":"1.0.0","is-data-descriptor":"1.0.0","kind-of":"6.0.3"}},"is-number":{"version":"3.0.0","dev":true,"integrity":"sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=","requires":{"kind-of":"3.2.2"}},"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}},"source-map":{"version":"0.5.7","dev":true,"integrity":"sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="}}},"ms":{"version":"2.0.0","dev":true,"integrity":"sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="},"readable-stream":{"version":"2.3.7","dev":true,"integrity":"sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==","requires":{"core-util-is":"1.0.3","inherits":"2.0.4","isarray":"1.0.0","process-nextick-args":"2.0.1","safe-buffer":"5.1.2","string_decoder":"1.1.1","util-deprecate":"1.0.2"}},"safe-buffer":{"version":"5.1.2","dev":true,"integrity":"sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="},"string_decoder":{"version":"1.1.1","dev":true,"integrity":"sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==","requires":{"safe-buffer":"5.1.2"}},"through2":{"version":"2.0.5","dev":true,"integrity":"sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==","requires":{"readable-stream":"2.3.7","xtend":"4.0.2"}},"to-regex-range":{"version":"2.1.1","dev":true,"integrity":"sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=","requires":{"is-number":"3.0.0","repeat-string":"1.6.1"}}}},"handlebars-utils":{"version":"1.0.6","dev":true,"integrity":"sha512-d5mmoQXdeEqSKMtQQZ9WkiUcO1E3tPbWxluCK9hVgIDPzQa9WsKo3Lbe/sGflTe7TomHEeZaOgwIkyIr1kfzkw==","requires":{"kind-of":"6.0.3","typeof-article":"0.1.1"},"dependencies":{"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"hard-rejection":{"version":"2.1.0","dev":true,"integrity":"sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA=="},"has":{"version":"1.0.3","dev":true,"integrity":"sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==","requires":{"function-bind":"1.1.1"}},"has-bigints":{"version":"1.0.1","dev":true,"integrity":"sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA=="},"has-flag":{"version":"4.0.0","dev":true,"integrity":"sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="},"has-own-prop":{"version":"2.0.0","dev":true,"integrity":"sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ=="},"has-symbols":{"version":"1.0.2","dev":true,"integrity":"sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="},"has-tostringtag":{"version":"1.0.0","dev":true,"integrity":"sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==","requires":{"has-symbols":"1.0.2"}},"has-value":{"version":"1.0.0","dev":true,"integrity":"sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=","requires":{"get-value":"2.0.6","has-values":"1.0.0","isobject":"3.0.1"},"dependencies":{"is-number":{"version":"3.0.0","dev":true,"integrity":"sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=","requires":{"kind-of":"3.2.2"},"dependencies":{"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"kind-of":{"version":"4.0.0","dev":true,"integrity":"sha1-IIE989cSkosgc3hpGkUGb65y3Vc=","requires":{"is-buffer":"1.1.6"}}}},"has-values":{"version":"1.0.0","dev":true,"integrity":"sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=","requires":{"is-number":"3.0.0","kind-of":"4.0.0"}},"helper-date":{"version":"1.0.1","dev":true,"integrity":"sha512-wU3VOwwTJvGr/w5rZr3cprPHO+hIhlblTJHD6aFBrKLuNbf4lAmkawd2iK3c6NbJEvY7HAmDpqjOFSI5/+Ey2w==","requires":{"date.js":"0.3.3","handlebars-utils":"1.0.6","moment":"2.29.1"}},"helper-markdown":{"version":"1.0.0","dev":true,"integrity":"sha512-AnDqMS4ejkQK0MXze7pA9TM3pu01ZY+XXsES6gEE0RmCGk5/NIfvTn0NmItfyDOjRAzyo9z6X7YHbHX4PzIvOA==","requires":{"handlebars-utils":"1.0.6","highlight.js":"9.18.5","remarkable":"1.7.4"}},"helper-md":{"version":"0.2.2","dev":true,"integrity":"sha1-wfWdflW7riM2L9ig6XFgeuxp1B8=","requires":{"ent":"2.2.0","extend-shallow":"2.0.1","fs-exists-sync":"0.1.0","remarkable":"1.7.4"}},"highlight.js":{"version":"9.18.5","dev":true,"integrity":"sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA=="},"homedir-polyfill":{"version":"1.0.3","dev":true,"integrity":"sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==","requires":{"parse-passwd":"1.0.0"}},"hook-std":{"version":"2.0.0","dev":true,"integrity":"sha512-zZ6T5WcuBMIUVh49iPQS9t977t7C0l7OtHrpeMb5uk48JdflRX0NSFvCekfYNmGQETnLq9W/isMyHl69kxGi8g=="},"hosted-git-info":{"version":"4.1.0","dev":true,"integrity":"sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==","requires":{"lru-cache":"6.0.0"}},"html-tag":{"version":"2.0.0","dev":true,"integrity":"sha512-XxzooSo6oBoxBEUazgjdXj7VwTn/iSTSZzTYKzYY6I916tkaYzypHxy+pbVU1h+0UQ9JlVf5XkNQyxOAiiQO1g==","requires":{"is-self-closing":"1.0.1","kind-of":"6.0.3"}},"htmlparser2":{"version":"7.2.0","dev":true,"integrity":"sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==","requires":{"domelementtype":"2.2.0","domhandler":"4.3.0","domutils":"2.8.0","entities":"3.0.1"}},"http-cache-semantics":{"version":"4.1.0","dev":true,"integrity":"sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="},"http-proxy-agent":{"version":"5.0.0","dev":true,"integrity":"sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==","requires":{"@tootallnate/once":"2.0.0","agent-base":"6.0.2","debug":"4.3.3"}},"http2-wrapper":{"version":"1.0.3","dev":true,"integrity":"sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==","requires":{"quick-lru":"5.1.1","resolve-alpn":"1.2.1"}},"https-proxy-agent":{"version":"5.0.0","dev":true,"integrity":"sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==","requires":{"agent-base":"6.0.2","debug":"4.3.3"}},"human-signals":{"version":"2.1.0","dev":true,"integrity":"sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="},"husky":{"version":"7.0.4","dev":true,"integrity":"sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ=="},"iconv-lite":{"version":"0.4.24","dev":true,"integrity":"sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==","requires":{"safer-buffer":"2.1.2"}},"ieee754":{"version":"1.2.1","dev":true,"integrity":"sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="},"ignore":{"version":"5.2.0","dev":true,"integrity":"sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ=="},"import-fresh":{"version":"3.3.0","dev":true,"integrity":"sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==","requires":{"parent-module":"1.0.1","resolve-from":"4.0.0"}},"import-from":{"version":"4.0.0","dev":true,"integrity":"sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ=="},"imurmurhash":{"version":"0.1.4","dev":true,"integrity":"sha1-khi5srkoojixPcT7a21XbyMUU+o="},"indent-string":{"version":"4.0.0","dev":true,"integrity":"sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="},"inflight":{"version":"1.0.6","dev":true,"integrity":"sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=","requires":{"once":"1.4.0","wrappy":"1.0.2"}},"info-symbol":{"version":"0.1.0","dev":true,"integrity":"sha1-J4QdcoZ920JCzWEtecEGM4gcang="},"inherits":{"version":"2.0.4","dev":true,"integrity":"sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="},"ini":{"version":"1.3.8","dev":true,"integrity":"sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="},"inquirer":{"version":"6.5.2","dev":true,"integrity":"sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==","requires":{"ansi-escapes":"3.2.0","chalk":"2.4.2","cli-cursor":"2.1.0","cli-width":"2.2.1","external-editor":"3.1.0","figures":"2.0.0","lodash":"4.17.21","mute-stream":"0.0.7","run-async":"2.4.1","rxjs":"6.6.7","string-width":"2.1.1","strip-ansi":"5.2.0","through":"2.3.8"}},"internal-slot":{"version":"1.0.3","dev":true,"integrity":"sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==","requires":{"get-intrinsic":"1.1.1","has":"1.0.3","side-channel":"1.0.4"}},"into-stream":{"version":"6.0.0","dev":true,"integrity":"sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==","requires":{"from2":"2.3.0","p-is-promise":"3.0.0"},"dependencies":{"readable-stream":{"version":"2.3.7","dev":true,"integrity":"sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==","requires":{"core-util-is":"1.0.3","inherits":"2.0.4","isarray":"1.0.0","process-nextick-args":"2.0.1","safe-buffer":"5.1.2","string_decoder":"1.1.1","util-deprecate":"1.0.2"}},"safe-buffer":{"version":"5.1.2","dev":true,"integrity":"sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="},"string_decoder":{"version":"1.1.1","dev":true,"integrity":"sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==","requires":{"safe-buffer":"5.1.2"}}}},"irregular-plurals":{"version":"3.3.0","dev":true,"integrity":"sha512-MVBLKUTangM3EfRPFROhmWQQKRDsrgI83J8GS3jXy+OwYqiR2/aoWndYQ5416jLE3uaGgLH7ncme3X9y09gZ3g=="},"is-accessor-descriptor":{"version":"0.1.6","dev":true,"integrity":"sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=","requires":{"kind-of":"3.2.2"},"dependencies":{"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"is-alphabetical":{"version":"1.0.4","dev":true,"integrity":"sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg=="},"is-alphanumerical":{"version":"1.0.4","dev":true,"integrity":"sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==","requires":{"is-alphabetical":"1.0.4","is-decimal":"1.0.4"}},"is-arrayish":{"version":"0.2.1","dev":true,"integrity":"sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="},"is-bigint":{"version":"1.0.4","dev":true,"integrity":"sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==","requires":{"has-bigints":"1.0.1"}},"is-boolean-object":{"version":"1.1.2","dev":true,"integrity":"sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==","requires":{"call-bind":"1.0.2","has-tostringtag":"1.0.0"}},"is-buffer":{"version":"1.1.6","dev":true,"integrity":"sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="},"is-builtin-module":{"version":"3.1.0","dev":true,"integrity":"sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg==","requires":{"builtin-modules":"3.2.0"}},"is-callable":{"version":"1.2.4","dev":true,"integrity":"sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w=="},"is-core-module":{"version":"2.8.1","dev":true,"integrity":"sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==","requires":{"has":"1.0.3"}},"is-data-descriptor":{"version":"0.1.4","dev":true,"integrity":"sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=","requires":{"kind-of":"3.2.2"},"dependencies":{"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"is-date-object":{"version":"1.0.5","dev":true,"integrity":"sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==","requires":{"has-tostringtag":"1.0.0"}},"is-decimal":{"version":"1.0.4","dev":true,"integrity":"sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw=="},"is-descriptor":{"version":"0.1.6","dev":true,"integrity":"sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==","requires":{"is-accessor-descriptor":"0.1.6","is-data-descriptor":"0.1.4","kind-of":"5.1.0"}},"is-docker":{"version":"2.2.1","dev":true,"integrity":"sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="},"is-even":{"version":"1.0.0","dev":true,"integrity":"sha1-drUFX7rY0pSoa2qUkBXhyXtxfAY=","requires":{"is-odd":"0.1.2"},"dependencies":{"is-number":{"version":"3.0.0","dev":true,"integrity":"sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=","requires":{"kind-of":"3.2.2"}},"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"is-expression":{"version":"4.0.0","dev":true,"integrity":"sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==","requires":{"acorn":"7.4.1","object-assign":"4.1.1"}},"is-extendable":{"version":"0.1.1","dev":true,"integrity":"sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="},"is-extglob":{"version":"2.1.1","dev":true,"integrity":"sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="},"is-fullwidth-code-point":{"version":"3.0.0","dev":true,"integrity":"sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="},"is-get-set-prop":{"version":"1.0.0","dev":true,"integrity":"sha1-JzGHfk14pqae3M5rudaLB3nnYxI=","requires":{"get-set-props":"0.1.0","lowercase-keys":"1.0.1"}},"is-glob":{"version":"4.0.3","dev":true,"integrity":"sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==","requires":{"is-extglob":"2.1.1"}},"is-hexadecimal":{"version":"1.0.4","dev":true,"integrity":"sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw=="},"is-js-type":{"version":"2.0.0","dev":true,"integrity":"sha1-c2FwBtZZtOtHKbunR9KHgt8PfiI=","requires":{"js-types":"1.0.0"}},"is-negative-zero":{"version":"2.0.2","dev":true,"integrity":"sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA=="},"is-number":{"version":"7.0.0","dev":true,"integrity":"sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="},"is-number-object":{"version":"1.0.6","dev":true,"integrity":"sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==","requires":{"has-tostringtag":"1.0.0"}},"is-obj":{"version":"2.0.0","dev":true,"integrity":"sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="},"is-obj-prop":{"version":"1.0.0","dev":true,"integrity":"sha1-s03nnEULjXxzqyzfZ9yHWtuF+A4=","requires":{"lowercase-keys":"1.0.1","obj-props":"1.3.0"}},"is-odd":{"version":"0.1.2","dev":true,"integrity":"sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc=","requires":{"is-number":"3.0.0"}},"is-path-cwd":{"version":"2.2.0","dev":true,"integrity":"sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ=="},"is-path-inside":{"version":"3.0.3","dev":true,"integrity":"sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ=="},"is-plain-obj":{"version":"1.1.0","dev":true,"integrity":"sha1-caUMhCnfync8kqOQpKA7OfzVHT4="},"is-plain-object":{"version":"5.0.0","dev":true,"integrity":"sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="},"is-proto-prop":{"version":"2.0.0","dev":true,"integrity":"sha512-jl3NbQ/fGLv5Jhan4uX+Ge9ohnemqyblWVVCpAvtTQzNFvV2xhJq+esnkIbYQ9F1nITXoLfDDQLp7LBw/zzncg==","requires":{"lowercase-keys":"1.0.1","proto-props":"2.0.0"}},"is-regex":{"version":"1.1.4","dev":true,"integrity":"sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==","requires":{"call-bind":"1.0.2","has-tostringtag":"1.0.0"}},"is-self-closing":{"version":"1.0.1","dev":true,"integrity":"sha512-E+60FomW7Blv5GXTlYee2KDrnG6srxF7Xt1SjrhWUGUEsTFIqY/nq2y3DaftCsgUMdh89V07IVfhY9KIJhLezg==","requires":{"self-closing-tags":"1.0.1"}},"is-shared-array-buffer":{"version":"1.0.1","dev":true,"integrity":"sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA=="},"is-ssh":{"version":"1.3.3","dev":true,"integrity":"sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ==","requires":{"protocols":"1.4.8"}},"is-stream":{"version":"2.0.1","dev":true,"integrity":"sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="},"is-string":{"version":"1.0.7","dev":true,"integrity":"sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==","requires":{"has-tostringtag":"1.0.0"}},"is-symbol":{"version":"1.0.4","dev":true,"integrity":"sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==","requires":{"has-symbols":"1.0.2"}},"is-text-path":{"version":"1.0.1","dev":true,"integrity":"sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=","requires":{"text-extensions":"1.9.0"}},"is-typedarray":{"version":"1.0.0","dev":true,"integrity":"sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="},"is-unicode-supported":{"version":"0.1.0","dev":true,"integrity":"sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="},"is-utf8":{"version":"0.2.1","dev":true,"integrity":"sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="},"is-weakref":{"version":"1.0.2","dev":true,"integrity":"sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==","requires":{"call-bind":"1.0.2"}},"is-windows":{"version":"1.0.2","dev":true,"integrity":"sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="},"is-wsl":{"version":"2.2.0","dev":true,"integrity":"sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==","requires":{"is-docker":"2.2.1"}},"isarray":{"version":"1.0.0","dev":true,"integrity":"sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="},"isexe":{"version":"2.0.0","dev":true,"integrity":"sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="},"isobject":{"version":"3.0.1","dev":true,"integrity":"sha1-TkMekrEalzFjaqH5yNHMvP2reN8="},"issue-parser":{"version":"6.0.0","dev":true,"integrity":"sha512-zKa/Dxq2lGsBIXQ7CUZWTHfvxPC2ej0KfO7fIPqLlHB9J2hJ7rGhZ5rilhuufylr4RXYPzJUeFjKxz305OsNlA==","requires":{"lodash.capitalize":"4.2.1","lodash.escaperegexp":"4.1.2","lodash.isplainobject":"4.0.6","lodash.isstring":"4.0.1","lodash.uniqby":"4.7.0"}},"java-parser":{"version":"2.0.1","dev":true,"integrity":"sha512-IPzs8LN8drvAZKbgW1MLLsrEeW4TwSy714I6ZHlEGNV6/42S2xRU5zDn3lP6uZQakwi7nyC00T6lZvwEnBujzw==","requires":{"chevrotain":"6.5.0","lodash":"4.17.21"}},"java-properties":{"version":"1.0.2","dev":true,"integrity":"sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ=="},"jju":{"version":"1.4.0","dev":true,"integrity":"sha1-o6vicYryQaKykE+EpiWXDzia4yo="},"js-tokens":{"version":"4.0.0","dev":true,"integrity":"sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="},"js-types":{"version":"1.0.0","dev":true,"integrity":"sha1-0kLmSU7Vcq08koCfyL7X92h8vwM="},"js-yaml":{"version":"4.1.0","dev":true,"integrity":"sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==","requires":{"argparse":"2.0.1"}},"jsdoc-type-pratt-parser":{"version":"2.2.3","dev":true,"integrity":"sha512-QPyxq62Q8veBSDtDrWmqaEPjSCeknUV9dH/OAGt3q9an8qC8UQDqitQiw1NvoMskIESpoRZ6qzt4H3rlK0xo8A=="},"jsdoctypeparser":{"version":"9.0.0","dev":true,"integrity":"sha512-jrTA2jJIL6/DAEILBEh2/w9QxCuwmvNXIry39Ay/HVfhE3o2yVV0U44blYkqdHA/OKloJEqvJy0xU+GSdE2SIw=="},"jsesc":{"version":"2.5.2","dev":true,"integrity":"sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="},"json-buffer":{"version":"3.0.1","dev":true,"integrity":"sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="},"json-parse-better-errors":{"version":"1.0.2","dev":true,"integrity":"sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="},"json-parse-even-better-errors":{"version":"2.3.1","dev":true,"integrity":"sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="},"json-schema":{"version":"0.4.0","dev":true,"integrity":"sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="},"json-schema-migrate":{"version":"2.0.0","dev":true,"integrity":"sha512-r38SVTtojDRp4eD6WsCqiE0eNDt4v1WalBXb9cyZYw9ai5cGtBwzRNWjHzJl38w6TxFkXAIA7h+fyX3tnrAFhQ==","requires":{"ajv":"8.10.0"}},"json-schema-traverse":{"version":"0.4.1","dev":true,"integrity":"sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="},"json-stable-stringify-without-jsonify":{"version":"1.0.1","dev":true,"integrity":"sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE="},"json-stringify-safe":{"version":"5.0.1","dev":true,"integrity":"sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="},"json2xml":{"version":"0.1.3","dev":true,"integrity":"sha1-mufCIL7dfGamaOJvesGC9nBOyiE="},"json5":{"version":"1.0.1","dev":true,"integrity":"sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==","requires":{"minimist":"1.2.5"}},"jsonc-eslint-parser":{"version":"1.4.1","dev":true,"integrity":"sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==","requires":{"acorn":"7.4.1","eslint-utils":"2.1.0","eslint-visitor-keys":"1.3.0","espree":"6.2.1","semver":"6.3.0"},"dependencies":{"eslint-utils":{"version":"2.1.0","dev":true,"integrity":"sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==","requires":{"eslint-visitor-keys":"1.3.0"}},"eslint-visitor-keys":{"version":"1.3.0","dev":true,"integrity":"sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ=="}}},"jsonfile":{"version":"6.1.0","dev":true,"integrity":"sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==","requires":{"universalify":"2.0.0"}},"jsonparse":{"version":"1.3.1","dev":true,"integrity":"sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="},"keyv":{"version":"4.1.1","dev":true,"integrity":"sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==","requires":{"json-buffer":"3.0.1"}},"kind-of":{"version":"6.0.3","dev":true,"integrity":"sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="},"kleur":{"version":"3.0.3","dev":true,"integrity":"sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="},"klona":{"version":"2.0.5","dev":true,"integrity":"sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ=="},"lazy-cache":{"version":"2.0.2","dev":true,"integrity":"sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=","requires":{"set-getter":"0.1.1"},"dependencies":{"kind-of":{"version":"3.2.2","dev":true,"integrity":"sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=","requires":{"is-buffer":"1.1.6"}}}},"leasot":{"version":"12.0.0","dev":true,"integrity":"sha512-TMe3cJTRUMpXsOFNXCig5U84wM44y84vawkl2fC7iAJif88l/b7BtTt49VrkMsivlxlqHYVu5PjuxB9sRQf39w==","requires":{"async":"3.2.3","chalk":"4.1.2","commander":"7.2.0","eol":"0.9.1","get-stdin":"8.0.0","globby":"11.1.0","json2xml":"0.1.3","lodash":"4.17.21","log-symbols":"4.1.0","strip-ansi":"6.0.1","text-table":"0.2.0"},"dependencies":{"commander":{"version":"7.2.0","dev":true,"integrity":"sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="}}},"levn":{"version":"0.4.1","dev":true,"integrity":"sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==","requires":{"prelude-ls":"1.2.1","type-check":"0.4.0"}},"lilconfig":{"version":"2.0.4","dev":true,"integrity":"sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA=="},"lines-and-columns":{"version":"1.2.4","dev":true,"integrity":"sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="},"linguist-languages":{"version":"7.15.0","dev":true,"integrity":"sha512-qkSSNDjDDycZ2Wcw+GziNBB3nNo3ddYUInM/PL8Amgwbd9RQ/BKGj2/1d6mdxKgBFnUqZuaDbkIwkE4KUwwmtQ=="},"lint-staged":{"version":"12.1.2","dev":true,"integrity":"sha512-bSMcQVqMW98HLLLR2c2tZ+vnDCnx4fd+0QJBQgN/4XkdspGRPc8DGp7UuOEBe1ApCfJ+wXXumYnJmU+wDo7j9A==","requires":{"cli-truncate":"3.1.0","colorette":"2.0.16","commander":"8.3.0","debug":"4.3.3","enquirer":"2.3.6","execa":"5.1.1","lilconfig":"2.0.4","listr2":"3.14.0","micromatch":"4.0.4","normalize-path":"3.0.0","object-inspect":"1.12.0","string-argv":"0.3.1","supports-color":"9.2.1","yaml":"1.10.2"},"dependencies":{"ansi-escapes":{"version":"4.3.2","dev":true,"integrity":"sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==","requires":{"type-fest":"0.21.3"}},"ansi-regex":{"version":"6.0.1","dev":true,"integrity":"sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="},"ansi-styles":{"version":"6.1.0","dev":true,"integrity":"sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ=="},"cli-cursor":{"version":"3.1.0","dev":true,"integrity":"sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==","requires":{"restore-cursor":"3.1.0"}},"emoji-regex":{"version":"9.2.2","dev":true,"integrity":"sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="},"is-fullwidth-code-point":{"version":"4.0.0","dev":true,"integrity":"sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="},"restore-cursor":{"version":"3.1.0","dev":true,"integrity":"sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==","requires":{"onetime":"5.1.2","signal-exit":"3.0.7"}},"rxjs":{"version":"7.5.4","dev":true,"integrity":"sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==","requires":{"tslib":"2.3.1"}},"string-width":{"version":"5.1.0","dev":true,"integrity":"sha512-7x54QnN21P+XL/v8SuNKvfgsUre6PXpN7mc77N3HlZv+f1SBRGmjxtOud2Z6FZ8DmdkD/IdjCaf9XXbnqmTZGQ==","requires":{"eastasianwidth":"0.2.0","emoji-regex":"9.2.2","strip-ansi":"7.0.1"}},"strip-ansi":{"version":"7.0.1","dev":true,"integrity":"sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==","requires":{"ansi-regex":"6.0.1"}},"supports-color":{"version":"9.2.1","dev":true,"integrity":"sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ=="},"type-fest":{"version":"0.21.3","dev":true,"integrity":"sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="}}},"liquidjs":{"version":"9.28.5","dev":true,"integrity":"sha512-K9mSxsAilSezwBtEUtaTpJy6cff3YLoBShyjNLExk5tk/rkygWBPJYWCoJIXKdKdXs/2M/WG3EXYU1ssAtSrsQ=="},"listr2":{"version":"3.14.0","dev":true,"integrity":"sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==","requires":{"cli-truncate":"2.1.0","colorette":"2.0.16","enquirer":"2.3.6","log-update":"4.0.0","p-map":"4.0.0","rfdc":"1.3.0","rxjs":"7.5.4","through":"2.3.8","wrap-ansi":"7.0.0"},"dependencies":{"cli-truncate":{"version":"2.1.0","dev":true,"integrity":"sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==","requires":{"slice-ansi":"3.0.0","string-width":"4.2.3"}},"slice-ansi":{"version":"3.0.0","dev":true,"integrity":"sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==","requires":{"ansi-styles":"4.3.0","astral-regex":"2.0.0","is-fullwidth-code-point":"3.0.0"}}}},"load-json-file":{"version":"4.0.0","dev":true,"integrity":"sha1-L19Fq5HjMhYjT9U62rZo607AmTs=","requires":{"graceful-fs":"4.2.9","parse-json":"4.0.0","pify":"3.0.0","strip-bom":"3.0.0"}},"locate-path":{"version":"5.0.0","dev":true,"integrity":"sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==","requires":{"p-locate":"4.1.0"}},"lodash":{"version":"4.17.21","dev":true,"integrity":"sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="},"lodash._reinterpolate":{"version":"3.0.0","dev":true,"integrity":"sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0="},"lodash.camelcase":{"version":"4.3.0","dev":true,"integrity":"sha1-soqmKIorn8ZRA1x3EfZathkDMaY="},"lodash.capitalize":{"version":"4.2.1","dev":true,"integrity":"sha1-+CbJtOKoUR2E46yinbBeGk87cqk="},"lodash.escaperegexp":{"version":"4.1.2","dev":true,"integrity":"sha1-ZHYsSGGAglGKw99Mz11YhtriA0c="},"lodash.get":{"version":"4.4.2","dev":true,"integrity":"sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="},"lodash.ismatch":{"version":"4.4.0","dev":true,"integrity":"sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc="},"lodash.isplainobject":{"version":"4.0.6","dev":true,"integrity":"sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="},"lodash.isstring":{"version":"4.0.1","dev":true,"integrity":"sha1-1SfftUVuynzJu5XV2ur4i6VKVFE="},"lodash.kebabcase":{"version":"4.1.1","dev":true,"integrity":"sha1-hImxyw0p/4gZXM7KRI/21swpXDY="},"lodash.last":{"version":"3.0.0","dev":true,"integrity":"sha1-JC9mMRLdTG5jcoxgo8kJ0b2tvUw="},"lodash.map":{"version":"4.6.0","dev":true,"integrity":"sha1-dx7Hg540c9nEzeKLGTlMNWL09tM="},"lodash.merge":{"version":"4.6.2","dev":true,"integrity":"sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="},"lodash.snakecase":{"version":"4.1.1","dev":true,"integrity":"sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40="},"lodash.template":{"version":"4.5.0","dev":true,"integrity":"sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==","requires":{"lodash._reinterpolate":"3.0.0","lodash.templatesettings":"4.2.0"}},"lodash.templatesettings":{"version":"4.2.0","dev":true,"integrity":"sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==","requires":{"lodash._reinterpolate":"3.0.0"}},"lodash.uniqby":{"version":"4.7.0","dev":true,"integrity":"sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI="},"lodash.upperfirst":{"version":"4.3.1","dev":true,"integrity":"sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984="},"lodash.zip":{"version":"4.2.0","dev":true,"integrity":"sha1-7GZi5IlkCO1KtsVCo5kLcswIACA="},"lodash.zipobject":{"version":"4.1.3","dev":true,"integrity":"sha1-s5n1q6j/YqdG9peb8gshT5ZNvvg="},"log-ok":{"version":"0.1.1","dev":true,"integrity":"sha1-vqPdNqzQuKckDXhza1uXxlREozQ=","requires":{"ansi-green":"0.1.1","success-symbol":"0.1.0"}},"log-symbols":{"version":"4.1.0","dev":true,"integrity":"sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==","requires":{"chalk":"4.1.2","is-unicode-supported":"0.1.0"}},"log-update":{"version":"4.0.0","dev":true,"integrity":"sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==","requires":{"ansi-escapes":"4.3.2","cli-cursor":"3.1.0","slice-ansi":"4.0.0","wrap-ansi":"6.2.0"},"dependencies":{"slice-ansi":{"version":"4.0.0","dev":true,"integrity":"sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==","requires":{"ansi-styles":"4.3.0","astral-regex":"2.0.0","is-fullwidth-code-point":"3.0.0"}},"wrap-ansi":{"version":"6.2.0","dev":true,"integrity":"sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==","requires":{"ansi-styles":"4.3.0","string-width":"4.2.3","strip-ansi":"6.0.1"}}}},"log-utils":{"version":"0.2.1","dev":true,"integrity":"sha1-pMIXoN2aUFFdm5ICBgkas9TgMc8=","requires":{"ansi-colors":"0.2.0","error-symbol":"0.1.0","info-symbol":"0.1.0","log-ok":"0.1.1","success-symbol":"0.1.0","time-stamp":"1.1.0","warning-symbol":"0.1.0"}},"logging-helpers":{"version":"1.0.0","dev":true,"integrity":"sha512-qyIh2goLt1sOgQQrrIWuwkRjUx4NUcEqEGAcYqD8VOnOC6ItwkrVE8/tA4smGpjzyp4Svhc6RodDp9IO5ghpyA==","requires":{"isobject":"3.0.1","log-utils":"0.2.1"}},"longest":{"version":"2.0.1","dev":true,"integrity":"sha1-eB4YMpaqlPbU2RbcM10NF676I/g="},"lowercase-keys":{"version":"1.0.1","dev":true,"integrity":"sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="},"lru-cache":{"version":"6.0.0","dev":true,"integrity":"sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==","requires":{"yallist":"4.0.0"}},"lz-string":{"version":"1.4.4","dev":true,"integrity":"sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY="},"make-dir":{"version":"3.1.0","dev":true,"integrity":"sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==","requires":{"semver":"6.3.0"}},"make-error":{"version":"1.3.6","dev":true,"integrity":"sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="},"map-age-cleaner":{"version":"0.1.3","dev":true,"integrity":"sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==","requires":{"p-defer":"1.0.0"}},"map-cache":{"version":"0.2.2","dev":true,"integrity":"sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8="},"map-obj":{"version":"4.3.0","dev":true,"integrity":"sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ=="},"map-visit":{"version":"1.0.0","dev":true,"integrity":"sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=","requires":{"object-visit":"1.0.1"}},"marked":{"version":"4.0.12","dev":true,"integrity":"sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ=="},"marked-terminal":{"version":"5.1.1","dev":true,"integrity":"sha512-+cKTOx9P4l7HwINYhzbrBSyzgxO2HaHKGZGuB1orZsMIgXYaJyfidT81VXRdpelW/PcHEWxywscePVgI/oUF6g==","requires":{"ansi-escapes":"5.0.0","cardinal":"2.1.1","chalk":"5.0.0","cli-table3":"0.6.1","marked":"4.0.12","node-emoji":"1.11.0","supports-hyperlinks":"2.2.0"},"dependencies":{"chalk":{"version":"5.0.0","dev":true,"integrity":"sha512-/duVOqst+luxCQRKEo4bNxinsOQtMP80ZYm7mMqzuh5PociNL0PvmHFvREJ9ueYL2TxlHjBcmLCdmocx9Vg+IQ=="}}},"mdast-util-from-markdown":{"version":"0.8.5","dev":true,"integrity":"sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==","requires":{"@types/mdast":"3.0.10","mdast-util-to-string":"2.0.0","micromark":"2.11.4","parse-entities":"2.0.0","unist-util-stringify-position":"2.0.3"}},"mdast-util-to-string":{"version":"2.0.0","dev":true,"integrity":"sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w=="},"mem":{"version":"8.1.1","dev":true,"integrity":"sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA==","requires":{"map-age-cleaner":"0.1.3","mimic-fn":"3.1.0"}},"meow":{"version":"8.1.2","dev":true,"integrity":"sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==","requires":{"@types/minimist":"1.2.2","camelcase-keys":"6.2.2","decamelize-keys":"1.1.0","hard-rejection":"2.1.0","minimist-options":"4.1.0","normalize-package-data":"3.0.3","read-pkg-up":"7.0.1","redent":"3.0.0","trim-newlines":"3.0.1","type-fest":"0.18.1","yargs-parser":"20.2.9"}},"merge":{"version":"2.1.1","dev":true,"integrity":"sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w=="},"merge-stream":{"version":"2.0.0","dev":true,"integrity":"sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="},"merge2":{"version":"1.4.1","dev":true,"integrity":"sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="},"micromark":{"version":"2.11.4","dev":true,"integrity":"sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==","requires":{"debug":"4.3.3","parse-entities":"2.0.0"}},"micromatch":{"version":"4.0.4","dev":true,"integrity":"sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==","requires":{"braces":"3.0.2","picomatch":"2.3.1"}},"mime":{"version":"3.0.0","dev":true,"integrity":"sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="},"mime-db":{"version":"1.51.0","dev":true,"integrity":"sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="},"mime-types":{"version":"2.1.34","dev":true,"integrity":"sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==","requires":{"mime-db":"1.51.0"}},"mimic-fn":{"version":"2.1.0","dev":true,"integrity":"sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="},"mimic-response":{"version":"1.0.1","dev":true,"integrity":"sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="},"min-indent":{"version":"1.0.1","dev":true,"integrity":"sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg=="},"minimatch":{"version":"3.1.2","dev":true,"integrity":"sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==","requires":{"brace-expansion":"1.1.11"}},"minimist":{"version":"1.2.5","dev":true,"integrity":"sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="},"minimist-options":{"version":"4.1.0","dev":true,"integrity":"sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==","requires":{"arrify":"1.0.1","is-plain-obj":"1.1.0","kind-of":"6.0.3"}},"mixin-deep":{"version":"1.3.2","dev":true,"integrity":"sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==","requires":{"for-in":"1.0.2","is-extendable":"1.0.1"},"dependencies":{"is-extendable":{"version":"1.0.1","dev":true,"integrity":"sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==","requires":{"is-plain-object":"2.0.4"}}}},"modify-values":{"version":"1.0.1","dev":true,"integrity":"sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw=="},"moment":{"version":"2.29.1","dev":true,"integrity":"sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="},"ms":{"version":"2.1.2","dev":true,"integrity":"sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="},"multimap":{"version":"1.1.0","dev":true,"integrity":"sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw=="},"mute-stream":{"version":"0.0.7","dev":true,"integrity":"sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="},"mvdan-sh":{"version":"0.5.0","dev":true,"integrity":"sha512-UWbdl4LHd2fUnaEcOUFVWRdWGLkNoV12cKVIPiirYd8qM5VkCoCTXErlDubevrkEG7kGohvjRxAlTQmOqG80tw=="},"nanomatch":{"version":"1.2.13","dev":true,"integrity":"sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==","requires":{"arr-diff":"4.0.0","array-unique":"0.3.2","define-property":"2.0.2","extend-shallow":"3.0.2","fragment-cache":"0.2.1","is-windows":"1.0.2","kind-of":"6.0.3","object.pick":"1.3.0","regex-not":"1.0.2","snapdragon":"0.8.2","to-regex":"3.0.2"}},"natural-compare":{"version":"1.4.0","dev":true,"integrity":"sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc="},"natural-compare-lite":{"version":"1.4.0","dev":true,"integrity":"sha1-F7CVgZiJef3a/gIB6TG6kzyWy7Q="},"neo-async":{"version":"2.6.2","dev":true,"integrity":"sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="},"nerf-dart":{"version":"1.0.0","dev":true,"integrity":"sha1-5tq3/r9a2Bbqgc9cYpxaDr3nLBo="},"node-emoji":{"version":"1.11.0","dev":true,"integrity":"sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==","requires":{"lodash":"4.17.21"}},"node-fetch":{"version":"2.6.7","dev":true,"integrity":"sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==","requires":{"whatwg-url":"5.0.0"}},"node-releases":{"version":"2.0.2","dev":true,"integrity":"sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg=="},"node-sql-parser":{"version":"4.1.1","dev":true,"integrity":"sha512-VV5ojNHj0ZOjA66DbzbJwESZmgE8OtfJamrsOq0CYxCatFeHh24uBoVP+GXo06qR4T4hzol67hZ62gVf/oZBvA==","requires":{"big-integer":"1.6.51"}},"normalize-package-data":{"version":"3.0.3","dev":true,"integrity":"sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==","requires":{"hosted-git-info":"4.1.0","is-core-module":"2.8.1","semver":"7.3.5","validate-npm-package-license":"3.0.4"}},"normalize-path":{"version":"3.0.0","dev":true,"integrity":"sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="},"normalize-url":{"version":"6.1.0","dev":true,"integrity":"sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="},"npm":{"version":"8.5.1","dev":true,"integrity":"sha512-zHrOHAatEPJ59o2JIPlhgc9LX9mb8xFrqu4kiiul4w1IGMTtKn2lqRiGIRKU0or69NSLXNmqbCP9bNJIr/wB6Q=="},"npm-run-path":{"version":"4.0.1","dev":true,"integrity":"sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==","requires":{"path-key":"3.1.1"}},"obj-props":{"version":"1.3.0","dev":true,"integrity":"sha512-k2Xkjx5wn6eC3537SWAXHzB6lkI81kS+icMKMkh4nG3w7shWG6MaWOBrNvhWVOszrtL5uxdfymQQfPUxwY+2eg=="},"object-assign":{"version":"4.1.1","dev":true,"integrity":"sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="},"object-copy":{"version":"0.1.0","dev":true,"integrity":"sha1-fn2Fi3gb18mRpBupde04EnVOmYw=","requires":{"copy-descriptor":"0.1.1","define-property":"0.2.5","kind-of":"3.2.2"}},"object-inspect":{"version":"1.12.0","dev":true,"integrity":"sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="},"object-keys":{"version":"1.1.1","dev":true,"integrity":"sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="},"object-visit":{"version":"1.0.1","dev":true,"integrity":"sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=","requires":{"isobject":"3.0.1"}},"object.assign":{"version":"4.1.2","dev":true,"integrity":"sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3","has-symbols":"1.0.2","object-keys":"1.1.1"}},"object.pick":{"version":"1.3.0","dev":true,"integrity":"sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=","requires":{"isobject":"3.0.1"}},"object.values":{"version":"1.1.5","dev":true,"integrity":"sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3","es-abstract":"1.19.1"}},"once":{"version":"1.4.0","dev":true,"integrity":"sha1-WDsap3WWHUsROsF9nFC6753Xa9E=","requires":{"wrappy":"1.0.2"}},"onetime":{"version":"5.1.2","dev":true,"integrity":"sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==","requires":{"mimic-fn":"2.1.0"}},"only-allow":{"version":"1.0.0","dev":true,"integrity":"sha512-DQazysAz1cw8JxxYVqg+wXWSm6K7smVqORwS+dtXQWPeRwjsWvRpxMRa5FPiOPxH4e05xCDv12ncAUF8JlVukw==","requires":{"boxen":"4.2.0","which-pm-runs":"1.1.0"},"dependencies":{"boxen":{"version":"4.2.0","dev":true,"integrity":"sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==","requires":{"ansi-align":"3.0.1","camelcase":"5.3.1","chalk":"3.0.0","cli-boxes":"2.2.1","string-width":"4.2.3","term-size":"2.2.1","type-fest":"0.8.1","widest-line":"3.1.0"}},"chalk":{"version":"3.0.0","dev":true,"integrity":"sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==","requires":{"ansi-styles":"4.3.0","supports-color":"7.2.0"}},"type-fest":{"version":"0.8.1","dev":true,"integrity":"sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="}}},"open":{"version":"8.4.0","dev":true,"integrity":"sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==","requires":{"define-lazy-prop":"2.0.0","is-docker":"2.2.1","is-wsl":"2.2.0"}},"open-cli":{"version":"7.0.1","dev":true,"integrity":"sha512-w//Mb5nLGTu9aIAsAehgxV+CGEkd+P3CbdoTW8y2coQ/fmGXBSrea0i4RBqGnd9prSPX1akrBYc0e3NnWM4SPA==","requires":{"file-type":"16.5.3","get-stdin":"9.0.0","meow":"10.1.2","open":"8.4.0","tempy":"1.0.1"},"dependencies":{"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"camelcase":{"version":"6.3.0","dev":true,"integrity":"sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="},"camelcase-keys":{"version":"7.0.2","dev":true,"integrity":"sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==","requires":{"camelcase":"6.3.0","map-obj":"4.3.0","quick-lru":"5.1.1","type-fest":"1.4.0"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"decamelize":{"version":"5.0.1","dev":true,"integrity":"sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA=="},"find-up":{"version":"5.0.0","dev":true,"integrity":"sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==","requires":{"locate-path":"6.0.0","path-exists":"4.0.0"}},"get-stdin":{"version":"9.0.0","dev":true,"integrity":"sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA=="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"indent-string":{"version":"5.0.0","dev":true,"integrity":"sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="},"locate-path":{"version":"6.0.0","dev":true,"integrity":"sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==","requires":{"p-locate":"5.0.0"}},"meow":{"version":"10.1.2","dev":true,"integrity":"sha512-zbuAlN+V/sXlbGchNS9WTWjUzeamwMt/BApKCJi7B0QyZstZaMx0n4Unll/fg0njGtMdC9UP5SAscvOCLYdM+Q==","requires":{"@types/minimist":"1.2.2","camelcase-keys":"7.0.2","decamelize":"5.0.1","decamelize-keys":"1.1.0","hard-rejection":"2.1.0","minimist-options":"4.1.0","normalize-package-data":"3.0.3","read-pkg-up":"8.0.0","redent":"4.0.0","trim-newlines":"4.0.2","type-fest":"1.4.0","yargs-parser":"20.2.9"}},"p-limit":{"version":"3.1.0","dev":true,"integrity":"sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==","requires":{"yocto-queue":"0.1.0"}},"p-locate":{"version":"5.0.0","dev":true,"integrity":"sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==","requires":{"p-limit":"3.1.0"}},"quick-lru":{"version":"5.1.1","dev":true,"integrity":"sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="},"read-pkg":{"version":"6.0.0","dev":true,"integrity":"sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==","requires":{"@types/normalize-package-data":"2.4.1","normalize-package-data":"3.0.3","parse-json":"5.2.0","type-fest":"1.4.0"}},"read-pkg-up":{"version":"8.0.0","dev":true,"integrity":"sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==","requires":{"find-up":"5.0.0","read-pkg":"6.0.0","type-fest":"1.4.0"}},"redent":{"version":"4.0.0","dev":true,"integrity":"sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==","requires":{"indent-string":"5.0.0","strip-indent":"4.0.0"}},"strip-indent":{"version":"4.0.0","dev":true,"integrity":"sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==","requires":{"min-indent":"1.0.1"}},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"trim-newlines":{"version":"4.0.2","dev":true,"integrity":"sha512-GJtWyq9InR/2HRiLZgpIKv+ufIKrVrvjQWEj7PxAXNc5dwbNJkqhAUoAGgzRmULAnoOM5EIpveYd3J2VeSAIew=="},"type-fest":{"version":"1.4.0","dev":true,"integrity":"sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="}}},"optionator":{"version":"0.9.1","dev":true,"integrity":"sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==","requires":{"deep-is":"0.1.4","fast-levenshtein":"2.0.6","levn":"0.4.1","prelude-ls":"1.2.1","type-check":"0.4.0","word-wrap":"1.2.3"}},"os-tmpdir":{"version":"1.0.2","dev":true,"integrity":"sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="},"p-cancelable":{"version":"2.1.1","dev":true,"integrity":"sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="},"p-defer":{"version":"1.0.0","dev":true,"integrity":"sha1-n26xgvbJqozXQwBKfU+WsZaw+ww="},"p-each-series":{"version":"2.2.0","dev":true,"integrity":"sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA=="},"p-filter":{"version":"2.1.0","dev":true,"integrity":"sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==","requires":{"p-map":"2.1.0"}},"p-is-promise":{"version":"3.0.0","dev":true,"integrity":"sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ=="},"p-limit":{"version":"2.3.0","dev":true,"integrity":"sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==","requires":{"p-try":"2.2.0"}},"p-locate":{"version":"4.1.0","dev":true,"integrity":"sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==","requires":{"p-limit":"2.3.0"}},"p-map":{"version":"4.0.0","dev":true,"integrity":"sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==","requires":{"aggregate-error":"3.1.0"}},"p-reduce":{"version":"2.1.0","dev":true,"integrity":"sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw=="},"p-retry":{"version":"4.6.1","dev":true,"integrity":"sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==","requires":{"@types/retry":"0.12.1","retry":"0.13.1"}},"p-try":{"version":"2.2.0","dev":true,"integrity":"sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="},"parent-module":{"version":"1.0.1","dev":true,"integrity":"sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==","requires":{"callsites":"3.1.0"}},"parse-author":{"version":"2.0.0","dev":true,"integrity":"sha1-00YL8d3Q367tQtp1QkLmX7aEqB8=","requires":{"author-regex":"1.0.0"}},"parse-entities":{"version":"2.0.0","dev":true,"integrity":"sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==","requires":{"character-entities":"1.2.4","character-entities-legacy":"1.1.4","character-reference-invalid":"1.1.4","is-alphanumerical":"1.0.4","is-decimal":"1.0.4","is-hexadecimal":"1.0.4"}},"parse-gitignore":{"version":"1.0.1","dev":true,"integrity":"sha512-UGyowyjtx26n65kdAMWhm6/3uy5uSrpcuH7tt+QEVudiBoVS+eqHxD5kbi9oWVRwj7sCzXqwuM+rUGw7earl6A=="},"parse-json":{"version":"5.2.0","dev":true,"integrity":"sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==","requires":{"@babel/code-frame":"7.16.7","error-ex":"1.3.2","json-parse-even-better-errors":"2.3.1","lines-and-columns":"1.2.4"}},"parse-passwd":{"version":"1.0.0","dev":true,"integrity":"sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY="},"parse-path":{"version":"4.0.3","dev":true,"integrity":"sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA==","requires":{"is-ssh":"1.3.3","protocols":"1.4.8","qs":"6.10.3","query-string":"6.14.1"}},"pascalcase":{"version":"0.1.1","dev":true,"integrity":"sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="},"path-exists":{"version":"4.0.0","dev":true,"integrity":"sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="},"path-is-absolute":{"version":"1.0.1","dev":true,"integrity":"sha1-F0uSaHNVNP+8es5r9TpanhtcX18="},"path-key":{"version":"3.1.1","dev":true,"integrity":"sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="},"path-parse":{"version":"1.0.7","dev":true,"integrity":"sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="},"path-type":{"version":"4.0.0","dev":true,"integrity":"sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="},"peek-readable":{"version":"4.1.0","dev":true,"integrity":"sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="},"php-parser":{"version":"https://codeload.github.com/glayzzle/php-parser/tar.gz/27abcb2337ac6450c068ef064982dfabf77916a5","dev":true,"resolved":"https://codeload.github.com/glayzzle/php-parser/tar.gz/27abcb2337ac6450c068ef064982dfabf77916a5"},"picocolors":{"version":"1.0.0","dev":true,"integrity":"sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="},"picomatch":{"version":"2.3.1","dev":true,"integrity":"sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="},"pify":{"version":"3.0.0","dev":true,"integrity":"sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="},"pkg-conf":{"version":"2.1.0","dev":true,"integrity":"sha1-ISZRTKbyq/69FoWW3xi6V4Z/AFg=","requires":{"find-up":"2.1.0","load-json-file":"4.0.0"}},"plur":{"version":"4.0.0","dev":true,"integrity":"sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==","requires":{"irregular-plurals":"3.3.0"}},"pluralize":{"version":"8.0.0","dev":true,"integrity":"sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="},"posix-character-classes":{"version":"0.1.1","dev":true,"integrity":"sha1-AerA/jta9xoqbAL+q7jB/vfgDqs="},"prelude-ls":{"version":"1.2.1","dev":true,"integrity":"sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="},"prettier":{"version":"2.5.1","dev":true,"integrity":"sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg=="},"prettier-config-sexy-mode":{"version":"1.0.1","dev":true,"integrity":"sha512-6ZdAv6LW2vq++c5fqoh2MWrIZk9fi67o608xizEPYKPMjGUNDsqjJ/pYiJLp7yDHgGerEERc4bRQqlh8a9W6YA==","requires":{"@prettier/plugin-php":"0.17.6","@prettier/plugin-pug":"1.19.2","@prettier/plugin-ruby":"2.0.0","@prettier/plugin-xml":"1.2.0","prettier":"2.5.1","prettier-plugin-go-template":"0.0.11","prettier-plugin-ini":"0.3.1","prettier-plugin-java":"1.6.1","prettier-plugin-jsdoc":"0.3.30","prettier-plugin-organize-imports":"2.3.4","prettier-plugin-package-perfection":"1.0.6","prettier-plugin-properties":"0.1.0","prettier-plugin-sh":"0.7.1","prettier-plugin-solidity":"1.0.0-beta.19","prettier-plugin-sql":"0.4.1","tslib":"2.3.1"},"dependencies":{"acorn":{"version":"7.4.1","dev":true,"integrity":"sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="},"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"commander":{"version":"4.1.1","dev":true,"integrity":"sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="},"emoji-regex":{"version":"10.0.0","dev":true,"integrity":"sha512-KmJa8l6uHi1HrBI34udwlzZY1jOEuID/ft4d8BSSEdRyap7PwBEt910453PJa5MuGvxkLqlt4Uvhu7tttFHViw=="},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"mimic-fn":{"version":"3.1.0","dev":true,"integrity":"sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ=="},"resolve-from":{"version":"4.0.0","dev":true,"integrity":"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}}}},"prettier-linter-helpers":{"version":"1.0.0","dev":true,"integrity":"sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==","requires":{"fast-diff":"1.2.0"}},"prettier-package-json":{"version":"2.6.0","dev":true,"integrity":"sha512-CS7utu4Jfm6xxCrIA4zZiOtNIwZhq0EyHnK01vmliV2QRU+L6/Ywy1tB6uUpT9Lwt5qpvRMHNApa6jxoRHfafA==","requires":{"commander":"4.1.1","cosmiconfig":"7.0.1","fs-extra":"10.0.0","glob":"7.2.0","minimatch":"3.1.2","parse-author":"2.0.0","sort-object-keys":"1.1.3","sort-order":"1.0.1"}},"prettier-plugin-go-template":{"version":"0.0.11","dev":true,"integrity":"sha512-qtgoEjvbgmcDp9TOqYNgrPrA41s6S1UMyzMqjcxdxQahTX0webWfbamyA/x3XeBFEEJmgXrRAirzJrIVzImsMg==","requires":{"prettier":"2.5.1","ulid":"2.3.0"}},"prettier-plugin-ini":{"version":"0.3.1","dev":true,"integrity":"sha512-p2MPOpkf0ebBme4n8U3TD7/uC3azlEyPgbfgU/l5SCgAvSKmXmRMSxKxonhSoQAGtdwQWcEADJbmzl+X72yN4Q==","requires":{"prettier":"2.5.1"}},"prettier-plugin-java":{"version":"1.6.1","dev":true,"integrity":"sha512-kSY17V/P88nILlILb5iMp16TVJy6Ls9Jy4zAzI4+GsEuRDZH5VqRuLd8aJS1ImWxVgRjNBmoFOjxYyxkRM0SRA==","requires":{"java-parser":"2.0.1","lodash":"4.17.21","prettier":"2.3.1"},"dependencies":{"chevrotain":{"version":"6.5.0","dev":true,"integrity":"sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==","requires":{"regexp-to-ast":"0.4.0"}},"prettier":{"version":"2.3.1","dev":true,"integrity":"sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA=="},"regexp-to-ast":{"version":"0.4.0","dev":true,"integrity":"sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw=="}}},"prettier-plugin-jsdoc":{"version":"0.3.30","dev":true,"integrity":"sha512-BTBojOMmrUA1qsWLpJN5whUfU/E72WBUQAB5AvrDkha+O8TxmqaAivnuW+87ItYGRPBFWWzj2r5iWELhBml1Ag==","requires":{"binary-search-bounds":"2.0.5","comment-parser":"1.3.0","linguist-languages":"7.15.0","mdast-util-from-markdown":"0.8.5","prettier":"2.5.1"}},"prettier-plugin-organize-imports":{"version":"2.3.4","dev":true,"integrity":"sha512-R8o23sf5iVL/U71h9SFUdhdOEPsi3nm42FD/oDYIZ2PQa4TNWWuWecxln6jlIQzpZTDMUeO1NicJP6lLn2TtRw==","requires":{"prettier":"2.5.1","typescript":"4.5.5"}},"prettier-plugin-package-perfection":{"version":"1.0.6","dev":true,"integrity":"sha512-7nxpRYdAdda6xiwrVDYDxcnxPfBu58JJPGtoJOwh2uohE6RfnUJOOjV76woXG8kO+mBptHAQtqoT6lsLRvatqA==","requires":{"prettier-package-json":"2.6.0"}},"prettier-plugin-properties":{"version":"0.1.0","dev":true,"integrity":"sha512-lObSgVaTVWSyYWxKMOzRGmvQp64S1qumu5vS91ZlMc198ay8EGUuDH+Tc019iMJXc2KNpdAYif2qAJA6mjTkgA==","requires":{"dot-properties":"1.0.1","linguist-languages":"7.15.0","prettier":"2.5.1"}},"prettier-plugin-sh":{"version":"0.7.1","dev":true,"integrity":"sha512-2MWRdGOSz0yf/z2kTKF1AqxDuH9MZD8faoDAz5ySGphxssi9oyM3Ys+jp7AfqsCXvGUDbRA4EJOlKS0yZKAW6w==","requires":{"mvdan-sh":"0.5.0","prettier":"2.5.1"}},"prettier-plugin-solidity":{"version":"1.0.0-beta.19","dev":true,"integrity":"sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g==","requires":{"@solidity-parser/parser":"0.14.1","emoji-regex":"10.0.0","escape-string-regexp":"4.0.0","prettier":"2.5.1","semver":"7.3.5","solidity-comments-extractor":"0.0.7","string-width":"4.2.3"},"dependencies":{"escape-string-regexp":{"version":"4.0.0","dev":true,"integrity":"sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="}}},"prettier-plugin-sql":{"version":"0.4.1","dev":true,"integrity":"sha512-HSBn1f1ZbFIqWm9vVJcdQl1tzdh8qYZbBNK7z3iWhDO/DXM7gZH2HnS5r99e3tYqf34WIdo2nSCWaqylo+5fPw==","requires":{"node-sql-parser":"4.1.1","prettier":"2.5.1","sql-formatter":"4.0.2"}},"pretty-format":{"version":"26.6.2","dev":true,"integrity":"sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==","requires":{"@jest/types":"26.6.2","ansi-regex":"5.0.1","ansi-styles":"4.3.0","react-is":"17.0.2"}},"process-nextick-args":{"version":"2.0.1","dev":true,"integrity":"sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="},"prompts":{"version":"2.4.2","dev":true,"integrity":"sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==","requires":{"kleur":"3.0.3","sisteransi":"1.0.5"}},"proto-props":{"version":"2.0.0","dev":true,"integrity":"sha512-2yma2tog9VaRZY2mn3Wq51uiSW4NcPYT1cQdBagwyrznrilKSZwIZ0UG3ZPL/mx+axEns0hE35T5ufOYZXEnBQ=="},"protocols":{"version":"1.4.8","dev":true,"integrity":"sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg=="},"pseudomap":{"version":"1.0.2","dev":true,"integrity":"sha1-8FKijacOYYkX7wqKw0wa5aaChrM="},"pug-error":{"version":"2.0.0","dev":true,"integrity":"sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ=="},"pug-lexer":{"version":"5.0.1","dev":true,"integrity":"sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==","requires":{"character-parser":"2.2.0","is-expression":"4.0.0","pug-error":"2.0.0"}},"pump":{"version":"3.0.0","dev":true,"integrity":"sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==","requires":{"end-of-stream":"1.4.4","once":"1.4.0"}},"punycode":{"version":"2.1.1","dev":true,"integrity":"sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="},"q":{"version":"1.5.1","dev":true,"integrity":"sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="},"qs":{"version":"6.10.3","dev":true,"integrity":"sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==","requires":{"side-channel":"1.0.4"}},"query-string":{"version":"6.14.1","dev":true,"integrity":"sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==","requires":{"decode-uri-component":"0.2.0","filter-obj":"1.1.0","split-on-first":"1.1.0","strict-uri-encode":"2.0.0"}},"queue-microtask":{"version":"1.2.3","dev":true,"integrity":"sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="},"quick-lru":{"version":"4.0.1","dev":true,"integrity":"sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g=="},"rc":{"version":"1.2.8","dev":true,"integrity":"sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==","requires":{"deep-extend":"0.6.0","ini":"1.3.8","minimist":"1.2.5","strip-json-comments":"2.0.1"}},"react-is":{"version":"17.0.2","dev":true,"integrity":"sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="},"read-pkg":{"version":"5.2.0","dev":true,"integrity":"sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==","requires":{"@types/normalize-package-data":"2.4.1","normalize-package-data":"2.5.0","parse-json":"5.2.0","type-fest":"0.6.0"},"dependencies":{"type-fest":{"version":"0.6.0","dev":true,"integrity":"sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="}}},"read-pkg-up":{"version":"7.0.1","dev":true,"integrity":"sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==","requires":{"find-up":"4.1.0","read-pkg":"5.2.0","type-fest":"0.8.1"},"dependencies":{"hosted-git-info":{"version":"2.8.9","dev":true,"integrity":"sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="},"normalize-package-data":{"version":"2.5.0","dev":true,"integrity":"sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==","requires":{"hosted-git-info":"2.8.9","resolve":"1.22.0","semver":"5.7.1","validate-npm-package-license":"3.0.4"}},"semver":{"version":"5.7.1","dev":true,"integrity":"sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="},"type-fest":{"version":"0.8.1","dev":true,"integrity":"sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="}}},"readable-stream":{"version":"3.6.0","dev":true,"integrity":"sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==","requires":{"inherits":"2.0.4","string_decoder":"1.3.0","util-deprecate":"1.0.2"}},"readable-web-to-node-stream":{"version":"3.0.2","dev":true,"integrity":"sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==","requires":{"readable-stream":"3.6.0"}},"redent":{"version":"3.0.0","dev":true,"integrity":"sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==","requires":{"indent-string":"4.0.0","strip-indent":"3.0.0"}},"redeyed":{"version":"2.1.1","dev":true,"integrity":"sha1-iYS1gV2ZyyIEacme7v/jiRPmzAs=","requires":{"esprima":"4.0.1"}},"refa":{"version":"0.9.1","dev":true,"integrity":"sha512-egU8LgFq2VXlAfUi8Jcbr5X38wEOadMFf8tCbshgcpVCYlE7k84pJOSlnvXF+muDB4igkdVMq7Z/kiNPqDT9TA==","requires":{"regexpp":"3.2.0"}},"regenerator-runtime":{"version":"0.13.9","dev":true,"integrity":"sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="},"regex-not":{"version":"1.0.2","dev":true,"integrity":"sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==","requires":{"extend-shallow":"3.0.2","safe-regex":"1.1.0"}},"regexp-ast-analysis":{"version":"0.3.0","dev":true,"integrity":"sha512-11PlbBSUxwWpdj6BdZUKfhDdV9g+cveqHB+BqBQDBD7ZermDBVgtyowUaXTvT0dO3tZYo2bDIr/GoED6X1aYSA==","requires":{"refa":"0.9.1","regexpp":"3.2.0"}},"regexp-to-ast":{"version":"0.5.0","dev":true,"integrity":"sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw=="},"regexp-tree":{"version":"0.1.24","dev":true,"integrity":"sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw=="},"regexpp":{"version":"3.2.0","dev":true,"integrity":"sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg=="},"regextras":{"version":"0.8.0","dev":true,"integrity":"sha512-k519uI04Z3SaY0fLX843MRXnDeG2+vHOFsyhiPZvNLe7r8rD2YNRjq4BQLZZ0oAr2NrtvZlICsXysGNFPGa3CQ=="},"registry-auth-token":{"version":"4.2.1","dev":true,"integrity":"sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==","requires":{"rc":"1.2.8"}},"relative":{"version":"3.0.2","dev":true,"integrity":"sha1-Dc2OxUpdNaPBXhBFA9ZTdbWlNn8=","requires":{"isobject":"2.1.0"},"dependencies":{"isobject":{"version":"2.1.0","dev":true,"integrity":"sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=","requires":{"isarray":"1.0.0"}}}},"remarkable":{"version":"1.7.4","dev":true,"integrity":"sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==","requires":{"argparse":"1.0.10","autolinker":"0.28.1"}},"repeat-element":{"version":"1.1.4","dev":true,"integrity":"sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ=="},"repeat-string":{"version":"1.6.1","dev":true,"integrity":"sha1-jcrkcOHIirwtYA//Sndihtp15jc="},"req-all":{"version":"0.1.0","dev":true,"integrity":"sha1-EwBR4qzligLqy/ydRIV3pzapJzo="},"require-directory":{"version":"2.1.1","dev":true,"integrity":"sha1-jGStX9MNqxyXbiNE/+f3kqam30I="},"require-from-string":{"version":"2.0.2","dev":true,"integrity":"sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="},"requireindex":{"version":"1.2.0","dev":true,"integrity":"sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww=="},"resolve":{"version":"1.22.0","dev":true,"integrity":"sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==","requires":{"is-core-module":"2.8.1","path-parse":"1.0.7","supports-preserve-symlinks-flag":"1.0.0"}},"resolve-alpn":{"version":"1.2.1","dev":true,"integrity":"sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="},"resolve-dir":{"version":"1.0.1","dev":true,"integrity":"sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=","requires":{"expand-tilde":"2.0.2","global-modules":"1.0.0"}},"resolve-from":{"version":"5.0.0","dev":true,"integrity":"sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="},"resolve-global":{"version":"1.0.0","dev":true,"integrity":"sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==","requires":{"global-dirs":"0.1.1"}},"resolve-url":{"version":"0.2.1","dev":true,"integrity":"sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo="},"responselike":{"version":"2.0.0","dev":true,"integrity":"sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==","requires":{"lowercase-keys":"2.0.0"}},"restore-cursor":{"version":"2.0.0","dev":true,"integrity":"sha1-n37ih/gv0ybU/RYpI9YhKe7g368=","requires":{"onetime":"2.0.1","signal-exit":"3.0.7"}},"ret":{"version":"0.1.15","dev":true,"integrity":"sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg=="},"retry":{"version":"0.13.1","dev":true,"integrity":"sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="},"reusify":{"version":"1.0.4","dev":true,"integrity":"sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="},"rfdc":{"version":"1.3.0","dev":true,"integrity":"sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA=="},"rimraf":{"version":"3.0.2","dev":true,"integrity":"sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==","requires":{"glob":"7.2.0"}},"run-async":{"version":"2.4.1","dev":true,"integrity":"sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ=="},"run-parallel":{"version":"1.2.0","dev":true,"integrity":"sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==","requires":{"queue-microtask":"1.2.3"}},"rxjs":{"version":"6.6.7","dev":true,"integrity":"sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==","requires":{"tslib":"1.14.1"}},"rxjs-report-usage":{"version":"1.0.6","dev":true,"integrity":"sha512-omv1DIv5z1kV+zDAEjaDjWSkx8w5TbFp5NZoPwUipwzYVcor/4So9ZU3bUyQ1c8lxY5Q0Es/ztWW7PGjY7to0Q==","requires":{"@babel/parser":"7.17.3","@babel/traverse":"7.17.3","@babel/types":"7.17.0","bent":"7.3.12","chalk":"4.1.2","glob":"7.2.0","prompts":"2.4.2"}},"safe-buffer":{"version":"5.2.1","dev":true,"integrity":"sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="},"safe-regex":{"version":"1.1.0","dev":true,"integrity":"sha1-QKNmnzsHfR6UPURinhV91IAjvy4=","requires":{"ret":"0.1.15"}},"safer-buffer":{"version":"2.1.2","dev":true,"integrity":"sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="},"scslre":{"version":"0.1.6","dev":true,"integrity":"sha512-JORxVRlQTfjvlOAaiQKebgFElyAm5/W8b50lgaZ0OkEnKnagJW2ufDh3xRfU75UD9z3FGIu1gL1IyR3Poa6Qmw==","requires":{"refa":"0.9.1","regexp-ast-analysis":"0.2.4","regexpp":"3.2.0"},"dependencies":{"regexp-ast-analysis":{"version":"0.2.4","dev":true,"integrity":"sha512-8L7kOZQaKPxKKAwGuUZxTQtlO3WZ+tiXy4s6G6PKL6trbOXcZoumwC3AOHHFtI/xoSbNxt7jgLvCnP1UADLWqg==","requires":{"refa":"0.9.1","regexpp":"3.2.0"}}}},"self-closing-tags":{"version":"1.0.1","dev":true,"integrity":"sha512-7t6hNbYMxM+VHXTgJmxwgZgLGktuXtVVD5AivWzNTdJBM4DBjnDKDzkf2SrNjihaArpeJYNjxkELBu1evI4lQA=="},"semantic-release":{"version":"19.0.2","dev":true,"integrity":"sha512-7tPonjZxukKECmClhsfyMKDt0GR38feIC2HxgyYaBi+9tDySBLjK/zYDLhh+m6yjnHIJa9eBTKYE7k63ZQcYbw==","requires":{"@semantic-release/commit-analyzer":"9.0.2","@semantic-release/error":"3.0.0","@semantic-release/github":"8.0.2","@semantic-release/npm":"9.0.0","@semantic-release/release-notes-generator":"10.0.3","aggregate-error":"3.1.0","cosmiconfig":"7.0.1","debug":"4.3.3","env-ci":"5.5.0","execa":"5.1.1","figures":"3.2.0","find-versions":"4.0.0","get-stream":"6.0.1","git-log-parser":"1.2.0","hook-std":"2.0.0","hosted-git-info":"4.1.0","lodash":"4.17.21","marked":"4.0.12","marked-terminal":"5.1.1","micromatch":"4.0.4","p-each-series":"2.2.0","p-reduce":"2.1.0","read-pkg-up":"7.0.1","resolve-from":"5.0.0","semver":"7.3.5","semver-diff":"3.1.1","signale":"1.4.0","yargs":"16.2.0"},"dependencies":{"normalize-package-data":{"version":"2.5.0","dev":true,"integrity":"sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==","requires":{"hosted-git-info":"2.8.9","resolve":"1.22.0","semver":"5.7.1","validate-npm-package-license":"3.0.4"}},"p-map":{"version":"2.1.0","dev":true,"integrity":"sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="},"readable-stream":{"version":"2.3.7","dev":true,"integrity":"sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==","requires":{"core-util-is":"1.0.3","inherits":"2.0.4","isarray":"1.0.0","process-nextick-args":"2.0.1","safe-buffer":"5.1.2","string_decoder":"1.1.1","util-deprecate":"1.0.2"}},"safe-buffer":{"version":"5.1.2","dev":true,"integrity":"sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="},"split2":{"version":"1.0.0","dev":true,"integrity":"sha1-UuLiIdiMdfmnP5BVbiY/+WdysxQ=","requires":{"through2":"2.0.5"}},"string_decoder":{"version":"1.1.1","dev":true,"integrity":"sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==","requires":{"safe-buffer":"5.1.2"}},"through2":{"version":"2.0.5","dev":true,"integrity":"sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==","requires":{"readable-stream":"2.3.7","xtend":"4.0.2"}},"type-fest":{"version":"1.4.0","dev":true,"integrity":"sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="}}},"semantic-release-config":{"version":"1.1.18","dev":true,"integrity":"sha512-NtEpAQ7eOJD5kdv2EqdLWuH04vzNR2DVYNqVQgDnzDqM7X2RVeszSo10mLUyY8GpTgqOQTYqRfE4MmX+RVU2gA==","requires":{"@semantic-release/changelog":"6.0.1","@semantic-release/commit-analyzer":"9.0.2","@semantic-release/exec":"6.0.3","@semantic-release/git":"10.0.1","@semantic-release/gitlab":"7.0.4","@semantic-release/npm":"9.0.0","@semantic-release/release-notes-generator":"10.0.3","conventional-changelog-emoji-config":"1.4.8","git-cz-emoji":"1.1.24","semantic-release":"19.0.2","semantic-release-gh":"0.0.7","semantic-release-npm-deprecate-old-versions":"1.3.2","semantic-release-python":"2.5.24","tslib":"2.3.1","yaml":"1.10.2"},"dependencies":{"ansi-escapes":{"version":"5.0.0","dev":true,"integrity":"sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==","requires":{"type-fest":"1.4.0"}},"ansi-styles":{"version":"3.2.1","dev":true,"integrity":"sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==","requires":{"color-convert":"1.9.3"}},"chalk":{"version":"2.4.2","dev":true,"integrity":"sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==","requires":{"ansi-styles":"3.2.1","escape-string-regexp":"1.0.5","supports-color":"5.5.0"}},"color-convert":{"version":"1.9.3","dev":true,"integrity":"sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==","requires":{"color-name":"1.1.3"}},"color-name":{"version":"1.1.3","dev":true,"integrity":"sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="},"figures":{"version":"3.2.0","dev":true,"integrity":"sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==","requires":{"escape-string-regexp":"1.0.5"}},"fs-extra":{"version":"9.1.0","dev":true,"integrity":"sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==","requires":{"at-least-node":"1.0.0","graceful-fs":"4.2.9","jsonfile":"6.1.0","universalify":"2.0.0"}},"has-flag":{"version":"3.0.0","dev":true,"integrity":"sha1-tdRU3CGZriJWmfNGfloH87lVuv0="},"lowercase-keys":{"version":"2.0.0","dev":true,"integrity":"sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="},"resolve-from":{"version":"4.0.0","dev":true,"integrity":"sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="},"strip-bom":{"version":"3.0.0","dev":true,"integrity":"sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM="},"strip-json-comments":{"version":"2.0.1","dev":true,"integrity":"sha1-PFMZQukIwml8DsNEhYwobHygpgo="},"supports-color":{"version":"5.5.0","dev":true,"integrity":"sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==","requires":{"has-flag":"3.0.0"}},"yargs":{"version":"16.2.0","dev":true,"integrity":"sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==","requires":{"cliui":"7.0.4","escalade":"3.1.1","get-caller-file":"2.0.5","require-directory":"2.1.1","string-width":"4.2.3","y18n":"5.0.8","yargs-parser":"20.2.9"},"dependencies":{"ansi-styles":{"version":"4.3.0","dev":true,"integrity":"sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==","requires":{"color-convert":"2.0.1"}},"color-convert":{"version":"2.0.1","dev":true,"integrity":"sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==","requires":{"color-name":"1.1.4"}},"color-name":{"version":"1.1.4","dev":true,"integrity":"sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="}}}}},"semantic-release-gh":{"version":"0.0.7","dev":true,"integrity":"sha512-T3eQwyiMXwWMzvDIFMPVn7mzJERVRZYtNEF58bVV0SL3I9UzUkM3UAY1idJUcG3ovbz7BeerfTdIAhE2GrD/sg==","requires":{"@octokit/rest":"18.12.0","@semantic-release/error":"2.2.0","aggregate-error":"3.1.0","bottleneck":"2.19.5","debug":"4.3.3","dir-glob":"3.0.1","fs-extra":"10.0.0","globby":"11.1.0","http-proxy-agent":"5.0.0","https-proxy-agent":"5.0.0","issue-parser":"6.0.0","lodash":"4.17.21","mime":"3.0.0","p-filter":"2.1.0","p-retry":"4.6.1","semantic-release":"19.0.2","tslib":"2.3.1","url-join":"4.0.1"},"dependencies":{"@semantic-release/error":{"version":"2.2.0","dev":true,"integrity":"sha512-9Tj/qn+y2j+sjCI3Jd+qseGtHjOAeg7dU2/lVcqIQ9TV3QDaDXDYXcoOHU+7o2Hwh8L8ymL4gfuO7KxDs3q2zg=="},"p-map":{"version":"2.1.0","dev":true,"integrity":"sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw=="}}},"semantic-release-npm-deprecate-old-versions":{"version":"1.3.2","dev":true,"integrity":"sha512-N9gKDq4/Spv2W7bBwZX8KdcnP4yKeBIjJtd/OefbFGnrWgzJ/6+XDk3Tg6ctr6xlNP8sHRnCEQQea/sMNA8PuA==","requires":{"execa":"4.1.0","node-fetch":"2.6.7","semver":"7.3.5"},"dependencies":{"execa":{"version":"4.1.0","dev":true,"integrity":"sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==","requires":{"cross-spawn":"7.0.3","get-stream":"5.2.0","human-signals":"1.1.1","is-stream":"2.0.1","merge-stream":"2.0.0","npm-run-path":"4.0.1","onetime":"5.1.2","signal-exit":"3.0.7","strip-final-newline":"2.0.0"}},"get-stream":{"version":"5.2.0","dev":true,"integrity":"sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==","requires":{"pump":"3.0.0"}},"human-signals":{"version":"1.1.1","dev":true,"integrity":"sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="}}},"semantic-release-python":{"version":"2.5.24","dev":true,"integrity":"sha512-u+eAsg8DMN6CjcNALozE0nH3W/wttk5NkRXB78IITJInhy6jwN5oHRRFOIiXM0yFwwA9MY7cv8YvtW7IbALTdg==","requires":{"execa":"4.1.0","form-data":"3.0.1","fs-extra":"10.0.0","got":"11.8.3","tslib":"2.3.1","uuid":"8.3.2"},"dependencies":{"execa":{"version":"4.1.0","dev":true,"integrity":"sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==","requires":{"cross-spawn":"7.0.3","get-stream":"5.2.0","human-signals":"1.1.1","is-stream":"2.0.1","merge-stream":"2.0.0","npm-run-path":"4.0.1","onetime":"5.1.2","signal-exit":"3.0.7","strip-final-newline":"2.0.0"}},"form-data":{"version":"3.0.1","dev":true,"integrity":"sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==","requires":{"asynckit":"0.4.0","combined-stream":"1.0.8","mime-types":"2.1.34"}},"get-stream":{"version":"5.2.0","dev":true,"integrity":"sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==","requires":{"pump":"3.0.0"}},"human-signals":{"version":"1.1.1","dev":true,"integrity":"sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="},"quick-lru":{"version":"5.1.1","dev":true,"integrity":"sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="}}},"semver":{"version":"7.3.5","dev":true,"integrity":"sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==","requires":{"lru-cache":"6.0.0"}},"semver-diff":{"version":"3.1.1","dev":true,"integrity":"sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==","requires":{"semver":"6.3.0"},"dependencies":{"semver":{"version":"6.3.0","dev":true,"integrity":"sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="}}},"semver-regex":{"version":"3.1.3","dev":true,"integrity":"sha512-Aqi54Mk9uYTjVexLnR67rTyBusmwd04cLkHy9hNvk3+G3nT2Oyg7E0l4XVbOaNwIvQ3hHeYxGcyEy+mKreyBFQ=="},"set-getter":{"version":"0.1.1","dev":true,"integrity":"sha512-9sVWOy+gthr+0G9DzqqLaYNA7+5OKkSmcqjL9cBpDEaZrr3ShQlyX2cZ/O/ozE41oxn/Tt0LGEM/w4Rub3A3gw==","requires":{"to-object-path":"0.3.0"}},"set-value":{"version":"2.0.1","dev":true,"integrity":"sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==","requires":{"extend-shallow":"2.0.1","is-extendable":"0.1.1","is-plain-object":"2.0.4","split-string":"3.1.0"}},"shebang-command":{"version":"2.0.0","dev":true,"integrity":"sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==","requires":{"shebang-regex":"3.0.0"}},"shebang-regex":{"version":"3.0.0","dev":true,"integrity":"sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="},"shellcheck":{"version":"1.0.0","dev":true,"integrity":"sha512-CdKbWXOknBwE1wNQzAnwfLf7QNOu/yqyLSGBKoq2WuChEqfg7dnZJ1pHR2P463PbVpBRz3KGkYnXJCoQrPwtYA=="},"side-channel":{"version":"1.0.4","dev":true,"integrity":"sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==","requires":{"call-bind":"1.0.2","get-intrinsic":"1.1.1","object-inspect":"1.12.0"}},"sigmund":{"version":"1.0.1","dev":true,"integrity":"sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA="},"signal-exit":{"version":"3.0.7","dev":true,"integrity":"sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="},"signale":{"version":"1.4.0","dev":true,"integrity":"sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==","requires":{"chalk":"2.4.2","figures":"2.0.0","pkg-conf":"2.1.0"},"dependencies":{"find-up":{"version":"2.1.0","dev":true,"integrity":"sha1-RdG35QbHF93UgndaK3eSCjwMV6c=","requires":{"locate-path":"2.0.0"}},"locate-path":{"version":"2.0.0","dev":true,"integrity":"sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=","requires":{"p-locate":"2.0.0","path-exists":"3.0.0"}},"p-limit":{"version":"1.3.0","dev":true,"integrity":"sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==","requires":{"p-try":"1.0.0"}},"p-locate":{"version":"2.0.0","dev":true,"integrity":"sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=","requires":{"p-limit":"1.3.0"}},"p-try":{"version":"1.0.0","dev":true,"integrity":"sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M="},"parse-json":{"version":"4.0.0","dev":true,"integrity":"sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=","requires":{"error-ex":"1.3.2","json-parse-better-errors":"1.0.2"}},"path-exists":{"version":"3.0.0","dev":true,"integrity":"sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="}}},"simple-git":{"version":"2.48.0","dev":true,"integrity":"sha512-z4qtrRuaAFJS4PUd0g+xy7aN4y+RvEt/QTJpR184lhJguBA1S/LsVlvE/CM95RsYMOFJG3NGGDjqFCzKU19S/A==","requires":{"@kwsites/file-exists":"1.1.1","@kwsites/promise-deferred":"1.1.1","debug":"4.3.3"}},"sisteransi":{"version":"1.0.5","dev":true,"integrity":"sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="},"slash":{"version":"3.0.0","dev":true,"integrity":"sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="},"slice-ansi":{"version":"5.0.0","dev":true,"integrity":"sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==","requires":{"ansi-styles":"6.1.0","is-fullwidth-code-point":"4.0.0"}},"snapdragon":{"version":"0.8.2","dev":true,"integrity":"sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==","requires":{"base":"0.11.2","debug":"2.6.9","define-property":"0.2.5","extend-shallow":"2.0.1","map-cache":"0.2.2","source-map":"0.5.7","source-map-resolve":"0.5.3","use":"3.1.1"},"dependencies":{"define-property":{"version":"0.2.5","dev":true,"integrity":"sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=","requires":{"is-descriptor":"0.1.6"},"dependencies":{"kind-of":{"version":"5.1.0","dev":true,"integrity":"sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw=="}}}}},"snapdragon-node":{"version":"2.1.1","dev":true,"integrity":"sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==","requires":{"define-property":"1.0.0","isobject":"3.0.1","snapdragon-util":"3.0.1"}},"snapdragon-util":{"version":"3.0.1","dev":true,"integrity":"sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==","requires":{"kind-of":"3.2.2"}},"solidity-comments-extractor":{"version":"0.0.7","dev":true,"integrity":"sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw=="},"sort-object-keys":{"version":"1.1.3","dev":true,"integrity":"sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg=="},"sort-order":{"version":"1.0.1","dev":true,"integrity":"sha1-2CK4zbkOpqnfloxL1FmHz1SBmeY="},"source-map":{"version":"0.6.1","dev":true,"integrity":"sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="},"source-map-resolve":{"version":"0.5.3","dev":true,"integrity":"sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==","requires":{"atob":"2.1.2","decode-uri-component":"0.2.0","resolve-url":"0.2.1","source-map-url":"0.4.1","urix":"0.1.0"}},"source-map-support":{"version":"0.5.21","dev":true,"integrity":"sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==","requires":{"buffer-from":"1.1.2","source-map":"0.6.1"}},"source-map-url":{"version":"0.4.1","dev":true,"integrity":"sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw=="},"spawn-error-forwarder":{"version":"1.0.0","dev":true,"integrity":"sha1-Gv2Uc46ZmwNG17n8NzvlXgdXcCk="},"spdx-correct":{"version":"3.1.1","dev":true,"integrity":"sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==","requires":{"spdx-expression-parse":"3.0.1","spdx-license-ids":"3.0.11"}},"spdx-exceptions":{"version":"2.3.0","dev":true,"integrity":"sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A=="},"spdx-expression-parse":{"version":"3.0.1","dev":true,"integrity":"sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==","requires":{"spdx-exceptions":"2.3.0","spdx-license-ids":"3.0.11"}},"spdx-license-ids":{"version":"3.0.11","dev":true,"integrity":"sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g=="},"split":{"version":"1.0.1","dev":true,"integrity":"sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==","requires":{"through":"2.3.8"}},"split-on-first":{"version":"1.1.0","dev":true,"integrity":"sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="},"split-string":{"version":"3.1.0","dev":true,"integrity":"sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==","requires":{"extend-shallow":"3.0.2"},"dependencies":{"is-extendable":{"version":"1.0.1","dev":true,"integrity":"sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==","requires":{"is-plain-object":"2.0.4"}}}},"split2":{"version":"3.2.2","dev":true,"integrity":"sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==","requires":{"readable-stream":"3.6.0"}},"sprintf-js":{"version":"1.0.3","dev":true,"integrity":"sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="},"sql-formatter":{"version":"4.0.2","dev":true,"integrity":"sha512-R6u9GJRiXZLr/lDo8p56L+OyyN2QFJPCDnsyEOsbdIpsnDKL8gubYFo7lNR7Zx7hfdWT80SfkoVS0CMaF/DE2w==","requires":{"argparse":"2.0.1"}},"static-extend":{"version":"0.1.2","dev":true,"integrity":"sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=","requires":{"define-property":"0.2.5","object-copy":"0.1.0"}},"stream-combiner2":{"version":"1.1.1","dev":true,"integrity":"sha1-+02KFCDqNidk4hrUeAOXvry0HL4=","requires":{"duplexer2":"0.1.4","readable-stream":"2.3.7"}},"strict-uri-encode":{"version":"2.0.0","dev":true,"integrity":"sha1-ucczDHBChi9rFC3CdLvMWGbONUY="},"string-argv":{"version":"0.3.1","dev":true,"integrity":"sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg=="},"string-width":{"version":"4.2.3","dev":true,"integrity":"sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==","requires":{"emoji-regex":"8.0.0","is-fullwidth-code-point":"3.0.0","strip-ansi":"6.0.1"}},"string.prototype.trimend":{"version":"1.0.4","dev":true,"integrity":"sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3"}},"string.prototype.trimstart":{"version":"1.0.4","dev":true,"integrity":"sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==","requires":{"call-bind":"1.0.2","define-properties":"1.1.3"}},"string_decoder":{"version":"1.3.0","dev":true,"integrity":"sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==","requires":{"safe-buffer":"5.2.1"}},"strip-ansi":{"version":"6.0.1","dev":true,"integrity":"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==","requires":{"ansi-regex":"5.0.1"}},"strip-bom":{"version":"4.0.0","dev":true,"integrity":"sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w=="},"strip-final-newline":{"version":"2.0.0","dev":true,"integrity":"sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="},"strip-indent":{"version":"3.0.0","dev":true,"integrity":"sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==","requires":{"min-indent":"1.0.1"}},"strip-json-comments":{"version":"3.0.1","dev":true,"integrity":"sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw=="},"striptags":{"version":"3.2.0","dev":true,"integrity":"sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw=="},"strtok3":{"version":"6.3.0","dev":true,"integrity":"sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==","requires":{"@tokenizer/token":"0.3.0","peek-readable":"4.1.0"}},"success-symbol":{"version":"0.1.0","dev":true,"integrity":"sha1-JAIuSG878c3KCUKDt2nEctO3KJc="},"supports-color":{"version":"7.2.0","dev":true,"integrity":"sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==","requires":{"has-flag":"4.0.0"}},"supports-hyperlinks":{"version":"2.2.0","dev":true,"integrity":"sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==","requires":{"has-flag":"4.0.0","supports-color":"7.2.0"}},"supports-preserve-symlinks-flag":{"version":"1.0.0","dev":true,"integrity":"sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="},"temp-dir":{"version":"2.0.0","dev":true,"integrity":"sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg=="},"tempy":{"version":"1.0.1","dev":true,"integrity":"sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==","requires":{"del":"6.0.0","is-stream":"2.0.1","temp-dir":"2.0.0","type-fest":"0.16.0","unique-string":"2.0.0"},"dependencies":{"type-fest":{"version":"0.16.0","dev":true,"integrity":"sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg=="}}},"term-size":{"version":"2.2.1","dev":true,"integrity":"sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg=="},"text-extensions":{"version":"1.9.0","dev":true,"integrity":"sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ=="},"text-table":{"version":"0.2.0","dev":true,"integrity":"sha1-f17oI66AUgfACvLfSoTsP8+lcLQ="},"through":{"version":"2.3.8","dev":true,"integrity":"sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="},"through2":{"version":"4.0.2","dev":true,"integrity":"sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==","requires":{"readable-stream":"3.6.0"}},"time-stamp":{"version":"1.1.0","dev":true,"integrity":"sha1-dkpaEa9QVhkhsTPztE5hhofg9cM="},"tmp":{"version":"0.0.33","dev":true,"integrity":"sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==","requires":{"os-tmpdir":"1.0.2"}},"to-fast-properties":{"version":"2.0.0","dev":true,"integrity":"sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="},"to-gfm-code-block":{"version":"0.1.1","dev":true,"integrity":"sha1-JdBFpfrlUxielje1kJANpzLYqoI="},"to-object-path":{"version":"0.3.0","dev":true,"integrity":"sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=","requires":{"kind-of":"3.2.2"}},"to-regex":{"version":"3.0.2","dev":true,"integrity":"sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==","requires":{"define-property":"2.0.2","extend-shallow":"3.0.2","regex-not":"1.0.2","safe-regex":"1.1.0"}},"to-regex-range":{"version":"5.0.1","dev":true,"integrity":"sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==","requires":{"is-number":"7.0.0"}},"token-types":{"version":"4.1.1","dev":true,"integrity":"sha512-hD+QyuUAyI2spzsI0B7gf/jJ2ggR4RjkAo37j3StuePhApJUwcWDjnHDOFdIWYSwNR28H14hpwm4EI+V1Ted1w==","requires":{"@tokenizer/token":"0.3.0","ieee754":"1.2.1"}},"toml-eslint-parser":{"version":"0.2.1","dev":true,"integrity":"sha512-lAESSx47NjCe/O9Y5dbAmP+U/EFvdxPRgA0Hp4TxCZP3Bs6hxRfLHxZvBuXvTGkVoK0DpYamPz/rCqYBzoFWVQ==","requires":{"eslint-visitor-keys":"2.1.0"}},"tr46":{"version":"0.0.3","dev":true,"integrity":"sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="},"traverse":{"version":"0.6.6","dev":true,"integrity":"sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc="},"trim-newlines":{"version":"3.0.1","dev":true,"integrity":"sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw=="},"ts-node":{"version":"9.1.1","dev":true,"integrity":"sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg==","requires":{"arg":"4.1.3","create-require":"1.1.1","diff":"4.0.2","make-error":"1.3.6","source-map-support":"0.5.21","typescript":"4.5.5","yn":"3.1.1"}},"tsconfig-paths":{"version":"3.12.0","dev":true,"integrity":"sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==","requires":{"@types/json5":"0.0.29","json5":"1.0.1","minimist":"1.2.5","strip-bom":"3.0.0"}},"tslib":{"version":"2.3.1","dev":true,"integrity":"sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="},"tsutils":{"version":"3.21.0","dev":true,"integrity":"sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==","requires":{"tslib":"1.14.1","typescript":"4.5.5"}},"tsutils-etc":{"version":"1.4.1","dev":true,"integrity":"sha512-6UPYgc7OXcIW5tFxlsZF3OVSBvDInl/BkS3Xsu64YITXk7WrnWTVByKWPCThFDBp5gl5IGHOzGMdQuDCE7OL4g==","requires":{"@types/yargs":"17.0.8","tsutils":"3.21.0","typescript":"4.5.5","yargs":"17.3.1"}},"tunnel-agent":{"version":"0.6.0","dev":true,"integrity":"sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=","requires":{"safe-buffer":"5.2.1"}},"type-check":{"version":"0.4.0","dev":true,"integrity":"sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==","requires":{"prelude-ls":"1.2.1"}},"type-fest":{"version":"0.18.1","dev":true,"integrity":"sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="},"typedarray-to-buffer":{"version":"3.1.5","dev":true,"integrity":"sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==","requires":{"is-typedarray":"1.0.0"}},"typeof-article":{"version":"0.1.1","dev":true,"integrity":"sha1-nwfnM8P7tkb/qeYcCN66zUYOBq8=","requires":{"kind-of":"3.2.2"}},"typescript":{"version":"4.5.5","dev":true,"integrity":"sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA=="},"ulid":{"version":"2.3.0","dev":true,"integrity":"sha512-keqHubrlpvT6G2wH0OEfSW4mquYRcbe/J8NMmveoQOjUqmo+hXtO+ORCpWhdbZ7k72UtY61BL7haGxW6enBnjw=="},"unbox-primitive":{"version":"1.0.1","dev":true,"integrity":"sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==","requires":{"function-bind":"1.1.1","has-bigints":"1.0.1","has-symbols":"1.0.2","which-boxed-primitive":"1.0.2"}},"union-value":{"version":"1.0.1","dev":true,"integrity":"sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==","requires":{"arr-union":"3.1.0","get-value":"2.0.6","is-extendable":"0.1.1","set-value":"2.0.1"}},"unique-string":{"version":"2.0.0","dev":true,"integrity":"sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==","requires":{"crypto-random-string":"2.0.0"}},"unist-util-stringify-position":{"version":"2.0.3","dev":true,"integrity":"sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==","requires":{"@types/unist":"2.0.6"}},"universal-user-agent":{"version":"6.0.0","dev":true,"integrity":"sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="},"universalify":{"version":"2.0.0","dev":true,"integrity":"sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="},"unset-value":{"version":"1.0.0","dev":true,"integrity":"sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=","requires":{"has-value":"0.3.1","isobject":"3.0.1"}},"uri-js":{"version":"4.4.1","dev":true,"integrity":"sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==","requires":{"punycode":"2.1.1"}},"urix":{"version":"0.1.0","dev":true,"integrity":"sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI="},"url-join":{"version":"4.0.1","dev":true,"integrity":"sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="},"use":{"version":"3.1.1","dev":true,"integrity":"sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ=="},"util-deprecate":{"version":"1.0.2","dev":true,"integrity":"sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="},"uuid":{"version":"8.3.2","dev":true,"integrity":"sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="},"v8-compile-cache":{"version":"2.3.0","dev":true,"integrity":"sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA=="},"validate-npm-package-license":{"version":"3.0.4","dev":true,"integrity":"sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==","requires":{"spdx-correct":"3.1.1","spdx-expression-parse":"3.0.1"}},"vscode-uri":{"version":"3.0.3","dev":true,"integrity":"sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA=="},"warning-symbol":{"version":"0.1.0","dev":true,"integrity":"sha1-uzHdEbeg+dZ6su2V9Fe2WCW7rSE="},"webidl-conversions":{"version":"3.0.1","dev":true,"integrity":"sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="},"whatwg-url":{"version":"5.0.0","dev":true,"integrity":"sha1-lmRU6HZUYuN2RNNib2dCzotwll0=","requires":{"tr46":"0.0.3","webidl-conversions":"3.0.1"}},"which":{"version":"2.0.2","dev":true,"integrity":"sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==","requires":{"isexe":"2.0.0"}},"which-boxed-primitive":{"version":"1.0.2","dev":true,"integrity":"sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==","requires":{"is-bigint":"1.0.4","is-boolean-object":"1.1.2","is-number-object":"1.0.6","is-string":"1.0.7","is-symbol":"1.0.4"}},"which-pm-runs":{"version":"1.1.0","dev":true,"integrity":"sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="},"widest-line":{"version":"3.1.0","dev":true,"integrity":"sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==","requires":{"string-width":"4.2.3"}},"word-wrap":{"version":"1.2.3","dev":true,"integrity":"sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ=="},"wordwrap":{"version":"1.0.0","dev":true,"integrity":"sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="},"wrap-ansi":{"version":"7.0.0","dev":true,"integrity":"sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==","requires":{"ansi-styles":"4.3.0","string-width":"4.2.3","strip-ansi":"6.0.1"}},"wrappy":{"version":"1.0.2","dev":true,"integrity":"sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="},"write-file-atomic":{"version":"3.0.3","dev":true,"integrity":"sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==","requires":{"imurmurhash":"0.1.4","is-typedarray":"1.0.0","signal-exit":"3.0.7","typedarray-to-buffer":"3.1.5"}},"xdg-basedir":{"version":"4.0.0","dev":true,"integrity":"sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q=="},"xtend":{"version":"4.0.2","dev":true,"integrity":"sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="},"y18n":{"version":"5.0.8","dev":true,"integrity":"sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="},"yallist":{"version":"4.0.0","dev":true,"integrity":"sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="},"yaml":{"version":"1.10.2","dev":true,"integrity":"sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg=="},"yaml-eslint-parser":{"version":"0.4.1","dev":true,"integrity":"sha512-GoJ/p1EW8O2tbTbuhfxjo1XhfUFU3uX3kwvfEQoOaZjO2Lubx8POjlsSqB+18b3SxkujAdQYT9r9nURaUWNYWQ==","requires":{"eslint-visitor-keys":"2.1.0","lodash":"4.17.21","yaml":"1.10.2"}},"yargs":{"version":"17.3.1","dev":true,"integrity":"sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==","requires":{"cliui":"7.0.4","escalade":"3.1.1","get-caller-file":"2.0.5","require-directory":"2.1.1","string-width":"4.2.3","y18n":"5.0.8","yargs-parser":"21.0.0"},"dependencies":{"yargs-parser":{"version":"21.0.0","dev":true,"integrity":"sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA=="}}},"yargs-parser":{"version":"20.2.9","dev":true,"integrity":"sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="},"yarnhook":{"version":"0.5.1","dev":true,"integrity":"sha512-YPLLXO/PzsFXKvRfsOG/r60WBz8RT7VbkkQv2oHDb6o+EjX0vcUSeA3aw5el2AEWjbcg1sgemjHyCwRIvQxZWw==","requires":{"execa":"4.1.0","find-parent-dir":"0.3.1"},"dependencies":{"execa":{"version":"4.1.0","dev":true,"integrity":"sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==","requires":{"cross-spawn":"7.0.3","get-stream":"5.2.0","human-signals":"1.1.1","is-stream":"2.0.1","merge-stream":"2.0.0","npm-run-path":"4.0.1","onetime":"5.1.2","signal-exit":"3.0.7","strip-final-newline":"2.0.0"}},"get-stream":{"version":"5.2.0","dev":true,"integrity":"sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==","requires":{"pump":"3.0.0"}},"human-signals":{"version":"1.1.1","dev":true,"integrity":"sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw=="}}},"year":{"version":"0.2.1","dev":true,"integrity":"sha1-QIOuUgoxiyPshgN/MADLiSvfm7A="},"yn":{"version":"3.1.1","dev":true,"integrity":"sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="},"yocto-queue":{"version":"0.1.0","dev":true,"integrity":"sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="}}} \ No newline at end of file diff --git a/.config/prettierignore b/.config/prettierignore new file mode 100644 index 00000000..1e725217 --- /dev/null +++ b/.config/prettierignore @@ -0,0 +1,26 @@ +.autodoc/ +.cache/ +.common/ +.config/ +.git/ +.github/ +.gitlab/ +.gitmodules +.husky/ +.modules/ +.npm/ +.pnpm-store/ +.shared/ +.task/ +.venv/ +.vscode/ +.variables.json +**/.cache/ +**/Dockerfile +**/*.handlebars +build/ +coverage/ +dist/ +node_modules/ +pnpm-lock.yaml +venv/ diff --git a/.config/proselint.json b/.config/proselint.json new file mode 100644 index 00000000..9eb2d04f --- /dev/null +++ b/.config/proselint.json @@ -0,0 +1,5 @@ +{ + "checks": { + "typography.symbols": false + } +} diff --git a/.config/requirements.txt b/.config/requirements.txt new file mode 100644 index 00000000..e5366afc --- /dev/null +++ b/.config/requirements.txt @@ -0,0 +1,25 @@ +ansible-core==2.11.8; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +ansible==4.10.0; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0") +certifi==2021.10.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" +cffi==1.15.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +charset-normalizer==2.0.12; python_full_version >= "3.6.0" and python_version >= "3.6" +cryptography==36.0.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +docker==5.0.3; python_version >= "3.6" +idna==3.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" +jinja2==3.0.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +markupsafe==2.1.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" +ntlm-auth==1.5.0; python_version >= "2.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" +packaging==21.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +pycparser==2.21; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +pyparsing==3.0.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" +python-vagrant==0.5.15 +pywin32==227; sys_platform == "win32" and python_version >= "3.6" +pywinrm==0.4.2 +pyyaml==5.4.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" +requests-ntlm==1.1.0 +requests==2.27.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" +resolvelib==0.5.5; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" +six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" +urllib3==1.26.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.6" +websocket-client==1.2.3; python_version >= "3.6" +xmltodict==0.12.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" diff --git a/.config/run b/.config/run new file mode 100644 index 00000000..0a785c90 --- /dev/null +++ b/.config/run @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +# @file .config/run +# @brief Wrapper for Task (the Taskfile.yml runner) diff --git a/.config/taskfiles/README.md b/.config/taskfiles/README.md new file mode 100644 index 00000000..659bacb4 --- /dev/null +++ b/.config/taskfiles/README.md @@ -0,0 +1,3 @@ +# Shared Taskfiles + +All of the taskfiles in this folder are kept in sync with the [Shared common file repository](https://gitlab.com/megabyte-labs/common/shared). If you need to make a change to any of the taskfiles, open the PR against the Shared common file repository. Once the PR is merged, the taskfiles will propagate down to the project-type specific common file repositories and then down to the project repositories. diff --git a/.config/taskfiles/ansible/Taskfile-ansibler.yml b/.config/taskfiles/ansible/Taskfile-ansibler.yml new file mode 100644 index 00000000..61e34847 --- /dev/null +++ b/.config/taskfiles/ansible/Taskfile-ansibler.yml @@ -0,0 +1,119 @@ +--- +version: '3' + +vars: + MAIN_TASKS_PATH: tasks/main.yml + META_PATH: meta/main.yml + MOLECULE_RESULTS_PATH: molecule/.results/logs + REQUIREMENTS_PATH: requirements.yml + VARIABLES_PATH: .variables.json + +tasks: + ansibler: + deps: + - :install:pipx:ansibler + cmds: + - task: compatibility-chart + - task: populate-platforms + - task: tasks:{{.REPOSITORY_SUBTYPE}} + + compatibility-chart: + deps: + - :install:software:jq + log: + error: Failed to generate operating system compatibility chart + start: Generating operating system compatibility chart + success: Successfully generated operating system compatibility chart + cmds: + - mkdir -p .cache + - if [ ! -f .cache/compatibility-chart.json ]; then echo "{}" > .cache/compatibility-chart.json; fi + - task: compatibility-chart:generate + - | + TMP="$(mktemp)" + jq -s -S '.[0] + .[1]' '{{.VARIABLES_PATH}}' .cache/compatibility-chart.json > "$TMP" + mv "$TMP" '{{.VARIABLES_PATH}}' + + compatibility-chart:ansifilter: + deps: + - :install:software:ansifilter + cmds: + - | + for LOG in {{.MOLECULE_RESULTS_PATH}}; do + if [ "$LOG" != '{{.MOLECULE_RESULTS_PATH}}' ]; then + TMP="$(mktemp)" && cat "$LOG" | ansifilter > "$TMP" && mv "$TMP" "$LOG" + fi + done + sources: + - '{{.MOLECULE_RESULTS_PATH}}/*' + + compatibility-chart:generate: + deps: + - :install:pipx:ansibler + cmds: + - task: compatibility-chart:ansifilter + - > + PATH="$PATH:$HOME/.local/bin" + + {{.PYTHON_HANDLE}}ansibler --generate-compatibility-chart --molecule-results-dir '{{.MOLECULE_RESULTS_PATH}}' + --json-file .cache/compatibility-chart.json + sources: + - '{{.MOLECULE_RESULTS_PATH}}/*' + generates: + - .cache/compatibility-chart.json + + populate-platforms: + deps: + - :install:pipx:ansibler + log: + error: Failed to populate platforms in `meta/main.yml + start: Populating the supported platforms listed in `meta/main.yml` based on the compatibility chart data + success: Successfully populated `meta/main.yml` platforms + cmds: + - cmd: | + PATH="$PATH:$HOME/.local/bin" + {{.PYTHON_HANDLE}}ansibler --populate-platforms --json-file .cache/compatibility-chart.json + ignore_error: true + sources: + - .cache/compatibility-chart.json + - meta/main.yml + + role-dependencies: + deps: + - :install:software:jq + log: + error: Failed to acquire role dependency information + start: Gathering information about role dependencies + success: Acquired role dependency information + cmds: + - mkdir -p .cache + - if [ ! -f .cache/role-dependencies.json ]; then echo "{}" > .cache/role-dependencies.json; fi + - task: role-dependencies:generate + - if [ -f role-dependencies.json ]; then mv role-dependencies.json .cache/role-dependencies.json; fi + - | + TMP="$(mktemp)" + jq -s -S '.[0] + .[1]' '{{.VARIABLES_PATH}}' .cache/role-dependencies.json > "$TMP" + mv "$TMP" '{{.VARIABLES_PATH}}' + + role-dependencies:generate: + deps: + - :install:pipx:ansibler + cmds: + - cmd: | + PATH="$PATH:$HOME/.local/bin" + {{.PYTHON_HANDLE}}ansibler --role-dependencies --json-file .cache/role-dependencies.json + ignore_error: true + sources: + - '{{.REQUIREMENTS_PATH}}' + generates: + - .cache/role-dependencies.json + + tasks:playbook: + deps: + - :ansible:collection-dependencies + - role-dependencies + + tasks:role: + deps: + - :ansible:collection-dependencies + - populate-platforms + - role-dependencies diff --git a/.config/taskfiles/ansible/Taskfile-playbook.yml b/.config/taskfiles/ansible/Taskfile-playbook.yml new file mode 100644 index 00000000..606ff69c --- /dev/null +++ b/.config/taskfiles/ansible/Taskfile-playbook.yml @@ -0,0 +1,515 @@ +--- +version: '3' + +tasks: + collections:download: + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + ansible-galaxy collection download -r requirements.yml + + docs: + deps: + - docs:roles + - docs:tags + + docs:roles: + deps: + - :install:software:jq + - :install:software:yq + log: + error: Failed to acquire README chart data for the roles + start: Scanning roles folder and generating chart data + success: Finished populating roles folder chart data + cmds: + - | + TMP="$(mktemp)" + jq --arg name "Role Name" --arg description "Description" --arg github 'GitHub            ' \ + '.role_var_chart = [[$name, $description, $github]]' .variables.json > "$TMP" + mv "$TMP" .variables.json + for ROLE_PATH in roles/*/*; do + if [[ "$ROLE_PATH" != *"roles/deprecated/"* ]] && [[ "$ROLE_PATH" != *"roles/cloud/"* ]] && [[ "$ROLE_PATH" != *"roles/helpers/"* ]]; then + if [ "$(yq e '.galaxy_info.project.documentation' "${ROLE_PATH}/meta/main.yml")" != 'null' ]; then + DOCUMENTATION_LINK="*[Documentation]($(yq e '.galaxy_info.project.documentation' "${ROLE_PATH}/meta/main.yml" | sed 's/null//'))* | " + else + DOCUMENTATION_LINK="" + fi + if [ "$(yq e '.galaxy_info.project.homepage' "${ROLE_PATH}/meta/main.yml")" != 'null' ]; then + HOMEPAGE_LINK="*[Homepage]($(yq e '.galaxy_info.project.homepage' "${ROLE_PATH}/meta/main.yml"))* | " + else + HOMEPAGE_LINK="" + fi + GITHUB_URL="$(yq e '.galaxy_info.project.github' "${ROLE_PATH}/meta/main.yml")" + if [ "$GITHUB_URL" == 'Not open-source' ]; then + GITHUB_SHIELD="**❌ Closed source**" + elif [ "$GITHUB_URL" != 'null' ]; then + GITHUB_PATH="$(echo "$GITHUB_URL" | sed 's/https:\/\/github.com\///' | sed 's/\/$//')" + GITHUB_OWNER="$(echo "$GITHUB_PATH" | sed 's/\/.*$//')" + GITHUB_PROJECT_SLUG="$(echo "$GITHUB_PATH" | sed 's/^.*\///')" + GITHUB_SHIELD="[![GitHub Repo stars](https://img.shields.io/github/stars/$GITHUB_OWNER/$GITHUB_PROJECT_SLUG?style=social)]($GITHUB_URL)" + else + UPSTREAM_URL="$(yq e '.galaxy_info.project.upstream' "${ROLE_PATH}/meta/main.yml")" + if [ "$UPSTREAM_URL" != 'null' ]; then + GITHUB_SHIELD="**[✅ Open-source]($UPSTREAM_URL)**" + else + GITHUB_SHIELD="*N/A*" + fi + fi + SOFTWARE_NAME="$(jq -r '.blueprint.name' "${ROLE_PATH}/package.json")" + DESCRIPTION="$(jq -r '.blueprint.overview' "${ROLE_PATH}/package.json")" + ROLE_GITHUB_LINK="*[Role on GitHub]($(jq -r '.blueprint.repository.github' "${ROLE_PATH}/package.json"))*" + ANSIBLE_GALAXY_NAMESPACE="$(yq e '.galaxy_info.namespace' "${ROLE_PATH}/meta/main.yml")" + ANSIBLE_GALAXY_ROLE_NAME="$(yq e '.galaxy_info.role_name' "${ROLE_PATH}/meta/main.yml")" + ROLE_ANSIBLE_GALAXY_URL="https://galaxy.ansible.com/${ANSIBLE_GALAXY_NAMESPACE}/${ANSIBLE_GALAXY_ROLE_NAME}" + if [ "$ANSIBLE_GALAXY_NAMESPACE" != 'null' ] && [ "$ANSIBLE_GALAXY_ROLE_NAME" != 'null' ]; then + URL_LINK="**[${SOFTWARE_NAME}](${ROLE_ANSIBLE_GALAXY_URL})**" + else + URL_LINK="**${SOFTWARE_NAME}**" + fi + if [ "$(jq -r '.blueprint.ansible_galaxy_project_id' "${ROLE_PATH}/package.json")" == 'null' ]; then + ROLE_GITHUB_LINK="" + fi + DESCRIPTION_LINKS="(${HOMEPAGE_LINK}${DOCUMENTATION_LINK}${ROLE_GITHUB_LINK})" + if [ "$DESCRIPTION_LINKS" != "()" ]; then + DESCRIPTION_LINKS=" $(echo "$DESCRIPTION_LINKS" | sed 's/ | )$/)/')" + else + DESCRIPTION_LINKS="" + fi + TMP="$(mktemp)" + jq --arg name "${URL_LINK}" --arg description "${DESCRIPTION}${DESCRIPTION_LINKS}" --arg github "${GITHUB_SHIELD}" \ + '.role_var_chart = .role_var_chart + [[$name, $description, $github]]' .variables.json > "$TMP" + mv "$TMP" .variables.json + fi + done + + docs:tags: + deps: + - :install:npm:leasot + summary: | + ```shell + # @description Processes leasot data and returns .variables.json data including charts written in @appnest/readme format + # + # @arg $1 The file that the leasot JSON was written to + # @arg $2 The tag being processed + function populateChartVar() { + ... + } + ``` + vars: + DOC_IDS: '@binaryapp,@binarycli,@brew,@cask,@chrome,@firefox,@gem,@helm,@npm,@pypi,@vscode' + log: + error: Failed to acquire package information from comments via `leasot` + start: Scanning and acquiring package information in comments via `leasot` + success: Acquired package information from comments + cmds: + - | + function populateChartVar() { + CHART='[["Package", "Description", "GitHub            "]' + jq --arg tag "$(echo $2 | tr '[a-z]' '[A-Z]')" -r '.[] | select(.tag == $tag) | . + | del(. ["file", "ref", "line", "tag"]) | .text' "$1" | while read COMMENT; do + if [ "$CHART" != '[' ]; then + CHART="${CHART}," + fi + LINK="$(echo $COMMENT | sed 's/ - .*//')" + URL_LINK="$(echo "$LINK" | sed 's/@tui //' | sed 's/@service //' | sed 's/@binary //' | sed 's/@cli //' | sed 's/@application //' | sed 's/@menubar //' | sed 's/@webapp //' | sed 's/^\[\([^)]*\)\](\([^)]*\)).*$/\[\1\](\2)/' | sed 's/ $//' | sed 's/\/$//')" + if [[ "$LINK" == *"[GitHub](NO_GITHUB_REPOSITORY_LINK)"* ]] || [[ "$LINK" != *"https://github.com"* ]]; then + GITHUB_SHIELD="*N/A*" + else + GITHUB_PATH="$(echo "$LINK" | sed 's/.*\[GitHub\](https:\/\/github.com\/\([^|]*\)).*/\1/' | \ + sed 's/.*\[.*\](https:\/\/github.com\/\([^)]*\)).*/\1/' | sed 's/\/$//')" + GITHUB_OWNER="$(echo "$GITHUB_PATH" | sed 's/\/.*$//')" + GITHUB_PROJECT_SLUG="$(echo "$GITHUB_PATH" | sed 's/^.*\///')" + GITHUB_URL="https://github.com/${GITHUB_OWNER}/${GITHUB_PROJECT_SLUG}" + GITHUB_SHIELD="[![GitHub Repo stars](https://img.shields.io/github/stars/$GITHUB_OWNER/$GITHUB_PROJECT_SLUG?style=social)]($GITHUB_URL)" + fi + if [[ "$LINK" == *"[Documentation]"* ]]; then + DOCUMENTATION_LINK="*[Documentation]("$(echo "$LINK" | sed 's/.*\[Documentation\](\([^)]*\)).*/\1/')")* | " + else + DOCUMENTATION_LINK="" + fi + if [[ "$LINK" == *"[Homepage]"* ]]; then + HOMEPAGE_LINK="*[Homepage]("$(echo "$LINK" | sed 's/.*\[Homepage\](\([^)]*\)).*/\1/' | sed 's/ $//')")* | " + else + HOMEPAGE_LINK="" + fi + if [[ "$LINK" == *"[Helm]"* ]] || [[ "$LINK" == *"[Operator]"* ]]; then + REFERENCE_LINK="*[Helm Reference]("$(echo "$LINK" | sed 's/.*\[Helm\](\([^)]*\)).*/\1/' | sed 's/.*\[Operator\](\([^)]*\)).*/\1/')")* | " + else + REFERENCE_LINK="" + fi + DESCRIPTION_LINKS="(${HOMEPAGE_LINK}${DOCUMENTATION_LINK}${REFERENCE_LINK})" + if [ "$DESCRIPTION_LINKS" != "()" ]; then + DESCRIPTION_LINKS=" $(echo "$DESCRIPTION_LINKS" | sed 's/ | )$/)/')" + else + DESCRIPTION_LINKS="" + fi + DESCRIPTION="$(echo $COMMENT | sed 's/.* - //' | sed 's/\"/\\\"/g')" + CHART="${CHART}[\"**$URL_LINK**\",\"${DESCRIPTION}${DESCRIPTION_LINKS}\",\"$GITHUB_SHIELD\"]" + done + CHART="${CHART}]" + TMP_CHART="$(mktemp)" + KEY="$(echo $2 | sed 's/^@//')" + jq --arg chart "$CHART" --arg key "${KEY}_var_chart" '.[$key] = ($chart | fromjson)' .variables.json > "$TMP_CHART" + mv "$TMP_CHART" .variables.json + } + TMP="$(mktemp)" + leasot --tags '{{.DOC_IDS}}' --reporter json './environments/prod/group_vars/**/*.yml' > "$TMP" || true + VARIABLES_JSON="$(jq '.' .variables.json)" + for ID in {{replace "," " " .DOC_IDS}}; do + populateChartVar "$TMP" "$ID" + done + + environment: + desc: Prompts for which environment to use and then symlinks to it + hide: '{{ne (print .REPOSITORY_TYPE "-" .REPOSITORY_SUBTYPE) "ansible-playbook"}}' + summary: | + # Switch environments using an interactive dialogue + + Ansible does not really provide any great ways to switch between environments (or sets of + `host_vars/`, `group_vars/` etc.). If you place all the files and folders you wish to constitute + as an environment inside a folder named as the name of the environment then you can use + this task to handle the symlinking and switching between environments. + + **Example of opening the interactive prompt:** + `task ansible:environment` + + **You can directly switch enironments to `environments/prod/` by running:** + `task ansible:environment -- prod` + cmds: + - task: environment:{{if .CLI_ARGS}}cli{{else}}prompt{{end}} + + environment:cli: + log: + error: Encountered an error while switching environments to `{{.CLI_ARGS}}` + start: Switching environment to `{{.CLI_ARGS}}` + success: Successfully switched environment to `{{.CLI_ARGS}}` + cmds: + - | + {{if .CLI_ARGS}} + for ITEM in environments/{{.CLI_ARGS}}/*; do + ITEM="$(echo $ITEM | sed 's/.*\///')" + if test -L "$ITEM" || ! test -e "$ITEM"; then + rm -f "$ITEM" + ln -s "./environments/{{.CLI_ARGS}}/$ITEM" "$ITEM" + .config/log success "Successfully symlinked "'`'"$ITEM"'`'" from ./environments/{{.CLI_ARGS}}/$ITEM" + else + .config/log warn "$ITEM exists in the root and was not a symlink so it was skipped to prevent possible data loss" + fi + done + {{else}} + exit 69 + {{end}} + - .config/log finish 'Success `environment:cli`' + + environment:prompt: + env: + MARKDOWN: | + # Symlink Environment + + Answer the prompt below to switch between environments. Each environment + should be a folder with folders and files you wish to link to from the root + of the project. They should normally consist of a host_vars, group_vars, + inventory, and files folder (but can contain any files/folders you wish to link). + Each environment should have its own folder in the `environments/` folder titled + as the name of the environment. After you select an answer, the script will + symlink all of the items in the environments folder to the root as long as there + is not anything except a symlink to the target location (i.e. it will overwrite + symlinks but not files). + + cmds: + - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP" + - task: environment:prompt:continue + + environment:prompt:continue: + interactive: true + deps: + - :install:software:gum + - :install:software:jq + cmds: + - | + ENVIRONMENT_OPTIONS="$(find ./environments -maxdepth 1 -mindepth 1 | sed 's/\.\/environments\///' | jq -R '[.]' | jq -s -c -r 'add | join(" ")')" + .config/log prompt 'Select an environment from the `environments/` folder to symlink to' + ENV_OPTION="$(gum choose $(echo $ENVIRONMENT_OPTIONS))" + task ansible:playbook:environment:cli -- "$ENV_OPTION" + + find-missing:files: + desc: Find roles that are missing files + hide: '{{ne (print .REPOSITORY_TYPE "-" .REPOSITORY_SUBTYPE) "ansible-playbook"}}' + summary: | + # Find roles that are missing any given file + + This task scans through all the folders in the roles/ directory and checks + for the presence of a file that you pass in through the CLI. + + **Example usage:** + `task find-missing -- logo.png` + + The example above will look through all the folders two levels deep (e.g. `./roles/tools/nmap`, + `./roles/system/snapd`) in the roles folder and display any roles that are missing the file. + log: + error: Failed to scan the `/roles/*` folders for roles missing `{{.CLI_ARGS}}` + start: Determining which roles in the `/roles/*` folders are missing `{{.CLI_ARGS}}` + success: Finished scanning for roles missing `{{.CLI_ARGS}}` (if there are any then they should be listed above) + cmds: + - | + FILES=$(find ./roles -mindepth 2 -maxdepth 2 -type d '!' -exec test -e "{}/{{.CLI_ARGS}}" ';' -print) + .config/log info 'Found '"$(echo "$FILES" | wc -l | xargs)"' roles missing {{.CLI_ARGS}}' + echo "$FILES" + preconditions: + - sh: test -d roles + msg: The `roles/` folder is missing. Is the project set up right? + + find-missing:roles: + summary: | + # Find roles that are not in main.yml + + This task will collect all the role paths in the `roles/` folder and return a list + of roles that are not present in the `main.yml` file. + + If you want to scan something other than `main.yml`, you can pass the file in as a + CLI argument like so: + + **Example scanning file other than `main.yml`:** + `task ansible:playbook:find-missing:roles -- playbooks/qubes.yml` + cmds: + - | + TMP="$(mktemp)" + RESULTS="$(mktemp)" + while read ROLE; do + ROLE_TITLE="$(echo "$ROLE" | sed 's/^.\///')" + if ! grep "$ROLE_TITLE" '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}main.yml{{end}}' > /dev/null; then + echo "$ROLE_TITLE" >> "$RESULTS" + fi + done< <(find ./roles -maxdepth 2 -mindepth 2) + if grep ".*" "$RESULTS"; then + .config/log info 'The roles missing from `{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}main.yml{{end}}` are:' + cat "$RESULTS" + else + .config/log info 'All of the roles are included in `{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}main.yml{{end}}`' + fi + + remotes: + deps: + - :install:software:git + - :install:software:jq + summary: | + # Ensures each role is added as a remote and a sub-repo (if applicable) + + This task cycles through all the roles in the `/roles` folder and ensures + they are added as remotes. This helps with managing the git trees used + for combining the many role repositories into the playbook mono-repository. + + It also adds a remote for a private submodule that is intended to store + files that are not meant to be shared. The remote is named `private`. + run: once + log: + error: Failed to set remotes for one or more of the roles in the `/roles/*` folders + start: Adding git remotes for all of the roles in the `/roles/*` folders + success: Successfully added git remotes for all of the roles in the `/roles/*` folders + cmds: + - git init -q + - | + for ROLE_RELATIVE_PATH in roles/*/*; do + if [ -f "${ROLE_RELATIVE_PATH}/package.json" ]; then + ROLE_FOLDER="$(basename $ROLE_RELATIVE_PATH)" + ROLE_HTTPS_REPO="$(jq -r '.blueprint.repository.gitlab' $ROLE_RELATIVE_PATH/package.json)" + ROLE_SSH_REPO="$(echo $ROLE_HTTPS_REPO | sed 's/https:\/\/gitlab.com\//git@gitlab.com:/' | sed 's/$/.git/')" + if git config "remote.${ROLE_FOLDER}.url" > /dev/null; then + git remote set-url "$ROLE_FOLDER" "$ROLE_SSH_REPO" + else + git remote add "$ROLE_FOLDER" "$ROLE_SSH_REPO" + fi + if [ -d $ROLE_RELATIVE_PATH/.git ] && [ ! -f $ROLE_RELATIVE_PATH/.gitrepo ]; then + task ansible:playbook:subrepo:init -- $ROLE_RELATIVE_PATH + fi + else + .config/log warn "${ROLE_RELATIVE_PATH}/package.json is missing!" + fi + done + + run: + cmds: + - task: run:{{if .CLI_ARGS}}cli{{else}}prompt{{end}} + + run:cli: + deps: + - task: :install:python:requirements + env: + INSTALL_OPTIONS: --no-dev + - :symlink:playbook + vars: + PLAYBOOK_MAIN: + sh: | + if [ -n "$PLAYBOOK_MAIN" ]; then + echo "$PLAYBOOK_MAIN" + else + echo "main.yml" + fi + log: + error: Error encounted while running `ansible-playbook -i inventories/{{.CLI_ARGS}} --ask-vault-pass main.yml` + start: Running `ansible-playbook -i inventories/{{.CLI_ARGS}} --ask-vault-pass main.yml` + success: Successfully ran `ansible-playbook -i inventories/{{.CLI_ARGS}} --ask-vault-pass main.yml` + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + if [ -z "$ANSIBLE_VAULT_PASSWORD" ]; then + .config/log info 'The `ANSIBLE_VAULT_PASSWORD` environment variable is not set so you will be prompted for the password' + ansible-playbook --skip-tags "mas" -i {{.CLI_ARGS}} --ask-vault-pass "{{.PLAYBOOK_MAIN}}" + else + echo "$ANSIBLE_VAULT_PASSWORD" > "$HOME/.VAULT_PASSWORD" + export ANSIBLE_VAULT_PASSWORD_FILE="$HOME/.VAULT_PASSWORD" + .config/log info 'Bypassing Ansible Vault password prompt since the `ANSIBLE_VAULT_PASSWORD` environment variable is set' + ansible-playbook --skip-tags "mas" -i {{.CLI_ARGS}} "{{.PLAYBOOK_MAIN}}" + fi + + run:prompt: + summary: '{{.MARKDOWN}}' + vars: + MARKDOWN: | + # Run the Playbook + + These set of prompts will run the `main.yml` playbook after you specify: + + (1) The "environment" + + The environment is a collection of folders that should, at the very minimum, + include "files", "group_vars", "host_vars", and "inventories". Each folder in + the "environments" folder constitutes a different environment. By using + environments, you can seperate different sets of variables/files or even seperate + your private variables out into a sub-module. + + (2) An inventory file + + The Ansible inventory stored in the "inventories" folder. This will generally be + a YML file with host connection information that also correlates the inventory with + the proper host_vars and group_vars. It is assumed that your sudo username and + password are encrypted inside the inventory (via "ansible-vault"). + PLAYBOOK_DESCRIPTIONS: | + # Playbook Descriptions + + Playbooks are where you store your main logic in Ansible. The `main.yml` file in the + root of the repository is a generic one-size-fits-all approach. You could theoretically + store all your logic in one playbook for multiple scenarios with conditional logic but + it might be easier to create seperate playbooks in some cases. The `main.yml` should work + in most scenarios and is a great starting point. + + ## Alternate Playbook Descriptions + + Alternate playbooks are stored in the `playbooks/` directory. They are described below. + + *If you want their description to show up in this message, you will have to edit the appropriate + field in `package.json`.* + + env: + MARKDOWN: '{{.MARKDOWN}}' + cmds: + - | + MD_TMP="$(mktemp)" + echo "$MARKDOWN" > "$MD_TMP" + .config/log md "$MD_TMP" + - task: run:prompt:continue + + run:prompt:continue: + interactive: true + deps: + - :install:software:jq + cmds: + - task: environment + - echo "\"inventories"$(find ./inventories/ -mindepth 1 -maxdepth 1 | sed 's/\.\/inventories\///' | jq -R '[.]' | jq -s -c -r 'add | join("\" \"inventories")')"\"" + - | + INVENTORY_OPTIONS_LENGTH="$(find ./inventories/ -mindepth 1 -maxdepth 1 | sed 's/\.\/inventories\///' | jq -R '[.]' | jq -s -c -r 'add | length')" + if [[ "$INVENTORY_OPTIONS_LENGTH" == '0' ]]; then + .config/log error 'There are no inventory files present in the `inventories/` folder' && exit 1 + else + INVENTORY_OPTIONS="$(echo "\""$(find ./inventories/ -mindepth 1 -maxdepth 1 | sed 's/\.\///' | jq -R '[.]' | jq -s -c -r 'add | join("\" \"")')"\"")" + .config/log prompt 'Which inventory would you like to use?' + CHOSEN_INVENTORY="$(.config/log choose $INVENTORY_OPTIONS)" + export PLAYBOOK_MAIN="main.yml" + if ! gum confirm "Would you like to use the main.yml playbook?"; then + PLAYBOOKS_OPTIONS="$(echo "\"playbooks"$(find ./playbooks/ -mindepth 1 -maxdepth 1 -name "*.yml" | sed 's/\.\/playbooks\///' | jq -R '[.]' | jq -s -c -r 'add | join("\" \"playbooks")')"\"")" + PLAYBOOK_DESCS_TMP="$(mktemp)" && echo "$PLAYBOOK_DESCRIPTIONS" > "$PLAYBOOK_DESCS_TMP" + TMP_LIST="$(mktemp)" + find ./playbooks/ -mindepth 1 -maxdepth 1 -name "*.yml" | sed 's/\.\/playbooks\///' | jq -R '[.]' | jq -s -c -r 'add' >> "$TMP_LIST" + echo "* **main.yml:** A generic playbook that attempts to install everything" >> "$PLAYBOOK_DESCS_TMP" + jq -c -r '.[]' "$TMP_LIST" | while read SLUG; do + PLAYBOOK_DESC="$(jq -r '.blueprint.fileDescriptions[$file]' package.json)" + if [ "$PLAYBOOK_DESC" != 'null' ]; then + echo "* **playbooks/${SLUG}.yml:** $PLAYBOOK_DESC" >> "$PLAYBOOK_DESCS_TMP" + fi + done + .config/log md "$PLAYBOOK_DESCS_TMP" + .config/log prompt 'Select the playbook you would like to provision with.' + export PLAYBOOK_MAIN="$(.config/log choose $PLAYBOOKS_OPTIONS)" + fi + task ansible:playbook:run:cli -- "$CHOSEN_INVENTORY" + fi + + subrepo:init: + deps: + - :install:software:subrepo + summary: | + # Add Role as a Sub-Repository + + Since roles should each have their own repository for Ansible Galaxy and to make it easier + for the community to download specific roles, they must be declared as sub-repositories. + Submodules could also be used but we use sub-repos instead because they are more flexible. + + In the main playbook, if someone clones the playbook, the playbook and all the roles will download + without any requirement to initialize submodules. At the same time, each role can be in its own + repository. The playbook recognizes roles like this because they have a `.gitrepo` file that is only + saved in the playbook version of the role. Users can interact with the playbook and its role + repositories transparently without any need to understand what git subrepos are. + + Managers of roles can update the role repositories without any need to understand what git subrepos + are. Managers of the playbook can use the tool [git-subrepo](https://github.com/ingydotnet/git-subrepo) + to perform various actions including pulling changes from individual role repositories and + other actions. + + Usage: + `task ansible:playbook:subrepo:init -- path/to/folder/with.git/folder` + cmds: + - task: :git:commit:automated + - | + BASENAME="$(basename {{.CLI_ARGS}})" + REMOTE="$(git remote get-url $BASENAME)" + HUSKY=0 git subrepo init {{.CLI_ARGS}} -r "$REMOTE" -b master + + types: + deps: + - :install:npm:quicktype + summary: | + # Generate Types from Vaulted Files + + Automatically generate types from vaulted files. + + **Example Usage:** + `task ansible:playbook:types -- ./environments/prod + vars: + TARGET_DIR: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}./environments/prod{{end}}' + cmds: + - task: vault:decrypt + vars: + TARGET_DIR: '{{.TARGET_DIR}}' + - | + qtype() { + local FILE_NO_EXT="$(echo "$1" | sed 's/.yml$//')" + yq e -o=json '.' $1 | quicktype -l schema -o ${FILE_NO_EXT}.schema.json + } + while read FILE; do + qtype "$FILE" + done < <(find {{.TARGET_DIR}} -type f -name "*vault.yml") + - task: vault:encrypt + vars: + TARGET_DIR: '{{.TARGET_DIR}}' + + vault:decrypt: + vars: + TARGET_DIR: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}./environments/prod{{end}}' + cmds: + - find {{.TARGET_DIR}} -type f -name "*vault.yml" -printf "%h/\"%f\" " | xargs ansible-vault decrypt + + vault:encrypt: + vars: + TARGET_DIR: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}./environments/prod{{end}}' + cmds: + - find {{.TARGET_DIR}} -type f -name "*vault.yml" -printf "%h/\"%f\" " | xargs ansible-vault encrypt diff --git a/.config/taskfiles/ansible/Taskfile-populate.yml b/.config/taskfiles/ansible/Taskfile-populate.yml new file mode 100644 index 00000000..686d5888 --- /dev/null +++ b/.config/taskfiles/ansible/Taskfile-populate.yml @@ -0,0 +1,180 @@ +--- +version: '3' + +vars: + META_PATH: meta/main.yml + REQUIREMENTS_PATH: requirements.yml + +tasks: + collection: + deps: + - :install:software:yq + log: + error: Failed to auto-populate the `{{.KEY}}` collection + start: Determining if the `{{.KEY}}` collection should be added to the `{{.REQUIREMENTS_PATH}}` + cmds: + - | + COLLECTIONS="$(yq eval '.collections' '{{.REQUIREMENTS_PATH}}')" + REFERENCES="$(grep -Ril '{{.KEY}}' ./tasks || true)" + if [[ ! "$COLLECTIONS" =~ '{{.KEY}}' ]] && [ "$REFERENCES" ]; then + yq eval -i -P '.collections = .collections + {{.VAL}}' '{{.REQUIREMENTS_PATH}}' + .config/log success 'Automatically added `{{.VAL}}` to {{.REQUIREMENTS_PATH}}' + fi + - task: :fix:yaml:dashes + vars: + CLI_ARGS: '{{.REQUIREMENTS_PATH}}' + + collection:force: + deps: + - :install:software:yq + log: + error: Failed to forcefully auto-populate the `{{.KEY}}` collection + start: Determining if the `{{.KEY}}` collection should be added to the `{{.REQUIREMENTS_PATH}}` (forcefully) + cmds: + - | + COLLECTIONS="$(yq eval '.collections' '{{.REQUIREMENTS_PATH}}')" + if [[ ! "$COLLECTIONS" =~ '{{.KEY}}' ]]; then + yq eval -i -P '.collections = .collections + {{.VAL}}' '{{.REQUIREMENTS_PATH}}' + .config/log success 'Automatically added `{{.VAL}}` to {{.REQUIREMENTS_PATH}}' + fi + - task: :fix:yaml:dashes + vars: + CLI_ARGS: '{{.REQUIREMENTS_PATH}}' + + dependencies: + desc: 'Attempt to automatically populate `{{.META_PATH}}` and `{{.REQUIREMENTS_PATH}}`' + hide: '{{ne (print .REPOSITORY_TYPE "-" .REPOSITORY_SUBTYPE) "ansible-role"}}' + summary: | + # Automatically populate `{{.META_PATH}}` and `{{.REQUIREMENTS_PATH}}` + + A role can sometimes have dependencies that need to be installed prior to being run (e.g. most + roles in Ansible >2.9 need the `community.general` collection installed). Roles also sometimes + need other roles to run before they are run (e.g. a task that installs a Node.js package needs + the Node.js installer to run first). This task will scan for common dependencies by doing a text + search for a handful of common strings. It will then attempt to automatically populate + `{{.META_PATH}}` and the `{{.REQUIREMENTS_PATH}}`. + + Items it attempts to auto-populate for: + + * chocolatey.chocolatey + * community.general + * community.general.homebrew + * community.general.gem + * community.general.npm + * community.general.snap + log: + start: Auto-populating the `{{.META_PATH}}` and `{{.REQUIREMENTS_PATH}}` with common dependencies (when appropriate) + success: Auto-populated the `{{.META_PATH}}` and `{{.REQUIREMENTS_PATH}}` with common dependencies + cmds: + - task: collection + vars: + KEY: chocolatey.chocolatey + VAL: >- + {"name": "chocolatey.chocolatey", "source": "https://galaxy.ansible.com"} + - task: collection + vars: + KEY: community.crypto + VAL: >- + {"name": "community.crypto", "source": "https://galaxy.ansible.com"} + - task: collection + vars: + KEY: community.general + VAL: >- + {"name": "community.general", "source": "https://galaxy.ansible.com"} + - task: collection:force + vars: + KEY: google.cloud + VAL: >- + {"name": "google.cloud", "source": "https://galaxy.ansible.com"} + - task: dependency + vars: + KEY: community.general.homebrew + ROLE: professormanhattan.homebrew + VAL: >- + {"role": "professormanhattan.homebrew", "when": "ansible_os_family == 'Darwin'"} + - task: dependency + vars: + KEY: community.general.npm + ROLE: professormanhattan.nodejs + VAL: >- + {"role": "professormanhattan.nodejs"} + - task: dependency + vars: + KEY: community.general.gem + ROLE: professormanhattan.ruby + VAL: >- + {"role": "professormanhattan.ruby"} + - task: dependency + vars: + KEY: community.general.snap + ROLE: professormanhattan.snapd + VAL: >- + {"role": "professormanhattan.snapd", "when": "ansible_system == 'Linux'"} + - task: dependency + vars: + KEY: professormanhattan.startmenu + ROLE: professormanhattan.startmenu + VAL: >- + {"role": "professormanhattan.startmenu", "when": "ansible_system == 'Windows'"} + - task: :ansible:sync:requirements + sources: + - '{{.META_PATH}}' + - '{{.REQUIREMENTS_PATH}}' + - tasks/**/*.yml + + dependency: + deps: + - :install:software:yq + log: + error: Failed to auto-populate the `{{.KEY}}` role + start: Determining if the `{{.KEY}}` role should be added to the `{{.META_PATH}}` + cmds: + - | + DEPENDENCIES="$(yq eval '.dependencies' '{{.META_PATH}}')" + REFERENCES="$(grep -Ril '{{.KEY}}' ./tasks || true)" + if [[ ! "$DEPENDENCIES" =~ '{{.ROLE}}' ]] && [ "$REFERENCES" ]; then + if [[ '{{.GALAXY_NAMESPACE}}.{{.GALAXY_ROLE_NAME}}' != '{{.ROLE}}' ]]; then + yq eval -i -P '.dependencies = .dependencies + {{.VAL}}' '{{.META_PATH}}' + .config/log success 'Automatically added `{{.VAL}}` to {{.META_PATH}}' + fi + fi + - task: :fix:yaml:dashes + vars: + CLI_ARGS: '{{ .META_PATH }}' + status: + - '[[ "$DEPENDENCIES" =~ "{{.ROLE}}" ]] || [ ! "$REFERENCES" ]' + + meta: + deps: + - :install:npm:prettier + - :install:software:jq + - :install:software:yq + vars: + DESCRIPTION: + sh: yq eval '.galaxy_info.description' '{{.META_PATH}}' + REFERENCE_LINK: Take a look at an [example meta/main.yml + file](https://gitlab.com/megabyte-labs/ansible-roles/androidstudio/-/blob/master/{{.META_PATH}}). + log: + error: Failed to synchronize `package.json` with `{{.META_PATH}}` + start: Updating `package.json` blueprint description and slug using values present in `{{.META_PATH}}` + success: Ensured `package.json` is synchronized with `{{.META_PATH}}` + cmds: + - | + TMP="$(mktemp)" + cat package.json + jq --arg a '{{.DESCRIPTION}}' --arg b '{{.GALAXY_ROLE_NAME}}' '.blueprint.description = $a | .blueprint.slug = $b' package.json > "$TMP" + mv "$TMP" package.json + - prettier --write package.json + sources: + - meta/main.yml + - package.json + preconditions: + - sh: 'test -f {{.META_PATH}}' + msg: 'The `{{.META_PATH}}` file is missing. A properly filled out `{{.META_PATH}}` file is required for the + update process. {{.REFERENCE_LINK}}' + - sh: '[[ "{{.DESCRIPTION}}" != "null" ]]' + msg: 'The `{{.META_PATH}}` file has a null value for the `galaxy_info.description` key. Ensure the description + is populated in `{{.META_PATH}}`. {{.REFERENCE_LINK}}' + - sh: '[[ "{{.GALAXY_ROLE_NAME}}" != "null" ]]' + msg: 'The `{{.META_PATH}}` file has a null value for the `galaxy_info.role_name` key. Ensure the role name is + populated in `{{.META_PATH}}`. {{.REFERENCE_LINK}}' diff --git a/.config/taskfiles/ansible/Taskfile-test.yml b/.config/taskfiles/ansible/Taskfile-test.yml new file mode 100644 index 00000000..31903592 --- /dev/null +++ b/.config/taskfiles/ansible/Taskfile-test.yml @@ -0,0 +1,985 @@ +--- +version: '3' + +vars: + MOLECULE_LOGS_PATH: molecule/.results/logs + MOLECULE_TEST_OPTIONS: ANSIBLE_STDOUT_CALLBACK=community.general.yaml + ANSIBLE_CALLBACKS_ENABLED="junit, ansible.posix.profile_tasks, ansible.posix.timer" + JUNIT_OUTPUT_DIR="molecule/.results/junit" JUNIT_FAIL_ON_CHANGE=true JUNIT_HIDE_TASK_ARGUMENTS=true + PYTHON_MOLECULE_HANDLE: + sh: | + if [ -n "$CI" ]; then + echo '' + else + if command -v unbuffer > /dev/null; then + echo 'unbuffer {{.PYTHON_HANDLE}}' + else + echo '{{.PYTHON_HANDLE}}' + fi + fi + +tasks: + allure:report: + deps: + - :install:software:allure + log: + error: Failed to generate and/or open the unit test report + start: Generating and opening unit test report + success: Successfully generated and opened unit test report + cmds: + - allure generate molecule/.results/junit --output allure-reports --clean + - mkdir -p molecule/.results/junit + - cp -rf allure-reports/history/ molecule/.results/junit/ + - .config/log info 'Opening JUnit results with Allure in the default browser' + - allure open allure-reports + + ansifilter: + deps: + - :install:software:ansifilter + cmds: + - TMP="$(mktemp)" && cat {{.RESULTS_FILE}} | ansifilter > "$TMP" && mv "$TMP" {{.RESULTS_FILE}} + status: + - '[ -n "$CI" ]' + + default: + log: + start: Running default Ansible test + cmds: + - task: molecule:docker:matrix + + local: + desc: Run the Ansible play on the local machine (or via WSL - see task summary) + hide: '{{ne (print .REPOSITORY_TYPE "-" .REPOSITORY_SUBTYPE) "ansible-playbook"}}' + summary: | + # Run the Ansible play on the local machine + + This task will use the inventory stored in `test//inventory`, the playbook + file stored in `test//test.yml`, and the Ansible configuration file stored in + `test//ansible.cfg` to run the play. At the beginning of the play, you will + be prompted for the sudo password. + cmds: + - task: local:test + + local:test: + cmds: + - | + if [ -n "$CI" ]; then + .config/log info '`$CI` environment variable is present' + task ansible:test:local:test:ci + else + task ansible:test:local:test:local + fi + + local:test:ci: + deps: + - :symlink:{{.REPOSITORY_SUBTYPE}} + cmds: + - | + if [ -n "$WINDOWS_ANSIBLE_TEST" ]; then + pip3 install ansible 'pywinrm[credssp]' + else + pip3 install ansible + fi + - | + PATH="$PATH:$HOME/.local/bin" + ansible-galaxy install -r requirements.yml + - task: local:test:logic + + local:test:local: + deps: + - :symlink:{{.REPOSITORY_SUBTYPE}} + - task: :install:python:requirements + env: + INSTALL_OPTIONS: --no-dev + cmds: + - task: local:test:logic + + local:test:logic: + vars: + ANSIBLE_CFG: |- + [winrm_connection] + scheme = https + server_cert_validation = ignore + transport = credssp,ssl + ROLE_NAME: '{{.GALAXY_NAMESPACE}}.{{.GALAXY_ROLE_NAME}}' + SUDO_PASS_PARAM: + sh: if [ -z "$CI" ]; then echo ' --ask-sudo-pass'; else echo ''; fi + WINDOWS_INVENTORY: windows-ci ansible_host=localhost ansible_user=runneradmin + ansible_password=AnsibleTest999 ansible_connection=winrm ansible_winrm_server_cert_validation=ignore + ansible_winrm_transport=credssp + log: + error: Encountered error while testing the Ansible playbook locally + start: Testing the Ansible playbook locally + success: Successfully tested the Ansible playbook locally + cmds: + - | + if [ -n "$WINDOWS_ANSIBLE_TEST" ]; then + echo '{{.WINDOWS_INVENTORY}}' > inventory + echo '{{.ANSIBLE_CFG}}' > ansible.cfg + else + echo 'localhost ansible_connection=local' > inventory + fi + - task: local:test:playbook + vars: + PLAY_HOSTS: + sh: if [ -z "$WINDOWS_ANSIBLE_TEST" ]; then echo 'localhost'; else echo 'windows-ci'; fi + PLAY_ROLE_NAME: '{{.ROLE_NAME}}' + - | + if [ -z "$CI" ]; then + .config/log info 'Prompting for sudo password (required for non-CI environments)' + fi + if [ -n "$WINDOWS_ANSIBLE_TEST" ]; then + export ANSIBLE_CONFIG="$PWD/ansible.cfg" + fi + PATH="$PATH:$HOME/.local/bin" + {{.PYTHON_MOLECULE_HANDLE}} ansible-playbook --skip-tags "mas" -i inventory{{.SUDO_PASS_PARAM}} test/{{OS}}/test.yml 2>&1 | tee debug.log || EXIT_CODE=$? + - task: post:molecule:log + vars: + RESULTS_FILE: debug.log + + local:test:playbook: + vars: + TEST_PLAY: | + --- + - hosts: {{.PLAY_HOSTS}} + roles: + - role: '{{.PLAY_ROLE_NAME}}' + cmds: + - echo '{{.TEST_PLAY}}' > test.yml + + molecule:ci:requirements: + cmds: + - | + if [ -n "$WINDOWS_ANSIBLE_TEST" ]; then + .config/log info 'Running `pip3 install ansible ansibler molecule pywinrm[credssp]`' + pip3 install ansible ansibler molecule 'pywinrm[credssp]' + else + .config/log info 'Running `pip3 install ansible ansibler molecule`' + pip3 install ansible ansibler molecule + fi + - | + .config/log info 'Running `ansible-galaxy install --ignore-errors -r requirements.yml`' + PATH="$PATH:$HOME/.local/bin" + ansible-galaxy install --ignore-errors -r requirements.yml + + molecule:dependencies: + run: once + cmds: + - | + if [ -n "$CI" ]; then + .config/log info '`$CI` environment is present' + task ansible:test:molecule:dependencies:ci + else + task ansible:test:molecule:dependencies:local + fi + + molecule:dependencies:ci: + cmds: + - task: molecule:ci:requirements + status: + - '[ -z "$CI" ]' + + molecule:dependencies:local: + deps: + - :install:python:requirements + - :install:software:expect + - :install:software:sshpass + log: + error: Encountered error while installing Ansible Galaxy requirements defined in `requirements.yml` + start: Installing Ansible Galaxy requirements defined in `requirements.yml` + success: Installed Ansible Galaxy requirements defined in `requirements.yml` + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + if poetry &> /dev/null; then + {{.PYTHON_HANDLE}} ansible-galaxy install --ignore-errors -r requirements.yml + else + .config/log info 'Current shell is already a Poetry virtual environment' + ansible-galaxy install --ignore-errors -r requirements.yml + fi + - task: :symlink:{{.REPOSITORY_SUBTYPE}} + status: + - '[ -n "$CI" ]' + + molecule:docker: + desc: Runs a Docker Molecule test + hide: '{{ne .REPOSITORY_TYPE "ansible"}}' + summary: | + # Runs a Docker Molecule test + + This task runs the project's Molecule tests using Docker. It only tests against + Linux systems. + + **Opens a prompt:** + `task ansible:test:molecule:docker` + + **Runs the test against the "CentOS-8" group directly:** + `task ansible:test:molecule:docker -- CentOS-8` + + **Save test results for use with auto-generating compatibility chart:** + `task ansible:test:molecule:docker:matrix` + cmds: + - | + if ! docker run --rm hello-world; then + .config/log warn 'The command `docker run --rm hello-world` failed' + if [ -f '/Applications/Docker.app' ]; then + .config/log info 'Attempting to open `Applications/Docker.app` (Docker Desktop for macOS)' + open /Applications/Docker.app + sleep 30 + fi + fi + - task: molecule:docker:{{if .CLI_ARGS}}cli{{else}}prompt{{end}} + + molecule:docker:cli: + deps: + - molecule:dependencies + - :install:software:docker + log: + error: The `{{.CLI_ARGS}}` Docker Molecule test finished with errors + start: Running Docker Molecule test on containers in the `{{.CLI_ARGS}}` group + success: Successfully ran the `{{.CLI_ARGS}}` Docker Molecule test + cmds: + - | + set -o pipefail + {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="{{.CLI_ARGS}}" {{.PYTHON_MOLECULE_HANDLE}}molecule test -s docker \ + -- --skip-tags skipdockertest 2>&1 | tee debug.log || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + fi + - task: post:molecule:log + vars: + RESULTS_FILE: debug.log + + molecule:docker:matrix: + deps: + - molecule:dependencies + - :install:software:docker + vars: + MOLECULE_DATE: + sh: date '+%Y-%m-%d' + log: + error: There were errors while running the test (results were logged to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt`) + start: Running Docker Molecule test with results teed to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt` + success: Finished running the test (results were logged to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt`) + cmds: + - mkdir -p {{.MOLECULE_LOGS_PATH}} + - | + SCENARIO="Linux" + if grep -Ril 'community.general.snap:' ./tasks; then + SCENARIO="Snap" + .config/log warn 'Running Docker Molecule tests on the Docker containers that are compatible with `snap` since the role has references to `snap`' + fi + set -o pipefail + {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="$SCENARIO" {{.PYTHON_MOLECULE_HANDLE}}molecule test -s docker -- --skip-tags skipdockertest 2>&1 | \ + tee '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt' || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + fi + - task: post:molecule:log + vars: + RESULTS_FILE: '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt' + + molecule:docker:prompt: + env: + MARKDOWN: | + # Ansible Molecule Test via Docker + + Choose a container group from the options below to begin the Molecule test. + The choices should be mostly self-explanatory. The `Snap` group is a special group + that should be used to test roles that contain `snap` logic. Only recent versions of + Debian and Ubuntu support snap installations inside a Docker container. Docker tests + are a quick way to test Ansible plays without consuming a large amount of system + resources. Granted, to fully test an Ansible play, a VirtualBox method should be used + instead. + cmds: + - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP" + - task: molecule:docker:prompt:continue + + molecule:docker:prompt:continue: + interactive: true + deps: + - :install:software:gum + - :install:software:jq + - :install:software:yq + cmds: + - | + DOCKER_OPTIONS="$(yq eval -o=j '.groups' molecule/docker/molecule.yml | jq -r 'keys | join(" ")')" + DOCKER_OPTIONS_LENGTH="$(yq eval -o=j '.groups' molecule/docker/molecule.yml | jq -r 'keys | length')" + if [[ "$DOCKER_OPTIONS_LENGTH" == '0' ]]; then + .config/log error 'There are no Molecule groups defined in `molecule/desktop/molecule.yml`' && exit 1 + else + .config/log info 'Press SPACE to select an item and ENTER when you are done selecting test environments' + .config/log prompt 'Which environment(s) would you like to run the test on?' + CHOSEN_OPTIONS="$(gum choose --no-limit $(echo "$DOCKER_OPTIONS"))" + COMBINED_OPTIONS="" + CHOSEN_COUNT="$(echo "$CHOSEN_OPTIONS" | wc -l)" + if [[ "$CHOSEN_COUNT" == '0' ]]; then + .config/log error 'No items were selected!' && exit 1 + else + while read CURRENT_OPTION; do + COMBINED_OPTIONS="${COMBINED_OPTIONS}:${CURRENT_OPTION}" + done< <(echo "$CHOSEN_OPTIONS") + CHOSEN_OPTION="${COMBINED_OPTIONS:1}" + export ANSIBLE_ENABLE_TASK_DEBUGGER=true + .config/log info 'Running `task ansible:test:molecule:docker:cli -- '"$CHOSEN_OPTION"'`' + task ansible:test:molecule:docker:cli -- "$CHOSEN_OPTION" + fi + fi + - task: allure:report + preconditions: + - sh: test -f molecule/docker/molecule.yml + msg: The `molecule/docker/molecule.yml` file must be present and in the proper format + + molecule:gcp: + deps: + - molecule:dependencies + - :install:software:gcloud + log: + error: Encountered error(s) while running the Google Cloud Platform Molecule test + start: Running Google Cloud Platform Molecule test + success: Finished running Google Cloud Platform Molecule test + cmds: + - task: molecule:gcp:preconditions + - | + set -o pipefail + .config/log 'Results will be available in the `debug.log` file' + {{.PYTHON_MOLECULE_HANDLE}}molecule test -s gcp 2>&1 | tee debug.log || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + fi + - task: post:molecule:log + vars: + RESULTS_FILE: debug.log + + molecule:gcp:matrix: + deps: + - molecule:dependencies + - :install:software:gcloud + - :install:software:yq + vars: + MOLECULE_DATE: + sh: date '+%Y-%m-%d' + log: + error: An error occurred while running the Google Cloud Platform Molecule test sequence + start: Running Docker Molecule test with results teed to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt` + success: Finished running and formatting the results of the Google Cloud Platform molecule test + cmds: + - task: molecule:gcp:preconditions + - mkdir -p {{.MOLECULE_LOGS_PATH}} + - | + set -o pipefail + {{.PYTHON_MOLECULE_HANDLE}}molecule test -s gcp 2>&1 | tee "{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt" || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + else + .config/log success 'Finished running the test (results were logged to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt`)' + fi + - task: post:molecule:log + vars: + RESULTS_FILE: '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt' + - | + RESULTS="{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt" + PLATFORM_LENGTH="$(yq e '.platforms | length' molecule/gcp/molecule.yml)" + INDEX=0 + while [ $INDEX -lt $PLATFORM_LENGTH ]; do + NAME="$(yq e '.platforms['$INDEX'].name' molecule/gcp/molecule.yml)" + ALIAS="$(yq e '.platforms['$INDEX'].alias' molecule/gcp/molecule.yml)" + sed -i -- 's/'"$NAME"'/'"$ALIAS"'/g' "$RESULTS" + INDEX=$((INDEX+1)) + done + + molecule:gcp:preconditions: + preconditions: + - sh: '[ -n "$GCE_SERVICE_ACCOUNT_EMAIL" ]' + msg: The GCE_SERVICE_ACCOUNT_EMAIL environment variable must be set (e.g. export + GCE_SERVICE_ACCOUNT_EMAIL=molecule@megabyte-labs.iam.gserviceaccount.com). + - sh: '[ -n "$GCE_CREDENTIALS_FILE" ]' + msg: The GCE_CREDENTIALS_FILE environment variable must be set and pointing to the GCP + service account JSON key (e.g. export GCE_CREDENTIALS_FILE=~/.config/gcp.json). + - sh: test -f "$GCE_CREDENTIALS_FILE" + msg: The GCE_CREDENTIALS_FILE environment variable is defined but is not pointing to a file that exists. + - sh: '[ -n "$GCE_PROJECT_ID" ]' + msg: The GCE_PROJECT_ID environment variable must be set (e.g. export GCE_PROJECT_ID=megabyte-labs) + + molecule:local: + desc: Runs a Molecule test on the localhost + hide: '{{ne .REPOSITORY_TYPE "ansible"}}' + summary: | + # Run a local Molecule test + + This option is the same as running the play on the localhost with the added + benefit of incorporating Molecule's test for idempotency and other tests. + + **Opens a prompt:** + `task ansible:test:local` + cmds: + - task: molecule:local:{{if .CLI_ARGS}}test{{else}}prompt{{end}} + + molecule:local:prompt: + env: + MARKDOWN: | + # Run Molecule Locally + + This testing option is provided for cases where you would like to locally test the + Ansible play with Molecule. This option assumes that the current user has sudo + privileges. + + ## Sudo Password + + A sudo password is required for all roles because Molecule has a step where it + ensures Python is installed with `become: true`. The sudo password could potentially + be logged in clear text if logging is in verbose mode so be careful when using this + method. + + ## Running Locally Without Molecule + + If you only want to install the play (without leveraging Molecule's features + like testing for idempotency and running test cases), then a more secure method would + be to run "ansible localhost --ask-sudo-pass -m include_role -a name=" after + installing the role and its dependencies with ansible-galaxy. + cmds: + - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP" + - task: molecule:local:prompt:continue + + molecule:local:prompt:continue: + deps: + - :install:software:gum + interactive: true + cmds: + - .config/log prompt 'What is the sudo password for the current user?' + - | + SUDO_PASS="$(.config/log password 'Enter sudo password for local machine..')" + export ANSIBLE_ENABLE_TASK_DEBUGGER=true + export TEST_PASSWORD="$SUDO_PASS" + task ansible:test:molecule:local:test + - task: allure:report + + molecule:local:test: + deps: + - molecule:dependencies + vars: + MOLECULE_DATE: + sh: date '+%Y-%m-%d' + log: + error: There was an error while running the Molecule test locally + start: Running the Molecule test locally + success: The local Molecule test was successfully run + cmds: + - | + set -o pipefail + if [ -z "$CI" ]; then + export PATH="$(poetry env info | grep 'Python: /' | sed 's/Python: //' | sed 's/$/\/bin/'):$PATH" + fi + {{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_MOLECULE_HANDLE}}molecule test -s local 2>&1 | \ + tee '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt' || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + fi + - task: post:molecule:log + vars: + RESULTS_FILE: '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt' + + molecule:ssh: + desc: Runs a Molecule test over SSH + hide: '{{ne .REPOSITORY_TYPE "ansible"}}' + summary: | + # Run an SSH Molecule test + + This option allows you to run the Molecule test against a single + SSH host. + + **Opens a prompt:** + `task ansible:test:molecule:ssh` + cmds: + - task: molecule:ssh:{{if .CLI_ARGS}}cli{{else}}prompt{{end}} + + molecule:ssh:cli: + deps: + - molecule:dependencies + log: + error: Errors encountered while running the SSH Molecule test + start: Running the Molecule test over SSH + success: Successfully ran the Molecule test over SSH + cmds: + - | + set -o pipefail + {{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_MOLECULE_HANDLE}}molecule test -s remote 2>&1 | tee debug.log || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + fi + - task: post:molecule:log + vars: + RESULTS_FILE: debug.log + + molecule:ssh:prompt: + env: + MARKDOWN: | + # Remote Ansible Molecule Test via SSH + + This testing option is provided for cases where you would like to remotely test + the Ansible play on remote machines via SSH. The prompts will ask you for the + host IP address or FQDN, user, and password. Before running this test, you should + ensure that you can already connect to the machine via SSH (i.e. the ~/.ssh keys + should already be set up). This test assumes that SSH does not require any passwords + to establish the connection. + cmds: + - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP" + - task: molecule:ssh:prompt:continue + + molecule:ssh:prompt:continue: + deps: + - :install:software:gum + interactive: true + cmds: + - | + .config/log prompt 'What is the IP address or the FQDN of the target host?' + IP_ANSWER="$(.config/log input 'Enter IP address or FQDN..')" + if [[ "$IP_ANSWER" =~ ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]] || \ + [[ "$IP_ANSWER" =~ ^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$ ]]; then + .config/log prompt 'What port should the SSH connection use?' + PORT_ANSWER="$(gum input --placeholder='Enter SSH port..' --value='22')" + if [[ "$PORT_ANSWER" =~ ^[0-9]*$ ]]; then + .config/log prompt 'What is the username of a user that has both sudo and SSH privileges?' + USER_ANSWER="$(.config/log input 'SSH username..')" + if [[ "$USER_ANSWER" ^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$ ]]; then + export ANSIBLE_ENABLE_TASK_DEBUGGER=true + export TEST_HOST="$IP_ANSWER" + export TEST_PORT="$PORT_ANSWER" + export TEST_SSH_USER="$USER_ANSWER" + export TEST_USER="$USER_ANSWER" + if [[ "$USER_ANSWER" != 'root' ]]; then + .config/log prompt 'What is the user's sudo password?' + SUDO_PASS_ANSWER="$(.config/log password 'Enter sudo password..')" + export TEST_PASSWORD="$SUDO_PASS_ANSWER" + export TEST_BECOME_PASSWORD="$SUDO_PASS_ANSWER" + fi + task ansible:test:molecule:ssh:cli + else + .config/log error 'Username is invalid!' && exit 1 + else + .config/log error 'That SSH port is not valid!' && exit 1 + else + .config/log error 'An invalid IP address / FQDN was entered.' && exit 1 + fi + + molecule:virtualbox: + desc: Runs a full E2E Molecule test for all supported operating systems + hide: '{{ne .REPOSITORY_TYPE "ansible"}}' + summary: | + # Run a full E2E Molecule test for all supported operating systems + + This task uses VirtualBox to run tests for all of our supported operating + systems in parallel. It is very RAM intensive so, if you want to run this, + your computer should have _at least 32GB of RAM_. + + **Opens a prompt:** + `task ansible:test:molecule:virtualbox` + + **Generate the compatibility matrix used in the README.md:** + `task ansible:test:molecule:virtualbox:matrix` + cmds: + - task: molecule:virtualbox:{{if .CLI_ARGS}}cli{{else}}prompt{{end}} + + # yamllint disable rule:truthy + molecule:virtualbox:cli: + deps: + - molecule:dependencies + - :install:software:vagrant + - :install:software:virtualbox + env: + OBJC_DISABLE_INITIALIZE_FORK_SAFETY: YES + log: + error: Errors encountered while running the `{{.CLI_ARGS}}` VirtualBox Molecule test + start: Running a VirtualBox Molecule test on platforms in the `{{.CLI_ARGS}}` group + success: Finished running the `{{.CLI_ARGS}}` VirtualBox Molecule test + cmds: + - | + set -o pipefail + {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="{{.CLI_ARGS}}" {{.PYTHON_MOLECULE_HANDLE}}molecule test 2>&1 | tee debug.log || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + fi + - task: post:molecule:log + vars: + RESULTS_FILE: debug.log + + molecule:virtualbox:converge: + desc: Provisions a desktop VirtualBox VM and then runs a Molecule test + hide: '{{ne .REPOSITORY_TYPE "ansible"}}' + summary: | + # Provision a desktop VirtualBox VM and then run a Molecule test + + This task opens a VM with an operating system of your choosing and then tests + the project's play against it. It then leaves the VM open for inspection. + + **Example with interactive prompt for VM type:** + `task test:molecule` + + **Example usage bypassing prompt:** + `task test:molecule -- ArchLinux` + + ## Available scenarios: + + * ArchLinux + * CentOS + * Debian + * Fedora + * macOS + * Ubuntu + * Windows + cmds: + - task: molecule:virtualbox:converge:{{if .CLI_ARGS}}cli{{else}}prompt{{end}} + + molecule:virtualbox:converge:cli: + deps: + - molecule:dependencies + - :install:software:vagrant + - :install:software:virtualbox + env: + OBJC_DISABLE_INITIALIZE_FORK_SAFETY: YES + log: + error: Errors were encountered while running the `{{.CLI_ARGS}}` VirtualBox Molecule converge play + start: Running the `{{.CLI_ARGS}}` VirtualBox Molecule converge play (this will leave the VirtualBox instance open for inspection) + success: Finished running the `{{.CLI_ARGS}}` VirtualBox Molecule converge play (you are encouraged to inspect the VM) + cmds: + - | + set -o pipefail + {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP={{.CLI_ARGS}} {{.PYTHON_MOLECULE_HANDLE}}molecule converge -s desktop 2>&1 | tee debug.log || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + fi + - task: post:molecule:log + vars: + RESULTS_FILE: debug.log + + molecule:virtualbox:converge:prompt: + env: + MARKDOWN: | + # Desktop Ansible Molecule Test via VirtualBox + + Choose a desktop environment below to run the Ansible play on. + After choosing, a VirtualBox VM will be created. Then, the Ansible play will run on the VM. + After it is done, the VM will be left open for inspection. Please do get carried away + ensuring everything is working as expected and looking for configuration optimizations that + can be made. The operating systems should all be the latest stable release but might + not always be the latest version. + cmds: + - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP" + - task: molecule:virtualbox:converge:prompt:continue + + molecule:virtualbox:converge:prompt:continue: + interactive: true + deps: + - :install:software:gum + - :install:software:jq + - :install:software:yq + vars: + VIRTUALBOX_OPTIONS: + sh: echo "\"$(yq eval -o=j '.groups' molecule/desktop/molecule.yml | jq -r 'keys | join("\" \"")')\"" + VIRTUALBOX_OPTIONS_LENGTH: + sh: yq eval -o=j '.groups' molecule/desktop/molecule.yml | jq -r 'keys | length' + cmds: + - | + if [[ '{{.VIRTUALBOX_OPTIONS_LENGTH}}' == '0' ]]; then + .config/log error 'There are no Molecule groups defined in `molecule/desktop/molecule.yml`' && exit 1 + else + .config/log prompt 'Which desktop operating system would you like to test the Ansible play against?' + CHOSEN_OPTION="$(.config/log choose {{.VIRTUALBOX_OPTIONS}})" + export ANSIBLE_ENABLE_TASK_DEBUGGER=true + task ansible:test:molecule:virtualbox:converge:cli -- "$CHOSEN_OPTION" + fi + - task: allure:report + preconditions: + - sh: test -f molecule/desktop/molecule.yml + msg: The `molecule/desktop/molecule.yml` file must be present and in the proper format + + molecule:virtualbox:matrix: + deps: + - molecule:dependencies + - :install:software:vagrant + - :install:software:virtualbox + vars: + MOLECULE_DATE: + sh: date '+%Y-%m-%d' + log: + error: Errors were encountered while running the full E2E test (see `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt` for details) + start: Running a full E2E test with VirtualBox (results will be saved to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt`) + success: Finished running the full E2E test (results are in `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt`) + cmds: + - mkdir -p {{.MOLECULE_LOGS_PATH}} + - | + set -o pipefail + {{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_MOLECULE_HANDLE}}molecule test 2>&1 | \ + tee '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt' || EXIT_CODE=$? + if [ "$EXIT_CODE" ]; then + fi + - task: post:molecule:log + vars: + RESULTS_FILE: '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt' + + # yamllint enable rule:truthy + molecule:virtualbox:prompt: + env: + MARKDOWN: | + # Ansible Molecule Test via Headless VirtualBox Instances + + This particular type of test is the best method for testing Ansible plays. It + uses VirtualBox and utilizes headless images. Despite that, running the test across + all the supported operating systems is RAM intensive. Ideally, you should have at + least 16GB of RAM to run all the tests at once. This type of test is used to generate + the compatibility chart so the results of this type of test have the final say. + + You do not need to run the tests on all instances at once. Use the prompt below to + narrow your test scope. + cmds: + - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP" + - task: molecule:virtualbox:prompt:continue + + molecule:virtualbox:prompt:continue: + interactive: true + deps: + - :install:software:gum + - :install:software:jq + - :install:software:yq + vars: + VIRTUALBOX_OPTIONS: + sh: echo "\"$(yq eval -o=j '.groups' molecule/desktop/molecule.yml | jq -r 'keys | join("\" \"")')\"" + VIRTUALBOX_OPTIONS_LENGTH: + sh: yq eval -o=j '.groups' molecule/desktop/molecule.yml | jq -r 'keys | length' + cmds: + - | + if [[ '{{.VIRTUALBOX_OPTIONS_LENGTH}}' == '0' ]]; then + .config/log error 'There are no Molecule groups defined in `molecule/desktop/molecule.yml`' && exit 1 + else + .config/log prompt 'What environment(s) would you like to target with this test?' + CHOSEN_OPTIONS="$(gum choose --no-limit {{.VIRTUALBOX_OPTIONS}})" + COMBINED_OPTIONS="" + CHOSEN_COUNT="$(echo "$CHOSEN_OPTIONS" | wc -l)" + if [[ "$CHOSEN_COUNT" == '0' ]]; then + .config/log error 'No items were selected!' && exit 1 + else + while read CURRENT_OPTION; do + COMBINED_OPTIONS="${COMBINED_OPTIONS}:${CURRENT_OPTION}" + done< <(echo "$CHOSEN_OPTIONS") + CHOSEN_OPTION="${COMBINED_OPTIONS:1}" + export ANSIBLE_ENABLE_TASK_DEBUGGER=true + task ansible:test:molecule:virtualbox:cli -- "$CHOSEN_OPTION" + fi + fi + - task: allure:report + preconditions: + - sh: test -f molecule/default/molecule.yml + msg: The `molecule/default/molecule.yml` file must be present and in the proper format + + post:molecule:log: + deps: + - :ci:commit:config + cmds: + - task: ansifilter + vars: + RESULTS_FILE: '{{.RESULTS_FILE}}' + - task: post:molecule:log:commit:ci + - cmd: | + git pull --ff-only origin master + task --list > /dev/null || (echo "ERROR: Invalid Taskfiles!" && exit 1) + git add '{{.RESULTS_FILE}}' + HUSKY=0 git commit -o ci.skip -m "📝 docs(molecule): New Molecule test results added." -n + git push origin master + ignore_error: true + status: + - '[ "{{.RESULTS_FILE}}" == "debug.log" ] || [ ! -d .git ]' + + post:molecule:log:commit:ci: + cmds: + - task: :ci:commit:config + status: + - '[ -z "$GITLAB_CI" ]' + + prompt: + env: + MARKDOWN: | + # Molecule Test + + There are currently six different options for running Molecule tests. + + ## 1. VirtualBox Headless + + Runs tests using VirtualBox headless VMs. It is the test type used to generate + the compatibility chart. + + ## 2. VirtualBox Desktop + + Runs tests using a VirtualBox desktop version VM. Use this type of test to run + the Ansible play and then open the VirtualBox VM to smoke test the software. + + ## 3. Docker + + Utilizes Docker to test the Ansible play. It has some limitations such as not being + able to test snap installations on all operating systems. It also can only run tests + on Linux environments. This is, however, the fastest way to test roles and requires + the least amount of RAM. + + ## 4. Local + + Runs the Ansible play on the local machine. Use this to run the Ansible play on your + local machine. You might use this if you want to inspect the software after running + the play. + + ## 5. SSH + + Runs the Ansible play on a remote machine after connecting via SSH. This requires + that you already have the SSH credentials configured (i.e. ~/.ssh is setup). + + ## 6. Google Cloud Platform + + Provisions Google Cloud Platform instances and tests the Ansible play on them. This + test requires that you have access to a GCP account and that the proper credentials + are in place. For help, see + [this guide](https://github.com/ProfessorManhattan/molecule-ansible-google-cloud/blob/master/README.md). + Without the environment variables mentioned in the guide set, this task will fail. + + ## Note on Debugging + + All of the tests below (except GCP) enable the built-in Ansible debugger. If a task + fails, the STDOUT will freeze and you will be able to enter a few different commands. + For example, if you enter "r", then Ansible will run the task again. For more + information on the Ansible debugger (including available commands), see + https://docs.ansible.com/ansible/latest/user_guide/playbooks_debugger.html#available-debug-commands. + cmds: + - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP" + - task: prompt:continue + + prompt:continue: + interactive: true + cmds: + - .config/log prompt 'What type of test would you like to perform?' + - | + CHOSEN_TEST="$(.config/log choose 'VirtualBox Headless' 'VirtualBox Desktop' 'Docker' 'Local' 'SSH' 'Google Cloud Platform')" + if [[ "$CHOSEN_TEST" == 'VirtualBox Headless' ]]; then + TEST_SLUG='virtualbox:prompt' + elif [[ "$CHOSEN_TEST" == 'VirtualBox Desktop' ]]; then + TEST_SLUG='virtualbox:converge:prompt' + elif [[ "$CHOSEN_TEST" == 'Docker' ]]; then + TEST_SLUG='docker:prompt' + elif [ '{{.ANSWER}}' == 'Local' ]; then + TEST_SLUG='local' + elif [ '{{.ANSWER}}' == 'SSH' ]; then + TEST_SLUG='ssh:prompt' + elif [ '{{.ANSWER}}' == 'Google Cloud Platform' ]; then + TEST_SLUG='gcp' + fi + export ANSIBLE_ENABLE_TASK_DEBUGGER=true + task ansible:test:molecule:${TEST_SLUG} + - task: allure:report + + vagrant: + desc: Runs the playbook using Vagrant + hide: '{{ne (print .REPOSITORY_TYPE "-" .REPOSITORY_SUBTYPE) "ansible-role"}}' + summary: | + # Run the playbook using Vagrant + + Using Vagrant, you can pick and choose which operating system and + virtualization provider you want to use to test the playbook. + + ## Possible virtualization providers: + + * hyperv + * libvirt + * parallels + * virtualbox + * vmware_fusion + * vmware_workstation + + ## Possible operating systems: + + * archlinux + * centos + * debian + * fedora + * macos + * ubuntu + * windows + + **Example opening interactive prompt:** + `task test:vagrant` + + **Example bypassing interactive prompt:** + `task test:vagrant -- --provider=vmware_workstation windows` + cmds: + - task: vagrant:{{if .CLI_ARGS}}cli{{else}}prompt{{end}} + + vagrant:cli: + deps: + - task: :install:python:requirements + vars: + INSTALL_OPTIONS: --no-dev + - :install:software:vagrant + - :install:software:virtualbox + log: + error: Encountered error when running `vagrant up {{.CLI_ARGS}}` + start: Running `vagrant up {{.CLI_ARGS}}` + success: Successfully ran `vagrant up {{.CLI_ARGS}}` + cmds: + - | + set -o pipefail + vagrant up {{.CLI_ARGS}} 2>&1 | tee debug.log || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + fi + - task: post:molecule:log + vars: + RESULTS_FILE: debug.log + + vagrant:prompt: + env: + MARKDOWN: | + # Launch VM via Vagrant and Run Playbook + + Use the following prompts to select the type of operating system and + the virtualization platform you wish to use with Vagrant. After you make your choice + the corresponding environment will be provisioned with Vagrant. + + The options are generated by inspecting your system for which virtualization + platforms are installed. The supported virtualization platforms are: + + * **KVM** - Shows if `qemu-system-x86_64` command is available + * **Parallels** (macOS only) - Shows if `Parallels Desktop.app` is installed + * **VirtualBox** - Shows if `vboxmanage` command is available + * **VMWare Fusion** (macOS only) - Shows if `vmrun` command is available + * **VMWare Workstation** (Linux only) - Shows if `vmware` command is available + cmds: + - TMP="$(mktemp)" && echo "$MARKDOWN" > "$TMP" && .config/log md "$TMP" + - task: vagrant:prompt:continue + + vagrant:prompt:continue: + deps: + - :install:software:gum + - :install:software:jq + interactive: true + vars: + PROMPT_OPTIONS: + sh: | + TMP="$(mktemp)" + if type qemu-system-x86_64 &> /dev/null; then + echo 'KVM' > "$TMP" + fi + if [[ '{{OS}}' == 'darwin' ]] && mdfind -name 'Parallels Desktop.app' &> /dev/null; then + echo 'Parallels' > "$TMP" + fi + if type vboxmanage &> /dev/null; then + echo 'VirtualBox' > "$TMP" + fi + if [[ '{{OS}}' == 'linux' ]] && type vmware &> /dev/null; then + echo 'VMWare Workstation' > "$TMP" + fi + if [[ '{{OS}}' == 'darwin' ]] && type vmrun &> /dev/null; then + echo 'VMWare Fusion' > "$TMP" + fi + LIST_LENGTH="$(jq -R -s -c -r 'split("\n") | length' < "$TMP")" + if [ "$LIST_LENGTH" != '0' ]; then + echo "\""$(jq -R -s -c -r 'split("\n") | join("\" \"")' < "$TMP")"\"" + else + echo "None" + fi + cmds: + - | + if [[ '{{.PROMPT_OPTIONS' == 'None' ]]; then + .config/log error 'No virtualization platforms installed. Install a platform (e.g. VirtualBox, VMWare, QEMU) to continue.' && exit 1 + else + .config/log prompt 'Which virtualization platform would you like to use?' + PLATFORM_CHOICE="$(.config/log choose '{{.PROMPT_OPTIONS}}')" + .config/log prompt 'Which desktop OS would you like to launch / provision?' + OS_CHOICE="$(.config/log choose 'ArchLinux' 'CentOS' 'Debian' 'Fedora' 'macOS' 'Ubuntu' 'Windows')" + task ansible:test:vagrant:cli -- --provider=\""$PLATFORM_CHOICE"\" "$OS_CHOICE" + fi diff --git a/.config/taskfiles/ansible/Taskfile.yml b/.config/taskfiles/ansible/Taskfile.yml new file mode 100644 index 00000000..3fb8b105 --- /dev/null +++ b/.config/taskfiles/ansible/Taskfile.yml @@ -0,0 +1,609 @@ +--- +version: '3' + +vars: + COLLECTION_DEPS: collection_dependencies + MAIN_TASKS_PATH: tasks/main.yml + META_PATH: meta/main.yml + MOLECULE_RESULTS_PATH: molecule/.results + PRE_SHARED_VAULT_KEY: YTtEnhPWtftHFcP3HneVmz6vX2qMqAwobTDAbvDwrdyunAaDCQ + REQUIREMENTS_PATH: requirements.yml + ROLE_NAME: '{{.GALAXY_NAMESPACE}}.{{.GALAXY_ROLE_NAME}}' + SAMPLE_PROJECT: https://github.com/megabyte-labs/ansible-snapd + VARIABLES_PATH: .variables.json + +tasks: + build:none: + log: + start: Skipping build stage because Ansible project's do not require building + cmds: + - task: :donothing + + collection-dependencies: + deps: + - :install:software:jq + - :install:software:yq + env: + COLLECTIONS: + sh: jq --arg collections "$(yq eval -o=json '.collections' {{.REQUIREMENTS_PATH}})" '.{{.COLLECTION_DEPS}} = ($collections | fromjson) | + .{{.COLLECTION_DEPS}}[] | "\"""' + -r {{.VARIABLES_PATH}} | jq --raw-input --slurp 'split("\n") | .[0:((. | length) - 1)]' + TMP: + sh: mktemp + log: + error: Failed to generate documentation variable for collection dependencies + start: Generating documentation variable for collection dependencies + success: Generated documentation variable for collection dependencies + cmds: + - | + jq --arg collections "$COLLECTIONS" '.{{.COLLECTION_DEPS}} = ($collections | fromjson)' '{{.VARIABLES_PATH}}' > "$TMP" + mv "$TMP" '{{.VARIABLES_PATH}}' + + collection-dependencies:markdown: + deps: + - :install:software:jq + vars: + COLLECTION_LENGTH: + sh: jq -r '.{{.COLLECTION_DEPS}} | length' '{{.VARIABLES_PATH}}' + FILE_PATH: .autodoc/{{.COLLECTION_DEPS}}.md + env: + MULTIPLE_COLLECTION_TEXT: "### Galaxy Collections\n\nThis role is dependent on multiple Ansible Galaxy collections. + The collections along with a links to their source are listed below.\n\n{{\"{{\"}}{{.COLLECTION_DEPS}}{{\"}}\"}}" + SINGLE_COLLECTION_TEXT: "### Galaxy Collection\n\nThis role is dependent on the following Ansible Galaxy + collection:\n\n{{\"{{\"}}{{.COLLECTION_DEPS}}{{\"}}\"}}" + log: + error: Failed to generate documentation partial for collection dependencies + start: Generating documentation partial for collection dependencies + success: Generated documentation partial for collection dependencies + cmds: + - mkdir -p '{{dir .FILE_PATH}}' + - | + {{if (eq .COLLECTION_LENGTH "0")}} + echo '' > '{{.FILE_PATH}}' + {{else if (eq .COLLECTION_LENGTH "1")}} + echo "$SINGLE_COLLECTION_TEXT" > '{{.FILE_PATH}}' + {{else}} + echo "$MULTIPLE_COLLECTION_TEXT" > '{{.FILE_PATH}}' + {{end}} + sources: + - '{{.FILE_PATH}}' + - '{{.VARIABLES_PATH}}' + + galaxy:import: + log: + error: Error occurred while importing Ansible Galaxy role + start: Triggering Ansible Galaxy import + success: Successfully imported role on Ansible Galaxy + cmds: + - | + GITHUB_ROLE_SLUG="$(jq -r '.blueprint.repository.github' package.json | sed 's/.*\///')" + {{.PYTHON_HANDLE}} ansible-galaxy role import --token "$ANSIBLE_GALAXY_TOKEN" {{.GITHUB_ORG}} "$GITHUB_ROLE_SLUG" + status: + - '[ -z "$ANSIBLE_GALAXY_TOKEN" ]' + + galaxy:requirements: + log: + error: Error encountered while installing the Ansible Galaxy requirements specified in `requirements.yml` + start: Installing the Ansible Galaxy requirements specified in `requirements.yml` + success: Installed the Ansible Galaxy requirements specified in `requirements.yml` + cmds: + - | + if [ -f ~/.netrc ]; then + chmod 600 ~/.netrc + fi + - cmd: '{{.PYTHON_HANDLE}} ansible-galaxy install -r requirements.yml --ignore-errors' + ignore_error: true + sources: + - requirements.yml + + init: + cmds: + - ansible-galaxy init --role-skeleton=/path/to/skeleton role_name + + keywords:sync: + deps: + - :install:npm:prettier + - :install:software:jq + - :install:software:yq + summary: | + # Sync Galaxy Tags with `package.json` Keywords + + This task syncs the Ansible Galaxy tags found in `meta/main.yml` with the keywords in the `package.json` + file. The Ansible Galaxy tags are capped to a maximum of 20 tags. + env: + MERGED_TAGS: + sh: jq -s --argjson galaxy "$(yq e -o=j '.galaxy_info.galaxy_tags' meta/main.yml)" '.[0].keywords + $galaxy | sort | unique' package.json + MERGED_TAGS_LENGTH: + sh: jq -s --argjson galaxy "$(yq e -o=j '.galaxy_info.galaxy_tags' meta/main.yml)" '.[0].keywords + $galaxy | sort | unique | length' package.json + log: + error: Error encountered while running the `package.json` / `meta/main.yml` synchronization logic + start: Synchronizing the keywords in `package.json` and `meta/main.yml` + success: Synchronized the keywords in `package.json` and `meta/main.yml` + cmds: + - | + GALAXY_INFO="$(yq e -o=j meta/main.yml)" + OPTIONAL_TAGS="$(jq '.keywords' .config/common-keywords.json)" + TMP="$(mktemp)" + RESULT="$MERGED_TAGS" + if [ "$MERGED_TAGS_LENGTH" -gt 20 ]; then + function updateList() { + REMOVE_KEY="$(jq -n --argjson optional "$OPTIONAL_TAGS" '$optional['"$1"']')" + RESULT="$(jq -n --argjson remove "$REMOVE_KEY" --argjson jq "$RESULT" '$jq | del(.[] | select(. == $remove))')" + } + LOOP_COUNT="$((MERGED_TAGS_LENGTH-20))" + for i in $(seq "$LOOP_COUNT"); do + updateList "$i" + done + fi + jq -r --argjson result "$MERGED_TAGS" '.keywords = $result' package.json > "$TMP" + mv "$TMP" package.json + prettier --write package.json > /dev/null + mkdir -p .cache/megabytelabs + jq -n --argjson result "$RESULT" --argjson gi "$GALAXY_INFO" '$gi | .galaxy_info.galaxy_tags = $result' > .cache/megabytelabs/galaxy-meta.json + yq eval -P .cache/megabytelabs/galaxy-meta.json > meta/main.yml + - task: :fix:prettier + vars: + CLI_ARGS: meta/main.yml + - task: :fix:yaml:dashes + vars: + CLI_ARGS: meta/main.yml + + mod-ansible-autodoc: + todo: Add ansible-autodoc-fork to the includes of the package + cmds: + - | + if [ -n "$CI" ]; then + pip3 install ansible-autodoc-fork + fi + - task: mod-ansible-autodoc:generate + - task: mod-ansible-autodoc:variables + + mod-ansible-autodoc:generate: + deps: + - :install:pipx:mod-ansible-autodoc + - :install:software:jq + env: + ACTIONS_DESCRIPTION: + sh: jq -r '.autodoc_actions_description' '{{.VARIABLES_PATH}}' + # PATH: + # sh: echo "$PATH:$(poetry env info | grep 'Python /' | sed 's/Python //')" + TAGS_DESCRIPTION: + sh: jq -r '.autodoc_tags_description' '{{.VARIABLES_PATH}}' + TODO_DESCRIPTION: + sh: jq -r '.autodoc_todo_description' '{{.VARIABLES_PATH}}' + VARIABLES_DESCRIPTION: + sh: jq -r '.autodoc_variables_description' '{{.VARIABLES_PATH}}' + log: + error: Error encountered while generating documentation partials with `mod-ansible-autodoc` + start: Compiling `mod-ansible-autodoc` documentation from comments in the play *.yml files + success: Successfully generated documentation partials with `mod-ansible-autodoc` + cmds: + - > + {{.PYTHON_HANDLE}}mod-ansible-autodoc --actions-title '## Features' --actions-description "$ACTIONS_DESCRIPTION" + --tags-title '### Tags' --tags-description "$TAGS_DESCRIPTION" --todo-title '### TODO' + --todo-description "$TODO_DESCRIPTION" --variables-title '## Variables' --variables-description + "$VARIABLES_DESCRIPTION" --variable-example-comment-prefix '#💬' + - mkdir -p .autodoc + - mv ansible_actions.md ansible_tags.md ansible_todo.md ansible_variables.json ansible_variables.md .autodoc + sources: + - '{{.VARIABLES_PATH}}' + - defaults/**/*.yml + - tasks/**/*.yml + - vars/**/*.yml + + mod-ansible-autodoc:variables: + deps: + - :install:software:jq + log: + error: Failed to merge `.autodoc/ansible_variables.json` into `.variables.json` + start: Merging `.autodoc/ansible_variables.json` into `.variables.json` + success: Successfully merged `.autodoc/ansible_variables.json` into `.variables.json` + cmds: + - | + ROLE_VARIABLES="$(jq -r '.role_variables' .autodoc/ansible_variables.json)" + TMP="$(mktemp)" + jq --arg vars "$ROLE_VARIABLES" '.role_variables = ($vars | fromjson)' '{{.VARIABLES_PATH}}' > "$TMP" + mv "$TMP" '{{.VARIABLES_PATH}}' + + prepare: + deps: + - :ci:commit:config + log: + error: Failed to ensure GitLab and GitHub are in sync + start: Preparing for Ansible Galaxy upload + success: GitLab and GitHub are in sync + cmds: + - task: :git:remotes + - git pull origin master + - git push github master + + publish: + cmds: + - task: galaxy:import + + quickstart: + deps: + - :install:software:jq + - :symlink:{{.REPOSITORY_SUBTYPE}} + - task: :install:python:requirements + vars: + INSTALL_OPTIONS: --no-dev + cmds: + - git reset --hard HEAD + - | + if ! git pull origin master; then + git config url."https://gitlab.com/".insteadOf git@gitlab.com: + git config url."https://github.com/".insteadOf git@github.com: + git pull origin master || true + fi + - task: quickstart:environment + - task: quickstart:map + - task: quickstart:demo + + quickstart:cli: + deps: + - :install:software:openssl + summary: | + # Ansible Quickstart CLI + + Use this task if you already know the inventory file you would like to run the + `main.yml` playbook on. The unique feature is that this task will first run + `playbooks/init.yml` (and skip any task tagged with `skip_on_init`), reboot, + and then run the normal `main.yml`. This allows you to provision a single computer + that serves both as the client and host of Ansible (since some software requires + reboots). + + {{.QUICKSTART_VAULT_SECURITY}} + vars: + INVENTORY: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}inventories/local.yml{{end}}' + QUICKSTART_VAULT_SECURITY: | + ## Ansible Vault Password Security + + To achieve automation wherever possible, we save your Ansible Vault password + on disk during the installation process (i.e. to preserve the information + inbetween reboots). + + To make it more secure, we employ the following measures: + 1. The password is forcibly removed anytime the ansible-playbook command fails + 2. It is passed in via the `ANSIBLE_VAULT_PASSWORD` variable to minimize the possibility + of the password displaying in the terminal or getting added to the command history. + 3. It is stored in `~/.VAULT_PASSWORD` so you can easily spot it if something + goes wrong. + env: + ANSIBLE_CALLBACKS_ENABLED: 'junit, ansible.posix.profile_tasks, ansible.posix.timer, sentry' + ANSIBLE_STDOUT_CALLBACK: community.general.yaml + ANSIBLE_VAULT_PASSWORD_FILE: ~/.VAULT_PASSWORD + JUNIT_FAIL_ON_CHANGE: true + JUNIT_HIDE_TASK_ARGUMENTS: true + JUNIT_OUTPUT_DIR: .results/junit + QUICKSTART_PLAYBOOK_MATCH: | + # Playbook Matching Inventory File Name + + A playbook with the same file name as the selected inventory was found in the + `playbooks/` directory. When the file names match up, this "quick start" script + will use the matching playbook file instead of the `main.yml` playbook found in + the root of the repository. + QUICKSTART_VAULT_SECURITY: '{{.QUICKSTART_VAULT_SECURITY}}' + log: + error: Error occurred while running the Ansible play + start: Running the Ansible play locally + success: Successfully ran the Ansible play locally + cmds: + - cmd: | + if [ ! -f "$ANSIBLE_VAULT_PASSWORD_FILE" ]; then + .config/log info 'Adding `~/ANSIBLE_PLAYBOOK_CONTINUE.sh` to run on reboot' + echo "#!/usr/env/bin bash" > ~/ANSIBLE_PLAYBOOK_CONTINUE.sh + echo "" > ~/ANSIBLE_PLAYBOOK_CONTINUE.sh + echo "cd $PWD" >> ~/ANSIBLE_PLAYBOOK_CONTINUE.sh + echo 'task ansible:quickstart:cli -- {{.INVENTORY}}' >> ~/ANSIBLE_PLAYBOOK_CONTINUE.sh + if [ -z "$ANSIBLE_VAULT_PASSWORD" ]; then + MD_TMP="$(mktemp)" + echo "$QUICKSTART_VAULT_SECURITY" > "$MD_TMP" + .config/log md "$MD_TMP" + .config/log info 'Read about security measures above.' + .config/log prompt 'What is your Ansible Vault password?' + export ANSIBLE_VAULT_PASSWORD="$(.config/log password 'Enter Vault password (leave empty if there is not a password yet)..')" + fi + fi + .config/log info 'Writing the `ANSIBLE_VAULT_PASSWORD` to temporary file in user home directory' + echo "$ANSIBLE_VAULT_PASSWORD" > ~/.VAULT_PASSWORD + INVENTORY="{{.INVENTORY}}" + INVENTORY_SLUG="$(echo "$INVENTORY" | sed 's/inventories\/\(.*\).yml/\1/')" + if [[ '{{.INVENTORY}}' == 'inventories/quickstart.yml' ]]; then + if [ -f '/etc/qubes-rpc' ] && [[ "$(whoami)" == 'user' ]]; then + .config/log info 'Assuming `ANSIBLE_USER` name to be `user` since this is Qubes' + export ANSIBLE_USER="user" + elif [ -z "$ANSIBLE_USER" ]; then + WHOAMI="$(whoami)" + .config/log prompt 'In the next step, select a user with `sudo` privileges' + if ! .config/log confirm 'Run playbook with `'"${WHOAMI}"'`?'; then + export ANSIBLE_USER="$(.config/log input 'Enter the username of the admin account you wish to use..')" + else + export ANSIBLE_USER="$WHOAMI" + fi + else + .config/log info '`ANSIBLE_USER` is set to `'"$ANSIBLE_USER"'`' + fi + if [ -z "$ANSIBLE_PASSWORD" ]; then + if sudo -n echo &> /dev/null; then + .config/log info 'Assuming passwordless sudo since `sudo -n echo` was truthy' + .config/log info 'Bypassing `ANSIBLE_PASSWORD` prompt' + else + .config/log prompt 'Enter the `sudo` password for your selected user (`'"$ANSIBLE_USER"'`)' + export ANSIBLE_PASSWORD="$(.config/log password 'Enter password (or just press enter if there is none)..')" + fi + else + .config/log info '`ANSIBLE_PASSWORD` is present as an environment variable.. bypassing password prompt' + fi + if [ -z "$MOLECULE_GROUP" ]; then + if [ -f '/etc/qubes-rpc' ]; then + .config/log info 'Machine is a Qubes dom0 environment' + export MOLECULE_GROUP="qubes" + else + .config/log info 'Assuming machine to be macOS/Linux' + export MOLECULE_GROUP="standard" + fi + fi + fi + if [ -f local/requirements.txt ]; then + pip3 install -r local/requirements.txt + else + .config/log warn 'The `local/requirements.txt` file is missing' + fi + if [ -f "playbooks/${INVENTORY_SLUG}.yml" ]; then + PLAY_TMP="$(mktemp)" && echo "$QUICKSTART_PLAYBOOK_MATCH" > "$PLAY_TMP" && .config/log md "$PLAY_TMP" + {{.PYTHON_HANDLE}}ansible-playbook --skip-tags "dotfiles,mas" -vvv -i {{.INVENTORY}} "playbooks/${INVENTORY_SLUG}.yml" || EXIT_CODE="$?" + else + .config/log info 'Using the `main.yml` playbook because there is no playbook located in `'"playbooks/${INVENTORY_SLUG}.yml"'`' + {{.PYTHON_HANDLE}}ansible-playbook --skip-tags "dotfiles,mas" -vvv -i {{.INVENTORY}} main.yml || EXIT_CODE="$?" + fi + #if [ -n "$EXIT_CODE" ]; then + # .config/log error 'There was an error while running the playbook.' + # .config/log warn 'Ensure there are no secrets in plain text (if you choose to upload the logs so our developers can fix this issue).' + # .config/log confirm 'Upload the playbook log so the developers can debug?' + # # sentry-cli + #fi + ignore_error: true + - sleep 5 + - .config/log info 'Ensuring the temporary vault password file is forcibly removed' + - rm -f ~/.VAULT_PASSWORD + - rm -f ~/ANSIBLE_PLAYBOOK_CONTINUE.sh + + quickstart:demo: + cmds: + - task: quickstart:cli + status: + - '[ -f files/inventory-map.json ]' + + quickstart:environment: + cmds: + - task: :ansible:playbook:environment:cli + vars: + CLI_ARGS: + sh: echo "$ENV" + status: + - '[ -z "$ENV" ]' + + quickstart:map: + deps: + - :install:pipx:getmac + - :install:software:gum + - :install:software:jq + summary: | + # Quickstart Mapping + + This task is a helper task that attempts to automatically select the appropriate + Ansible inventory based on the MAC address. If the mapping entry is not already + saved, this task will guide the user through a series of prompts. + + {{.MAC_ADDRESS_EXPLANATION}} + vars: + INVENTORY_OPTIONS: + sh: echo "\""$(find ./inventories/ -mindepth 1 -maxdepth 1 | sed 's/\.\/inventories\//inventories\//' | jq -R '[.]' | jq -s -c -r 'add | join("\" \"")')"\"" + INVENTORY_OPTIONS_LENGTH: + sh: find ./inventories/ -mindepth 1 -maxdepth 1 | sed 's/\.\/inventories\///' | jq -R '[.]' | jq -s -c -r 'add | length' + MAC_ADDRESS_EXPLANATION: | + ## Mapping MAC Addresses to Inventory Files + + In order to achieve a completely automated flow, we have the ability + to define a map of MAC addresses and inventory files in the `files/` + folder. + + You can get your MAC address by using the `getmac` program that this + task installs or you can run the following on Linux: + + ``` + cat /sys/class/net/$(ip route show default | awk '/default/ {print $5}')/address + ``` + + On Windows, if you are provisioning with Docker (the default "Quick Start" method) + and if you make sure you are running it on either a clean system or have no other + Docker containers running, then the MAC address will be: + + ``` + 02:42:ac:11:00:02 + ``` + + If you wanted to run the Quick Start method on Windows, you could create a file in + `files/inventory-map.json` that looks something like this: + + ``` + { + "02:42:ac:11:00:02": "inventories/workstation.yml" + } + ``` + + This configuration would instruct the script to automatically use the `inventories/workstation.yml` + inventory. If your MAC address is missing, the script will open an interactive prompt and include + the ability to save your MAC address to the file for later use. + env: + INVENTORY_EXPLANATION: | + # Which Inventory Should I Use? + + A special / great starting point is the `quickstart.yml` inventory which allows you to pass your username + and password through a prompt. It is intended to be used to provision one machine at a time from that machine. + It is also intended to be used with the **Quick Start** links at the top of the README.md. It can be used to + provision any of our supported operating systems. + + ## TLDR: Choose `quickstart.yml` + MAC_ADDRESS_EXPLANATION: '{{.MAC_ADDRESS_EXPLANATION}}' + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + # MAC_ADDRESS="$(cat /sys/class/net/$(ip route show default | awk '/default/ {print $5}')/address)" # Does not work on macOS + MAC_ADDRESS="$(getmac)" + TARGET_INV="$(jq --arg macAddress "$MAC_ADDRESS" -r '.[$macAddress]' files/inventory-map.json)" + if [[ "$TARGET_INV" == '' ]]; then + .config/log warn 'Initializing `files/inventory-map.json` since it appears to be an empty file' + echo '{}' > files/inventory-map.json + fi + if [ "$TARGET_INV" != 'null' ] && [ "$TARGET_INV" != '' ]; then + .config/log info "Provisioning with "'`'"$TARGET_INV"'`' + task ansible:quickstart:cli -- "$TARGET_INV" + else + MD_TMP="$(mktemp)" + echo "$MAC_ADDRESS_EXPLANATION" > "$MD_TMP" + .config/log md "$MD_TMP" + .config/log warn "MAC address missing from inventory-map.json ($MAC_ADDRESS). Details printed above." + if [ '{{.INVENTORY_OPTIONS_LENGTH}}' != '0' ]; then + .config/log prompt 'Given the information above, would you like to save your MAC address to the `files/inventory-map.json` file?' + if .config/log confirm 'Save MAC address?'; then + INV_TMP="$(mktemp)" && echo "$INVENTORY_EXPLANATION" > "$INV_TMP" && .config/log md "$INV_TMP" + .config/log prompt 'Which inventory file would you like the associate the MAC address with?' + INVENTORY_FILE="$(.config/log choose {{.INVENTORY_OPTIONS}})" + TMP="$(mktemp)" + .config/log info 'Generating new `files/inventory-map.json` file' + jq --arg inventory "$INVENTORY_FILE" --arg macaddr "$MAC_ADDRESS" '.[$macaddr] = $inventory' files/inventory-map.json > "$TMP" + mv "$TMP" files/inventory-map.json + .config/log success "Successfully associated the selected inventory with this machine's MAC address (remember to git add / commit)" + task ansible:quickstart:cli -- "$INVENTORY_FILE" + else + INV_TMP="$(mktemp)" && echo "$INVENTORY_EXPLANATION" > "$INV_TMP" && .config/log md "$INV_TMP" + .config/log prompt 'Which inventory file would you like to use?' + INVENTORY_FILE="$(.config/log choose {{.INVENTORY_OPTIONS}})" + task ansible:quickstart:cli -- "$INVENTORY_FILE" + fi + else + .config/log error 'There are no inventories defined in the `inventories/` folder.' + .config/log info 'Try running `task environment` to link to sample environments' + exit 1 + fi + fi + status: + - '[ ! -f files/inventory-map.json ]' + + sync:requirements: + deps: + - :install:software:jq + - :install:software:yq + log: + error: Failed to synchronize role dependencies in `{{.META_PATH}}` to `{{.REQUIREMENTS_PATH}}` + start: Ensuring role dependencies in `{{.META_PATH}}` are also listed in `{{.REQUIREMENTS_PATH}}` + success: Successfully ensured role dependencies in `{{.META_PATH}}` are also listed in `{{.REQUIREMENTS_PATH}}` + cmds: + - | + ROLES="$(yq eval '.roles' '{{.REQUIREMENTS_PATH}}')" + yq eval -o=json '.dependencies' '{{.META_PATH}}' | jq -rc '.[] .role' | while read ROLE_NAME; do + if [[ ! "$ROLES" =~ "$ROLE_NAME" ]]; then + yq eval -i -P '.roles = .roles + {"name": "'"$ROLE_NAME"'"}' '{{.REQUIREMENTS_PATH}}' + fi + done + - task: :fix:yaml:dashes + vars: + CLI_ARGS: '{{.REQUIREMENTS_PATH}}' + + update:galaxy-id: + log: + error: Failed to look up or inject the Ansible Galaxy project ID into `package.json` + start: Adding Ansible Galaxy project ID to `package.json` (if available) + success: Successfully ensured Ansible Galaxy project ID is in `package.json` (if the project is on Ansible Galaxy) + cmds: + - | + TMP="$(mktemp)" + PROJECT_ID="$(ansible-galaxy info '{{.ROLE_NAME}}' 2> /dev/null | grep -E 'id: [0-9]' | cut -d ' ' -f2)" + if [ "$PROJECT_ID" ]; then + jq --arg a "$PROJECT_ID" '.blueprint.ansible_galaxy_project_id = $a' package.json > "$TMP" + mv "$TMP" package.json + fi + status: + - jq -e 'has("blueprint.ansible_galaxy_project_id")' package.json + + update:variables: + cmds: + - task: :ansible:ansibler:ansibler + - task: update:variables:descriptions + + update:variables:descriptions: + deps: + - :install:software:jq + - :install:software:yq + vars: + ALT_PREFIX: This repository is the home of an [Ansible](https://www.ansible.com/) role that + DESCRIPTION: + sh: yq e '.galaxy_info.description' '{{.META_PATH}}' + DESCRIPTION_LOWER: '{{lower (trunc 1 .DESCRIPTION)}}{{substr 1 (len .DESCRIPTION) .DESCRIPTION}}' + SUBHEADER_PREFIX: An Ansible role that + env: + ALT: '{{.ALT_PREFIX}} {{.DESCRIPTION_LOWER}}' + GALAXY_INFO: + sh: yq e -o=json '.galaxy_info' '{{.META_PATH}}' + SUBHEADER: '{{.SUBHEADER_PREFIX}} {{.DESCRIPTION_LOWER}}' + TMP: + sh: mktemp + log: + error: Failed to inject `.variables.json` with description variables + start: Injecting description variables into `.variables.json` + success: Successfully updated `.variables.json` with description variables + cmds: + - jq -S --arg alt "$ALT" --arg galaxyinfo "$GALAXY_INFO" --arg subheader "$SUBHEADER" '.alternative_description = $alt | + .galaxy_info = ($galaxyinfo | fromjson) | .subheader_description = $subheader' '{{.VARIABLES_PATH}}' > "$TMP" + - mv "$TMP" '{{.VARIABLES_PATH}}' + sources: + - '.common/variables.{{.REPOSITORY_SUBTYPE}}.json' + - '{{.META_PATH}}' + - package.json + preconditions: + - sh: type jq > /dev/null + msg: jq is not installed. + - sh: type yq > /dev/null + msg: yq is not installed. + - sh: 'test -f "{{.META_PATH}}"' + msg: 'The `{{.META_PATH}}` file is missing. A properly populated `{{.META_PATH}}` is required. You can find an + example of one at {{.SAMPLE_PROJECT}}.' + - sh: 'test -f "{{.VARIABLES_PATH}}"' + msg: 'The `{{.VARIABLES_PATH}}` file is missing!' + + update:variables:playbook: + cmds: + - task: :ansible:playbook:docs + + vault:lint:file: + summary: | + # Check for unencrypted Ansible Vault files + + This task is leveraged by `lint-staged` to ensure that any file that matches `**/*vault.yml` is encrypted + with Ansible Vault. + log: + error: '`{{.CLI_ARGS}}` is not encrypted! All files matching `**/*vault.yml` must be encrypted by `ansible-vault`.' + start: Checking if `{{.CLI_ARGS}}` is encrypted with `ansible-vault` + success: Ensured `{{.CLI_ARGS}}` is encrypted + cmds: + - | + head -1 '{{.CLI_ARGS}}' | grep --quiet '^\$ANSIBLE_VAULT;' || { + if [ -s '{{.CLI_ARGS}}' ]; then + exit 1 + fi + } + + verify: + deps: + - :install:software:poetry + log: + error: Failed to connect to Ansible Galaxy with provided token + start: Verifying connection can be made to Ansible Galaxy + success: Successfully connected to Ansible Galaxy + cmds: + - poetry update ansible + - poetry run ansible-galaxy role setup --token "$ANSIBLE_GALAXY_TOKEN" null null null null --list diff --git a/.config/taskfiles/app/Taskfile-virtualbox.yml b/.config/taskfiles/app/Taskfile-virtualbox.yml new file mode 100644 index 00000000..d4c48270 --- /dev/null +++ b/.config/taskfiles/app/Taskfile-virtualbox.yml @@ -0,0 +1,34 @@ +--- +version: '3' + +tasks: + clear: + deps: + - :install:software:virtualbox + summary: | + # Remove All VMs / Reset + + This task will remove all the VirtualBox VMs. It is useful in scenarios + where VirtualBox is being called through automation and things can + potentially break. When they break, they need a reset. + + **Example resetting all VMs:** + `task app:virtualbox:clear` + + There is the capability of only resetting VMs that match a certain pattern. + + **Example resetting all VMs matching a pattern:** + `task app:virtualbox:clear -- 'macOS'` + vars: + DEFAULT_PATTERN: default_ + cmds: + - cmd: killall -9 VBoxHeadless + ignore_error: true + - | + while read VM; do + VM_NAME="$(echo $VM | sed 's/^"\(.*\)" {.*}/\1/')" + VM_UUID="$(echo $VM | sed 's/^".*".{\(.*\)}/\1/')" + vboxmanage startvm "$VM_UUID" --type emergencystop || true + vboxmanage unregistervm "$VM_UUID" || true + rm -rf "$HOME/VirtualBox VMs/$VM_NAME" || true + done < <(vboxmanage list vms | grep '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.DEFAULT_PATTERN}}{{end}}') diff --git a/.config/taskfiles/app/Taskfile.yml b/.config/taskfiles/app/Taskfile.yml new file mode 100644 index 00000000..7d11bf77 --- /dev/null +++ b/.config/taskfiles/app/Taskfile.yml @@ -0,0 +1,29 @@ +--- +version: '3' + +vars: + ROWY_HOMEPAGE: https://megabyte.space/tables + ROWY_PATH: ./rowy + ROWY_SLUG: tables + +tasks: + rowy:build: + deps: + - :install:npm:browserslist + - :install:software:yarn + env: + REACT_APP_FIREBASE_PROJECT_ID: + sh: jq -r '.blueprint.firebase.projectId' package.json + REACT_APP_FIREBASE_PROJECT_WEB_API_KEY: + sh: jq -r '.blueprint.firebase.webApiKey' package.json + cmds: + - mkdir -p "$(dirname '{{.ROWY_PATH}}')" + - git clone https://github.com/rowyio/rowy.git {{.ROWY_PATH}} + - cd {{.ROWY_PATH}} && yarn + - browserslist --update-db + - | + TMP="$(mktemp)" + jq --arg rowy '{{.ROWY_HOMEPAGE}}' '.homepage = $rowy' {{.ROWY_PATH}}/package.json > "$TMP" + mv "$TMP" {{.ROWY_PATH}}/package.json + - cd {{.ROWY_PATH}} && yarn build + - mv {{.ROWY_PATH}}/build dist/{{.ROWY_SLUG}} diff --git a/.config/taskfiles/boilerplate/Taskfile-populate.yml b/.config/taskfiles/boilerplate/Taskfile-populate.yml new file mode 100644 index 00000000..b259de5f --- /dev/null +++ b/.config/taskfiles/boilerplate/Taskfile-populate.yml @@ -0,0 +1,192 @@ +--- +version: '3' + +vars: + DEFAULT_ANSIBLE_LICENSE: license (MIT) + DEFAULT_NAMESPACE: professormanhattan + GITHUB_ROLE_PATH_PREFIX: https://github.com/megabyte-labs/ansible- + GITLAB_ROLE_PATH_PREFIX: https://gitlab.com/megabyte-labs/ansible-roles/ + MIN_ANSIBLE_VERSION: 2.10 + +tasks: + all:after: + deps: + - :install:software:jq + cmds: + - | + if [ "$(jq -r '.blueprint.overview' package.json)" == 'null' ]; then + .config/log warn 'The `blueprint.overview` field is missing from `package.json`' + fi + if [ "$(jq -r '.blueprint.description' package.json)" == 'null' ]; then + .config/log error 'The `blueprint.description` field is missing from `package.json`' + EXIT_PROGRAM=true + fi + if [ "$(jq -r '.blueprint.name' package.json)" == 'null' ]; then + .config/log error 'The `blueprint.name` field is missing from `package.json`' + EXIT_PROGRAM=true + fi + if [ "$EXIT_PROGRAM" == 'true' ]; then + exit 1 + fi + + all:before: + cmds: + - | + if [ ! -f package.json ]; then + .config/log error 'The `package.json` file must exist. See `https://gitlab.com/megabyte-labs/ansible-roles/androidstudio` for an example of one.' + exit 1 + fi + - task: group + - task: subgroup + + angular: 'true' + + ansible: + cmds: + - task: all:before + - task: ansible:{{.REPOSITORY_SUBTYPE}} + - task: all:after + + ansible:playbook: 'true' + + ansible:role: + deps: + - :install:software:jq + - :install:software:yq + cmds: + - | + if [ ! -f meta/main.yml ]; then + .config/log error 'The `meta/main.yml` file must exist. See `https://gitlab.com/megabyte-labs/ansible-roles/androidstudio` for an example of one.' + exit 1 + fi + - | + if [ "$(yq e '.galaxy_info.author' meta/main.yml)" == 'null' ]; then + .config/log info 'Setting `author` to `{{.GALAXY_AUTHOR}}` in `meta/main.yml`' + yq e -i '.galaxy_info.author = {{.GALAXY_AUTHOR}}' meta/main.yml + fi + - | + if [ "$(yq e '.galaxy_info.company' meta/main.yml)" == 'null' ]; then + .config/log info 'Setting `company` to `{{.GALAXY_COMPANY}}` in `meta/main.yml`' + yq e -i '.galaxy_info.company = {{.GALAXY_COMPANY}}' meta/main.yml + fi + - | + if [ "$(yq e '.galaxy_info.min_ansible_version' meta/main.yml)" == 'null' ]; then + .config/log info 'Setting `min_ansible_version` to `{{.MIN_ANSIBLE_VERSION}}` in `meta/main.yml`' + yq e -i '.galaxy_info.min_ansible_version = {{.MIN_ANSIBLE_VERSION}}' meta/main.yml + fi + - | + if [ "$(yq e '.galaxy_info.license' meta/main.yml)" == 'null' ]; then + .config/log info 'Setting `license` to `{{.DEFAULT_ANSIBLE_LICENSE}}` in `meta/main.yml`' + yq e -i '.galaxy_info.license = {{.DEFAULT_ANSIBLE_LICENSE}}' meta/main.yml + fi + - | + ROLE_NAME="$(yq e '.galaxy_info.role_name' meta/main.yml)" + if [ "$ROLE_NAME" == 'null' ]; then + .config/log warn 'The `meta/main.yml` file is missing the `.galaxy_info.role_name` property. Adding it as the folder name - please edit if necessary.' + ROLE_NAME="$(basename $PWD)" yq e -i '.galaxy_info.role_name = env(ROLE_NAME)' meta/main.yml + fi + SLUG="$(jq -r '.blueprint.slug' package.json)" + if [ "$SLUG" == 'null' ]; then + .config/log info 'Adding `slug` to package.json' + TMP="$(mktemp)" && jq --arg slug "$ROLE_NAME" '.blueprint.slug = $slug' package.json > "$TMP" && mv "$TMP" package.json + fi + GITLAB_REPO="$(jq -r '.blueprint.repository.gitlab' package.json)" + if [ "$GITLAB_REPO" == 'null' ]; then + GITLAB_REPO="{{.GITLAB_ROLE_PATH_PREFIX}}$ROLE_NAME" + .config/log info 'Adding GitLab repository to `package.json`' + TMP="$(mktemp)" && jq --arg repo "$GITLAB_REPO" '.blueprint.repository.gitlab = $repo' package.json > "$TMP" && mv "$TMP" package.json + fi + GITHUB_REPO="$(jq -r '.blueprint.repository.github' package.json)" + if [ "$GITHUB_REPO" == 'null' ]; then + GITHUB_REPO="{{.GITHUB_ROLE_PATH_PREFIX}}$ROLE_NAME" + .config/log info 'Adding GitHub repository to `package.json`' + TMP="$(mktemp)" && jq --arg repo "$GITHUB_REPO" '.blueprint.repository.github = $repo' package.json > "$TMP" && mv "$TMP" package.json + fi + if [ "$(yq e '.galaxy_info.issue_tracker_url' meta/main.yml)" == 'null' ]; then + export ISSUE_TRACKER="$GITLAB_REPO/-/issues" + .config/log info 'Adding `issue_tracker_url` to `meta/main.yml`' + yq e -i '.galaxy_info.issue_tracker_url = env(ISSUE_TRACKER)' meta/main.yml + fi + - | + NAME="$(jq -r '.blueprint.name' package.json)" + if [ "$NAME" != 'null' ]; then + if [ "$(jq -r '.blueprint.title' package.json)" == 'null' ]; then + .config/log info 'Populating the `blueprint.title` in package.json using the `blueprint.name` value' + TMP="$(mktemp)" && jq --arg title "$NAME Ansible Role" '.blueprint.title = $title' package.json > "$TMP" && mv "$TMP" package.json + fi + fi + - | + DESC="$(yq e '.galaxy_info.description' meta/main.yml)" + if [ "$DESC" == 'null' ]; then + BP_DESC="$(jq -r '.blueprint.description' package.json)" + if [ "$BP_DESC" == 'null' ]; then + .config/log error 'The `description` in `meta/main.yml` is missing. It must be present.' + .config/log info 'For an example `meta/main.yml` file see `https://gitlab.com/megabyte-labs/ansible-roles/androidstudio`.' + exit 1 + else + yq e -i '.galaxy_info.description = env(BP_DESC)' meta/main.yml + .config/log info '`meta/main.yml` description populated using value from `package.json` `blueprint.description`' + fi + else + if [ "$(jq -r '.blueprint.description' package.json)" == 'null' ]; then + TMP="$(mktemp)" && jq --arg desc "$DESC" '.blueprint.description = $desc' package.json > "$TMP" && mv "$TMP" package.json + fi + fi + + common: 'true' + + docker: 'true' + + documentation: 'true' + + go: 'true' + + group: + deps: + - :install:software:jq + - :install:software:yq + cmds: + - | + GROUP="$(jq -r '.blueprint.group' package.json)" + SUBGROUP="$(jq -r '.blueprint.subgroup' package.json)" + TASK_GROUP="{{.REPOSITORY_TYPE}}" + TASK_SUBGROUP="{{.REPOSITORY_SUBTYPE}}" + if ([ "$GROUP" != 'null' ] && [ "$TASK_GROUP" != 'null' ] && [ "$GROUP" != "$TASK_GROUP" ]) || \ + ([ "$GROUP" == 'null' ] && [ "$TASK_GROUP" != 'null' ]); then + .config/log info 'Setting `blueprint.group` in `package.json` equal to `vars.REPOSITORY_TYPE` from `Taskfile.yml`' + TMP="$(mktemp)" && jq --arg group "$TASK_GROUP" '.blueprint.group = $group' package.json > "$TMP" && mv "$TMP" package.json + elif [ "$GROUP" != 'null' ] && [ "$TASK_GROUP" == 'null' ]; then + .config/log info 'Setting `vars.REPOSITORY_TYPE` equal to value in `blueprint.group` in `package.json`' + yq e -i '.vars.REPOSITORY_TYPE = env(GROUP)' Taskfile.yml + elif [ "$GROUP" == 'null' ] && [ "$TASK_GROUP" == 'null' ]; then + .config/log error 'Either `blueprint.group` in `package.json` or `vars.REPOSITORY_TYPE` in `Taskfile.yml` must be defined' + fi + + misc: 'true' + + npm: 'true' + + packer: 'true' + + python: 'true' + + subgroup: + deps: + - :install:software:jq + - :install:software:yq + cmds: + - | + if ([ "$SUBGROUP" != 'null' ] && [ "$TASK_SUBGROUP" != 'null' ] && [ "$SUBGROUP" != "$TASK_SUBGROUP" ]) || \ + ([ "$SUBGROUP" == 'null' ] && [ "$TASK_SUBGROUP" != 'null' ]); then + .config/log info 'Setting `blueprint.subgroup` in `package.json` equal to `vars.REPOSITORY_SUBTYPE` from `Taskfile.yml`' + TMP="$(mktemp)" && jq --arg group "$TASK_SUBGROUP" '.blueprint.subgroup = $group' package.json > "$TMP" && mv "$TMP" package.json + elif [ "$SUBGROUP" != 'null' ] && [ "$TASK_SUBGROUP" == 'null' ]; then + .config/log info 'Setting `vars.REPOSITORY_SUBTYPE` equal to value in `blueprint.subgroup` in `package.json`' + yq e -i '.vars.REPOSITORY_SUBTYPE = env(SUBGROUP)' Taskfile.yml + elif [ "$SUBGROUP" == 'null' ] && [ "$TASK_SUBGROUP" == 'null' ]; then + .config/log error 'Either `blueprint.subgroup` in `package.json` or `vars.REPOSITORY_SUBTYPE` in `Taskfile.yml` must be defined' + fi + + type: + cmds: + - 'true' diff --git a/.config/taskfiles/boilerplate/Taskfile-prompt.yml b/.config/taskfiles/boilerplate/Taskfile-prompt.yml new file mode 100644 index 00000000..972a731c --- /dev/null +++ b/.config/taskfiles/boilerplate/Taskfile-prompt.yml @@ -0,0 +1,187 @@ +--- +version: '3' + +vars: + COMMON_DOCS_URL: https://gitlab.com/megabyte-labs/documentation/shared/-/raw/master/common.json + COMMON_URL: 'https://gitlab.com/megabyte-labs/common/' + PROJECT_SUBTYPE_VARS_URL: '{{.COMMON_URL}}{{.REPOSITORY_TYPE}}/-/raw/master/project-{{.REPOSITORY_SUBTYPE}}/.config/variables.json' + PROJECT_VARS_URL: '{{.COMMON_URL}}{{.REPOSITORY_TYPE}}/-/raw/master/project/.config/variables.json' + +tasks: + all: + cmds: + - task: name + - task: :boilerplate:populate:group + - task: group + - task: :boilerplate:populate:subgroup + - task: subgroup + - task: title + - task: description + - task: overview + - task: slug + - task: project-specific + - task: build + - task: test + + build: + summary: | + This task prompts the user for the `build` command to place in `scripts.build` inside + of `package.json`. + + For the default value, it looks at the corresponding common respoitory by first checking the + `project-subtype` folder and then the `project` folder's value for `scriptsBuild` inside of the + `.config/variables.json` file. + cmds: + - .config/log prompt 'Enter the build command placed in `scripts.build` inside of `package.json`' + - | + BUILD_ANSWER="$(.config/log input 'Enter build command..')" + task boilerplate:prompt:build:continue -- "$BUILD_ANSWER" + status: + - | + [[ "$(jq -r '.blueprint.description' package.json)" != "null" ]] + + build:continue: + cmds: + - TMP="$(mktemp)" && jq --arg cmd '{{.CLI_ARGS | replace "'" "\'"}}' '.scripts.build = $cmd' package.json && mv "$TMP" package.json + + description: + cmds: + - .config/log prompt 'Enter a description for the project' + - | + DESC_ANSWER="$(.config/log input 'Enter description..')" + task boilerplate:prompt:description:continue -- "$DESC_ANSWER" + status: + - | + [[ "$(jq -r '.blueprint.description' package.json)" != "null" ]] + + description:continue: + cmds: + - TMP="$(mktemp)" && jq --arg desc '{{.CLI_ARGS | replace "'" "\'"}}' '.blueprint.description = $desc' package.json && mv "$TMP" package.json + + group: + prompt: + type: select + message: Select a group + options: + - angular + - ansible + - docker + - go + - npm + - packer + - python + answer: + cmds: + - | + TMP="$(mktemp)" && jq --arg group '{{.ANSWER}}' '.blueprint.group = $group' package.json > "$TMP" && mv "$TMP" package.json + - | + TYPE='{{.ANSWER}}' yq e -i '.vars.REPOSITORY_TYPE = env(TYPE)' Taskfile.yml + status: + - | + [[ "$(jq -r '.blueprint.group' package.json)" != "null" ]] && [[ "$(yq e '.vars.REPOSITORY_TYPE' Taskfile.yml)" != "null" ]] + + name: + cmds: + - .config/log prompt 'Enter a name for the project' + - | + NAME_ANSWER="$(.config/log input 'Enter the project name..')" + task boilerplate:prompt:name:continue -- "$NAME_ANSWER" + status: + - | + [[ "$(jq -r '.blueprint.name' package.json)" != "null" ]] + + name:continue: + cmds: + - TMP="$(mktemp)" && jq --arg name '{{.CLI_ARGS}}' '.blueprint.name = $name' package.json > "$TMP" && mv "$TMP" package.json + + overview: + prompt: + type: input + message: Enter an overview for the project + answer: + cmds: + - TMP="$(mktemp)" && jq --arg overview '{{.ANSWER}}' '.blueprint.overview = $overview' package.json > "$TMP" && mv "$TMP" package.json + status: + - | + [[ "$(jq -r '.blueprint.overview' package.json)" != "null" ]] + + project-specific: 'true' + + slug: + cmds: + - .config/log prompt 'Enter a slug for the project' + - | + SLUG_ANSWER="$(.config/log input 'Enter a slug..')" + task boilerplate:prompt:slug:continue -- "$SLUG_ANSWER" + status: + - | + [[ "$(jq -r '.blueprint.slug' package.json)" != "null" ]] + + slug:continue: + cmds: + - TMP="$(mktemp)" && jq --arg slug '{{.CLI_ARGS}}' '.blueprint.slug = $slug' package.json > "$TMP" && mv "$TMP" package.json + + subgroup: + env: + SUBGROUP_GROUP: + sh: | + TASK_GROUP="$(yq e '.vars.REPOSITORY_TYPE' Taskfile.yml)" + if [ "$TASK_GROUP" == 'null' ]; then + PKG_GROUP="$(jq -r '.blueprint.group' package.json)" + if [ "$PKG_GROUP" == 'null' ]; then + echo 'generic' + else + echo "$PKG_GROUP" + fi + else + echo "$TASK_GROUP" + fi + prompt: + type: select + message: Select a subgroup + options: + sh: curl -sSL '{{.COMMON_DOCS_URL}}' | jq --arg type "$SUBGROUP_GROUP" '.groups[$type]' + answer: + cmds: + - | + TMP="$(mktemp)" && jq --arg subtype '{{.ANSWER}}' '.blueprint.subgroup = $subtype' package.json > "$TMP" && mv "$TMP" package.json + - | + SUBTYPE='{{.ANSWER}}' yq e -i '.vars.REPOSITORY_SUBTYPE = env(SUBTYPE)' Taskfile.yml + status: + - | + [[ "$(jq -r '.blueprint.subgroup' package.json)" != "null" ]] && [[ "$(yq e '.vars.REPOSITORY_SUBTYPE' Taskfile.yml)" != "null" ]] + + test: + summary: | + This task prompts the user for the `test` command to place in `scripts.test` inside + of `package.json`. + + For the default value, it looks at the corresponding common respoitory by first checking the + `project-subtype` folder and then the `project` folder's value for `scriptsTest` inside of the + `.config/variables.json` file. + cmds: + - .config/log prompt 'Enter the test command placed in `scripts.test` inside of `package.json`' + - | + TEST_ANSWER="$(.config/log input 'Enter test command..')" + task boilerplate:prompt:test:continue -- "$TEST_ANSWER" + status: + - | + [[ "$(jq -r '.scripts.test' package.json)" != "null" ]] + + test:continue: + cmds: + - TMP="$(mktemp)" && jq --arg cmd '{{.CLI_ARGS | replace "'" "\'"}}' '.scripts.test = $cmd' package.json && mv "$TMP" package.json + + title: + cmds: + - .config/log prompt 'Enter the title of the README.md' + - | + TITLE_ANSWER="$(.config/log input 'Enter README.md title..')" + task boilerplate:prompt:title:continue -- "$TITLE_ANSWER" + status: + - | + [[ "$(jq -r '.blueprint.title' package.json)" != "null" ]] + + title:continue: + cmds: + - TMP="$(mktemp)" && jq --arg title '{{.CLI_ARGS | replace "'" "\'"}}' '.blueprint.title = $title' package.json && mv "$TMP" package.json diff --git a/.config/taskfiles/boilerplate/Taskfile.yml b/.config/taskfiles/boilerplate/Taskfile.yml new file mode 100644 index 00000000..4d709467 --- /dev/null +++ b/.config/taskfiles/boilerplate/Taskfile.yml @@ -0,0 +1,136 @@ +--- +version: '3' + +tasks: + check:package: + interactive: true + deps: + - :install:modules:local + - :install:software:jq + vars: + BLUEPRINT_REQUIRED_FIELDS: title description group name overview slug subgroup + run: once + log: + error: Error occurred while validating/prompting for blueprint settings + start: Ensuring required fields in the blueprint section of `package.json` are present + succes: Successfully ensured `package.json` minimum blueprint requirements are present + cmds: + - task: prime:package + - task: ensure:gitlab-ci + - task: :boilerplate:prompt:all + - task: :boilerplate:populate:type + - task: update:taskfile + + clean: + deps: + - :install:software:jq + log: + error: Failed to clean `package.json` + start: Cleaning `package.json` + success: Cleaned `package.json` + cmds: + - | + TMP="$(mktemp)" + jq 'del(."standard-version")' package.json > "$TMP" + mv "$TMP" package.json + - | + TMP="$(mktemp)" + jq 'del(."lint-staged")' package.json > "$TMP" + mv "$TMP" package.json + + ensure:gitlab-ci: + run: once + cmds: + - | + if [ ! -f .gitlab-ci.yml ]; then + echo '---' > .gitlab-ci.yml + echo 'stages:' >> .gitlab-ci.yml + echo ' - lint' >> .gitlab-ci.yml + echo '' >> .gitlab-ci.yml + fi + + prime:package: + deps: + - :install:software:jq + run: once + log: + error: Failed to merge shared `package.json` settings + start: Ensuring `package.json` has shared settings + success: Successfully merged shared `package.json` settings + cmds: + - curl -s https://gitlab.com/megabyte-labs/common/shared/-/raw/master/package.json > package-reference.json + - task: prime:package:ensure-deps + vars: + PKG_FILE: package.json + - task: prime:package:ensure-deps + vars: + PKG_FILE: package-reference.json + - | + DEPS="$(jq -s '.[0].dependencies * .[1].dependencies' package-reference.json package.json)" + DEV_DEPS="$(jq -s '.[0].devDependencies * .[1].devDependencies' package-reference.json package.json)" + OPT_DEPS="$(jq -s '.[0].optionalDependencies * .[1].optionalDependencies' package-reference.json package.json)" + ESLINT_CONFIG="$(jq -r '.eslintConfig.extends' package-reference.json)" + PRETTIER_CONFIG="$(jq -r '.prettier' package-reference.json)" + TMP="$(mktemp)" + jq --arg deps "$DEPS" --arg devDeps "$DEV_DEPS" --arg optDeps "$OPT_DEPS" --arg eslint "$ESLINT_CONFIG" \ + --arg prettier "$PRETTIER_CONFIG" '.dependencies = ($deps | fromjson) | .devDependencies = ($devDeps + | fromjson) | .optionalDependencies = ($optDeps | fromjson) | .eslintConfig.extends = $eslint + | .prettier = $prettier' package.json > "$TMP" + mv "$TMP" package.json + - rm package-reference.json + + prime:package:ensure-deps: + deps: + - :install:software:jq + run: once + cmds: + - | + if [ "$(jq -r '.dependencies' {{.PKG_FILE}})" == 'null' ]; then + TMP="$(mktemp)" + jq '.dependencies = {}' {{.PKG_FILE}} > "$TMP" + mv "$TMP" {{.PKG_FILE}} + fi + - | + if [ "$(jq -r '.devDependencies' {{.PKG_FILE}})" == 'null' ]; then + TMP="$(mktemp)" + jq '.devDependencies = {}' {{.PKG_FILE}} > "$TMP" + mv "$TMP" {{.PKG_FILE}} + fi + - | + if [ "$(jq -r '.optionalDependencies' {{.PKG_FILE}})" == 'null' ]; then + TMP="$(mktemp)" + jq '.optionalDependencies = {}' {{.PKG_FILE}} > "$TMP" + mv "$TMP" {{.PKG_FILE}} + fi + + update:taskfile: + deps: + - :install:software:yq + run: once + log: + error: Error encountered while ensuring `Taskfile.yml` has correct settings + start: Ensuring `Taskfile.yml` has correct settings + success: Successfully applied `Taskfile.yml` assurances + cmds: + - | + GROUP="$(jq -r '.blueprint.group' package.json)" + SUBGROUP="$(jq -r '.blueprint.subgroup' package.json)" + TASK_GROUP="$(yq eval '.vars.REPOSITORY_TYPE' Taskfile.yml)" + TASK_SUBGROUP="$(yq eval '.vars.REPOSITORY_SUBTYPE' Taskfile.yml)" + if [ "$GROUP" != "$TASK_GROUP" ]; then + yq e -i ".vars.REPOSITORY_TYPE = \"$GROUP\"" Taskfile.yml + fi + if [ "$SUBGROUP" != "$TASK_SUBGROUP" ]; then + yq e -i ".vars.REPOSITORY_SUBTYPE = \"$SUBGROUP\"" Taskfile.yml + fi + UPSTREAM='upstream:project' + if [ '{{.REPOSITORY_TYPE}}.{{.REPOSITORY_SUBTYPE}}' == 'common.shared' ]; then + UPSTREAM='upstream:shared' + elif [ '{{.REPOSITORY_TYPE}}.{{.REPOSITORY_SUBTYPE}}' == 'documentation.shared' ]; then + UPSTREAM='upstream:commondocs' + elif [ '{{.REPOSITORY_TYPE}}' == 'common' ]; then + UPSTREAM='upstream:common' + elif [ '{{.REPOSITORY_TYPE}}' == 'documentation' ]; then + UPSTREAM='upstream:docs' + fi + yq e -i ".tasks.start.cmds[0].task = \"$UPSTREAM\"" Taskfile.yml diff --git a/.config/taskfiles/ci/Taskfile-github.yml b/.config/taskfiles/ci/Taskfile-github.yml new file mode 100644 index 00000000..7137eb1b --- /dev/null +++ b/.config/taskfiles/ci/Taskfile-github.yml @@ -0,0 +1,45 @@ +--- +version: '3' + +tasks: + actions:test: + deps: + - :install:software:act + - :install:software:docker + desc: Locally test the on-push GitHub Action event (only works for Linux containers) + hide: + sh: '! test -d .github/workflows' + summary: | + # Test GitHub Actions + + This task ensures Docker and Act are installed. It then uses Act to locally + test Linux-based on-push GitHub Action events. + + See [Act's README.md](https://github.com/nektos/act) for more information. + log: + error: Error encountered while testing GitHub Actions locally with `act` + start: Testing GitHub Actions locally with `act` + success: Completed local GitHub Actions test + cmds: + - act + + synchronize: + deps: + - :install:software:git + summary: | + Forces a push to the GitHub master branch so that GitHub stays mirrored with + the GitLab master branch. + env: + GITHUB_HTTP_REPO: + sh: jq -r '.blueprint.repository.github' package.json + cmds: + - cmd: | + if [ "$GITHUB_HTTP_REPO" != 'null' ]; then + GITHUB_REPO_WITH_TOKEN="$(echo "$GITHUB_HTTP_REPO" | sed "s/github.com/${GITHUB_TOKEN}@github.com/")" + git remote add github "${GITHUB_REPO_WITH_TOKEN}.git" + git fetch --unshallow origin + git push github master --force + else + .config/log warn 'The .blueprint.repository.github field is missing! Cannot synchronize to GitHub.' + fi + ignore_error: true diff --git a/.config/taskfiles/ci/Taskfile.yml b/.config/taskfiles/ci/Taskfile.yml new file mode 100644 index 00000000..66709e87 --- /dev/null +++ b/.config/taskfiles/ci/Taskfile.yml @@ -0,0 +1,190 @@ +--- +version: '3' + +tasks: + before: + deps: + - :install:software:git + cmds: + - task: commit:config + - task: checkout + - task: lockfiles + - task: before:npm + status: + - '[ -z "$CI" ]' + + before:npm: + deps: + - :install:npm:pnpm + log: + error: Error encountered while configuring pnpm to store its cache in `.pnpm-store` + start: Configuring pnpm to store its cache in `.pnpm-store` + success: Successfully updated pnpm to store its cache in `.pnpm-store` + cmds: + - pnpm config set store-dir .pnpm-store + + checkout: + deps: + - :install:software:git + log: + error: Failed to pull latest changes + start: Pulling latest changes + success: Successfully pulled latest changes + cmds: + - cmd: git pull + ignore_error: true + - | + if [ "$CI_COMMIT_REF_NAME" == 'synchronize' ]; then + git checkout master + git pull origin master + else + git checkout "$CI_COMMIT_REF_NAME" + git pull origin "$CI_COMMIT_REF_NAME" + fi + status: + - '[ -n "$NO_GIT_CREDS" ]' + + codeclimate: + deps: + - :install:software:docker + log: + error: Encountered error while running CodeClimate for CI + start: Running CodeClimate for CI + success: Successfully finished running CodeClimate for CI + cmds: + - | + if [ -f .config/codeclimate.yml ]; then + cp .config/codeclimate.yml .codeclimate.yml + else + curl -sSL https://gitlab.com/megabyte-labs/common/shared/-/raw/master/.config/codeclimate.yml > .codeclimate.yml + fi + - docker pull megabytelabs/codeclimate-internet + - docker tag megabytelabs/codeclimate-internet codeclimate/codeclimate + - task: :lint:codeclimate:load:custom-engines + - task: codeclimate:gitlab + + codeclimate:gitlab: + cmds: + - docker run --rm --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock + --volume /tmp/cc:/tmp/cc codeclimate/codeclimate analyze --dev -f json > gl-code-quality-report.json + status: + - '[ -z "$GITLAB_CI" ]' + + commit: + deps: + - :install:software:git + vars: + CURR_BRANCH: + sh: git rev-parse --abbrev-ref HEAD + log: + error: Encountered error while pushing changes to {{.CURR_BRANCH}} + start: Bypassing git hooks and pushing changes to {{.CURR_BRANCH}} (if there are any changes) + cmds: + - task: commit:config + - task: lockfiles:clean + - task --list > /dev/null || (echo "ERROR Invalid Taskfiles!" && exit 1) + - git add --all + - git diff --cached "*" + - | + if [[ $(git status --porcelain) ]]; then + git commit -m "☁️ chore(automation): Applying changes from upstream repository." + git push -q -o ci.skip origin {{.CURR_BRANCH}} || FAILED_PUSH=$? + if [ -n "$FAILED_PUSH" ]; then + git pull -X theirs origin {{.CURR_BRANCH}} + task --list &> /dev/null || (echo "ERROR: Invalid Taskfiles!" && exit 1) + git add --all + git commit -m "☁️ chore(automation): Merging changes from upstream repository with -X theirs." + git push --force -q -o ci.skip origin {{.CURR_BRANCH}} + fi + fi + status: + - '[ -z "$CI" ] || [ -n "$NO_GIT_CREDS" ]' + + commit:config: + deps: + - :install:software:git + run: once + log: + error: Error configuring git + start: Configuring git to use CI-friendly parameters + success: Successfully configured git to use CI parameters + cmds: + - git remote set-url origin "https://root:$GROUP_ACCESS_TOKEN@$CI_SERVER_HOST/$CI_PROJECT_PATH.git" + - git config user.email "$GITLAB_CI_EMAIL" + - git config user.name "$GITLAB_CI_NAME" + status: + - '[ -z "$GROUP_ACCESS_TOKEN" ] || [ -z "$GITLAB_CI_EMAIL" ] || [ -z "$GITLAB_CI_NAME" ] || [ -z "$GITLAB_CI" ]' + + lockfiles: + cmds: + - | + if [ -f local/package-lock.json ]; then + cp local/package-lock.json package-lock.json + fi + - | + if [ -f local/yarn.lock ]; then + cp local/yarn.lock yarn.lock + fi + + lockfiles:clean: + cmds: + - | + if [ -f local/package-lock.json ]; then + rm -f package-lock.json + fi + - | + if [ -f local/yarn.lock ];then + rm -f yarn.lock + fi + + mirror:github: + deps: + - :donothing + + submodules: + deps: + - :install:software:git + log: + error: Encountered error while ensuring submodules are up-to-date + start: Ensuring submodules are configured and up-to-date with their master remote + success: Ensured submodules are up-to-date + cmds: + - > + git submodule foreach 'git config user.email "$GITLAB_CI_EMAIL"; git config user.name "$GITLAB_CI_NAME"; + DEFAULT_BRANCH="$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@')"; + git checkout -q "$DEFAULT_BRANCH"; git pull -q origin "$DEFAULT_BRANCH" --ff-only' + status: + - '[ -z "$CI" ] || [ -n "$NO_GIT_CREDS" ]' + + synchronize: + deps: + - commit:config + run: once + log: + error: Failed to update the `synchronize` branch + start: Synchronizing the `synchronize` branch with the `master` branch + success: Successfully updated the `synchronize` branch + cmds: + - git fetch origin + - git checkout -b synchronize || git checkout synchronize + - git reset --hard HEAD + - git pull -q origin master + - | + git push --force -q -o ci.skip origin synchronize || FAILED_SYNC=$? + if [ -n "$FAILED_SYNC" ]; then + task git:gitlab:protected:off -- "synchronize" + git push --force -q -o ci.skip origin synchronize + fi + - task: synchronize:gitlab + - git checkout master + - task: :ci:github:synchronize + status: + - '[ -n "$NO_GIT_CREDS" ] || ([ "$FULLY_AUTOMATED_TASKS" != "true" ] && [ -z "$CI" ])' + + synchronize:gitlab: + run: once + cmds: + - curl -s --request POST --form "token=${CI_JOB_TOKEN}" --form ref=master --form "variables[PIPELINE_SOURCE]=$PIPELINE_SOURCE" + "https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/trigger/pipeline" + status: + - '[ -z "$GITLAB_CI" ] || [ -n "$NO_GITLAB_SYNCHRONIZE" ]' diff --git a/.config/taskfiles/cloud/Taskfile-cloudflare.yml b/.config/taskfiles/cloud/Taskfile-cloudflare.yml new file mode 100644 index 00000000..b1129272 --- /dev/null +++ b/.config/taskfiles/cloud/Taskfile-cloudflare.yml @@ -0,0 +1,19 @@ +--- +version: '3' + +tasks: + cloudflare:dns: + summary: | + This task sets up CNAME Record pointing to the given Value, in Cloudflare DNS. This is primarily used in + conjunciton with Heroku tasks to setup custom domains. These variables are needed: + CONFIGURE_CLOUDFLARE_DNS: Set to `true` to configure Cloudflare DNS + CLOUDFLARE_DNS_ZONE: Name of the DNS Zone where the record should be added to + CLOUDFLARE_API_TOKEN: API Token to authenticate to Cloudflare + CLOUDFLARE_RECORD_NAME: The name of the record + CLOUDFLARE_RECORD_VALUE: Target for the CNAME record + cmds: + - | + {{if eq .CONFIGURE_CLOUDFLARE_DNS "true")}}curl -X POST "https://api.cloudflare.com/client/v4/zones/{{.CLOUDFLARE_DNS_ZONE}}/dns_records/" \ + -H "Authorization: Bearer {{.CLOUDFLARE_API_TOKEN}}" \ + -H "Content-Type: application/json" \ + --data '{"type":"CNAME","name":"{{.CLOUDFLARE_RECORD_NAME}}","content":"{{.CLOUDFLARE_RECORD_VALUE}}","proxied":true,"ttl":3600}'{{end}} diff --git a/.config/taskfiles/cloud/Taskfile-dyno.yml b/.config/taskfiles/cloud/Taskfile-dyno.yml new file mode 100644 index 00000000..04568130 --- /dev/null +++ b/.config/taskfiles/cloud/Taskfile-dyno.yml @@ -0,0 +1,591 @@ +--- +version: '3' + +vars: + CLOUDFLARE_API_TOKEN: '' # Needed when `CONFIGURE_CLOUDFLARE_DNS` is set to `true` + + CLOUDFLARE_DNS_ZONE: '' # Needed when `CONFIGURE_CLOUDFLARE_DNS` is set to `true` + CONFIGURE_CLOUDFLARE_DNS: false # Set to `true` to configure Cloudflare DNS to point to the custom domain. Provide values for the below variables when set to `true` + CONFIGURE_CUSTOM_DOMAIN: false # Used in conjunction with `DOMAIN` value set for each application. Set to `true` to configure custom domains +env: + REGION: us # The region to deploy the application to + +tasks: + appsmith: + summary: | + Task to deploy `appsmith` to Heroku. + + Below are the environment variables that can be configured. Set the values in the `env` section. + + APPSMITH_DISABLE_TELEMETRY: Share anonymous usage data + APPSMITH_ENCRYPTION_PASSWORD: Encryption password to encrypt all sensitive credentials in the database. You can use any random string. The more random, the better. + APPSMITH_ENCRYPTION_SALT: Encryption salt used to encrypt all sensitive credentials in the database. You can use any random string. The more random, the better. + APPSMITH_MONGODB_URI: Your Mongo Database URI. Since Heroku doesn't support a managed MongoDB instance, you'll have to create a Mongo DB instance on another service such as https://cloud.mongodb.com + APPSMITH_SUPERVISOR_PASSWORD: Basic authentication password to access Supervisor UI - An web interface, which allow you to manage various process + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + env: + APPSMITH_DISABLE_TELEMETRY: true + APPSMITH_ENCRYPTION_PASSWORD: 'kna%si*sj19lk>0s' + APPSMITH_ENCRYPTION_SALT: 'm,a-01s' + APPSMITH_MONGODB_URI: mongo.example.com + APPSMITH_SUPERVISOR_PASSWORD: "sdf'6as9I1a" + cmds: + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"appsmith-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/appsmithorg/appsmith/tarball/master\"},\ + \"overrides\":{\"env\":{\"APPSMITH_DISABLE_TELEMETRY\": $APPSMITH_DISABLE_TELEMETRY, \"APPSMITH_ENCRYPTION_PASSWORD\":\ + \"$APPSMITH_ENCRYPTION_PASSWORD\", \"APPSMITH_ENCRYPTION_SALT\": \"$APPSMITH_ENCRYPTION_SALT\", \"APPSMITH_MONGODB_URI\":\ + \"$APPSMITH_MONGODB_URI\", \"APPSMITH_SUPERVISOR_PASSWORD\": \"$APPSMITH_SUPERVISOR_PASSWORD\"}}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a appsmith-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: appsmith + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + baserow: + summary: | + Task to deploy `baserow` to Heroku. + + Below are the environment variables that can be configured. Set the values in the `env` section. + + BASEROW_PUBLIC_URL: The public URL of your Heroku Baserow app. If empty, the default Heroku app URL is used, but if it differs it must be changed. (eg. https://baserow-test.com). + BASEROW_AMOUNT_OF_WORKERS: The amount of workers per dyno. + AWS_ACCESS_KEY_ID: The spaces API key. + AWS_SECRET_ACCESS_KEY: The spaces API secret key. + AWS_STORAGE_BUCKET_NAME: The name of your space. + AWS_S3_REGION_NAME: Name of the Digital Ocean spaces region (eg. ams3) or Name of the AWS S3 region to use (eg. eu-west-1) + AWS_S3_ENDPOINT_URL: Custom S3 URL to use when connecting to S3, including scheme. + AWS_S3_CUSTOM_DOMAIN: Your custom domain where the files can be downloaded from. + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + env: + AWS_ACCESS_KEY_ID: '' + AWS_S3_CUSTOM_DOMAIN: '' + AWS_S3_ENDPOINT_URL: '' + AWS_S3_REGION_NAME: '' + AWS_SECRET_ACCESS_KEY: '' + AWS_STORAGE_BUCKET_NAME: '' + BASEROW_AMOUNT_OF_WORKERS: '1' + BASEROW_PUBLIC_URL: https://baserow.megabyte.space + cmds: + - task: bucket:create + vars: + AWS_ACCESS_KEY_ID: '{{.AWS_ACCESS_KEY_ID}}' + AWS_REGION: '{{.AWS_S3_REGION_NAME}}' + AWS_SECRET_ACCESS_KEY: '{{.AWS_SECRET_ACCESS_KEY}}' + BUCKET_NAME: '{{.AWS_STORAGE_BUCKET_NAME}}' + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"baserow-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/bram2w/baserow/tarball/master\"},\ + \"overrides\":{\"env\":{{\"BASEROW_PUBLIC_URL\":\"$BASEROW_PUBLIC_URL\",\"BASEROW_AMOUNT_OF_WORKERS\":\ + \"$BASEROW_AMOUNT_OF_WORKERS\",\"AWS_ACCESS_KEY_ID\":\"$AWS_ACCESS_KEY_ID\",\"AWS_SECRET_ACCESS_KEY\":\ + \"$AWS_SECRET_ACCESS_KEY\",\"AWS_STORAGE_BUCKET_NAME\":\"$AWS_STORAGE_BUCKET_NAME\",\"AWS_S3_REGION_NAME\":\ + \"$AWS_S3_REGION_NAME\",\"AWS_S3_ENDPOINT_URL\":\"$AWS_S3_ENDPOINT_URL\",\"AWS_S3_CUSTOM_DOMAIN\":\"$AWS_S3_CUSTOM_DOMAIN\"}}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a baserow-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: baserow + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + chatwoot: + summary: | + Task to deploy `chatwoot` to Heroku. + + Below are the environment variables that can be configured. Set the values in the `env` section. + FRONTEND_URL: Public root URL of the Chatwoot installation. This will be used in the emails. + INSTALLATION_ENV: Installation method used for Chatwoot. + RACK_ENV: Environment for rack middleware. + RAILS_ENV: Environment for rails middleware. + REDIS_OPENSSL_VERIFY_MODE: OpenSSL verification mode for Redis connections. Ref https://help.heroku.com/HC0F8CUS/redis-connection-issues + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + env: + FRONTEND_URL: https://chatwoot.megabyte.space + INSTALLATION_ENV: heroku + RACK_ENV: production + RAILS_ENV: production + REDIS_OPENSSL_VERIFY_MODE: none + cmds: + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"chatwoot-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/chatwoot/chatwoot/tarball/master\"},\ + \"overrides\":{\"env\":{\"FRONTEND_URL\": \"$FRONTEND_URL\", \"INSTALLATION_ENV\": \"$INSTALLATION_ENV\", \"RACK_ENV\":\ + \"$RACK_ENV\", \"RAILS_ENV\": \"$RAILS_ENV\", \"REDIS_OPENSSL_VERIFY_MODE\": \"$REDIS_OPENSSL_VERIFY_MODE\"}}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a chatwoot-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: chatwoot + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + directus: + summary: | + Task to deploy `directus` to Heroku. + + Below are the environment variables that can be configured. Set the values in the `env` section. + + ACCESS_TOKEN_TTL: The access token TTL. + ADMIN_EMAIL: The initial admin email. + ADMIN_PASSWORD: The initial admin password. + CACHE_ENABLED: Whether the cache should be enabled or not. + CACHE_NAMESPACE: The cache namespace. + CACHE_STORE: The cache store to use. + CONFIG_PATH: Application config loader path. + DB_CLIENT: Database server type. + EMAIL_SMTP_HOST: The email server host. + EMAIL_SMTP_POOL: Whether to setup smtp pooling or not. + EMAIL_SMTP_PORT: The email server port. + EMAIL_SMTP_SECURE: Whether email connection is secure or not. + EMAIL_TRANSPORT: The email transport. + EXTENSIONS_PATH: The application's extension folder. + OAUTH_PROVIDERS: OAuth providers. + PUBLIC_URL: Application public URL. + RATE_LIMITER_DURATION: Rate limiter duration in minutes. + RATE_LIMITER_ENABLED: Whether the rate limiter should be enabled or not. + RATE_LIMITER_KEY_PREFIX: The rate limiter key prefixes. + RATE_LIMITER_POINTS: Rate limiter points. + RATE_LIMITER_STORE: The rate limiter storage type. + REFRESH_TOKEN_COOKIE_SAME_SITE: Same site cookie policy. + REFRESH_TOKEN_COOKIE_SECURE: Whether cookies should be secure (http-only) or not. + REFRESH_TOKEN_TTL: The refresh token TTL. + STORAGE_CLOUD_BUCKET: The storage bucket name. + STORAGE_CLOUD_DRIVER: The storage driver. + STORAGE_CLOUD_ENDPOINT: The storage endpoint URL. + STORAGE_CLOUD_KEY: The storage key id. + STORAGE_CLOUD_PUBLIC_URL: The storage key id. + STORAGE_CLOUD_REGION: The storage bucket region. + STORAGE_CLOUD_ROOT: Storage root location. + STORAGE_CLOUD_SECRET: The storage secret key. + STORAGE_LOCATIONS: The storage key. Please refer to the docs for more information. + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + env: + ACCESS_TOKEN_TTL: 15m + ADMIN_EMAIL: admin@email.com + ADMIN_PASSWORD: RandomPasword$ + CACHE_ENABLED: true + CACHE_NAMESPACE: cache + CACHE_STORE: redis + CONFIG_PATH: /app/directus.config.js + DB_CLIENT: pg + EMAIL_SMTP_HOST: smtp.example.com + EMAIL_SMTP_POOL: true + EMAIL_SMTP_PORT: '587' + EMAIL_SMTP_SECURE: false + EMAIL_TRANSPORT: smtp + EXTENSIONS_PATH: /app/extensions + OAUTH_PROVIDERS: '' + PUBLIC_URL: / + RATE_LIMITER_DURATION: '1' + RATE_LIMITER_ENABLED: true + RATE_LIMITER_KEY_PREFIX: rate-limitter + RATE_LIMITER_POINTS: '30' + RATE_LIMITER_STORE: redis + REFRESH_TOKEN_COOKIE_SAME_SITE: true + REFRESH_TOKEN_COOKIE_SECURE: true + REFRESH_TOKEN_TTL: 7d + STORAGE_CLOUD_BUCKET: your-bucket + STORAGE_CLOUD_DRIVER: s3 + STORAGE_CLOUD_ENDPOINT: https://nyc3.digitaloceanspaces.com + STORAGE_CLOUD_KEY: your-s3-key-id + STORAGE_CLOUD_PUBLIC_URL: https://your-bucket.nyc3.digitaloceanspaces.com + STORAGE_CLOUD_REGION: nyc3 + STORAGE_CLOUD_ROOT: / + STORAGE_CLOUD_SECRET: your-s3-secret-key + STORAGE_LOCATIONS: cloud + cmds: + - task: bucket:create + vars: + AWS_ACCESS_KEY_ID: '{{.STORAGE_CLOUD_KEY}}' + AWS_REGION: '{{.STORAGE_CLOUD_REGION}}' + AWS_SECRET_ACCESS_KEY: '{{.STORAGE_CLOUD_SECRET}}' + BUCKET_NAME: '{{.STORAGE_CLOUD_BUCKET}}' + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"directus-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/directus-community/heroku-template/tarball/master\"},\ + \"overrides\":{\"env\":{\"ACCESS_TOKEN_TTL\": \"$ACCESS_TOKEN_TTL\", \"ADMIN_EMAIL\": \"$ADMIN_EMAIL\", \"ADMIN_PASSWORD\":\ + \"$ADMIN_PASSWORD$\", \"CACHE_ENABLED\": $CACHE_ENABLED, \"CACHE_NAMESPACE\": \"$CACHE_NAMESPACE\", \"CACHE_STORE\":\ + \"$CACHE_STORE\", \"CONFIG_PATH\": \"$CONFIG_PATH\", \"DB_CLIENT\": \"$DB_CLIENT\", \"EMAIL_SMTP_HOST\": \"$EMAIL_SMTP_HOST\",\ + \"EMAIL_SMTP_POOL\": $EMAIL_SMTP_POOL, \"EMAIL_SMTP_PORT\": \"$EMAIL_SMTP_PORT\", \"EMAIL_SMTP_SECURE\": $EMAIL_SMTP_SECURE,\ + \"EMAIL_TRANSPORT\": \"$EMAIL_TRANSPORT\", \"EXTENSIONS_PATH\": \"$EXTENSIONS_PATH\", \"OAUTH_PROVIDERS\": \"$OAUTH_PROVIDERS\",\ + \"PUBLIC_URL\": \"$PUBLIC_URL\", \"RATE_LIMITER_DURATION\": \"$RATE_LIMITER_DURATION\", \"RATE_LIMITER_ENABLED\": $RATE_LIMITER_ENABLED,\ + \"RATE_LIMITER_KEY_PREFIX\": \"$RATE_LIMITER_KEY_PREFIX\", \"RATE_LIMITER_POINTS\": \"$RATE_LIMITER_POINTS\", \"RATE_LIMITER_STORE\":\ + \"$RATE_LIMITER_STORE\", \"REFRESH_TOKEN_COOKIE_SAME_SITE\": $REFRESH_TOKEN_COOKIE_SAME_SITE, \"REFRESH_TOKEN_COOKIE_SECURE\":\ + \"$REFRESH_TOKEN_COOKIE_SECURE\", \"REFRESH_TOKEN_TTL\": \"$REFRESH_TOKEN_TTL\", \"STORAGE_CLOUD_BUCKET\": \"$STORAGE_CLOUD_BUCKET\",\ + \"STORAGE_CLOUD_DRIVER\": \"$STORAGE_CLOUD_DRIVER\", \"STORAGE_CLOUD_ENDPOINT\": \"$STORAGE_CLOUD_ENDPOINT\", \"STORAGE_CLOUD_KEY\":\ + \"$STORAGE_CLOUD_KEY\", \"STORAGE_CLOUD_PUBLIC_URL\": \"$STORAGE_CLOUD_PUBLIC_URL\", \"STORAGE_CLOUD_REGION\": \"$STORAGE_CLOUD_REGION\",\ + \"STORAGE_CLOUD_ROOT\": \"$STORAGE_CLOUD_ROOT\", \"STORAGE_CLOUD_SECRET\": \"$STORAGE_CLOUD_SECRET\", \"STORAGE_LOCATIONS\": \"$STORAGE_LOCATIONS\"}}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a directus-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: directus + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + ghostonheroku: + summary: | + Task to deploy `ghost-on-heroku` to Heroku. + + Below are the environment variables that can be configured. Set the values in the `env` section. + + PUBLIC_URL: The HTTPS URL of this app: either your custom domain or default 'herokuapp.com' hostname. + S3_ACCESS_KEY_ID: Set your AWS Access Key ID to enable S3 file storage. Leave blank to disable file uploads. + S3_ACCESS_SECRET_KEY: AWS Access Secret Key, if using S3 file storage. + S3_ASSET_HOST_URL: Optional custom CDN asset host url, if using S3 file storage. + S3_BUCKET_NAME: Name of your S3 bucket on AWS, if using S3 file storage. + S3_BUCKET_REGION: Region of your S3 bucket on AWS, if using S3 file storage. + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + env: + PUBLIC_URL: https://ghost.megabyte.space + S3_ACCESS_KEY_ID: S3 access key id + S3_ACCESS_SECRET_KEY: S3 secret access key + S3_ASSET_HOST_URL: e.g https://my.custom.domain/ + S3_BUCKET_NAME: S3 bucket + S3_BUCKET_REGION: S3 bucket region (e.g. us-east-1) + + cmds: + - task: bucket:create + vars: + AWS_ACCESS_KEY_ID: '{{.S3_ACCESS_KEY_ID}}' + AWS_REGION: '{{.S3_BUCKET_REGION}}' + AWS_SECRET_ACCESS_KEY: '{{.S3_ACCESS_SECRET_KEY}}' + BUCKET_NAME: '{{.S3_BUCKET_NAME}}' + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"ghostonheroku-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/cobyism/ghost-on-heroku/tarball/master\"},\ + \"overrides\":{\"env\":{\"PUBLIC_URL\": \"$PUBLIC_URL\", \"S3_ACCESS_KEY_ID\": \"$S3_ACCESS_KEY_ID\",\ + \"S3_ACCESS_SECRET_KEY\": \"$S3_ACCESS_SECRET_KEY\", \"S3_ASSET_HOST_URL\": \"$S3_ASSET_HOST_URL\",\ + \"S3_BUCKET_NAME\": \"$S3_BUCKET_NAME\", \"S3_BUCKET_REGION\": \"$S3_BUCKET_REGION\"}}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a ghostonheroku-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: ghostonheroku + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + hasura: + summary: | + Task to deploy `hasura` to Heroku. + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + cmds: + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"hasura-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/hasura/graphql-engine-heroku/tarball/master\"}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a hasura-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: hasura + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + manet: + summary: | + Task to deploy `manet` to Heroku. + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + cmds: + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"manet-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/vbauer/manet/tarball/master\"}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a manet-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: manet + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + metabase: + summary: | + Task to deploy `metabase` to Heroku. + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + cmds: + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"metabase-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/metabase/metabase-deploy/tarball/master\"}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a metabase-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: metabase + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + nocodb: + summary: | + Task to deploy `nocodb` to Heroku. + + Below are the environment variables that can be configured. Set the values in the `env` section. + + NC_ONE_CLICK: Used for Heroku one-click deployment + NODE_TLS_REJECT_UNAUTHORIZED: Reject unauthorized + AWS_ACCESS_KEY_ID: For Litestream - S3 access key id + AWS_SECRET_ACCESS_KEY: For Litestream - S3 secret access key + AWS_BUCKET_REGION: Region where the bucket is present + AWS_BUCKET: For Litestream - S3 bucket + AWS_BUCKET_PATH: For Litestream - S3 bucket path (like folder within S3 bucket) + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + env: + AWS_ACCESS_KEY_ID: S3 access key id + AWS_BUCKET: S3 bucket + AWS_BUCKET_PATH: S3 bucket path (like folder within S3 bucket) + + AWS_BUCKET_REGION: S3 Region + AWS_SECRET_ACCESS_KEY: S3 secret access key + NC_ONE_CLICK: true + NODE_TLS_REJECT_UNAUTHORIZED: '0' + cmds: + - task: bucket:create + vars: + AWS_ACCESS_KEY_ID: '{{.AWS_ACCESS_KEY_ID}}' + AWS_REGION: '{{.AWS_BUCKET_REGION}}' + AWS_SECRET_ACCESS_KEY: '{{.AWS_SECRET_ACCESS_KEY}}' + BUCKET_NAME: '{{.AWS_BUCKET}}' + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"nocodb-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/nocodb/nocodb-seed-heroku/tarball/master\"},\ + \"overrides\":{\"env\":{\"NC_ONE_CLICK\": $NC_ONE_CLICK, \"NODE_TLS_REJECT_UNAUTHORIZED\": \"$NODE_TLS_REJECT_UNAUTHORIZED\",\ + \"AWS_ACCESS_KEY_ID\": \"$AWS_ACCESS_KEY_ID\", \"AWS_SECRET_ACCESS_KEY\": \"$AWS_SECRET_ACCESS_KEY\", \"AWS_BUCKET\":\ + \"$AWS_BUCKET\", \"AWS_BUCKET_PATH\": \"$AWS_BUCKET_PATH\"}}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a nocodb-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: nocodb + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + tooljet: + summary: | + Task to deploy `tooljet` to Heroku. + + Below are the environment variables that can be configured. Set the values in the `env` section. + + DEPLOYMENT_PLATFORM: Platform ToolJet is deployed on + DISABLE_MULTI_WORKSPACE: Disables Multi-Workspace feature + DISABLE_SIGNUPS: Disable sign up in login page only applicable if Multi-Workspace feature is turned on + LOCKBOX_MASTER_KEY: Master key for encrypting datasource credentials. + NODE_ENV: Environment [production/development] + NODE_OPTIONS: Node options configured to increase node memory to support app build + SECRET_KEY_BASE: Used by ToolJet server as the input secret to the application's key generator. + TOOLJET_HOST: Public URL of ToolJet installtion. This is usually https://.herokuapp.com + TOOLJET_SERVER_URL: URL of ToolJet server installtion. (This is same as the TOOLJET_HOST for Heroku deployments) + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + env: + DEPLOYMENT_PLATFORM: heroku + DISABLE_MULTI_WORKSPACE: false + DISABLE_SIGNUPS: false + LOCKBOX_MASTER_KEY: m@s73rk8s + NODE_ENV: production + NODE_OPTIONS: --max-old-space-size=4096 + SECRET_KEY_BASE: SomeC0m6l00 + TOOLJET_HOST: https://tooljet.herokuapp.com + TOOLJET_SERVER_URL: https://tooljet.herokuapp.com + + cmds: + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"tooljet-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/tooljet/tooljet/tarball/master\"},\ + \"overrides\":{\"env\":{\"DEPLOYMENT_PLATFORM\": \"$DEPLOYMENT_PLATFORM\", \"DISABLE_MULTI_WORKSPACE\":\ + \"$DISABLE_MULTI_WORKSPACE\", \"DISABLE_SIGNUPS\": $DISABLE_SIGNUPS, \"LOCKBOX_MASTER_KEY\":\ + \"$LOCKBOX_MASTER_KEY\", \"NODE_ENV\": \"$NODE_ENV\", \"NODE_OPTIONS\": \"$NODE_OPTIONS\",\ + \"SECRET_KEY_BASE\": \"$SECRET_KEY_BASE\", \"TOOLJET_HOST\": \"$TOOLJET_HOST\", \"TOOLJET_SERVER_URL\": \"$TOOLJET_SERVER_URL\"}}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a tooljet-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: tooljet + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + urltopdf: + summary: | + Task to deploy `url-to-pdf-api` to Heroku. + + Below are the environment variables that can be configured. Set the values in the `env` section. + + ALLOW_HTTP: When set to "true", unsecure requests are allowed + API_TOKENS: Comma-separated list of accepted keys in x-api-key header + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + env: + ALLOW_HTTP: false + API_TOKENS: '' + cmds: + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body "{\"app\":{\"region\":\"$REGION\",\"name\":\"urltopdf-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/alvarcarto/url-to-pdf-api/tarball/master\"},\ + \"overrides\":{\"env\":{\"ALLOW_HTTP\": $ALLOW_HTTP, \"API_TOKENS\": \"$API_TOKENS\"}}}" + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a urltopdf-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: urltopdf + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' + whoogle: + summary: | + Task to deploy `whoogle` to Heroku. + + Below are the environment variables that can be configured. Set the values in the `env` section. + + WHOOGLE_ALT_IG: The site to use as a replacement for instagram.com when site alternatives are enabled in the config. + WHOOGLE_ALT_IMG: The site to use as a replacement for imgur.com when site alternatives are enabled in the config. + WHOOGLE_ALT_MD: The site to use as a replacement for medium.com when site alternatives are enabled in the config. + WHOOGLE_ALT_RD: The site to use as a replacement for reddit.com when site alternatives are enabled in the config. + WHOOGLE_ALT_TL: The Google Translate alternative to use for all searches following the 'translate ___' structure. + WHOOGLE_ALT_TW: The site to use as a replacement for twitter.com when site alternatives are enabled in the config. + WHOOGLE_ALT_WIKI: The site to use as a replacement for wikipedia.com when site alternatives are enabled in the config. + WHOOGLE_ALT_YT: The site to use as a replacement for youtube.com when site alternatives are enabled in the config. + WHOOGLE_CONFIG_ALTS: [CONFIG] Use social media alternatives (set to 1 or leave blank) + WHOOGLE_CONFIG_BLOCK: [CONFIG] Block websites from search results (comma-separated list) + WHOOGLE_CONFIG_COUNTRY: [CONFIG] The country to use for restricting search results (use values from https://raw.githubusercontent.com/benbusby/whoogle-search/develop/app/static/settings/countries.json) + WHOOGLE_CONFIG_DISABLE: [CONFIG] Disable ability for client to change config (set to 1 or leave blank) + WHOOGLE_CONFIG_GET_ONLY: [CONFIG] Search using GET requests only (set to 1 or leave blank) + WHOOGLE_CONFIG_LANGUAGE: [CONFIG] The language to use for the interface (use values from https://raw.githubusercontent.com/benbusby/whoogle-search/develop/app/static/settings/languages.json) + WHOOGLE_CONFIG_NEAR: [CONFIG] Restrict results to only those near a particular city + WHOOGLE_CONFIG_NEW_TAB: [CONFIG] Always open results in new tab (set to 1 or leave blank) + WHOOGLE_CONFIG_SAFE: [CONFIG] Use safe mode for searches (set to 1 or leave blank) + WHOOGLE_CONFIG_SEARCH_LANGUAGE: [CONFIG] The language to use for search results (use values from https://raw.githubusercontent.com/benbusby/whoogle-search/develop/app/static/settings/languages.json) + WHOOGLE_CONFIG_STYLE: [CONFIG] Custom CSS styling (provide CSS or leave blank) + WHOOGLE_CONFIG_THEME: [CONFIG] Set theme to 'dark', 'light', or 'system' + WHOOGLE_CONFIG_TOR: [CONFIG] Use Tor, if available (set to 1 or leave blank) + WHOOGLE_CONFIG_VIEW_IMAGE: [CONFIG] Enable View Image option (set to 1 or leave blank) + WHOOGLE_MINIMAL: Remove everything except basic result cards from all search queries (set to 1 or leave blank) + WHOOGLE_PASS: The password for basic auth. WHOOGLE_USER must also be set if used. Leave empty to disable. + WHOOGLE_PROXY_LOC: The location of the proxy server (host or ip). Leave empty to disable. + WHOOGLE_PROXY_PASS: The password of the proxy server. Leave empty to disable. + WHOOGLE_PROXY_TYPE: The type of the proxy server. For example "socks5". Leave empty to disable. + WHOOGLE_PROXY_USER: The username of the proxy server. Leave empty to disable. + WHOOGLE_URL_PREFIX: The URL prefix to use for the whoogle instance (i.e. "/whoogle") + WHOOGLE_USER: The username for basic auth. WHOOGLE_PASS must also be set if used. Leave empty to disable. + + The below variable can be used to pass the value of Custom Domain for the application + DOMAIN: The custom domain to be added to the application. Set this to a valid value to add the domain (`CONFIGURE_CUSTOM_DOMAIN` should be set to `true`). + vars: + DOMAIN: '' + env: + WHOOGLE_ALT_IG: farside.link/bibliogram/u + WHOOGLE_ALT_IMG: farside.link/rimgo + WHOOGLE_ALT_MD: farside.link/scribe + WHOOGLE_ALT_RD: farside.link/libreddit + WHOOGLE_ALT_TL: farside.link/lingva + WHOOGLE_ALT_TW: farside.link/nitter + WHOOGLE_ALT_WIKI: farside.link/wikiless + WHOOGLE_ALT_YT: farside.link/invidious + WHOOGLE_CONFIG_ALTS: '' + WHOOGLE_CONFIG_BLOCK: '' + WHOOGLE_CONFIG_COUNTRY: countryUS + WHOOGLE_CONFIG_DISABLE: '' + WHOOGLE_CONFIG_GET_ONLY: '' + WHOOGLE_CONFIG_LANGUAGE: lang_en + WHOOGLE_CONFIG_NEAR: '' + WHOOGLE_CONFIG_NEW_TAB: '' + WHOOGLE_CONFIG_SAFE: '' + WHOOGLE_CONFIG_SEARCH_LANGUAGE: lang_en + WHOOGLE_CONFIG_STYLE: ':root { /* LIGHT THEME COLORS */ --whoogle-background: #d8dee9; --whoogle-accent: #2e3440; --whoogle-text: #3B4252; --whoogle-contrast-text: #eceff4; --whoogle-secondary-text: #70757a; --whoogle-result-bg: #fff; --whoogle-result-title: #4c566a; --whoogle-result-url: #81a1c1; --whoogle-result-visited: #a3be8c; /* DARK THEME COLORS */ --whoogle-dark-background: #222; --whoogle-dark-accent: #685e79; --whoogle-dark-text: #fff; --whoogle-dark-contrast-text: #000; --whoogle-dark-secondary-text: #bbb; --whoogle-dark-result-bg: #000; --whoogle-dark-result-title: #1967d2; --whoogle-dark-result-url: #4b11a8; --whoogle-dark-result-visited: #bbbbff; }' + WHOOGLE_CONFIG_THEME: system + WHOOGLE_CONFIG_TOR: '' + WHOOGLE_CONFIG_VIEW_IMAGE: '' + WHOOGLE_MINIMAL: '' + WHOOGLE_PASS: '' + WHOOGLE_PROXY_LOC: '' + WHOOGLE_PROXY_PASS: '' + WHOOGLE_PROXY_TYPE: '' + WHOOGLE_PROXY_USER: '' + WHOOGLE_URL_PREFIX: /whoogle + WHOOGLE_USER: '' + cmds: + - | + RNDM=$(shuf -i 10000-1000000 -n 1) + heroku api POST /app-setups --body '{\"app\":{\"region\":\"$REGION\",\"name\":\"whoogle-$RNDM\"},\ + \"source_blob\":{\"url\":\"https://api.github.com/repos/benbusby/whoogle-search/tarball/master\"},\ + \"overrides\":{\"env\":{\"WHOOGLE_ALT_IG\": \"$WHOOGLE_ALT_IG\", \"WHOOGLE_ALT_IMG\":\ + \"$WHOOGLE_ALT_IMG\", \"WHOOGLE_ALT_MD\": \"$WHOOGLE_ALT_MD\", \"WHOOGLE_ALT_RD\": \"$WHOOGLE_ALT_RD\",\ + \"WHOOGLE_ALT_TL\": \"$WHOOGLE_ALT_TL\", \"WHOOGLE_ALT_TW\": \"$WHOOGLE_ALT_TW\", \"WHOOGLE_ALT_WIKI\":\ + \"$WHOOGLE_ALT_WIKI\", \"WHOOGLE_ALT_YT\": \"$WHOOGLE_ALT_YT\", \"WHOOGLE_CONFIG_ALTS\": \"$WHOOGLE_CONFIG_ALTS\",\ + \"WHOOGLE_CONFIG_BLOCK\": \"$WHOOGLE_CONFIG_BLOCK\", \"WHOOGLE_CONFIG_COUNTRY\": \"$WHOOGLE_CONFIG_COUNTRY\",\ + \"WHOOGLE_CONFIG_DISABLE\": \"$WHOOGLE_CONFIG_DISABLE\", \"WHOOGLE_CONFIG_GET_ONLY\": \"$WHOOGLE_CONFIG_GET_ONLY\",\ + \"WHOOGLE_CONFIG_LANGUAGE\": \"$WHOOGLE_CONFIG_LANGUAGE\", \"WHOOGLE_CONFIG_NEAR\": \"$WHOOGLE_CONFIG_NEAR\",\ + \"WHOOGLE_CONFIG_NEW_TAB\": \"$WHOOGLE_CONFIG_NEW_TAB\", \"WHOOGLE_CONFIG_SAFE\": \"$WHOOGLE_CONFIG_SAFE\",\ + \"WHOOGLE_CONFIG_SEARCH_LANGUAGE\": \"$WHOOGLE_CONFIG_SEARCH_LANGUAGE\", \"WHOOGLE_CONFIG_STYLE\":\ + \"$WHOOGLE_CONFIG_STYLE\", \"WHOOGLE_CONFIG_THEME\": \"$WHOOGLE_CONFIG_THEME\", \"WHOOGLE_CONFIG_TOR\":\ + \"$WHOOGLE_CONFIG_TOR\", \"WHOOGLE_CONFIG_VIEW_IMAGE\": \"$WHOOGLE_CONFIG_VIEW_IMAGE\", \"WHOOGLE_MINIMAL\":\ + \"$WHOOGLE_MINIMAL\", \"WHOOGLE_PASS\": \"$WHOOGLE_PASS\", \"WHOOGLE_PROXY_LOC\": \"$WHOOGLE_PROXY_LOC\",\ + \"WHOOGLE_PROXY_PASS\": \"$WHOOGLE_PROXY_PASS\", \"WHOOGLE_PROXY_TYPE\": \"$WHOOGLE_PROXY_TYPE\",\ + \"WHOOGLE_PROXY_USER\": \"$WHOOGLE_PROXY_USER\", \"WHOOGLE_URL_PREFIX\": \"$WHOOGLE_URL_PREFIX\", \"WHOOGLE_USER\": \"$WHOOGLE_USER\"}}}' + {{if and (eq .CONFIGURE_CUSTOM_DOMAIN "true") (ne .DOMAIN "")}}heroku domains:add {{.DOMAIN}} -a whoogle-$RNDM {{end}} + - task: cloudflare:dns + vars: + CLOUDFLARE_API_TOKEN: '{{.CLOUDFLARE_API_TOKEN}}' + CLOUDFLARE_DNS_ZONE: '{{.CLOUDFLARE_DNS_ZONE}}' + CLOUDFLARE_RECORD_NAME: whoogle + CLOUDFLARE_RECORD_VALUE: '{{.DOMAIN}}' + CONFIGURE_CLOUDFLARE_DNS: '{{.CONFIGURE_CLOUDFLARE_DNS}}' diff --git a/.config/taskfiles/cloud/Taskfile-heroku.yml b/.config/taskfiles/cloud/Taskfile-heroku.yml new file mode 100644 index 00000000..591b0fd9 --- /dev/null +++ b/.config/taskfiles/cloud/Taskfile-heroku.yml @@ -0,0 +1,19 @@ +--- +version: '3' + +tasks: + sync:ssh-keys: + deps: + - :install:software:heroku + cmds: + - .config/log info 'Clearing Heroku SSH keys' + - heroku keys:clear + - .config/log start 'Syncing SSH keys with Heroku' + - | + for KEY in `ls $HOME/.ssh/*.pub`; do + heroku keys:add "$KEY" + done + - .config/log success 'Finished syncing SSH keys with Heroku' + preconditions: + - sh: '[ -n "$HEROKU_API_KEY" ]' + msg: The HEROKU_API_KEY must be set to a personal access token. diff --git a/.config/taskfiles/cloud/Taskfile-s3.yml b/.config/taskfiles/cloud/Taskfile-s3.yml new file mode 100644 index 00000000..90563f77 --- /dev/null +++ b/.config/taskfiles/cloud/Taskfile-s3.yml @@ -0,0 +1,62 @@ +--- +version: '3' + +tasks: + bucket:create: + deps: + - :install:software:s5cmd + summary: | + # Create S3 Bucket + + This task creates an S3 bucket in the given account + + `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, `AWS_REGION` and `BUCKET_NAME` are passed from the calling task. The first 3 + variables are set as environment variables + env: + AWS_ACCESS_KEY_ID: '{{.AWS_ACCESS_KEY_ID}}' + AWS_REGION: '{{.AWS_REGION}}' + AWS_SECRET_ACCESS_KEY: '{{.AWS_SECRET_ACCESS_KEY}}' + cmds: + - s5cmd mb s3://'{{.BUCKET_NAME}}' + + bucket:jumpusb:populate: + deps: + - :install:software:axel + summary: | + # Populate an S3 Bucket with JumpUSB Assets + + This script first downloads the script from the JumpUSB repository: + + https://gitlab.com/megabyte-labs/jumpusb/-/blob/master/local/distros.json + + After that, it downloads all the operating systems and uploads them to an + S3 bucket using the same path that is defined in each object's path key. + For example, the following: + + ``` + { + "url": "https://mirrors.edge.kernel.org/zorinos-isos/16/Zorin-OS-16.1-Core-64-bit.iso", + "url_axel": "https://{mirrors.edge.kernel.org/zorinos-isos,mirror2.sandyriver.net/pub/zorinos,mirror.clarkson.edu/zorinos/isos,distro.ibiblio.org/zorinos}/16/Zorin-OS-16.1-Core-64-bit.iso", + "path": "/iso/zorin/zorin-16.1-amd64.iso", + "persistence_base": "persistence_ext4_4GB_casper-rw.dat.7z", + "persistence_file": "zorin.dat", + "live": true + } + ``` + + 1. Downloads the ISO with Axel using the `url_axel` URL (and falls back to the `url` if `url_axel` is not present) + 2. Then uploads the file into the `/iso/zorin` directory of the S3 bucket where the file is named `zorin-16.1-amd64.iso + env: + DISTROS_TMP: + sh: mktemp + cmds: + - curl -sSL https://gitlab.com/megabyte-labs/jumpusb/-/raw/master/local/distros.json > "$DISTROS_TMP" + bucket:synchronize: + deps: + - :install:software:s5cmd + summary: | + # Synchronize S3 Buckets + + Synchronize the contents of one S3 bucket with another using s5cmd. + cmds: + - s5cmd sync '{{.SOURCE_BUCKET}}' '{{.DEST_BUCKET}}' diff --git a/.config/taskfiles/cloud/Taskfile.yml b/.config/taskfiles/cloud/Taskfile.yml new file mode 100644 index 00000000..23de5a75 --- /dev/null +++ b/.config/taskfiles/cloud/Taskfile.yml @@ -0,0 +1,5 @@ +--- +version: '3' + +tasks: + launch: 'true' diff --git a/.config/taskfiles/common/Taskfile-code.yml b/.config/taskfiles/common/Taskfile-code.yml new file mode 100644 index 00000000..bccbba70 --- /dev/null +++ b/.config/taskfiles/common/Taskfile-code.yml @@ -0,0 +1,25 @@ +--- +version: '3' + +tasks: + count: + deps: + - :install:software:tokei + desc: Display a chart detailing the lines of code for each language used + log: + error: Encountered error while running `tokei . --exclude .common .modules` + start: Analyzing project for code-language statistics + cmds: + - tokei . --exclude .common .modules + + find:todo: + deps: + - :install:npm:leasot + desc: Scan code base for TODOs and FIXMEs + vars: + LEASOT_IGNORE: .common .modules .venv node_modules venv + log: + error: Encountered error while running `leasot --ignore {{.LEASOT_IGNORE}} {{.CLI_ARGS}}` + start: Scanning project for TODOs and FIXMEs + cmds: + - leasot --ignore {{.LEASOT_IGNORE}} {{.CLI_ARGS}} diff --git a/.config/taskfiles/common/Taskfile-start.yml b/.config/taskfiles/common/Taskfile-start.yml new file mode 100644 index 00000000..c7d54b8b --- /dev/null +++ b/.config/taskfiles/common/Taskfile-start.yml @@ -0,0 +1,176 @@ +--- +version: '3' + +vars: + ENV_GOBIN: + sh: mktemp + ENV_GOROOT: + sh: mktemp + ENV_PATH: + sh: mktemp + +env: + PREPARE_PROJECT: 'true' + UPDATE_GIT_REPOS: 'true' + +tasks: + ci: + run: once + cmds: + - task: setup + status: + - '[ -n "$CI" ]' + + commit: + run: once + cmds: + - task: :git:commit:automated + - task: :git:push:all + - task: :ci:synchronize + + dev: + cmds: + - task: setup + status: + - '([ -z "$CI" ] && [ -f /.dockerenv ])' + + devcontainer: + cmds: + - task: :install:modules:local + - task: :install:python:requirements:poetry + - task: :git:remotes + status: + - '[ ! -f /.devcontainer ]' + + docker: + cmds: + - task: docker:taskfile + status: + - '[ "{{.DOCKER_BUILDING}}" != "true" ]' + + docker:taskfile: + cmds: + - task: :deps:run:parallel + status: + - '[ ! -f Taskfile.yml ]' + + init: + run: once + log: + error: Error encountered while initializing the update process + start: Initializing the update process + success: Finished initializing the update process + cmds: + - task: :ci:before + - task: :ci:submodules + - task: :repair + + prepare: + run: once + cmds: + - task: :prepare + status: + - '[ "$PREPARE_PROJECT" != "true" ] || [ -f /.devcontainer ]' + + prereqs: + env: + PATH: + sh: | + if type brew &> /dev/null; then + echo "$PATH" + else + . "$HOME/.profile" &> /dev/null || true + echo "$PATH" + fi + run: once + log: + error: Error encountered while installing pre-requisite software + start: Ensuring pre-requisite software is installed and added to the PATH + success: Pre-requisite software is installed + cmds: + - task: :install:software:jq + - task: :install:software:yq + - task: :install:software:node + - task: :install:software:poetry + - task: :install:software:go + - task: :install:software:task + + register:env: + cmds: + - cmd: | + . "$HOME/.profile" &> /dev/null || true + echo "$PATH" > '{{.ENV_PATH}}' + echo "$GOBIN" > '{{.ENV_GOBIN}}' + echo "$GOROOT" > '{{.ENV_GOROOT}}' + ignore_error: true + status: + - type jq &> /dev/null + - type yq &> /dev/null + - type node &> /dev/null + - type go &> /dev/null + - type poetry &> /dev/null + - '[ -n "$GOBIN" ]' + - '[ -n "$GOROOT" ]' + + setup: + run: once + cmds: + - task: :install:software:brew + - task: prereqs + - task: register:env + vars: + ENV_GOBIN: '{{.ENV_GOBIN}}' + ENV_GOROOT: '{{.ENV_GOROOT}}' + ENV_PATH: '{{.ENV_PATH}}' + - task: setup:start + env: + GOBIN: + sh: cat '{{.ENV_GOBIN}}' + GOROOT: + sh: cat '{{.ENV_GOROOT}}' + PATH: + sh: cat '{{.ENV_PATH}}' + + setup:start: + run: once + cmds: + - task: setup:start:ci + - task: setup:start:dev + + setup:start:ci: + run: once + cmds: + - task: init + - task: prepare + - task: update + - task: :ci:commit + - task: :ci:synchronize + status: + - '[ -z "$CI" ]' + + setup:start:dev: + run: once + cmds: + - task: init + - task: prepare + - task: update + - task: commit + - task: devcontainer + status: + - '[ -n "$CI" ]' + + update: + run: once + cmds: + - task: :upstream:{{.PROJECT_TYPE}} + - task: update:git + status: + - '[ "$UPDATE_PROJECT" == "true" ] || [ -f /.devcontainer ]' + + update:git: + run: once + cmds: + - task: :git:update + status: + - '[ -n "$CI" ] || [ "{{.DOCKER_BUILDING}}" == "true" ]' + - '[ "$UPDATE_GIT_REPOS" == "false" ] || [ "{{.DOCKER_BUILDING}}" == "true" ]]' diff --git a/.config/taskfiles/common/Taskfile-update.yml b/.config/taskfiles/common/Taskfile-update.yml new file mode 100644 index 00000000..f5b95553 --- /dev/null +++ b/.config/taskfiles/common/Taskfile-update.yml @@ -0,0 +1,373 @@ +--- +version: '3' + +tasks: + all: + deps: + - ':{{if eq .REPOSITORY_TYPE "packer"}}packer:update:descriptions{{else}}donothing{{end}}' + - ':{{if eq .REPOSITORY_TYPE "docker"}}docker:update:sort{{else}}donothing{{end}}' + - files + - :python:requirementstxt + cmds: + - task: all:continue + + all:continue: + deps: + - all:continue:remotes + - all:docs + - ':{{if eq .REPOSITORY_SUBTYPE "playbook"}}ansible:playbook:collections:download{{else}}donothing{{end}}' + + all:continue:remotes: + cmds: + - task: upstream:remotes + - task: :git:remotes + - task: ':{{if eq .REPOSITORY_SUBTYPE "playbook"}}ansible:playbook:remotes{{else}}donothing{{end}}' + + all:docs: + cmds: + - task: '{{if eq .REPOSITORY_TYPE "common"}}:donothing{{else}}{{if eq .REPOSITORY_TYPE "documentation"}}:donothing{{else}}all:docs:generate{{end}}{{end}}' + + all:docs:generate: + deps: + - contributing + - readme + + ansible: + deps: + - :ansible:populate:dependencies + - :ansible:populate:meta + - :ansible:update:galaxy-id + + contributing: + deps: + - :install:npm:prettier + - :install:npm:readme + vars: + CONTRIB_TEMPLATE: + sh: | + if [ -f ".config/docs/blueprint-contributing-{{.REPOSITORY_SUBTYPE}}.md" ]; then + echo ".config/docs/blueprint-contributing-{{.REPOSITORY_SUBTYPE}}.md" + else + echo ".config/docs/blueprint-contributing.md" + fi + log: + error: Encountered error while generating `docs/CONTRIBUTING.md` + start: Generating `docs/CONTRIBUTING.md` using document partials + success: Successfully generated `docs/CONTRIBUTING.md` + cmds: + - mkdir -p docs + - > + readme generate --silent --headingPrefix '{}' --config .variables.json + --input "{{.CONTRIB_TEMPLATE}}" --output docs/CONTRIBUTING.md + - task: markdown:scrub + vars: + SCRUB_FILE: docs/CONTRIBUTING.md + - prettier --write docs/CONTRIBUTING.md > /dev/null + sources: + - docs/CONTRIBUTING.md + - .config/docs/**/* + - .variables.json + preconditions: + - sh: test -f .variables.json + msg: The `.variables.json` file is not present. + - sh: type readme > /dev/null + msg: '`@appnest/readme` is not installed globally.' + + files: + deps: + - files:ansible:keywords:sync + - files:dockerignore + - files:docs + - files:go:dummy + - files:husky + - files:initctl + log: + error: Encountered error while updating miscellaneous files + start: Updating miscellaneous files + success: Updated miscellaneous files + + files:ansible:keywords:sync: + cmds: + - task: :{{if eq .REPOSITORY_SUBTYPE "role"}}ansible:keywords:sync{{else}}donothing{{end}} + + files:dockerignore: + cmds: + - | + if [ -f .dockerignore ]; then + TMP="$(mktemp)" + echo '# To keep changes, only add items to end of file' > "$TMP" + cat .gitignore >> "$TMP" + if [ ! -f .config/dockerignore ]; then + curl -sSL https://gitlab.com/megabyte-labs/common/shared/-/raw/master/common/.config/dockerignore > .config/dockerignore + fi + cat .config/dockerignore >> "$TMP" + echo '# Saved entries below' >> "$TMP" + cat .dockerignore | sed -n '/# Saved entries below/,$p' | sed 's/# Saved entries below//' | awk 'NF' >> "$TMP" + mv "$TMP" .dockerignore + elif [ '{{.PROJECT_TYPE}}' == 'project' ]; then + if [ -f .config/dockerignore ]; then + rm .config/dockerignore + fi + fi + + files:docs: + cmds: + - mkdir -p docs/partials && echo '' > docs/partials/guide.md + status: + - '[ -f docs/partials/guide.md ]' + + files:go:dummy: + cmds: + - echo -e 'package main\n\nfunc main() {\n}\n' > .config/dummy.go + status: + - '[ "{{.REPOSITORY_SUBTYPE}}" != "cli" ] || [ "{{.REPOSITORY_TYPE}}" == "go" ]' + + files:husky: + vars: + CONTAINER: + sh: if [ "{{.DOCKER_ENVIRONMENT}}" == "true" ]; then echo "docker"; fi + cmds: + - task: :common:{{if eq .CONTAINER "docker"}}husky:ci{{else}}husky{{end}} + + files:initctl: + cmds: + - | + if [ -f initctl ] && [ -f .config/initctl ]; then + cp .config/initctl initctl + .config/log info 'Ensured that the `initctl` polyfill file is synchronized with the upstream version' + fi + + finish: + cmds: + - cmd: git push + ignore_error: true + - task: :ci:synchronize + + init: + log: + error: Encountered error while initializing project + start: Ensuring project is initialized + cmds: + - | + if ! test -f package.json; then + echo '{"blueprint": {}}' > package.json + fi + status: + - test -f package.json + + man-page: + deps: + - :install:modules:local + - :install:npm:remark + - :install:npm:prettier + - :install:npm:readme + vars: + MAN_TEMPLATE: '{{.REPOSITORY_SUBTYPE}}-blueprint-man.md' + cmds: + - | + if test -f ".config/docs/{{.MAN_TEMPLATE}}"; then + readme generate --headingPrefix '{}' --silent --config .variables.json --input ".config/docs/{{.MAN_TEMPLATE}}" --output MAN.md + prettier --write MAN.md > /dev/null + .config/log info 'Generated MAN.md' + mkdir -p dist + remark --use man MAN.md --output dist/man + .config/log success 'Converted MAN.md to man page located in `dist/man`' + rm MAN.md + fi + sources: + - .config/docs/**/* + - .variables.json + - dist/man + preconditions: + - sh: test -f .variables.json + msg: The `.variables.json` file is not present. + + markdown:scrub: + vars: + DIVIDER_SRC: + REGEX: .*https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/.*.png)][(]\(.*\)[)].*$ + cmds: + - task: markdown:scrub:{{OS}} + vars: + DIVIDER_SRC: '{{.DIVIDER_SRC}}' + REGEX: '{{.REGEX}}' + SCRUB_FILE: '{{.SCRUB_FILE}}' + + markdown:scrub:darwin: /usr/bin/sed -i .bak 's^{{.REGEX}}^{{.DIVIDER_SRC}}^g' {{.SCRUB_FILE}} && rm {{.SCRUB_FILE}}.bak + + markdown:scrub:linux: sed -i 's^{{.REGEX}}^{{.DIVIDER_SRC}}^g' {{.SCRUB_FILE}} + + modules: + deps: + - :install:software:git + summary: | + # Ensure all submodules in `.modules/` are updated + + Some of our projects include submodules. These submodules are generally + stored in the `.modules/` folder in the root of the project. Some projects + might symlink files to one of the submodules stored in the `.modules/` folder. + If you are ok with the risk, you can use this task to update all the submodules + to the latest on the remote's master branch. + log: + error: Failed to ensure submodules in the `.modules/` folder are up-to-date + start: Ensuring submodules in the `.modules/` folder are up-to-date + success: Successfully ensured submodules in the `.modules/` folder are up-to-date + cmds: + - git submodule update --init --recursive + - | + ROOT_DIR="$PWD" + if ls .modules/*/ > /dev/null 2>&1; then + for SUBMODULE_PATH in .modules/*/; do + cd $SUBMODULE_PATH + DEFAULT_BRANCH=$(git remote show origin | grep 'HEAD branch' | cut -d' ' -f5) + git reset --hard HEAD + git checkout "$DEFAULT_BRANCH" + git pull origin "$DEFAULT_BRANCH" --ff-only || true + cd "$ROOT_DIR" + done + .config/log success 'Ensured submodules in the `.modules` folder are pointing to the master branch' + fi + status: + - '! ls .modules/*/ > /dev/null 2>&1' + + project: + log: + start: Pulling `master` if `origin` is defined + cmds: + - | + if [ -d .git ] && git branch -r | grep origin > /dev/null; then + git pull origin master --no-rebase || true + fi + + readme: + deps: + - ':{{if eq .REPOSITORY_TYPE "ansible"}}ansible:collection-dependencies:markdown{{else}}donothing{{end}}' + - ':{{if eq .REPOSITORY_SUBTYPE "role"}}ansible:mod-ansible-autodoc{{else}}donothing{{end}}' + - :install:npm:prettier + - :install:npm:readme + vars: + README_TEMPLATE: blueprint-readme-{{.REPOSITORY_SUBTYPE}}.md + log: + error: Error encountered while generating `README.md` + start: Generating `README.md` + success: Generated README.md successfully + cmds: + - > + readme generate --headingPrefix '{}' --silent --config .variables.json + --input ".config/docs/{{.README_TEMPLATE}}" + - task: markdown:scrub + vars: + SCRUB_FILE: README.md + - task: ':{{if eq .REPOSITORY_TYPE "packer"}}packer:update:readme{{else}}donothing{{end}}' + - prettier --write README.md > /dev/null + sources: + - .autodoc/* + - .config/docs/**/* + - .variables.json + - README.md + preconditions: + - sh: 'test -f .config/docs/{{.README_TEMPLATE}}' + msg: 'The README.md template file is not present at `.config/docs/{{.README_TEMPLATE}}`.' + - sh: test -f .variables.json + msg: The `.variables.json` file is not present. + + repositories: + cmds: + - task: modules + - task: project + + start: + deps: + - :common:requirements + - variables + + update: + deps: + - ':{{if eq .REPOSITORY_SUBTYPE "role"}}common:update:ansible{{else}}donothing{{end}}' + - ':{{if eq .REPOSITORY_TYPE "packer"}}{{if eq .REPOSITORY_SUBTYPE "server"}}packer:latestos{{else}}donothing{{end}}{{else}}donothing{{end}}' + - init + - repositories + summary: | + # Refresh project with latest upstream code and ensure project files are up-to-date + + This task will pull the latest upstream code and overwrite any files that are out of date. + Ideally, you should run this task often to ensure there are no merge conflicts and to + ensure you are using the latest production settings. This task is also run by CI so + normally if you pull the latest changes, you should already have the updates that this + task applies. + + **Example usage:** + `task update` + run: once + cmds: + - task: start + - task: all + - | + if [ -f Dockerfile ]; then + task {{if eq .REPOSITORY_TYPE "docker"}}docker:update:labels{{else}}donothing{{end}} + fi + + upstream:remotes: + deps: + - :install:software:jq + log: + error: Error adding upstream git remotes + start: Ensuring upstream git remotes are added (if applicable) + success: Successfully added upstream git remotes + cmds: + - | + COUNT="$(jq -r '.blueprint.upstreamRemotes | length' package.json)" + for i in $(seq $COUNT); do + REMOTE="$(jq -r --arg count "$(("$i" - 1))" '.blueprint.upstreamRemotes[($count | tonumber)].remote' package.json)" + URL="$(jq -r --arg count "$(("$i" - 1))" '.blueprint.upstreamRemotes[($count | tonumber)].url' package.json)" + if [ "$REMOTE" != 'null' ] && [ "$URL" != 'null' ]; then + if git remote | grep "$REMOTE"; then + git remote remove $REMOTE + fi + git remote add $REMOTE $URL + else + .config/log warn '`blueprint.upstreamRemotes` objects should have the `remote`, `url`, and `branch` keys' + fi + done + status: + - '[ "$(jq -r ".blueprint.upstreamRemotes | length" package.json)" == "0" ]' + + upstream:remotes:pull: + deps: + - :install:software:jq + log: + error: Error pulling latest from upstream remotes + start: Pulling latest changes from upstream remotes + success: Successfully pulled latest changes from upstream remotes + cmds: + - | + COUNT="$(jq -r '.blueprint.upstreamRemotes | length' package.json)" + for i in $(seq $COUNT); do + REMOTE="$(jq -r --arg count "$(("$i" - 1))" '.blueprint.upstreamRemotes[($count | tonumber)].remote' package.json)" + URL="$(jq -r --arg count "$(("$i" - 1))" '.blueprint.upstreamRemotes[($count | tonumber)].url' package.json)" + BRANCH="$(jq -r --arg count "$(("$i" - 1))" '.blueprint.upstreamRemotes[($count | tonumber)].branch' package.json)" + if [ "$REMOTE" != 'null' ] && [ "$URL" != 'null' ] && [ "$BRANCH" != 'null' ]; then + if git remote | grep "$REMOTE"; then + git remote remove $REMOTE + fi + git remote add $REMOTE $URL + git pull $REMOTE $BRANCH + else + .config/log warn '`blueprint.upstreamRemotes` objects must have the `remote`, `url`, and `branch` keys' + fi + done + + variables: + deps: + - :install:software:jq + cmds: + - task: :upstream:variables + vars: + INPUT_FILE: .config/variables.json + OUTPUT_FILE: .variables.json + - task: ':{{if eq .REPOSITORY_TYPE "go"}}{{if eq .REPOSITORY_SUBTYPE "cli"}}go:help{{else}}donothing{{end}}{{else}}donothing{{end}}' + - task: ':{{if eq .REPOSITORY_TYPE "packer"}}packer:update:variables{{else}}donothing{{end}}' + - task: ':{{if eq .REPOSITORY_TYPE "ansible"}}ansible:update:variables{{if eq .REPOSITORY_SUBTYPE "playbook"}}:playbook{{end}}{{else}}donothing{{end}}' + - task: ':{{if eq .REPOSITORY_SUBTYPE "tap"}}install:tap:scan{{else}}donothing{{end}}' + - task: ':{{if eq .REPOSITORY_SUBTYPE "scoop"}}install:scoop:scan{{else}}donothing{{end}}' diff --git a/.config/taskfiles/common/Taskfile-util.yml b/.config/taskfiles/common/Taskfile-util.yml new file mode 100644 index 00000000..2c36d6a6 --- /dev/null +++ b/.config/taskfiles/common/Taskfile-util.yml @@ -0,0 +1,88 @@ +--- +version: '3' + +vars: + MAP_FILE: .cache/task-map/Taskfile.json + +tasks: + task:map:generate: + deps: + - :install:software:jq + - :install:software:yq + vars: + MAP_FILE_QUERY: >- + [.includes | with_entries(select(.value.optional == true)) | to_entries | .[] | + { "key": .key, "value": .value.taskfile }] + (.includes | with_entries(select(.value.optional == null)) | + to_entries) + { "key": "", "value": "Taskfile.yml" } + log: + error: Encountered error while generating Taskfile task map array + start: Generating Taskfile task map array + success: Successfully generated Taskfile task map array at `{{.MAP_FILE}}` + cmds: + - | + COMBINED="$(mktemp)" + echo '[]' > "$COMBINED" + TMP_COMBINED="$(mktemp)" + TMP_JQ="$(mktemp)" + TMP_YQ="$(mktemp)" + mkdir -p "$(dirname '{{.MAP_FILE}}')" + yq e -o=j '{{.MAP_FILE_QUERY}}' Taskfile.yml > "{{.MAP_FILE}}" + jq -c '.[]' "{{.MAP_FILE}}" | while read i; do + PREFIX="$(echo "$i" | jq -r '.key')" + FILE="$(echo "$i" | jq -r '.value')" + if [ -f "$FILE" ]; then + yq e -o=j '.tasks' "$FILE" > "$TMP_YQ" + jq --arg prefix "$PREFIX:" '[to_entries | .[] | .key as $key | .value | ._task? = $prefix + $key]' "$TMP_YQ" > "$TMP_JQ" + jq -s '.[0] + .[1]' "$COMBINED" "$TMP_JQ" > "$TMP_COMBINED" + cp "$TMP_COMBINED" "$COMBINED" + fi + done + mv "$COMBINED" '{{.MAP_FILE}}' + sources: + - .config/taskfiles/**/*.yml + - Taskfile.yml + + task:tag:command: + deps: + - task:map:generate + vars: + TAG: '{{if .TAG}}{{.TAG}}{{else}}{{.CLI_ARGS}}{{end}}' + TEMPLATE: 'task $i && ' + log: + error: Encountered error while building Taskfile task list from tag named `{{.TAG}}` + start: Selecting by Taskfile task tag named `{{.TAG}}` + success: Wrote task tag command to `tag-command.txt` + cmds: + - | + RESULT="" + jq -cr '(.. | select(.tags?[]? == "{{.TAG}}")) | ._task' {{.MAP_FILE}} | while read i; do + RESULT="$RESULT{{.TEMPLATE}}" + done + if [ '{{.TEMPLATE}}' == 'task $i && ' ]; then + RESULT="$(echo $RESULT | sed 's/ && $//')" + fi + echo "$RESULT" > tag-command.txt + + task:tag:deps: + deps: + - task:map:generate + vars: + TAG: '{{if .TAG}}{{.TAG}}{{else}}{{.CLI_ARGS}}{{end}}' + TEMPLATE: '- task: $i\n' + log: + error: Encountered error while building Taskfile task deps file from tag named `{{.TAG}}` + start: Selecting by Taskfile task tag named `{{.TAG}}` for deps file + success: Completed `Taskfile.yml` update logic for deps task named `deps:{{.TAG}}` + cmds: + - | + RESULT="" + COUNT="0" + jq -r '(.. | select(.tags?[]? == "{{.TAG}}")) | ._task' {{.MAP_FILE}} | while read i; do + COUNT="$(("$COUNT" + 1))" + RESULT="$RESULT{{.TEMPLATE}}" + done + if [ "$COUNT" -gt 0 ]; then + ARR="$(echo -e "$RESULT")" yq e -i '.tasks["deps:run:parallel"].cmds = env(ARR)' Taskfile.yml + else + .config/log warn 'There were no results so nothing was added to the `Taskfile.yml`' + fi diff --git a/.config/taskfiles/common/Taskfile.yml b/.config/taskfiles/common/Taskfile.yml new file mode 100644 index 00000000..b9e2023d --- /dev/null +++ b/.config/taskfiles/common/Taskfile.yml @@ -0,0 +1,236 @@ +--- +version: '3' + +vars: + DOCKER_BUILDING: + sh: | + if [ -f /.dockerenv ] || [ "$container" == 'docker' ]; then + if [ -n "$BUILD_DATE" ]; then + echo "true" + fi + fi + DOCKER_ENVIRONMENT: + sh: | + if [ -f /.dockerenv ] || [ "$container" == 'docker' ]; then + echo "true" + fi + ENVIRONMENT_TYPE: + sh: | + if [ -f /.dockerenv ] || [ "$container" == 'docker' ]; then + if [ -n "$BUILD_DATE" ]; then + echo "docker-build" + else + echo "docker" + fi + else + echo "unknown" + fi + +tasks: + clean: + deps: + - :install:software:rsync + vars: + RANDOM_STRING: + sh: openssl rand -hex 14 + log: + error: Error removing `{{.CLEAN_TARGETS}}` + start: Removing `{{.CLEAN_TARGETS}}` + success: Removed `{{.CLEAN_TARGETS}}` + cmds: + - mkdir -p '/tmp/{{.RANDOM_STRING}}' + - mkdir -p '/tmp/{{.RANDOM_STRING}}-empty' + - | + for TMP_FILE in {{.CLEAN_TARGETS}}; do + if [ -d "$TMP_FILE" ]; then + mv "$TMP_FILE" "/tmp/{{.RANDOM_STRING}}/$TMP_FILE" 2> /dev/null + (rsync -a --delete '/tmp/{{.RANDOM_STRING}}-empty' "/tmp/{{.RANDOM_STRING}}/$TMP_FILE" && rm -rf "/tmp/{{.RANDOM_STRING}}-$TMP_FILE") & + fi + done + wait + + commit: + deps: + - :install:modules:local + - :install:npm:commitizen + - :install:npm:commitlint + log: + start: Initiating commit dialog + cmds: + - . ./.config/husky/pre-commit + - exec < /dev/tty && git cz --hook || true + + husky: + deps: + - :install:modules:local + - :install:npm:commitizen + - :install:npm:commitlint + - :install:npm:husky + cmds: + - task: husky:install + - task: husky:permissions + status: + - '[ "{{.DOCKER_ENVIRONMENT}}" == "true" ] || [ "$SEMANTIC_RELEASE" == "true" ]' + + husky:ci: + cmds: + - task: husky:permissions + status: + - '[ "{{.DOCKER_ENVIRONMENT}}" != "true" ] || [ "$SEMANTIC_RELEASE" == "true" ]' + + husky:install: + deps: + - :install:npm:husky + log: + error: Error installing Husky git hooks + start: Installing Husky git hooks + cmds: + - | + if [ -d .git ] && [ "${container:=}" != 'docker' ]; then + husky install .config/husky > /dev/null + .config/log success 'Installed Husky git hooks' + else + .config/log warn 'Cannot run `husky install` because there is no `.git/` folder (or this is a Docker container)' + fi + status: + - '[ ! -d .git ] || [ "{{.DOCKER_ENVIRONMENT}}" == "true" ]' + + husky:permissions: + log: + error: Encountered error while ensuring git hook scripts have the appropriate permissions + start: Ensuring git hook scripts are executable + success: Ensured git hook scripts are executable + cmds: + - | + chmod +x .config/log + if [ -f .config/log ]; then chmod +x .config/log; fi + while read PATHH; do + chmod +x "$PATHH" + done < <(find ./.config/husky/* -maxdepth 0 -type f) + .config/log success 'Ensured git hook scripts are executable' + + prepare-release: + deps: + - :install:npm:standard-version + summary: | + # Prepare a new release + + > NOTE: This is no longer used, in favor of semantic-release which handles publishing during CI/CD + + This task performs the following tasks in order: + + 1. Ensures the project is up-to-date with the latest upstream changes + 2. Lints the project with all available linters + 3. Updates the version of the project in the `package.json` file and other relevant files + 4. Add the appropriate details to the CHANGELOG.md file + cmds: + - task: update + - task: :lint:all + - standard-version --no-verify + + repair: + vars: + MODEL_TMP: + sh: mktemp + cmds: + - | + curl -sSL '{{.MODEL_TASKFILE}}' > '{{.MODEL_TMP}}' + export ZXXXY="$(yq e '.' {{.MODEL_TMP}})" + yq e -i '. | .vars = (env(ZXXXY) | .vars) | .env = (env(ZXXXY) | .env)' Taskfile.yml + yq eval-all -i 'select(fileIndex == 0) * select(fileIndex == 1)' '{{.MODEL_TMP}}' Taskfile.yml + + requirements: + deps: + - :install:modules:local + - :install:python:requirements + summary: | + # Ensure local dependencies are installed + + This task ensures local dependencies are installed. These include dependencies defined in `package.json` + and `pyproject.toml` (if the project is a Python project). + run: once + + reset: + cmds: + - task: clean + - task: update + + reset:force: + deps: + - software:git + log: + error: Failed to reset project back to its HEAD + start: Forcing project to reset aggressively + success: Successfully reset project back to its HEAD + cmds: + - task: clean + - git reset --hard HEAD + - git clean -fxd :/ + - git checkout master + - git pull origin master --no-rebase + + shell: + deps: + - :install:software:docker + cmds: + - task: shell:{{if .CLI_ARGS}}cli{{else}}prompt{{end}} + + shell:cli: + vars: + DC: '{{.CLI_ARGS}}' + WORKDIR: + sh: basename $PWD + log: + start: Starting a Docker container for `{{.DOCKER_CONTAINER}}` and attaching to `/bin/bash` + cmds: + - .config/log info 'Running `docker run -it -v "$PWD:/{{.WORKDIR}}" -w /{{.WORKDIR}} --rm megabytelabs/ansible-molecule-{{.DC}}:latest /bin/bash`' + - cmd: docker run -it -v "$PWD:/{{.WORKDIR}}" -w /{{.WORKDIR}} --rm megabytelabs/ansible-molecule-{{.DC}}:latest /bin/bash + ignore_error: true + + shell:prompt: + vars: + MARKDOWN: | + # Launch a Docker Shell Environment + + Open a shell session quickly, safely, and easily using Docker. Select an option from + the prompt below to download and shell into a Docker environment. The environment + will be automatically deleted after you exit the terminal session. + cmds: + - task: :log:markdown + vars: + MARKDOWN: '{{.MARKDOWN}}' + - task: shell:prompt:question + + shell:prompt:question: + interactive: true + prompt: + type: select + message: Which operating system would you like to open up a terminal session with? + options: + - Archlinux + - CentOS 7 + - CentOS 8 + - Debian 9 + - Debian 10 + - Fedora 33 + - Fedora 34 + - Ubuntu 18.04 + - Ubuntu 20.04 + - Ubuntu 21.04 + answer: + cmds: + - task: shell:cli + env: + DC: + sh: echo '{{.ANSWER}}' | sed 's/ /-/g' | tr '[:upper:]' '[:lower:]' + + start: + run: once + cmds: + - task: :common:start:docker + - task: :common:start:ci + - task: :common:start:dev + + update: + deps: + - :common:update:update diff --git a/.config/taskfiles/docker/Taskfile-build.yml b/.config/taskfiles/docker/Taskfile-build.yml new file mode 100644 index 00000000..eca503b8 --- /dev/null +++ b/.config/taskfiles/docker/Taskfile-build.yml @@ -0,0 +1,190 @@ +--- +version: '3' + +tasks: + fat: + cmds: + - task: :docker:update:version + - task: fat:build + + fat:build: + deps: + - :install:software:docker + - :install:software:jq + desc: Build a normal Docker container from the Dockerfile + summary: | + # Build Docker Container from Dockerfile + + This task builds a normal Docker container. It expects the Dockerfile to be + named `Dockerfile` and for it to be in the root of the repository. + + For more information on building Docker containers, see + [Docker's build guide](https://docs.docker.com/engine/reference/commandline/build/). + vars: + BUILD_DATE: + sh: type git &> /dev/null && git show -s --format=%cI + REVISION: + sh: type git &> /dev/null && git rev-parse HEAD + SLUG: + sh: jq -r '.blueprint.slug' package.json + TAG_POST: '{{if .CLI_ARGS}}{{if ne .DOCKER_BASE_TAG .CLI_ARGS}}-{{.CLI_ARGS}}{{end}}{{end}}' + VERSION: + sh: jq -r '.version' package.json + run: when_changed + hide: + sh: '[ ! -f Dockerfile ]' + log: + error: Error building `{{.DOCKER_IMAGE}}:{{.VERSION}}` + start: Building Docker container `{{.DOCKER_IMAGE}}:{{.VERSION}}` (also tagged as latest) + success: Successfully built Docker container named `{{.DOCKER_IMAGE}}:{{.VERSION}}` + cmds: + - .config/log info 'Running `docker build --build-arg BUILD_DATE={{.BUILD_DATE}} --build-arg REVISION={{.REVISION}} --build-arg VERSION={{.VERSION}} + --tag {{.DOCKER_IMAGE}}:latest{{.TAG_POST}} + {{if (contains "codeclimate" .CLI_ARGS)}} --tag codeclimate/codeclimate-{{.SLUG}}{{end}}{{if .CLI_ARGS}} + --target {{.CLI_ARGS}}{{end}} .`' + - docker build --build-arg BUILD_DATE={{.BUILD_DATE}} --build-arg REVISION={{.REVISION}} --build-arg VERSION={{.VERSION}} + --tag {{.DOCKER_IMAGE}}:latest{{.TAG_POST}} + {{if (contains "codeclimate" .CLI_ARGS)}} --tag codeclimate/codeclimate-{{.SLUG}}{{end}} + {{if .CLI_ARGS}} --target {{.CLI_ARGS}}{{end}} . + + slim: + deps: + - :install:software:docker + - :install:software:docker-slim + desc: Build a slim version of the Docker image + hide: + sh: '[ ! -f Dockerfile ]' + summary: | + # Build a compressed and secure container from the `regular` Docker image + + This task takes the Docker container generated by running `task build:normal` and + compresses it using DockerSlim. Compressing it actually makes the container more + secure too because there is a smaller attack surface with unnecessary tools and services + removed. + + For more information, see [DockerSlim's GitHub page](https://github.com/docker-slim/docker-slim). + cmds: + - task: fat + - task: slim:command + - task: slim:finish + + slim:command: + vars: + DIND_MOUNT: + sh: | + if [ "$(jq -r '.blueprint.dockerInDocker' package.json)" == 'true' ]; then + echo "--mount {{.DIND_MOUNT_MAPPING}} " + fi + SLIM_COMMAND: + sh: | + COMMAND_TYPE="$(jq -r '.blueprint.dockerSlimCommand | type' package.json)" + if [ "$COMMAND_TYPE" == 'string' ]; then + echo "$(jq -r '.blueprint.dockerSlimCommand' package.json)" + else + COMMAND="$(jq --arg type "{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}$(jq -r '.blueprint.slug' package.json){{end}}" \ + -r '.blueprint.dockerSlimCommand[$type]' package.json)" + if [ "$COMMAND" != 'null' ]; then + echo "$(jq --arg type "{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}$(jq -r '.blueprint.slug' package.json){{end}}" \ + -r '.blueprint.dockerSlimCommand[$type]' package.json)" + fi + fi + TAG_POST: '{{if .CLI_ARGS}}{{if ne .DOCKER_BASE_TAG .CLI_ARGS}}-{{.CLI_ARGS}}{{end}}{{end}}' + log: + error: Error building `{{.DOCKER_IMAGE}}:slim` with `docker-slim` + start: Building Docker container named `{{.DOCKER_IMAGE}}:slim` + success: Successfully built Docker container named `{{.DOCKER_IMAGE}}:slim` + cmds: + - > + .config/log info 'Running `docker-slim build {{.DIND_MOUNT}}--tag {{.DOCKER_IMAGE}}:slim{{.TAG_POST}} + {{if (contains "codeclimate" .CLI_ARGS)}}--tag codeclimate/codeclimate-{{.SLUG}}:slim {{end}} + {{.SLIM_COMMAND | replace "'" "\""}} {{.DOCKER_IMAGE}}:latest{{.TAG_POST}}`' + - > + docker-slim build {{.DIND_MOUNT}}--tag {{.DOCKER_IMAGE}}:slim{{.TAG_POST}} + {{if (contains "codeclimate" .CLI_ARGS)}}--tag codeclimate/codeclimate-{{.SLUG}}:slim {{end}} + {{.SLIM_COMMAND | replace "'" "\""}} {{.DOCKER_IMAGE}}:latest{{.TAG_POST}} + + slim:finish: + deps: + - slim:prettier + - slim:sizes + + slim:prettier: + deps: + - :install:npm:prettier + vars: + IMAGE_SLUG: + sh: jq -r '.blueprint.slug' package.json + log: + error: Failed to format `slim.report.json` with Prettier + start: Formatting `slim.report.json` with Prettier + success: Formatted `slim.report.json` with Prettier + cmds: + - prettier --write slim.report.json > /dev/null + - mv slim.report.json docs/slim{{if .CLI_ARGS}}.{{.CLI_ARGS}}{{end}}.report.json + + slim:sizes: + cmds: + - task: slim:sizes:assets + - task: slim:sizes:calculate + - task: slim:sizes:clean + + slim:sizes:assets: + deps: + - :install:software:docker + - :install:software:gzip + vars: + SLUG: + sh: echo "{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}$(jq -r '.blueprint.slug' package.json){{end}}" + TAG_POST: '{{if .CLI_ARGS}}{{if ne .DOCKER_BASE_TAG .CLI_ARGS}}-{{.CLI_ARGS}}{{end}}{{end}}' + log: + error: Encountered error while generating compressed Docker image tar file + start: Generating compressed Docker image tar file(s) + success: Successfully generated compressed Docker image tar file(s) + cmds: + - | + function zipUp() { + docker save "{{.DOCKER_IMAGE}}:$1{{.TAG_POST}}" > "$1{{.TAG_POST}}.tar" + gzip -f "$1{{.TAG_POST}}.tar" + } + zipUp "latest" & + zipUp "slim" & + wait + + slim:sizes:calculate: + deps: + - :install:software:jq + vars: + DOCKER_IMAGE_SLUG: + sh: echo "{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}$(jq -r '.blueprint.slug' package.json){{end}}" + PREVIOUS_REGULAR_SIZE: + sh: jq --arg slug '{{.DOCKER_IMAGE_SLUG}}' -r '.blueprint.dockerLatestSize[$slug]' package.json + PREVIOUS_SLIM_SIZE: + sh: jq --arg slug '{{.DOCKER_IMAGE_SLUG}}' -r '.blueprint.dockerSlimSize[$slug]' package.json + REGULAR_SIZE: + sh: stat {{if (eq OS "darwin")}}-f%z{{else}}-c%s{{end}} latest{{if .CLI_ARGS}}{{if ne .DOCKER_BASE_TAG .CLI_ARGS}}-{{.CLI_ARGS}}{{end}}{{end}}.tar.gz | numfmt --to iec + SLIM_SIZE: + sh: stat {{if (eq OS "darwin")}}-f%z{{else}}-c%s{{end}} slim{{if .CLI_ARGS}}{{if ne .DOCKER_BASE_TAG .CLI_ARGS}}-{{.CLI_ARGS}}{{end}}{{end}}.tar.gz | numfmt --to iec + log: + error: Failed to acquire / inject `:slim` image file size information into `package.json` blueprint data + start: Injecting `:slim` image file size into `package.json` blueprint data + cmds: + - | + TMP="$(mktemp)" + jq --arg a '{{.SLIM_SIZE}}' --arg b '{{.REGULAR_SIZE}}' --arg slug '{{.DOCKER_IMAGE_SLUG}}' \ + '.blueprint.dockerSlimSize[$slug] = $a | .blueprint.dockerLatestSize[$slug] = $b' package.json > "$TMP" + mv "$TMP" package.json + - task: slim:sizes:clean + - task: :common:update:update + status: + - '[[ "{{.PREVIOUS_SLIM_SIZE}}" == "{{.SLIM_SIZE}}" ]]' + - '[[ "{{.PREVIOUS_REGULAR_SIZE}}" == "{{.REGULAR_SIZE}}" ]]' + + slim:sizes:clean: + cmds: + - rm -f latest*.tar.gz + - rm -f latest*.tar + - rm -f slim*.tar.gz + - rm -f slim*.tar + status: + - > + ! test -n "$(find . -maxdepth 1 -name '*.tar.gz' -print -quit)" diff --git a/.config/taskfiles/docker/Taskfile-test.yml b/.config/taskfiles/docker/Taskfile-test.yml new file mode 100644 index 00000000..931d4631 --- /dev/null +++ b/.config/taskfiles/docker/Taskfile-test.yml @@ -0,0 +1,206 @@ +--- +version: '3' + +vars: + SCENARIO: + sh: | + {{if .CLI_ARGS}}echo "{{.CLI_ARGS}}"{{else}} + SCENARIO="$(jq -r '.blueprint.dockerBaseTag' package.json)" + if [ "$SCENARIO" != 'null' ]; then + echo "$SCENARIO" + else + echo 'default' + fi + {{end}} + SLUG: + sh: jq -r '.blueprint.slug' package.json + +tasks: + codeclimate: + cmds: + - | + if [ -d ./test ]; then + TEST_FOLDERS="$(find ./test -mindepth 1 -maxdepth 1 -type d)" + if echo "$TEST_FOLDERS" | grep '/test/' &> /dev/null; then + CURR_DIR="$PWD" + for TEST_DIR in $TEST_FOLDERS; do + if [ -f "${TEST_DIR}/.codeclimate.yml" ]; then + RAN_CODECLIMATE=true + task -t "$CURR_DIR/Taskfile.yml" lint:codeclimate + fi + done + if [ -z "$RAN_CODECLIMATE" ]; then + .config/log warn 'None of the folders in the `./test` directory contained a `.codeclimate.yml` file so CodeClimate was not tested' + fi + fi + fi + status: + - '[[ "$(docker images -q {{.DOCKER_IMAGE}}:latest-codeclimate 2> /dev/null)" == "" ]]' + - '[[ "$(docker images -q {{.DOCKER_IMAGE}}:slim-codeclimate 2>/dev/null)" == "" ]]' + + container-structure-test: + desc: Runs ContainerStructureTest for Dockerfile build + hide: + sh: '[ ! -d ./test/structure ] || ! find ./test/structure -mindepth 1 -maxdepth 1 -name "*.yml" | grep .yml' + cmds: + - task: container-structure-test:latest + - task: container-structure-test:slim + + container-structure-test:latest: + deps: + - :install:software:container-structure-test + - :install:software:docker + vars: + TAG_POST: '{{if .CLI_ARGS}}{{if ne .DOCKER_BASE_TAG .CLI_ARGS}}-{{.CLI_ARGS}}{{end}}{{end}}' + log: + error: '`container-structure-test` reported error(s) when testing + `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest{{.TAG_POST}}`' + start: Testing the `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest` + Docker image with `container-structure-test{{.TAG_POST}}` + success: '`{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest{{.TAG_POST}}` was + successfully validated by `container-structure-test`' + cmds: + - container-structure-test test --image {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest{{.TAG_POST}} --config ./test/structure/{{.SCENARIO}}.yml + status: + - '[[ "$(docker images -q {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest{{.TAG_POST}} 2>/dev/null)" == "" ]] || [ ! -f ./test/structure/{{.SCENARIO}}.yml ]' + + container-structure-test:slim: + deps: + - :install:software:container-structure-test + - :install:software:docker + vars: + TAG_POST: '{{if .CLI_ARGS}}{{if ne .DOCKER_BASE_TAG .CLI_ARGS}}-{{.CLI_ARGS}}{{end}}{{end}}' + log: + error: '`container-structure-test` reported error(s) when testing + `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim{{.TAG_POST}}`' + start: Testing the `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim{{.TAG_POST}}` + Docker image with `container-structure-test` + success: '`{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim{{.TAG_POST}}` was + successfully validated by `container-structure-test`' + cmds: + - container-structure-test test --image {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim{{.TAG_POST}} --config ./test/structure/{{.SCENARIO}}.yml + status: + - '[[ "$(docker images -q {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim{{.TAG_POST}} 2> /dev/null)" == "" ]] | [ ! -f ./test/structure/{{.SCENARIO}}.yml ]' + + container-structure-tests: + cmds: + - | + if [ -d ./test/structure ]; then + TESTS="$(find ./test/structure -mindepth 1 -maxdepth 1 -name "*.yml")" + if echo "$TESTS" | grep '.yml' > /dev/null; then + for TEST_FILE in $TESTS; do + TARGET="$(echo "$TEST_FILE" | sed 's/.*\/\([^\/]*\).yml$/\1/')" + task docker:test:container-structure-test -- "$TARGET" + done + else + .config/log error 'The `./test/structure` folder is present but there are no container-structure-tests in it. Add tests \ + for each build target in the `Dockerfile` that you would like to generate an image for.' + exit 1 + fi + else + .config/log warn 'No tests are defined in `./test/structure` so no container-structure-tests will be performed' + fi + + gitlab-runner: + deps: + - :install:software:docker + - :install:software:gitlab-runner + - :install:software:jq + - :install:software:yq + env: + CI_TESTS: + sh: yq e 'keys | .[] | select( . | test("integration"))' .gitlab-ci.yml + log: + error: Error encountered while validating with the integration tests in `.gitlab-ci.yml` + start: Running integration tests defined in `.gitlab-ci.yml` + success: Successfully passed the integration tests defined in `.gitlab-ci.yml` + cmds: + - | + for CI_TEST in $CI_TESTS; do + export CI_TEST + .config/log warn 'GitLab Runner no longer supports running gitlab-runner exec so this test is no longer active (for now)' + # if [ "$(yq e '.[strenv(CI_TEST)].image' .gitlab-ci.yml)" != 'null' ]; then + # .config/log info 'Simulating the `'"$CI_TEST"'` job in `.gitlab-ci.yml`' + # gitlab-runner exec "$CI_TEST" + # .config/log success 'Successfully ran the `'"$CI_TEST"'` job in `.gitlab-ci.yml`' + # else + # .config/log warn "$CI_TEST does not appear to be an option with an image in .gitlab-ci.yml" + # fi + done + status: + - > + ! yq e 'keys | .[] | select( . | test("integration"))' .gitlab-ci.yml | grep 'integration:' > /dev/null + + output: + deps: + - :install:software:docker + desc: For each folder matching `./test/output*`, ensure slim and latest output match with default command + summary: | + # Ensure Identical Output + + This task ensures the output of the default command is identical for both the latest and slim images. + If both of the command's outputs match, then that is a good indicator that the slim image is + behaiving like the latest image. The test is performed on each project folder in the `test-output/` folder. + vars: + DIND_MOUNT: + sh: | + if [ "$(jq -r '.blueprint.dockerInDocker' package.json)" == 'true' ]; then + echo "-v {{.DIND_MOUNT_MAPPING}} " + fi + MOUNT_PATH: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}$PWD{{end}}' + OUTPUT_COMMAND: + sh: | + BP_OUTPUT="$(jq -r '.blueprint.dockerOutputCommand' package.json)" + if [ "$BP_OUTPUT" != 'null' ]; then + echo "$BP_OUTPUT" + else + echo '.' + fi + hide: + sh: '! find ./test -mindepth 1 -maxdepth 1 -type d | grep output' + log: + error: There was an error while comparing `latest` and `slim` image outputs for `{{.DOCKER_IMAGE}}` + start: Comparing outputs of the `latest` and `slim` images for `{{.DOCKER_IMAGE}}` + success: The outputs of the `latest` and `slim` images match for `{{.DOCKER_IMAGE}}`! + cmds: + - | + LATEST_OUTPUT="$(docker run -v "{{.MOUNT_PATH}}:/work" -w /work {{.DOCKER_IMAGE}}:latest {{.OUTPUT_COMMAND}} || true)" + .config/log success 'Acquired `latest` image output' + .config/log info 'Acquiring `slim` image output' + SLIM_OUTPUT="$(docker run -v "{{.MOUNT_PATH}}:/work" -w /work {{.DOCKER_IMAGE}}:slim {{.OUTPUT_COMMAND}} || true)" + .config/log success 'Acquired `slim` image output' + if [ "$LATEST_OUTPUT" != "$SLIM_OUTPUT" ]; then + .config/log error 'The `latest` image output did not match the `slim` image output with the command `{{.OUTPUT_COMMAND}}`' + echo "$LATEST_OUTPUT" > latest.log + .config/log info 'The `latest` image output has been written to `latest.log`' + echo "$SLIM_OUTPUT" > slim.log + .config/log info 'The `slim` image output has been written to `slim.log`' + exit 1 + else + .config/log success 'The output from the `{{.OUTPUT_COMMAND}}` command generated the same output for both the `slim` and `latest` builds' + fi + + outputs: + log: + error: Error intitializing Docker output tests + start: Initializing Docker output test phase + success: Completed Docker output test phase + cmds: + - | + if [ -d test ]; then + TESTS="$(find ./test -mindepth 1 -maxdepth 1 -type d -name "output-*")" + if echo "$TESTS" | grep 'output'; then + for TEST_FILE in $TESTS; do + TARGET="$(echo "$TEST_FILE" | sed 's/.*\/\([^\/]*\).yml$/\1/')" + task docker:test:output -- "$TARGET" + done + else + .config/log info 'No folders matching `test/output*` were found so output comparison test is being skipped' + fi + else + .config/log info 'No `./test` folder. See the `docs/CONTRIBUTING.md` to see how to set up the tests for Docker image builds' + fi + status: + - '(! find ./test -mindepth 1 -maxdepth 1 -type d -name "output-*" | grep output-) + || [[ "$(docker images -q {{.DOCKER_IMAGE}}:slim 2> /dev/null)" == "" ]] + || [[ "$(docker images -q {{.DOCKER_IMAGE}}:latest 2>/dev/null)" == "" ]]' diff --git a/.config/taskfiles/docker/Taskfile-update.yml b/.config/taskfiles/docker/Taskfile-update.yml new file mode 100644 index 00000000..61ef8502 --- /dev/null +++ b/.config/taskfiles/docker/Taskfile-update.yml @@ -0,0 +1,147 @@ +--- +version: '3' + +tasks: + labels: + deps: + - :install:software:jq + vars: + GROUP_URL: + sh: jq -r '.repository.group.dockerfile' .variables.json + log: + start: Ensuring `Dockerfile` labels are up-to-date + success: '`Dockerfile` labels are up-to-date' + cmds: + - task: labels:add + - task: labels:update + sources: + - .variables.json + - Dockerfile + - package.json + preconditions: + - sh: test -f Dockerfile + msg: A `Dockerfile` is not present in the root of this project! + - sh: test -f .variables.json + msg: The `.variables.json` file is not present in the root of this project! + - sh: test -f package.json + msg: The `package.json` file is not present in the root of this project! + + labels:add: + vars: + AUTHORS: + sh: jq -r '.docker_label_authors' .variables.json + DESCRIPTION: + sh: jq -r '.description' package.json + HELP_EMAIL: + sh: jq -r '.email.help' .variables.json + LICENSE: + sh: jq -r '.license' .variables.json + ORGANIZATION: + sh: jq -r '.organization' .variables.json + SOURCE: + sh: jq -r '.blueprint.repository.github' package.json + URL: + sh: jq -r '.link.home' .variables.json + log: + error: Failed to add Dockerfile labels + start: Ensuring default Dockerfile labels are present (e.g. `org.opencontainers.image` tags) + success: Successfully injected `org.opencontainers.image` labels into Dockerfile + cmds: + - | + echo 'ARG BUILD_DATE' >> Dockerfile + echo 'ARG REVISION' >> Dockerfile + echo 'ARG VERSION' >> Dockerfile + echo '' >> Dockerfile + echo 'LABEL maintainer="{{.ORGANIZATION}} <{{.HELP_EMAIL}}>"' >> Dockerfile + echo 'LABEL org.opencontainers.image.authors="{{.AUTHORS}}"' >> Dockerfile + echo 'LABEL org.opencontainers.image.created=$BUILD_DATE' >> Dockerfile + echo 'LABEL org.opencontainers.image.description="{{.DESCRIPTION}}"' >> Dockerfile + echo 'LABEL org.opencontainers.image.documentation="{{.SOURCE}}/blob/master/README.md"' >> Dockerfile + echo 'LABEL org.opencontainers.image.licenses="{{.LICENSE}}"' >> Dockerfile + echo 'LABEL org.opencontainers.image.revision=$REVISION' >> Dockerfile + echo 'LABEL org.opencontainers.image.source="{{.SOURCE}}"' >> Dockerfile + echo 'LABEL org.opencontainers.image.url="{{.URL}}"' >> Dockerfile + echo 'LABEL org.opencontainers.image.vendor="{{.ORGANIZATION}}"' >> Dockerfile + echo 'LABEL org.opencontainers.image.version=$VERSION' >> Dockerfile + echo 'LABEL space.megabyte.type="{{.REPOSITORY_SUBTYPE}}"' >> Dockerfile + echo '' >> Dockerfile + status: + - grep 'org.opencontainers.image.documentation' Dockerfile + + # yamllint disable rule:line-length + labels:update: + vars: + AUTHORS: + sh: jq -r '.docker_label_authors' .variables.json + CMD_PREFIX: sed -i{{if (eq OS "darwin")}} .bak{{end}} + GROUP_URL: + sh: jq -r '.repository.group.dockerfile' .variables.json + LICENSE: + sh: jq -r '.license' .variables.json + ORGANIZATION: + sh: jq -r '.organization' .variables.json + URL: + sh: jq -r '.link.home' .variables.json + env: + DESCRIPTION: + sh: jq -r '.description' package.json + REPOSITORY_GITHUB: + sh: jq -r '.blueprint.repository.github' package.json + log: + error: Encountered error while updating `Dockerfile` labels + cmds: + - | + {{.CMD_PREFIX}} "s^.*org.opencontainers.image.authors.*^LABEL org.opencontainers.image.authors=\"{{.AUTHORS}}\"^g" Dockerfile + {{.CMD_PREFIX}} "s^.*org.opencontainers.image.description.*^LABEL org.opencontainers.image.description=\"${DESCRIPTION}\"^g" Dockerfile + {{.CMD_PREFIX}} "s^.*org.opencontainers.image.documentation.*^LABEL org.opencontainers.image.documentation=\"${REPOSITORY_GITHUB}/blob/master/README.md\"^g" Dockerfile + {{.CMD_PREFIX}} "s^.*org.opencontainers.image.licenses.*^LABEL org.opencontainers.image.licenses=\"{{.LICENSE}}\"^g" Dockerfile + {{.CMD_PREFIX}} "s^.*org.opencontainers.image.source.*^LABEL org.opencontainers.image.source=\"${REPOSITORY_GITHUB}.git\"^g" Dockerfile + {{.CMD_PREFIX}} "s^.*org.opencontainers.image.url.*^LABEL org.opencontainers.image.url=\"{{.URL}}\"^g" Dockerfile + {{.CMD_PREFIX}} "s^.*org.opencontainers.image.vendor.*^LABEL org.opencontainers.image.vendor=\"{{.ORGANIZATION}}\"^g" Dockerfile + - rm -f Dockerfile.bak + # yamllint enable rule:line-length + + sort: + todo: Make this remove duplicates using a command like `uniq -u` + log: + error: Encountered error while sorting `*.txt` files in the `./local` folder` + start: Sorting, removing empty lines, and removing duplicate lines from all `*.txt` files in the `./local` folder + success: Successfully sorted `*.txt` files in the `./local` folder + cmds: + - find ./local -type f -name '*.txt' -exec sort {} -o {} \; -exec sed -i '/^$/d' {} \; + status: + - '! find ./local -type f -name "*.txt" | grep txt' + + version: + deps: + - :install:software:jq + vars: + VERSION_COMMAND: + sh: jq -r '.blueprint.versionCommand' package.json + log: + error: Error acquiring software version + start: Acquiring software version + success: Finished software version acquisition sequence + cmds: + - docker build --tag {{.DOCKER_IMAGE}}:latest --target "{{.DOCKER_BASE_TAG}}" . + - | + if [ -n "$VERSION_OVERRIDE" ]; then + VERSION="$VERSION_OVERRIDE" + else + VERSION="$({{.VERSION_COMMAND}})" || EXIT_CODE=$? + VERSION="$(echo "$VERSION" | perl -pe 'if(($v)=/([0-9]+([.][0-9]+)+)/){print"$v\n";exit}$_=""')" + fi + if [ -n "$EXIT_CODE" ]; then + .config/log warn 'Failed to run version command (`{{.VERSION_COMMAND}}`) - will retry after images are finished building' + else + CURR_VERSION="$(jq -r '.version' package.json)" + TMP="$(mktemp)" + jq --arg version "$VERSION" '.version = $version' package.json > "$TMP" + mv "$TMP" package.json + if [ "$CURR_VERSION" != "$(jq -r '.version' package.json)" ]; then + .config/log info 'Updating documentation with new software version' + task common:update:update + fi + fi + status: + - '[ "{{.VERSION_COMMAND}}" == "null" ]' diff --git a/.config/taskfiles/docker/Taskfile.yml b/.config/taskfiles/docker/Taskfile.yml new file mode 100644 index 00000000..c1b839d5 --- /dev/null +++ b/.config/taskfiles/docker/Taskfile.yml @@ -0,0 +1,371 @@ +--- +version: '3' + +vars: + DIND_MOUNT_MAPPING: /var/run/docker.sock:/var/run/docker.sock + DOCKER_BASE_TAG: + sh: | + if [ -f Dockerfile ] && type jq &> /dev/null; then + BASE_TAG="$(jq -r '.blueprint.dockerBaseTag' package.json)" + if [ "$BASE_TAG" != 'null' ]; then + echo "$BASE_TAG" + else + if grep -m1 "^FROM .* AS .*" Dockerfile > /dev/null; then + FIRST_LINE=$(head -n 1 Dockerfile | sed 's/.* AS \(.*\)/\1/') + if [ -n "$FIRST_LINE" ]; then + echo "$FIRST_LINE" + else + echo "" + fi + else + echo "" + fi + fi + fi + DOCKER_IMAGE: + sh: | + if type jq &> /dev/null; then + echo "{{.DOCKERHUB_PROFILE}}/$(jq -r '.blueprint.slug' package.json)" + fi + +tasks: + build: + desc: Build a regular Docker image and then generate a slim build from it + summary: | + # Build the Docker Images + + This task will build all of the corresponding Docker images as long as they + have a [container-structure-test](https://github.com/GoogleContainerTools/container-structure-test) + created. + + For regular projects, having a `Docker.test.yml` container-structure-test is enough. However, + for projects with multiple build targets, there should be a test for each target. So, if there + are 2 build targets in the `Dockerfile` named `build1` and `build2` then there should be + a `Docker.build1.test.yml` and `Docker.build2.test.yml` container-structure-test. + hide: + sh: '[ ! -f Dockerfile ]' + log: + error: Encountered error while running Docker build sequence + start: Running Docker build sequence + success: Finished running Docker build sequence + cmds: + - task: ensure:running + - task: taskfile:deps + - | + if [ -d ./test/structure ]; then + TESTS="$(find ./test/structure -mindepth 1 -maxdepth 1 -type f -name "*.yml")" + if echo "$TESTS" | grep '.yml' > /dev/null; then + for TEST_FILE in $TESTS; do + TARGET="$(echo "$TEST_FILE" | sed 's/.*\/\([^\/]*\).yml$/\1/')" + if [ "$(jq --arg slug "$TARGET" '.blueprint.dockerSlimCommand | type' package.json)" == 'object' ]; then + if [ "$(jq --arg slug "$TARGET" '.blueprint.dockerSlimCommand[$slug]' package.json)" != 'null' ]; then + task docker:build:slim -- "$TARGET" + else + .config/log warn 'The `.blueprint.dockerSlimCommand` is missing - skipping slim build and building just a regular image' + task docker:build:fat -- "$TARGET" + fi + else + if [ "$(jq --arg slug "$TARGET" '.blueprint.dockerSlimCommand' package.json)" != 'null' ]; then + task docker:build:slim -- "$TARGET" + else + task docker:build:fat -- "$TARGET" + fi + fi + done + else + .config/log error 'The `./test/structure` folder is present but there are no container-structure-tests in it. Add tests \ + for each build target in the `Dockerfile` that you would like to generate an image for.' + exit 1 + fi + else + .config/log info 'No `./test/structure` folder is present so assuming there is only one build target' + if [ "$(jq -r '.blueprint.dockerSlimCommand' package.json)" != null ]; then + .config/log info '`.blueprint.dockerSlimCommand` is present in `package.json` so building both a `latest` and `slim` image' + task docker:build:slim + else + task docker:build:fat + fi + fi + + ensure:running: + summary: | + Ensures Docker Desktop is running if on a macOS machine. + cmds: + - | + if ! docker stats --no-stream; then + if [ -f /Applications/Docker.app ]; then + open /Applications/Docker.app + while (! docker stats --no-stream); do + echo "Waiting for Docker to launch..." + sleep 1 + done + else + .config/log error 'Docker is either not running or not installed!' + fi + fi + status: + - '[[ "{{OS}}" != "darwin" ]]' + + login: + deps: + - :install:software:docker + log: + error: Failed to authenticate `{{.DOCKERHUB_USER}}` with the DockerHub registry + start: Logging into DockerHub registry with `{{.DOCKERHUB_USER}}` + success: Authenticated to DockerHub registry with `{{.DOCKERHUB_USER}}` + cmds: + - | + if [ -n "$DOCKERHUB_REGISTRY_PASSWORD" ]; then + echo "$DOCKERHUB_REGISTRY_PASSWORD" | docker login -u {{.DOCKERHUB_USER}} --password-stdin + else + .config/log warn 'The `DOCKERHUB_REGISTRY_PASSWORD` is not an environment variable. This is required to login to DockerHub.' + fi + + prepare: + cmds: + - task: build + status: + - '[ ! -f Dockerfile ]' + + publish: + desc: Publish the Docker images (using `Docker*.test.yml` files) + hide: + sh: '[ ! -f Dockerfile ]' + summary: | + # Publish the Docker Images + + This task will publish all of the corresponding Docker images as long as they + have a [container-structure-test](https://github.com/GoogleContainerTools/container-structure-test) + created. + + For regular projects, having a `Docker.test.yml` container-structure-test is enough. However, + for projects with multiple build targets, there should be a test for each target. So, if there + are 2 build targets in the `Dockerfile` named `build1` and `build2` then there should be + a `Docker.build1.test.yml` and `Docker.build2.test.yml` container-structure-test. + cmds: + - | + if [ -d ./test/structure ]; then + TESTS="$(find ./test/structure -mindepth 1 -maxdepth 1 -type f -name "*.yml")" + if echo "$TESTS" | grep '.yml'; then + for TEST_FILE in $TESTS; do + TARGET="$(echo "$TEST_FILE" | sed 's/.*\/\([^\/]*\).yml$/\1/')" + task docker:publish:images -- "$TARGET" + done + else + .config/log error 'The `./test/structure` folder is present but there are no container-structure-tests in it. Add tests \ + for each build target in the `Dockerfile` that you would like to generate an image for.' + exit 1 + fi + else + .config/log info 'No `./test/structure` folder is present so assuming there is only one build target' + task docker:publish:images + fi + - task: pushrm + status: + - '[ ! -f Dockerfile ]' + + publish:image: + vars: + SLIM_ENABLED: + sh: | + TYPE="$(jq -r '.blueprint.dockerSlimCommand | type' package.json)" + if [ "$TYPE" != 'object' ]; then + jq -r '.blueprint.dockerSlimCommand' package.json | sed 's/null/false/' + else + TARGET="{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.TARGET}}{{end}}" + jq --arg target "$TARGET" -r '.blueprint.dockerSlimCommand[$target]' package.json | sed 's/null/false/' + fi + log: + error: Failed to tag / push `{{.DOCKER_IMAGE}}:{{.TARGET_TAG}}` + start: Tagging and pushing `{{.DOCKER_IMAGE}}:{{.TARGET_TAG}}` + success: Finished uploading `{{.DOCKER_IMAGE}}:{{.TARGET_TAG}}` + cmds: + - docker tag {{.DOCKER_IMAGE}}:{{.SOURCE_TAG}} {{.DOCKER_IMAGE}}:{{.TARGET_TAG}} + - docker push {{.DOCKER_IMAGE}}:{{.TARGET_TAG}} + status: + - '[[ "{{.SOURCE_TAG}}" == "slim"* ]]' + - '[[ "{{.SLIM_ENABLED}}" == "false" ]]' + + publish:images: + vars: + MAJOR_VERSION: + sh: jq -r '.version' package.json | sed 's/\..*\..*$//' + MINOR_VERSION: + sh: jq -r '.version' package.json | sed 's/^[^\.]*\.//' | sed 's/\.[^\.]*$//' + TAG_POST: '{{if ne .DOCKER_BASE_TAG .CLI_ARGS}}-{{if (contains "codeclimate" .CLI_ARGS)}}codeclimate{{else}}{{.CLI_ARGS}}{{end}}{{end}}' + TARGET: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.TARGET}}{{end}}' + VERSION: + sh: jq -r ".version" package.json + log: + error: An error occurred while publishing the Docker images + start: Publishing Docker images + success: Finished uploading all Docker images + cmds: + - task: publish:image + vars: + SOURCE_TAG: latest{{.TAG_POST}} + TARGET_TAG: 'v{{.VERSION}}{{.TAG_POST}}' + - task: publish:image + vars: + SOURCE_TAG: latest{{.TAG_POST}} + TARGET_TAG: 'v{{.MAJOR_VERSION}}{{.TAG_POST}}' + - task: publish:image + vars: + SOURCE_TAG: latest{{.TAG_POST}} + TARGET_TAG: v{{.MAJOR_VERSION}}.{{.MINOR_VERSION}}{{.TAG_POST}} + - task: publish:image + vars: + SOURCE_TAG: latest{{.TAG_POST}} + TARGET_TAG: latest{{.TAG_POST}} + - task: publish:image + vars: + SOURCE_TAG: slim{{.TAG_POST}} + TARGET_TAG: 'v{{.VERSION}}-slim{{.TAG_POST}}' + - task: publish:image + vars: + SOURCE_TAG: slim{{.TAG_POST}} + TARGET_TAG: 'v{{.MAJOR_VERSION}}-slim{{.TAG_POST}}' + - task: publish:image + vars: + SOURCE_TAG: slim{{.TAG_POST}} + TARGET_TAG: v{{.MAJOR_VERSION}}.{{.MINOR_VERSION}}-slim{{.TAG_POST}} + - task: publish:image + vars: + SOURCE_TAG: slim{{.TAG_POST}} + TARGET_TAG: slim{{.TAG_POST}} + + pushrm: + deps: + - :install:github:docker-pushrm + - :install:software:docker + vars: + DOCKERHUB_DESCRIPTION: + sh: jq -r '.description' package.json + env: + DOCKER_PASS: + sh: echo "$DOCKERHUB_REGISTRY_PASSWORD" + DOCKER_USER: '{{.DOCKERHUB_USER}}' + cmds: + - cmd: docker pushrm {{.DOCKER_IMAGE}} + ignore_error: true + - cmd: docker pushrm --short '{{.DOCKERHUB_DESCRIPTION}}' {{.DOCKER_IMAGE}} + ignore_error: true + + shell: + deps: + - :install:software:docker + - :install:software:jq + interactive: true + desc: Open the terminal of an existing Docker image + hide: + sh: '[ ! -f Dockerfile ]' + summary: | + # Shell into Docker Image + + This task will start a shell session with one of the Docker images + that are currently present on machine. The task looks at the output + from `docker images` and filters the list based on the project's + expected DockerHub slug or a string, if you pass in CLI arguments. + + **Displays a list of images that match the name in package.json:** + `task docker:shell` + + **Displays a list of images that have the pattern abc in their name:** + `task docker:shell -- 'abc'` + vars: + IMAGE_OPTIONS: + sh: docker images | grep {{.DOCKER_IMAGE}} | sed 's/^\([^ ]*\).*$/\1/' | jq -Rsc 'split("\n")' | jq 'del(.[-1])' + prompt: + type: select + message: Which Docker image would you like to shell into? + options: '{{.IMAGE_OPTIONS}}' + answer: + cmds: + - docker run -v "${PWD}:/work" -w /work -it --entrypoint /bin/sh --rm {{.ANSWER}} + + taskfile:deps: + vars: + DOCKER_TASKS_TAG: + sh: jq -r '.blueprint.dockerTasksTag' package.json + cmds: + - task: :common:util:task:tag:deps + vars: + TAG: '{{.DOCKER_TASKS_TAG}}' + status: + - '[ "{{.DOCKER_TASKS_TAG}}" == "null" ]' + + test: + deps: + - :install:software:docker + desc: Perform all available tests on the Docker image + hide: + sh: '[ ! -f Dockerfile ]' + summary: | + # Test the Docker Images + + This task will publish all of the corresponding Docker images as long as they + have a [container-structure-test](https://github.com/GoogleContainerTools/container-structure-test) + created. + + For regular projects, having a `Docker.test.yml` container-structure-test is enough. However, + for projects with multiple build targets, there should be a test for each target. So, if there + are 2 build targets in the `Dockerfile` named `build1` and `build2` then there should be + a `Docker.build1.test.yml` and `Docker.build2.test.yml` container-structure-test. + + ## Comparing Slim Output to Latest Output + + If the `dockerSlimCommand` is present in the `blueprint` section of `package.json`, the output from running + `npm run test:dockerslim` on every project/folder in the `test-output/` folder will be compared. The comparison + will ensure that the `slim` output matches the `latest` output. Each folder in the `test-output/` folder must + have a `package.json` file present with the `test:dockerslim` script defined under `scripts`. + + If there is no `test-output/` folder, then this kind of test is skipped. + + ## `dockerSlimCommand` with Multiple Build Targets + + If there are multiple build targets, then the `dockerSlimCommand` should be an object with keys equal to + the build targets and values equal to the appropriate DockerSlim command. If you are only using + `Docker.test.yml` then you can simply set `dockerSlimCommand` equal to the appropriate command. However, + if you have two targets named `build1` and `build2`, then your `dockerSlimCommand` should look something + like: + + ```json + { + "blueprint": { + "dockerSlimCommand": { + "build1": "...DockerSlim build command options here for build1", + "build2": "...DockerSlim build command options here for build2" + } + } + } + ``` + + ## CodeClimate CLI Testing + + Any folder in the `test/` folder that starts with `codeclimate` gets scanned by CodeClimate CLI. + log: + error: Encountered error during Docker test sequence + start: Initiating Docker test sequence + success: Successfully completed Docker test sequence + cmds: + - task: :docker:test:container-structure-tests + - task: :docker:test:output + - task: :docker:test:codeclimate + - task: :docker:test:gitlab-runner + + verify: + cmds: + - task: login + + version:software: + cmds: + - | + if grep -q "CMD.\[\"--version\"\]" Dockerfile; then + VERSION=$(docker run --cap-drop=ALL -e PY_COLORS=0 --rm {{.DOCKER_IMAGE}}:latest | perl \ + -pe 'if(($v)=/([0-9]+([.][0-9]+)+)/){print"$v";exit}$_=""') + if [[ $VERSION == *.*.* ]]; then + echo $VERSION + elif [[ $VERSION == *.* ]]; then + echo $VERSION.0 + fi + fi diff --git a/.config/taskfiles/dotfiles/Taskfile.yml b/.config/taskfiles/dotfiles/Taskfile.yml new file mode 100644 index 00000000..fc301647 --- /dev/null +++ b/.config/taskfiles/dotfiles/Taskfile.yml @@ -0,0 +1,10 @@ +--- +version: '3' + +tasks: + donothing: + summary: | + Add CLOUDFLARE_ACCOUNT_ID to package.json blueprint + Add Cloudflare_API_TOKEN to globals + cmds: + - task donothing diff --git a/.config/taskfiles/fix/Taskfile.yml b/.config/taskfiles/fix/Taskfile.yml new file mode 100644 index 00000000..76f3784d --- /dev/null +++ b/.config/taskfiles/fix/Taskfile.yml @@ -0,0 +1,527 @@ +--- +version: '3' + +vars: + PRETTIERIGNORE_CONFIG: .config/prettierignore + SHELLCHECK_EXCLUDE: SC1091 + +tasks: + all: + deps: + - eslint + - misc + - '{{if .REPOSITORY_TYPE eq "packer"}}packer{{else}}:donothing{{end}}' + - '{{if .REPOSITORY_TYPE eq "python"}}python{{else}}:donothing{{end}}' + - toml + - xml + log: + start: Running all stable fixers in parallel + cmds: + - task: yaml:dashes + + eslint: + deps: + - :install:modules:local + - :install:npm:eslint + - :install:software:unbuffer + desc: Fix ESLint errors automatically + summary: | + # Fix Errors with ESLint Automatically + + This task is an [`eslint`](https://eslint.org/) task. It will attempt to automatically fix + `eslint` issues. It will report any issues it was unable to fix. The configuration found in + `package.json` includes logic for fixing/linting TypeScript, JavaScript, JSON, TOML, and YAML. + + **Example using `eslint` to fix a single JS/TS file:** + `task fix:js -- single.ts` + + **Example fixing all files in a project:** + `task fix:js` + vars: + ESLINT_LOG: + sh: mktemp + log: + error: ESLint found some issues that need to be fixed manually + start: Auto-fixing with ESLint + success: ESLint fixing appears to have corrected all the issues {{if .CLI_ARGS}}in `{{.CLI_ARGS}}`{{else}}in the project{{end}} + cmds: + - 'unbuffer eslint -c package.json --no-eslintrc --format {{.ESLINT_FORMATTER}} --cache --cache-location .cache/eslintcache + --fix {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} | tee {{.ESLINT_LOG}}' + - | + if [ '{{.ESLINT_FIX_RECURSIVE}}' == 'true' ]; then + COUNT=0 + while [ $COUNT -le 5 ]; do + COUNT=$((COUNT + 1)) + if grep 'potentially fixable with the `--fix` option' < {{.ESLINT_LOG}} > /dev/null; then + COUNT="$COUNT" + unbuffer eslint -c package.json --no-eslintrc --format {{.ESLINT_FORMATTER}} --cache --cache-location .cache/eslintcache \ + --fix {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} | tee {{.ESLINT_LOG}} + fi + done + fi + - rm {{.ESLINT_LOG}} + + eslint:staged: + deps: + - :install:modules:local + - :install:npm:eslint + desc: Auto-fix only modified files with ESLint + env: + ESLINT_STAGED_ONLY: on + log: + error: Failed to auto-fix modified files with ESLint + start: Auto-fixing modified files with ESLint + success: Successfully auto-fixed modified files with ESLint + cmds: + - > + eslint -c package.json --no-eslintrc --format {{.ESLINT_FORMATTER}} --cache + --cache-location .cache/eslintcache --fix {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + + go: + deps: + - :install:software:go + - :install:software:golangci-lint + desc: Fix Go with all available Go fixers + summary: | + # Auto-Fix Go Files + + This task will attempt to auto-fix Go files with + [golangci-lint](https://golangci-lint.run/) which runs multiple linters all + defined in `.config/golangci-lint`. + + **Example auto-fixing an individual file:** + `task fix:go -- path/filename.go` + log: + error: Encountered error while autofixing Go files + start: Auto-fixing Go files.. + success: Successfully auto-fixed Go files + cmds: + - golangci-lint run -c .config/golangci.yml --fix{{if .CLI_ARGS}} {{.CLI_ARGS}}{{end}} + + js: + cmds: + - task: eslint + + json: + deps: + - :install:modules:local + - :install:npm:eslint + desc: Format and sort JSON + summary: | + # Automatically Alphabetize and Format JSON + + This task will format and organize JSON files based on the configuration stored in + `.eslintrc.cjs`. + + **Example sorting a single JSON file:** + `task fix:json -- single.json` + + **Example looping through project:** + `task fix:json` + log: + error: '{{if .CLI_ARGS}}Manual fixing is still required for `{{.CLI_ARGS}}`{{else}}Failed to fix all project JSON issues with ESLint{{end}}' + start: Linting JSON with ESLint + success: ESLint has fixed all of the JSON issues {{if .CLI_ARGS}}in `{{.CLI_ARGS}}`{{else}}in the project{{end}} + cmds: + - eslint -c package.json --no-eslintrc --cache --cache-location .cache/eslintcache --fix + --format pretty --ext .json {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + + markdown: + deps: + - :install:npm:markdown-table-formatter + desc: Auto-fixes markdown files + summary: | + # Auto-Fix Markdown Files + + This task performs various tasks that cleanup markdown files. It currently: + + 1. Runs `markdown-table-formatter` which fixes up markdown tables + + You can either pass a file as a CLI argument or run it without a CLI argument to + auto-fix all the markdown files in the project that are not ignored by .gitignore. + + **Example running against whole project:** + `task fix:markdown` + + **Example running on single file:** + `task fix:markdown -- README.md` + log: + error: Error encountered while auto-fixing markdown file(s) + start: Auto-fixing markdown file(s) + success: Successfully finished auto-fixing markdown file(s) + cmds: + - > + {{if .CLI_ARGS}} + markdown-table-formatter {{.CLI_ARGS}} + {{else}} + find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.md' \) + -print0 | xargs -0 -r -n1 markdown-table-formatter + {{end}} + + misc: + deps: + - :install:pipx:pre-commit-hooks + summary: | + # Miscellaneous fixes + + This task allows you to loop through the files in the project and apply miscellaneous + fixes. You can also specify a single file. The fixes applied are: + + 1. Remove UTF-8 BOM + 2. Ensure line endings are LF + + **Example applying fixes to all files:** + `task fix:misc` + + **Example applying fixes to single file:** + `task fix:misc -- singlefile.js` + log: + error: Encountered an error while performing miscellaneous fixes{{if .CLI_ARGS}} on `{{.CLI_ARGS}}`{{end}} + start: Performing miscellaneous fixes such as removing BOM and ensuring LF line endings + success: Finished performing miscellaneous fixes{{if .CLI_ARGS}} on `{{.CLI_ARGS}}`{{end}} + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + function misc() { + fix-byte-order-marker "$1" + mixed-line-ending --fix=lf "$1" + } + {{if .CLI_ARGS}} + misc '{{.CLI_ARGS}}' + {{else}} + while read PATHH; do + misc "$PATHH" + done < <(find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f) + {{end}} + + packer: + deps: + - :install:software:packer + desc: Automatically fix and format Packer templates + hide: '{{ne .REPOSITORY_TYPE "packer"}}' + summary: | + # Automatically fix and format Packer templates + + Packer has the ability to fix old templates that are using outdated methods. This task + loops through all the files in the root of this repository that end with `template.json`. + For each template, the task runs `packer fix`. + + **Example applying fixes to all files matching `*template.json`:** + `task fix:packer` + + **Example applying fixes to single file:** + `task fix:packer -- mytemplate.json` + + For more information on `packer fix`, see [Packer's website](https://www.packer.io/docs/commands/fix). + log: + start: Attempting to fix Packer template{{if .CLI_ARGS}}{{else}}s{{end}} + stop: Error running `packer fix` + success: Successfully ran `packer fix` on the template(s) + cmds: + - | + function packerFix() { + TMP="$(mktemp)" + packer fix "$1" > "$TMP" + mv "$TMP" "$1" + .config/log success "Successfully ran `packer fix` on $1" + } + {{if .CLI_ARGS}} + packerFix '{{.CLI_ARGS}}' + {{else}} + for TEMPLATE in *template.json; do + packerFix "$TEMPLATE" + done + {{end}} + + php: + desc: Fix PHP with all available PHP auto-fixers + summary: | + # Auto-Fix PHP Files + + This task will attempt to auto-fix PHP files with the following auto-fixers: + + * [go]() + + If you would like to skip auto-fixing on the whole project and instead auto-fix an + individual file, then you can do so by passing the file path as a CLI + parameter like so: + + **Example auto-fixing an individual file:** + `task fix:php -- path/filename.php` + cmds: + - task: :donothing + + prettier: + deps: + - :install:modules:local + - :install:npm:prettier + desc: Automatically format most files using Prettier + summary: | + # Automatically Format Files Using Prettier + + This task will run Prettier on the project. Prettier will automatically fix formatting + mistakes like inconsistent indent lengths, trailing spaces, and more. It will use the + configuration specified in the `package.json` file under the `prettier` key. + + If this command is incompatible with a file then you can add the file to the `.prettierignore` + file. + + **Example of bulk fixing:** + `task fix:formatting` + + **Example of fixing a single file** + `task fix:formatting -- path/filename.ext` + + For more information, see [Prettier's website](https://prettier.io/). + log: + start: Running Prettier on {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}project{{end}} + cmds: + - cmd: | + prettier --plugin node_modules/prettier-plugin-toml-fixed --ignore-path {{.PRETTIERIGNORE_CONFIG}} \ + --write {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + .config/log warn 'Failed to run `prettier` - falling back to `prettier`' + prettier --plugin node_modules/prettier-plugin-toml-fixed --ignore-path {{.PRETTIERIGNORE_CONFIG}} \ + --write {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + fi + ignore_error: true + + python: + deps: + - :install:pipx:add-trailing-comma + - :install:pipx:isort + - :install:pipx:pyformat + desc: Automatically format Python files using Black + hide: '{{ne .REPOSITORY_TYPE "python"}}' + summary: | + # Automatically Format Python Files with Black + + Black is the defacto standard when it comes to autoformatting Python files. This task will + automatically format files that end with the `.py` extension. It ignores `.py` files that are + in any of the locations specified in the `IGNORED_FOLDERS` variable in `Taskfile.yml`. + + **Example applying fixes to all Python files:** + `task fix:python` + + **Example applying fixes to single Python file:** + `task fix:python -- myfile.py` + + For more information, see [Black's GitHub page](https://github.com/psf/black). + log: + error: Error while running `black` auto-fixer + start: Fixing Python with `black` + success: Fixed {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}project{{end}} with `black` + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + {{if .CLI_ARGS}} + pyformat -i {{.CLI_ARGS}} + isort --overwrite-in-place {{.CLI_ARGS}} + add-trailing-comma --exit-zero-even-if-changed {{.CLI_ARGS}} + {{else}} + while read PATHH; do + pyformat -i "$PATHH" + isort --overwrite-in-place "$PATHH" + add-trailing-comma --exit-zero-even-if-changed "$PATHH" + done < <(find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.py' \)) + {{end}} + + shell: + cmds: + - task: shellcheck + - task: prettier + + shellcheck: + deps: + - :install:npm:shellcheck + desc: Automatically apply fixes to `.sh` and `.sh.j2` files using Shellcheck + summary: | + # Automatically apply fixes to shell scripts using Shellcheck + + Shellcheck is generally used to lint shell scripts. This task uses Shellcheck and `git apply` + to automatically apply Shellcheck's recommendations. It only runs on files that are not in the + `IGNORED_FOLDERS` variable in `Taskfile.yml`. + + This task is experimental. It may work to get rid of the Shellcheck lin.config/log errors but the code + should still be tested since it might break things. Ideally, you should apply Shellcheck's + recommendations manually. + + **To see what changes Shellcheck will make to a file named `test.sh`, for example, you can run:** + `npx shellcheck -f diff test.sh` + + **Example applying fixes to all shell scripts:** + `task fix:scripts` + + **Example applying fixes to a single shell script:** + `task fix:scripts -- myfile.sh` + + For more information, see [Shellcheck's GitHub page](https://github.com/koalaman/shellcheck). + log: + error: Encountered an error while linting with `shellcheck` on {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}project{{end}} + start: Running `shellcheck` auto-fixer + success: Autofixed {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}project{{end}} with `shellcheck` + cmds: + - | + .config/log warn 'This is an experimental fix method - please manually inspect and test the scripts' + {{if .CLI_ARGS}} + shellcheck -e {{.SHELLCHECK_EXCLUDE}} -f diff {{.CLI_ARGS}} | git apply + {{else}} + find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.sh' -o -name '*.sh.j2' \) \ + -print0 | xargs -0 shellcheck -e {{.SHELLCHECK_EXCLUDE}} -f diff | git apply + {{end}} + + spelling:markdown: + deps: + - :install:go:misspell + desc: Auto-fix spelling errors in markdown files using `misspell` + log: + error: Encountered error while attempting to auto-correct spelling mistakes with `misspell` + start: Auto-fixing using the `misspell` spell-check engine + success: Successfully auto-fixed files using `misspell` + cmds: + - > + {{if .CLI_ARGS}} + misspell -w {{.CLI_ARGS}} + {{else}} + find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.md' \) + -print0 | xargs -0 -r -n1 misspell -w + {{end}} + + toml: + deps: + - :install:pipx:toml-sort + desc: Format `.toml` files by alphabetizing and flattening + summary: | + # Automatically format `.toml` files + + [toml-sort](https://pypi.org/project/toml-sort/) will automatically alphabetize and flatten + `.toml` files. + + **Example running on whole project:** + `task fix:toml` + + **Example usage on specific file:** + `task fix:toml -- pyproject.toml` + log: + error: Encountered an error while running `toml-sort` + start: Running `toml-sort` auto-fixer + success: Successfully ran the `toml-sort` auto-fixer + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + {{if .CLI_ARGS}} + if [ -f '{{.CLI_ARGS}}' ]; then + toml-sort -i --all {{.CLI_ARGS}} + fi + {{else}} + while read PATHH; do + toml-sort -i --all "$PATHH" + done < <(find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.toml' \)) + {{end}} + + xml: + deps: + - :install:npm:eslint + summary: | + # Autofix XML files + + This task will automatically apply updates to XML files. It uses Prettier and the Prettier plugin + named `@prettier/plugin-xml` for auto-formatting. + + **Example fixing all `.xml` files:** + `task fix:xml` + + **Example fixing specific `.xml` file(s):** + `task fix:xml -- 'file_name.xml'` + log: + error: Errors were reported by ESLint when auto-fixing XML + start: Attempting to auto-fix any XML files with ESLint + success: Any XML files present in the project were successfully fixed/validated by ESLint + cmds: + - eslint -c package.json --no-eslintrc --cache --cache-location .cache/eslintcache --fix + --format pretty --ext .xml {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + + yaml: + desc: Auto-format and sort YML files + summary: | + # Auto-Format and Sort YML files + + This task will automatically apply updates to YML files. It currently performs the following + tasks: + + 1. Ensures the first line of the file is `---` + 2. Sorts the file according to the layout specified by `.eslintrc.cjs` + + **Example usage:** + `task fix:yaml` + + **Example fixing single file:** + `task fix:yaml -- filename.yml` + cmds: + - task: yaml:dashes + - task: yaml:order + + yaml:dashes: + summary: | + # Ensures YML files start with a "---" + + This task will add a `---` as the first line of YML files if it is missing. It only works + on one YML file at a time. This task is intended to be used with lint-staged (with settings) + normally found in the `package.json` file. You must specify the path/filename to the YML + file you wish to fix with this task. If the file does not exist or if the file does not end + with `.yml` or `yaml` then an error will be reported. + + This task is mainly intended to be used with `lint-staged`. + + **Example usage:** + `task fix:yaml:dashes` + + **Example usage for one specific file:** + `task fix:yaml:dashes -- path/filename.yml` + log: + error: Error encountered while ensuring YML dashes + start: Ensuring {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}project{{end}} has YML files that start with `---` + success: Successfully ensured {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}project{{end}} has YML files that start with `---` + cmds: + - | + function yamlDashes() { + if test -f "$1" && [[ "`head -c 14 "$1"`" != '$ANSIBLE_VAULT' ]] && [[ "`head -c 3 "$1"`" != '---' ]]; then + TMP=$(mktemp) + echo '---' | cat - "$1" > "$TMP" + mv "$TMP" "$1" + fi + } + {{if .CLI_ARGS}} + yamlDashes '{{.CLI_ARGS}}' + {{else}} + while read YML_FILE; do + yamlDashes "$YML_FILE" + done < <(find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.yml' -o -name '*.yaml' \)) + {{end}} + + yaml:order: + deps: + - :install:modules:local + - :install:npm:eslint + summary: | + # Ensure YML files are sorted properly + + This task will sort YML keys alphabetically and also obey the order specified in `.eslintrc.cjs`. + It uses [ESLint](https://eslint.org/) along with the [`eslint-plugin-yml`](https://ota-meshi.github.io/eslint-plugin-yml/) + plugin. + + By default, the plugin will automatically fix all YML files but you can also pass in a single file or glob + that needs to be fixed. + + **Example fixing all `.yml` files:** + `task fix:yaml:order` + + **Example fixing specific `.yml` file(s):** + `task fix:yaml:order -- 'file_name.{yml,yaml}'` + log: + error: There are still some errors. Try running the command again. + start: Ensuring YML file(s) are in order specified in configuration + success: Successfully ensured YML file order + cmds: + - eslint -c package.json --no-eslintrc --format pretty --fix --cache + --cache-location .cache/eslintcache --ext yml,yaml {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} diff --git a/.config/taskfiles/git/Taskfile-bug.yml b/.config/taskfiles/git/Taskfile-bug.yml new file mode 100644 index 00000000..07a70027 --- /dev/null +++ b/.config/taskfiles/git/Taskfile-bug.yml @@ -0,0 +1,53 @@ +--- +version: '3' + +tasks: + bridge: + deps: + - bridge:github + - bridge:gitlab + + run: once + bridge:github: + deps: + - :install:software:git-bug + run: once + cmds: + - git bug bridge configure --name="$(jq -r '.name' package.json)" --target=github + --url="$(jq -r '.blueprint.repository.github' package.json)" --token="$GITHUB_TOKEN" + status: + - '[ -z "$GITHUB_TOKEN" ]' + + bridge:gitlab: + deps: + - :install:software:git-bug + run: once + cmds: + - git bug bridge configure --name="$(jq -r '.name' package.json)" --target=gitlab + --url="$(jq -r '.blueprint.repository.gitlab' package.json)" --token="$GITLAB_TOKEN" --base-url=https://gitlab.com/ + status: + - '[ -z "$GITLAB_TOKEN" ]' + + pull: + deps: + - bridge + - :git:remotes + - :install:software:git-bug + cmds: + - git bug pull all + + push: + deps: + - bridge + - :git:remotes + - :install:software:git-bug + cmds: + - git bug push all + + webui: + deps: + - bridge + - pull + - :install:software:git-bug + cmds: + - git bug webui diff --git a/.config/taskfiles/git/Taskfile-github.yml b/.config/taskfiles/git/Taskfile-github.yml new file mode 100644 index 00000000..fd1a2d89 --- /dev/null +++ b/.config/taskfiles/git/Taskfile-github.yml @@ -0,0 +1,168 @@ +--- +version: '3' + +tasks: + create: + deps: + - :ci:commit:config + - :install:software:gh + - :install:software:jq + - :git:remotes + vars: + DESCRIPTION: + sh: jq -r '.description' package.json + GITHUB_SLUG: + sh: jq -r '.blueprint.repository.github' package.json | sed 's/.*\///' + HOMEPAGE: + sh: jq -r '.homepage' package.json + PROJECT_TYPE: + sh: if [[ $(jq -r '.private' package.json) == 'true' ]]; then echo '--private'; else echo '--public'; fi + run: once + log: + error: Error while ensuring GitHub repository exists + start: Checking for presence of GitHub repository and creating one if it does not exist + success: Ensure GitHub repository is present + cmds: + - cmd: | + gh repo create "{{.GITHUB_ORG}}/{{.GITHUB_SLUG}}" --enable-wiki={{.GITHUB_WIKI}} -y --description "{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}" \ + --homepage "{{.HOMEPAGE}}" {{.PROJECT_TYPE}} > /dev/null + task --list > /dev/null || (echo "ERROR: Invalid Taskfiles!" && exit 1) + git add --all + git commit --quiet -m "✨ feat(birth): Birth" -n || true + git push github master + ignore_error: true + status: + - '[ -z "$GITHUB_TOKEN" ] || (test -d .git && gh repo view "{{.GITHUB_ORG}}/{{.GITHUB_SLUG}}" > /dev/null)' + preconditions: + - sh: '[ "{{.DESCRIPTION}}" != "null" ]' + msg: The `.description` in `package.json` must be set. + - sh: '[ "{{.GITHUB_SLUG}}" != "null" ]' + msg: The `.name` in `package.json` must be set. + - sh: '[ "{{.HOMEPAGE}}" != "null" ]' + msg: The `.homepage` in `package.json` must be set. + + ids: + deps: + - :install:software:gh + - :install:software:jq + - create + vars: + CURRENT_PROJECT_ID: + sh: jq -r '.blueprint.github_id' package.json + PROJECT_ID: + sh: gh repo view --json id | jq -r '.id' + log: + error: Failed to add GitHub project ID to package.json + start: Saving GitHub project ID to package.json + success: Added GitHub project ID to package.json + cmds: + - TMP="$(mktemp)" && jq --arg projectId "{{.PROJECT_ID}}" '.blueprint.github_id = $projectId' package.json > "$TMP" && mv "$TMP" package.json + status: + - '[[ "{{.CURRENT_PROJECT_ID}}" == "{{.PROJECT_ID}}" ]]' + + ssh-keys: + deps: + - :install:software:gh + - :install:software:jq + cmds: + - .config/log info 'Clearing GitLab SSH keys' + - | + ACCEPT_HEADER="Accept: application/vnd.github+json" + GH_KEYS_URL="https://api.github.com/user/keys" + KEYIDS="$(curl -sSL -H "$ACCEPT_HEADER" -H "Authorization: token $GITHUB_TOKEN" "$GH_KEYS_URL" | jq '.[].id')" + for KEYID in $KEYIDS; do + curl -H "$ACCEPT_HEADER" -H "Authorization: token $GITHUB_TOKEN" -X DELETE "$GH_KEYS_URL/$KEYID" + done + - .config/log start 'Syncing SSH keys with GitHub' + - | + for KEY in `ls $HOME/.ssh/*.pub`; do + TITLE="$(cat $KEY | sed 's/\(.*\=\) \(.*\)$/\2/')" + gh ssh-key add $KEY -t "$TITLE" + done + - .config/log success 'Finished syncing SSH keys with GitHub' + preconditions: + - sh: '[ -n "$GITHUB_TOKEN" ]' + msg: The GITHUB_TOKEN must be set to a personal access token. + + update: + run: once + cmds: + - task: update:deps + status: + - '[ -z "$GITHUB_TOKEN" ]' + + update:deps: + deps: + - update:meta + - ids + - variables + + update:meta: + deps: + - :install:software:gh + - :install:software:jq + - create + vars: + DESCRIPTION: + sh: jq -r '.description' package.json + GITHUB_SLUG: + sh: jq -r '.blueprint.repository.github' package.json | sed 's/.*\///' + HOMEPAGE: + sh: jq -r '.homepage' package.json + PRIVATE: + sh: jq -r '.private' package.json | sed 's/null/false/' + log: + error: Error while updating GitHub repository metadata + start: Updating GitHub project metadata + success: Updated GitHub repository metadata + cmds: + - | + OPTIONAL_TAGS="$(jq '.keywords' .config/common-keywords.json)" + TOPICS="$(jq '.keywords' package.json | sed 's/null/[]/')" + TOPICS_LENGTH="$(jq -r '.keywords | length' package.json)" + if [ ! -z "$GITHUB_TOKEN" ]; then + gh api -X PATCH repos/{{.GITHUB_ORG}}/{{.GITHUB_SLUG}} -f description="{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}" -f homepage="{{.HOMEPAGE}}" \ + -f has_issues={{.GITHUB_ISSUES}} -f has_wiki={{.GITHUB_WIKI}} -f private="{{.PRIVATE}}" --silent + .config/log success 'Ensured GitHub metadata is up-to-date' + RESULT="$TOPICS" + if [ "$TOPICS_LENGTH" -gt 20 ]; then + function updateList() { + REMOVE_KEY="$(jq -n --argjson optional "$OPTIONAL_TAGS" '$optional['"$1"']')" + RESULT="$(jq -n --argjson remove "$REMOVE_KEY" --argjson jq "$RESULT" '$jq | del(.[] | select(. == $remove))')" + } + LOOP_COUNT="$((TOPICS_LENGTH-20))" + for i in $(seq "$LOOP_COUNT"); do + updateList "$i" + done + fi + MINIMIZED_TOPICS="$(jq -n --argjson tags "$RESULT" '$tags | .[]' | xargs | sed 's/ /","/g' | sed 's/^/"/' | sed 's/$/"/')" + if [[ "$MINIMIZED_TOPICS" != '""' ]]; then + curl -s -X PUT -H "Accept: application/vnd.github.mercy-preview+json" -u "{{.GITHUB_ORG}}:$GITHUB_TOKEN" \ + 'https://api.github.com/repos/{{.GITHUB_ORG}}/{{.GITHUB_SLUG}}/topics' -d '{"names":['"$MINIMIZED_TOPICS"']}' > /dev/null + .config/log success 'Updated GitHub topics successfully' + fi + else + .config/log warn 'The `GITHUB_TOKEN` environment variable is not set so the GitHub repository cannot be updated via the API.' + fi + sources: + - .config/common-keywords.json + - .variables.json + - package.json + preconditions: + - sh: '[ "{{.DESCRIPTION}}" != "null" ]' + msg: The `.description` in `package.json` must be set. + - sh: '[ "{{.HOMEPAGE}}" != "null" ]' + msg: The `.homepage` in `package.json` must be set. + + variables: + deps: + - :install:software:gh + - create + log: + error: Error setting GitHub Actions Ansible Galaxy token + start: Setting GitHub Actions Ansible Galaxy token + success: GitHub Actions Ansible Galaxy token set + cmds: + - gh secret set ANSIBLE_GALAXY_TOKEN -b "$ANSIBLE_GALAXY_TOKEN" + status: + - '[ -z "$GITHUB_TOKEN" ] || [ -z "$ANSIBLE_GALAXY_TOKEN" ] || [ "{{.REPOSITORY_TYPE}}" != "ansible" ]' diff --git a/.config/taskfiles/git/Taskfile-gitlab.yml b/.config/taskfiles/git/Taskfile-gitlab.yml new file mode 100644 index 00000000..b8520e5e --- /dev/null +++ b/.config/taskfiles/git/Taskfile-gitlab.yml @@ -0,0 +1,477 @@ +--- +version: '3' + +vars: + REPOSITORY_LIST: .cache/repository-list.txt + +tasks: + access-token: + deps: + - :install:software:glab + - :install:software:jq + - create + vars: + REPO_ID: + sh: glab api projects/:fullpath | jq '.id' + REQUEST_DATA: '{ "name":"PROJECT_CI_ACCESS_TOKEN", "scopes":["write_repository", "read_repository"] }' + TOKEN_STATUS: + sh: type glab > /dev/null && glab api projects/:fullpath/access_tokens | jq -r '.[] | select(.name=="PROJECT_CI_ACCESS_TOKENX") | .id' || true + run: once + log: + error: Failed to configure project-specific access token + start: Configuring project-specific access token + success: Configured project-specific access token + cmds: + - | + curl -sSL --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" --header \ + 'Content-Type:application/json' --data '{{.REQUEST_DATA}}' \ + 'https://gitlab.com/api/v4/projects/{{.REPO_ID}}/access_tokens' > /dev/null + status: + - '[ -n "{{.TOKEN_STATUS}}" ] || ! glab api projects/:fullpath/access_tokens' + + branches: + deps: + - :install:software:glab + - create + vars: + REPO_ID: + sh: glab api projects/:fullpath | jq '.id' + run: once + log: + error: Error encountered while setting up protected branches + start: Setting up protected branches + success: Set up protected branches + cmds: + - | + glab api projects/:fullpath/protected_branches -X POST -f name=master -f code_owner_approval_required=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=main -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=main -f code_owner_approval_required=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=next -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=next -f code_owner_approval_required=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=alpha -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=alpha -f code_owner_approval_required=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=beta -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=beta -f code_owner_approval_required=true &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=protected/* -f code_owner_approval_required=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=synchronize -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=synchronize -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null & + if [[ '{{.REPOSITORY_TYPE}}' == 'ansible' ]]; then + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=test/linux -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=test/linux \ + -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=test/darwin -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=test/darwin \ + -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=test/windows -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=test/windows \ + -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null & + fi + if [[ '{{.REPOSITORY_TYPE}}' == 'packer' ]]; then + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=hyperv -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=hyperv \ + -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=kvm -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=kvm \ + -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=parallels -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=parallels \ + -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=virtualbox -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=virtualbox \ + -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null & + glab api projects/:fullpath/repository/branches -X POST -f id={{.REPO_ID}} -f branch=vmware -f ref=master &> /dev/null & + glab api projects/:fullpath/protected_branches -X POST -f name=vmware \ + -f code_owner_approval_required=true -f allow_force_push=true &> /dev/null & + fi + wait + + create: + deps: + - :install:software:glab + - :install:software:jq + - :git:remotes + vars: + DESCRIPTION: + sh: jq -r '.description' package.json + GITLAB_GROUP: + sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/https:\/\/gitlab.com\///' | sed 's!/[^/]*$!!' + GITLAB_PATH: + sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/https:\/\/gitlab.com\///' + NAME: + sh: jq -r '.blueprint.name' package.json + PROJECT_TYPE: + sh: if [[ $(jq -r '.private' package.json) == 'true' ]]; then echo '--private'; else echo '--public'; fi + TMP: + sh: mktemp + run: once + log: + error: Encountered error while creating GitLab repository + start: Ensuring GitLab repository has been created + success: Ensured GitLab repository exists + cmds: + - cmd: | + NO_PROMPT=1 glab repo create '{{.GITLAB_PATH}}' --group '{{.GITLAB_GROUP}}' \ + --description '{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}' --name '{{.NAME}}' \ + {{.PROJECT_TYPE}} --tag "$(jq -r '.keywords | tostring' package.json | sed 's/\[//' | sed 's/\]//')" + task --list > /dev/null || (echo "ERROR: Invalid Taskfiles!" && exit 1) + git add --all + HUSKY=0 git commit --quiet -m "🎂 Birth" -n || true + git push gitlab master + ignore_error: true + status: + - '[ -z "$GITLAB_TOKEN" ] || (test -d .git && glab repo view "{{.GITLAB_PATH}}" > /dev/null)' + preconditions: + - sh: '[ "{{.DESCRIPTION}}" != "null" ]' + msg: The `.description` in `package.json` must be set. + - sh: '[ "{{.NAME}}" != "null" ]' + msg: The `.blueprint.name` variable in `package.json` must be set. + + group:exec: + cmds: + - task: group:exec:{{if .CLI_ARGS}}cli{{else}}prompt{{end}} + + group:exec:cli: + log: + error: Failed to run group:exec logic + start: Running group:exec logic + success: Successfully ran group:exec logic + cmds: + - task git:gitlab:group:repositories -- {{index (splitList " ---- " .CLI_ARGS) 0}} + - | + BASE_PWD="$PWD" + function execRepo() { + REPO_DETAILS="$1" + REPO_URL="$(echo "$REPO_DETAILS" | sed 's/ .*$//')" + REPO_PATH="$(echo "$REPO_DETAILS" | sed 's/^.* //')" + DIR_NAME="$(dirname "$REPO_PATH")" + mkdir -p "$DIR_NAME" + if [ -d "$REPO_PATH" ]; then + cd "$REPO_PATH" + git pull origin master || continue + cd "$BASE_PWD" + else + git clone "$REPO_URL" "$REPO_PATH" + fi + .config/log info "Running bash command on $REPO_PATH" + ( + cd "$REPO_PATH" + {{trimPrefix "'" (trimSuffix "'" (index (splitList " ---- " .CLI_ARGS) 1))}} + cd "$BASE_PWD" + ) + .config/log success "Finished running bash command on $REPO_PATH" + } + cat {{.REPOSITORY_LIST}} | (while IFS= read -r REPO_DETAILS; do + .config/log info "Executing logic on $REPO_DETAILS" + execRepo "$REPO_DETAILS"{{if eq .GROUP_EXEC_ASYNC "true"}} &{{end}} + done{{if eq .GROUP_EXEC_ASYNC "true"}} + wait){{else}}){{end}} + + group:exec:prompt: + deps: + - :install:software:glab + - :install:software:jq + interactive: true + cmds: + - | + GITLAB_GROUPS="$(glab api --paginate groups | jq -r '.[].full_path')" + GROUP_OPTION="$(gum choose "$ENVIRONMENT_OPTIONS")" + GUM_CONFIRM_MSG="Would you like to use the previously run script (i.e. .cache/gitlab-group.sh)?" + if [ -f .cache/gitlab-group.sh ] && gum confirm "$GUM_CONFIRM_MSG"; then + bash .cache/gitlab-group.sh + else + TMP="$(mktemp)" + gum write --placeholder "Enter the script you would like to run including the hashbang (CTRL+D to finish)" --show-cursor-line --show-line-numbers > "$TMP" + bash "$TMP" + mkdir -p .cache + mv "$TMP" > .cache/gitlab-group.sh + fi + + group:repositories: + deps: + - :install:software:glab + - :install:software:jq + summary: | + # Return repositories belonging to group and sub-groups + + Given a GitLab group path (which can include subgroups as well), this task will cycle through + the group and its' subgroups and generate a list of repositories. The repositories are + saved to a file located at `{{.REPOSITORY_LIST}}`. + + **Example specifying a group and subgroup:** + `task {{.TASK}} -- megabyte-labs/ansible-roles` + log: + error: Encountered error while generating a list of repositories + start: Generating list of repositories + success: List of repositories generated + cmds: + - mkdir -p .cache + - rm -f {{.REPOSITORY_LIST}} + - task: group:repositories:loop + + group:repositories:loop: + cmds: + - glab api "groups/{{urlquery .CLI_ARGS}}" | jq -r '.projects[] | .ssh_url_to_repo + " " + .path_with_namespace' >> {{.REPOSITORY_LIST}} + - | + .config/log info 'Recursively acquiring repository details from subgroups' + SUBGROUPS="$(glab api groups/{{urlquery .CLI_ARGS}}/subgroups | jq -r '.[] | .full_path')" + if [ -n "$SUBGROUPS" ]; then + echo "$SUBGROUPS" | (while IFS= read -r SUBGROUP; do + task {{.TASK}} -- "$SUBGROUP" & + done + wait) + fi + + ids: + deps: + - :install:software:glab + - :install:software:jq + - create + log: + error: Error acquiring GitLab project and group IDs + start: Acquiring GitLab project and group IDs + success: Saved GitLab project and group IDs to package.json + cmds: + - | + API_RES="$(glab api projects/:fullpath)" + PROJECT_ID="$(echo "$API_RES" | jq '.id')" + TMP="$(mktemp)" && jq --arg projectId "$PROJECT_ID" '.blueprint.gitlab_project_id = $projectId' package.json > "$TMP" + mv "$TMP" package.json + GROUP_ID="$(echo "$API_RES" | jq '.namespace.id')" + TMP_GROUP="$(mktemp)" && jq --arg groupId "$GROUP_ID" '.blueprint.gitlab_group_id = $groupId' package.json > "$TMP_GROUP" + mv "$TMP_GROUP" package.json + + integrations: + deps: + - create + run: once + cmds: + - task: integrations:github + + integrations:github: + deps: + - :install:software:glab + - :install:software:jq + log: + error: Error enabling GitLab's GitHub integration + start: Ensuring GitLab's GitHub integration is enabled + success: GitLab's GitHub integration is enabled + cmds: + - glab api projects/:fullpath/integrations/github -X PUT -f token="$GITHUB_TOKEN" + -f repository_url="$(jq -r '.blueprint.repository.github' package.json)" --silent + status: + - '[ -z "$GITLAB_TOKEN" ] || [ -z $GITHUB_TOKEN ]' + + mirror: + deps: + - :install:software:glab + - :install:software:jq + - :git:github:create + - create + vars: + GITHUB_SLUG: + sh: jq -r '.blueprint.repository.github' package.json | sed 's/.*\///' + GITLAB_REPO_ID: + sh: glab api projects/:fullpath | jq -r '.id' + PUSH_MIRROR_COUNT: + sh: glab api projects/:fullpath/remote_mirrors | jq '. | length' + log: + error: Error ensuring push/pull mirroring is enabled between GitLab and GitHub + start: Ensuring push/pull mirroring from GitLab to GitHub is set up + success: Push and pull mirroring from GitLab to GitHub are enabled + cmds: + - | + if [[ "{{.PUSH_MIRROR_COUNT}}" == '0' ]]; then + glab api projects/:fullpath/remote_mirrors --method POST --header "Content-Type: application/json" \ + -f "url=https://{{.GITHUB_USER}}:$GITHUB_TOKEN@github.com/{{.GITHUB_ORG}}/{{.GITHUB_SLUG}}.git" \ + -f 'enabled=true' > /dev/null + .config/log success 'Successfully set up push mirroring from GitLab to GitHub' + fi + - > + curl -s -H 'Content-Type: application/json' -H "Authorization: Bearer $GITLAB_TOKEN" -X PUT --data + "{\"mirror\": true, \"import_url\": \"https://{{.GITHUB_USER}}:$GITHUB_TOKEN@github.com/{{.GITHUB_ORG}}/{{.GITHUB_SLUG}}.git\"}" + 'https://gitlab.com/api/v4/projects/{{.GITLAB_REPO_ID}}' > /dev/null + status: + - '[ -z "$GITLAB_TOKEN ] || [ -z "GITHUB_TOKEN ]' + + pipelines: + deps: + - :install:software:glab + - :install:software:jq + - create + vars: + PIPELINE_COUNT: + sh: jq -r '.gitlab_pipelines | length' .variables.json + log: + error: Error setting up GitLab pipelines + start: Ensuring GitLab pipelines are set up according to the configuration + success: GitLab pipelines are set up + cmds: + - | + PIPELINES="$(jq -r '.gitlab_pipelines' .variables.json)" + PIPELINE_RES="$(glab api projects/:fullpath/pipeline_schedules)" + for INDEX in {1..{{.PIPELINE_COUNT}}}; do + PIPELINE_INDEX="$((INDEX - 1))" + ACTIVE="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].active')" + CRON="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].cron' | sed 's/"//g')" + DESC="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].description')" + REF="$(echo "$PIPELINES" | jq -r --arg i "$PIPELINE_INDEX" '.[$i | tonumber].ref')" + if (! echo "$PIPELINE_RES" | grep "$DESC") > /dev/null; then + glab api projects/:fullpath/pipeline_schedules -X POST -f active="$ACTIVE" -f description="$DESC" -f ref="$REF" \ + -f cron="$CRON" -f cron_timezone='{{.TIMEZONE}}' --silent + if [ "$DESC" != 'null' ]; then .config/log success "Pipeline with description of '${DESC}' successfully added"; fi + else + if [ "$DESC" != 'null' ]; then .config/log info "Pipeline with description of '${DESC}' already added"; fi + fi + done + status: + - '[ -z "$GITLAB_TOKEN" ]' + + pipelines:clear: + deps: + - create + log: + error: Error clearing GitLab pipelines + start: Clearing GitLab pipelines + success: Cleared GitLab pipelines + cmds: + - | + TMP="$(mktemp)" + glab api projects/:id/pipeline_schedules -X GET > "$TMP" + for PIPELINE_ID in $(jq -r '.[].id' "$TMP"); do + glab api projects/:fullpath/pipeline_schedules/"$PIPELINE_ID" -X DELETE + done + preconditions: + - sh: '[ ! -z "$GITLAB_TOKEN" ]' + msg: The `GITLAB_TOKEN` environment variable must be set to run this task + + protected:off: + deps: + - :install:software:glab + vars: + BRANCH: '{{if .BRANCH}}{{.BRANCH}}{{else}}{{.CLI_ARGS}}{{end}}' + todo: figure out how to do it without deleteing the protection first + cmds: + - glab api projects/:fullpath/protected_branches/{{.BRANCH}} -X DELETE + - glab api projects/:fullpath/protected_branches -X POST -f name={{.BRANCH}} -f allow_force_push=true > /dev/null + status: + - '[ -z "$GITLAB_TOKEN" ]' + + protected:on: + deps: + - :install:software:glab + vars: + BRANCH: '{{if .BRANCH}}{{.BRANCH}}{{else}}{{.CLI_ARGS}}{{end}}' + todo: figure out how to do it without deleteing the protection first + cmds: + - glab api projects/:fullpath/protected_branches/{{.BRANCH}} -X DELETE + - glab api projects/:fullpath/protected_branches -X POST -f name={{.BRANCH}} -f allow_force_push=false > /dev/null + status: + - '[ -z "$GITLAB_TOKEN" ]' + + ssh-keys: + deps: + - :install:software:glab + - :install:software:jq + cmds: + - .config/log info 'Clearing GitLab SSH keys' + - | + KEY_URL='https://gitlab.com/api/v4/user/keys' + KEYIDS="$(curl -sSL --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "$KEY_URL" | jq '.[].id')" + for KEYID in $KEYIDS; do + curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" -X DELETE "$KEY_URL/$KEYID" + done + - .config/log start 'Syncing SSH keys with GitLab' + - | + for KEY in `ls $HOME/.ssh/*.pub`; do + TITLE="$(cat $KEY | sed 's/\(.*\=\) \(.*\)$/\2/')" + glab ssh-key add $KEY -t "$TITLE" + done + - .config/log success 'Finished syncing SSH keys with GitLab' + preconditions: + - sh: '[ -n "$GITLAB_TOKEN" ]' + msg: The GITLAB_TOKEN must be set to a personal access token. + + update: + run: once + cmds: + - task: update:deps + status: + - '[ -z "$GITLAB_TOKEN" ] || ! type glab &> /dev/null' + + update:deps: + deps: + - access-token + - branches + - mirror + - integrations + - ids + - pipelines + - update:meta + - wiki + + update:meta: + deps: + - :install:software:glab + - :install:software:jq + - create + vars: + DESCRIPTION: + sh: jq -r '.description' package.json + ISSUES_TEMPLATE: '## Summary\r\n\r\n(Summarize the bug encountered concisely)\r\n\r\n## + Steps to reproduce\r\n\r\n(How one can reproduce the issue - this is very important)\r\n\r\n## + Example Project\r\n\r\n(If possible, please create an example project here on GitLab.com that + exhibits the problematic\r\nbehavior, and link to it here in the bug report.\r\nIf you are using + an older version of GitLab, this will also determine whether the bug has been fixed\r\nin a more + recent version)\r\n\r\n## What is the current bug behavior?\r\n\r\n(What actually happens)\r\n\r\n## + What is the expected correct behavior?\r\n\r\n(What you should see instead)\r\n\r\n## Relevant + logs and/or screenshots\r\n\r\n(Paste any relevant logs - please use code blocks (```) to format + console output, logs, and code, as\r\nit is very hard to read otherwise.)\r\n\r\n## Possible + fixes\r\n\r\n(If you can, link to the line of code that might be responsible for the problem)' + NAME: + sh: jq -r '.blueprint.name' package.json + PROJECT_TYPE: + sh: if [[ $(jq -r '.private' package.json) == 'true' ]]; then echo 'private'; else echo 'public'; fi + TEST_COVERAGE_REGEX: + sh: jq -r '.build_coverage_regex' .variables.json | sed 's/^null$//' + log: + error: Error ensuring GitLab metadata is up-to-date + start: Ensuring GitLab metadata is up-to-date + success: GitLab metadata is up-to-date + cmds: + - | + KEYWORDS="$(jq -r '.keywords | tostring' package.json | sed 's/\[//' | sed 's/\]//' | sed 's/"//g')" + PROJECT_ID="$(glab api projects/:fullpath -X PUT -f build_coverage_regex="{{.TEST_COVERAGE_REGEX}}" \ + -f wiki_enabled={{.GITLAB_WIKI}} -f visibility="{{.PROJECT_TYPE}}" -f topics="$KEYWORDS" | jq '.id')" + curl -s -H 'Content-Type: application/json' -H "Authorization: Bearer $GITLAB_TOKEN" -X PUT --data \ + '{"description": "{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}", "issues_template": "{{.ISSUES_TEMPLATE}}", "name": "{{.NAME}}"}' \ + "https://gitlab.com/api/v4/projects/$PROJECT_ID" > /dev/null + status: + - '[ -z "$GITLAB_TOKEN" ]' + preconditions: + - sh: '[ "{{.DESCRIPTION}}" != "null" ]' + msg: The `.description` in `package.json` must be set. + - sh: '[ "{{.NAME}}" != "null" ]' + msg: The `.blueprint.name` variable in `package.json` must be set. + + wiki: + deps: + - :common:update:variables + - :install:software:glab + - :install:software:jq + - create + vars: + DOCS_URL: + sh: jq -r '.docs.link' .variables.json + log: + error: Failed to update GitLab wiki settings + start: Setting GitLab wiki settings + success: GitLab wiki settings are up-to-date + cmds: + - glab api projects/:fullpath/services/external-wiki -X PUT -f external_wiki_url="{{.DOCS_URL}}" --silent + status: + - '[ -z "$GITLAB_TOKEN" ]' + preconditions: + - sh: '[ "{{.DOCS_URL}}" != "null" ]' + msg: The `.docs.link` variable in `.variables.json` must be set. + - sh: '[ "{{.GROUP}}" != "null" ]' + msg: The `.group` variable in `.variables.json` must be set. diff --git a/.config/taskfiles/git/Taskfile-gitomatic.yml b/.config/taskfiles/git/Taskfile-gitomatic.yml new file mode 100644 index 00000000..a919da73 --- /dev/null +++ b/.config/taskfiles/git/Taskfile-gitomatic.yml @@ -0,0 +1,13 @@ +--- +version: '3' + +tasks: + key:generate: + cmds: + - ssh-keygen -t ecdsa -b 521 -C "$GITLAB_EMAIL" -f ~/.ssh/id_ecdsa_gitomatic -P "" + + sync: + deps: + - :install:go:gitomatic + cmds: + - gitomatic -privkey ~/.ssh/id_ecdsa_gitomatic -interval 14m -pull=true -push=true -author "gitomatic sync" -email "gitomatic@megabyte.space" ./ diff --git a/.config/taskfiles/git/Taskfile-hook.yml b/.config/taskfiles/git/Taskfile-hook.yml new file mode 100644 index 00000000..8ec7cd2b --- /dev/null +++ b/.config/taskfiles/git/Taskfile-hook.yml @@ -0,0 +1,103 @@ +--- +version: '3' + +vars: + GIT_NOTIFY_COLOR: '#0450ec' + +tasks: + commit-msg: + deps: + - :lint:commit + log: + error: '`commit-msg` hook encountered an error!' + start: '`commit-msg` hook running..' + + post-checkout: + deps: + - :install:npm:git-notify + - :install:npm:yarnhook + log: + error: '`post-checkout` hook encountered an error!' + start: '`post-checkout` hook running..' + cmds: + - git-notify checkout --prefix "@notify" --color "{{.GIT_NOTIFY_COLOR}}" "$GIT_PARAMS" + - yarnhook + + post-commit: + log: + error: '`post-commit` hook encountered an error!' + start: '`post-commit` hook running..' + cmds: + - 'true' + + post-merge: + deps: + - :install:npm:git-notify + - :install:npm:yarnhook + log: + error: '`post-merge` hook encountered an error!' + start: '`post-merge` hook running..' + cmds: + - git-notify merge --prefix "@notify" --color "{{.GIT_NOTIFY_COLOR}}" "$GIT_PARAMS" + - yarnhook + + post-rewrite: + deps: + - :install:npm:git-notify + - :install:npm:yarnhook + log: + error: '`post-rewrite` hook encountered an error!' + start: '`post-rewrite` hook running..' + cmds: + - git-notify rewrite --prefix "@notify" --color "{{.GIT_NOTIFY_COLOR}}" "$GIT_PARAMS" + - yarnhook + + pre-commit: + deps: + - :fix:json + - :fix:misc + - :security:gitleaks + - :security:private-keys + log: + error: '`pre-commit` hook encountered an error!' + start: '`pre-commit` hook running..' + + pre-push: + vars: + GITHUB_TOKEN: + sh: echo "$GITHUB_TOKEN" + GITLAB_TOKEN: + sh: echo "$GITLAB_TOKEN" + log: + error: '`pre-push` hook encountered an error!' + start: '`pre-push` hook running..' + cmds: + - | + REMOTE={{index (split " " (trimAll "'" .CLI_ARGS)) "_0"}} + URL={{index (split " " (trimAll "'" .CLI_ARGS)) "_1" | replace .GITLAB_TOKEN "" | replace .GITHUB_TOKEN ""}} + if [[ $URL == http* ]]; then + URL="$(echo $URL | sed 's/^[^@]*@/git@/')" + fi + HOST="$(echo $URL | sed 's/^.*@//' | sed 's/:.*$//')" + ORG="$(echo $URL | sed 's/^.*://' | sed 's/\/.*$//')" + PROJECT="$(echo $URL | sed 's/^[^\/]*\///' | sed 's/.[^\.]*$//')" + IFS=' ' + while read local_ref local_sha remote_ref remote_sha; do + # local_ref -> refs/heads/master + # local_sha -> b14959a077c9b6ced19c29d5ba9c75ddbad5d3ea + # remote_ref -> refs/heads/master + # remote_sha -> 9901a2eb2f34d5744276a80c4b339a88a92a7b53 + REMOTE_BRANCH="$(echo $remote_ref | sed 's/^[^\/]*\/[^\/]*\///')" + .config/log star "Repository -----> https://$HOST/$ORG/$PROJECT" + if [ "$HOST" == 'gitlab.com' ]; then + if [ "$REMOTE_BRANCH" != 'master' ]; then + .config/log star "Open PR --------> https://$HOST/$ORG/$PROJECT/-/merge_requests/new" + fi + .config/log star "View commit ----> https://$HOST/$ORG/$PROJECT/-/commit/$local_sha" + elif [ "$HOST" == 'github.com' ]; then + if [ "$REMOTE_BRANCH" != 'master' ]; then + .config/log star "Open PR --------> https://$HOST/$ORG/$PROJECT/pull/new/$REMOTE_BRANCH" + fi + .config/log star "View commit ----> https://$HOST/$ORG/$PROJECT/commit/$local_sha" + fi + done diff --git a/.config/taskfiles/git/Taskfile-issues.yml b/.config/taskfiles/git/Taskfile-issues.yml new file mode 100644 index 00000000..582bf1e5 --- /dev/null +++ b/.config/taskfiles/git/Taskfile-issues.yml @@ -0,0 +1,82 @@ +--- +version: '3' + +tasks: + exportall: + deps: + - exportall:github + - exportall:gitlab + + exportall:github: + vars: + SLUG: + sh: jq -r '.blueprint.slug' package.json + env: + GH_CURL_AUTH: + sh: | + echo "Authorization: token $GITHUB_TOKEN" + cmds: + - git issue exportall github {{.GITHUB_ORG}} {{.SLUG}} + status: + - '[ -z "$GITHUB_TOKEN" ]' + + exportall:gitlab: + vars: + GITLAB_PATH: + sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/https:\/\/gitlab.com\///' | sed 's/\/[^\/]*$//' | sed 's/\//%2F/g' + GITLAB_SLUG: + sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/.*\/\([^\/]*\)$/\1/' + env: + GL_CURL_AUTH: + sh: | + echo "PRIVATE-TOKEN: $GITLAB_TOKEN" + cmds: + - git issue exportall gitlab {{.GITLAB_PATH}} {{.GITLAB_SLUG}} + status: + - '[ -z "$GITLAB_TOKEN" ]' + + import: + deps: + - import:github + - import:gitlab + + import:github: + vars: + SLUG: + sh: jq -r '.blueprint.slug' package.json + env: + GH_CURL_AUTH: + sh: | + echo "Authorization: token $GITHUB_TOKEN" + cmds: + - git issue import github {{.GITHUB_ORG}} {{.SLUG}} + status: + - '[ -z "$GITHUB_TOKEN" ]' + + import:gitlab: + vars: + GITLAB_PATH: + sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/https:\/\/gitlab.com\///' | sed 's/\/[^\/]*$//' | sed 's/\//%2F/g' + GITLAB_SLUG: + sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/.*\/\([^\/]*\)$/\1/' + env: + GL_CURL_AUTH: + sh: | + echo "PRIVATE-TOKEN: $GITLAB_TOKEN" + cmds: + - git issue import gitlab {{.GITLAB_PATH}} {{.GITLAB_SLUG}} + status: + - '[ -z "$GITLAB_TOKEN" ]' + + init: + cmds: + - cmd: git issue init + ignore_error: true + + synchronize: + deps: + - :install:software:git-issue:admin + cmds: + - task: init + - task: import + - task: exportall diff --git a/.config/taskfiles/git/Taskfile.yml b/.config/taskfiles/git/Taskfile.yml new file mode 100644 index 00000000..b4554582 --- /dev/null +++ b/.config/taskfiles/git/Taskfile.yml @@ -0,0 +1,322 @@ +--- +version: '3' + +vars: + EMOJI_END: + sh: | + if [ -f '.variables.json' ] && type jq &> /dev/null; then + BP_END="$(jq -r '.emoji_end' .variables.json)" + if [ "$BP_END" != 'null' ]; then + echo "$BP_END" + else + if [ "$(jq -r '.emoji_endings[15]' .variables.json)" != 'null' ]; then + echo "$(jq --arg place "$(shuf -i 0-15 -n 1)" -r '.emoji_endings[($place | tonumber)]' .variables.json)" + else + echo "" + fi + fi + fi + EMOJI_START: + sh: | + if [ -f '.variables.json' ] && type jq &> /dev/null; then + BP_START="$(jq -r '.emoji_start' .variables.json)" + if [ "$BP_START" != 'null' ]; then + echo "$BP_START" + else + if [ "$(jq -r '.emoji_beginnings[15]' .variables.json)" != 'null' ]; then + echo "$(jq --arg place "$(shuf -i 0-15 -n 1)" -r '.emoji_beginnings[($place | tonumber)]' .variables.json)" + else + echo "" + fi + fi + fi + GITHUB_ISSUES: true + GITHUB_WIKI: false + GITLAB_WIKI: false + +env: + CLICOLOR: + sh: if [[ "${container:=}" == 'docker' ]]; then echo "0"; else echo "1"; fi + +tasks: + commit:automated: + deps: + - remotes + log: + start: Running automated commit + cmds: + - task --list > /dev/null || (echo "ERROR > Invalid Taskfile(s)!" && exit 1) + - git add --all + - cmd: > + HUSKY=0 git commit -m "☁️ chore(automation): Automated update" -n + ignore_error: true + status: + - '[ "$FULLY_AUTOMATED_TASKS" != "true" ] || [ "$SEMANTIC_RELEASE" == "true" ]' + + convert:folder:submodule: + deps: + - :git:github:update + - :git:gitlab:update + vars: + BASENAME: + sh: basename "$PWD" + GITLAB_REPO: + sh: jq -r '.blueprint.repository.gitlab' package.json + log: + error: Error converting `{{.BASENAME}}` to a submodule + start: Converting `{{.BASENAME}}` directory into a submodule + success: Converted the `{{.BASENAME}}` directory to a submodule + cmds: + - git init + - git remote add origin "{{.GITLAB_REPO}}" + - git add --all + - git commit --quiet -m "🧐 refactor(submodule): Adding folder/project to its own git repository." + - git push --quiet -u --no-progress origin master + - cd .. && rm -rf {{.BASENAME}} + - cd .. && git add {{.BASENAME}} + - cd .. && git commit --quiet -m "🧐 refactor(submodule): Removing folder which will now be a submodule." + - cd .. && git submodule add -b master "{{.GITLAB_REPO}}" {{.BASENAME}} + - cd .. && git add {{.BASENAME}} + - cd .. && git commit --quiet -m "🧐 refactor(submodule): Adding new submodule which was previously a directory." + - cd .. && git push --quiet -u --no-progress origin HEAD + preconditions: + - sh: '[[ ! $(git rev-parse --git-dir) =~ ".git/modules" ]]' + msg: Cannot convert the directory to a submodule - the directory already appears to be a submodule. + + convert:folder:subrepo: + deps: + - :install:software:subrepo + summary: | + # Convert Folder to Sub-repo + + This task will work if you have Taskfiles set up in a sub-directory of a project + that is already a git repository. Instead of using sub-modules, this method makes + the main repository's structure look as if no sub-module wizardry is going on. + + To use this task, go into a sub-directory that you want to make a git subrepo and + then run `task git:convert:folder:subrepo` while making sure that the + `FULLY_AUTOMATED_TASKS` variables is set equal to `true`. There may be other + tokens required for accessing the APIs of GitHub, GitLab, etc. + vars: + BASENAME: + sh: basename "$PWD" + GITLAB_REPO: + sh: jq -r '.blueprint.repository.gitlab' package.json + log: + error: Error encountered while converting `{{.BASENAME}}` into a sub-repo + start: Converting `{{.BASENAME}}` into a sub-repo + success: Converted `{{.BASENAME}}` into a sub-repo + cmds: + - rm -rf .git + - git config url."git@gitlab.com:".insteadOf "https://gitlab.com/" + - git config url."git@github.com:".insteadOf "https://github.com/" + - task: commit:automated + - | + ROLE_DIR="$PWD" + while ! test -d .git; do cd ..; done + if ! git remote get-url {{.BASENAME}} &> /dev/null; then + git remote add {{.BASENAME}} {{.GITLAB_REPO}} + fi + RELATIVE_DIR="$(echo $ROLE_DIR | sed 's@'"$PWD"'/@@')" + task git:commit:automated + HUSKY=0 git subrepo init "$RELATIVE_DIR" -r {{.BASENAME}} -b master + status: + - '[ "$FULLY_AUTOMATED_TASKS" != "true" ]' + + filter: + deps: + - :install:software:bfg + desc: Remove large unnecessary and large files from the repository + summary: | + # Clean Up Git Repository + + This task will remove files that may have been committed by accident like node_modules. + It will also use [bfg](https://rtyley.github.io/bfg-repo-cleaner/) to remove all files + that are over 50MB. + log: + error: Error filtering git history + start: Filtering unnecessary items from git history + success: Successfully filtered unnecessary items from git history + cmds: + - git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch -r **/node_modules/' --prune-empty -- --all + - bfg --strip-blobs-bigger-than 50M + + push:all: + deps: + - :ci:commit:config + - :git:github:create + - :git:gitlab:create + log: + error: Encountered error while running `git push all master --force || git push origin master` + start: Running `git push all master --force || git push origin master` + success: Successfully ran `git push all master --force || git push origin master` + cmds: + - | + if ! git push all master --force; then + git pull -X theirs origin master + git add --all + HUSKY=0 git commit -m "☁️ chore(automation): Automated update to all remotes" -n + if ! git push all master --force; then + task git:gitlab:protected:off -- "master" + git push all master --force + task git:gitlab:protected:on -- "master" + fi + fi + status: + - '[ -n "$CI" ] || [ "$SEMANTIC_RELEASE" == "true" ] || [ "$FULLY_AUTOMATED_TASKS" != "true" ]' + + remotes: + deps: + - :install:software:git + - :install:software:jq + - :install:software:subrepo + desc: Configure the the `origin`, `gitlab`, `github`, and `all` git remotes + summary: | + # Configure Git Remotes + + This task will set the origin to the GitLab repository associated with this project. It will then also create + a remote named `all` which will point to both the GitLab repository and the GitHub mirror. You can then + push to both repositories at the same time by running `git push all master`. + + **Example usage:** + `task git:remotes` + env: + GITHUB_REPO: + sh: jq -r '.blueprint.repository.github' package.json | sed 's/^https:\/\//git@/' | sed 's/github.com\//github.com:/' + GITLAB_REPO: + sh: jq -r '.blueprint.repository.gitlab' package.json | sed 's/^https:\/\//git@/' | sed 's/gitlab.com\//gitlab.com:/' + run: once + log: + error: Error setting git remotes + start: Setting up git remotes + success: Git remotes are set up + cmds: + - git init -q + - | + if [ ! -z "$GITLAB_REPO" ]; then + if git config remote.origin.url > /dev/null; then + git remote set-url origin "${GITLAB_REPO}.git" + else + git remote add origin "${GITLAB_REPO}.git" + .config/log success 'Added git remote named `origin`' + fi + if git config remote.gitlab.url > /dev/null; then + git remote set-url gitlab "${GITLAB_REPO}.git" + else + git remote add gitlab "${GITLAB_REPO}.git" + .config/log success 'Added git remote named `gitlab`' + fi + fi + - | + if [ ! -z "$GITHUB_REPO" ]; then + if git config remote.github.url > /dev/null; then + git remote set-url github "${GITHUB_REPO}.git" + else + git remote add github "${GITHUB_REPO}.git" + .config/log success 'Added git remote named `github`' + fi + fi + - | + if [ ! -z "$GITLAB_REPO" ] && [ ! -z "$GITHUB_REPO" ]; then + if git config remote.all.url > /dev/null; then + git remote rm all + fi + git remote add all "${GITLAB_REPO}.git" + git remote set-url --add --push all "${GITHUB_REPO}.git" + git remote set-url --add --push all "${GITLAB_REPO}.git" + .config/log success 'Added git remote named `all`' + fi + status: + - '[[ "$(git config remote.all.url)" == "${GITLAB_REPO}.git" ]] || [ -n "$CI" ]' + - '[[ "$(git config remote.github.url)" == "${GITHUB_REPO}.git" ]] || [ -n "$CI" ]' + - '[[ "$(git config remote.gitlab.url)" == "${GITLAB_REPO}.git" ]] || [ -n "$CI" ]' + + remove:history:cli: + deps: + - :install:software:git + log: + error: Failed to remove `{{.CLI_ARGS}}` from the git history + start: Removing `{{.CLI_ARGS}}` from the git history + success: Removed `{{.CLI_ARGS}}` from the git history + cmds: + - git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch {{.CLI_ARGS}}' HEAD + + remove:submodules: + deps: + - :install:software:git + desc: Remove all submodules in the current directory and optionally filter by RegEx + summary: | + # Remove submodules in current directory + + This task will remove all the submodules in the current directory and its' children. + You can optionally specify RegEx to only remove submodules that match a particular pattern. + Please note that this task is not _perfect_. You should commit your current changes before using it + and then reset the repository with `git reset --hard HEAD` if anything pops up on `git status` that + you do not like after running it. + + **Example removing all submodules that are children of the working directory:** + `task git:remove-submodules` + + **Example removing all submodules that are children of the working directory and matching a pattern:** + `task git:remove-submodules -- docs` + vars: + GITMODULES_PATH: .gitmodules + REGEX_ARG: + sh: if [ -z "{{.CLI_ARGS}}" ]; then echo ""; else echo " | grep {{.CLI_ARGS}}"; fi + RELATIVE_PATH: + sh: pwd | sed "s,^$(git rev-parse --show-toplevel),," | cut -c2- + # /home/hawkwood/Downloads/Backup/Code + ROOT_GIT: + sh: git rev-parse --git-dir | sed 's/\.git\/modules\(.*\)/.gitmodules/' | sed 's/\.git$/.gitmodules/' | sed 's/.gitmodules$//' + # /home/hawkwood/Downloads/Backup/Code/docker/ci-pipeline/hadolint + TOP_LEVEL: + sh: git rev-parse --show-toplevel + log: + error: Error encountered while attempting to remove submodules + start: Attempting to remove submodules + success: Successfully removed submodules + cmds: + - | + if [ -f '.gitmodules' ]; then + MODULE_PATHS=$(git config --file "{{.GITMODULES_PATH}}" --name-only --get-regexp "{{.RELATIVE_PATH}}" | + sed 's/^submodule\.//' | grep "path$" | sed 's/\.path$//'{{.REGEX_ARG}}) + for MODULE_PATH in "$MODULE_PATHS"; do + # https://github.com/a14m/gitsubmodule/blob/master/gitsubmodule + git config -f '{{.GITMODULES_PATH}}' --remove-section "submodule.$MODULE_PATH" | true + git add '{{.GITMODULES_PATH}}' + # /home/hawkwood/Downloads/Backup/Code/.git/modules/docker/ci-pipeline/hadolint/config + {{if .ROOT_GIT}} + CONFIG_PATH="$(pwd | sed 's,{{.ROOT_GIT}},,')" + {{else}} + CONFIG_PATH='.git' + {{end}} + git config -f '{{.ROOT_GIT}}./{{if ne .ROOT_GIT ""}}.git/modules/{{end}}'"$CONFIG_PATH"'/config' --remove-section "submodule.$MODULE_PATH" + git rm --cached "$MODULE_PATH" + rm -rf "{{.ROOT_GIT}}./.git/modules/$CONFIG_PATH" + rm -rf "$MODULE_PATH" + done + .config/log success "Successfully removed the project's submodules" + else + .config/log info 'This task does not run unless there is a `.gitmodules` file in the current directory' + fi + + update: + cmds: + - task: update:start + status: + - '[ "$FULLY_AUTOMATED_TASKS" != "true" ]' + + update:finish: + deps: + - :git:gitlab:update + - :git:github:update + run: once + + update:start: + run: once + cmds: + - task: :git:gitlab:create + - task: :git:github:create + - task: update:finish + status: + - '[ -n "$CI" ] || ([ -z "$GITLAB_TOKEN" ] && [ -z "$GITHUB_TOKEN" ])' diff --git a/.config/taskfiles/go/Taskfile-goreleaser.yml b/.config/taskfiles/go/Taskfile-goreleaser.yml new file mode 100644 index 00000000..9a4709f5 --- /dev/null +++ b/.config/taskfiles/go/Taskfile-goreleaser.yml @@ -0,0 +1,58 @@ +--- +version: '3' + +vars: + GORELEASER_CONFIG: + sh: if [ -f .goreleaser.yml ]; then echo ".goreleaser.yml"; else echo ".config/goreleaser-{{OS}}.yml"; fi + +tasks: + build: + deps: + - :install:go:goreleaser + - :install:go:nfpm + - :install:software:jq + vars: + CURRENT_TAG: + sh: jq -r '.blueprint.currentBuildTag' package.json + log: + error: GoReleaser encountered an error (config file -> `{{.GORELEASER_CONFIG}}`) + start: Building with project with GoReleaser + success: Completed building the project with GoReleaser + cmds: + - | + unset GITHUB_TOKEN + unset GITLAB_TOKEN + export GOVERSION="$(go version)" + if [ '{{.CURRENT_TAG}}' != 'null' ]; then + export GORELEASER_CURRENT_TAG={{.CURRENT_TAG}} + fi + goreleaser build --config {{.GORELEASER_CONFIG}} --rm-dist --skip-validate + + check: + deps: + - :install:go:goreleaser + log: + error: GoReleaser configuration appears to be invalid (config file -> `{{.GORELEASER_CONFIG}}) + start: Validating the configuration file + success: GoReleaser configuration is valid! + cmds: + - goreleaser check --config {{.GORELEASER_CONFIG}} + + release: + deps: + - :install:go:goreleaser + - :install:go:nfpm + - :install:software:jq + vars: + CURRENT_TAG: + sh: jq -r '.version' package.json + cmds: + - .config/log start 'Publishing compiled assets with GoReleaser' + - task: :publish:snap:register + - | + unset GITLAB_TOKEN + export GOVERSION="$(go version)" + export GORELEASER_CURRENT_TAG=v{{.CURRENT_TAG}} + goreleaser release --config {{.GORELEASER_CONFIG}} --rm-dist --skip-validate + - rm -f build/task.rb build/config.yaml + - .config/log success 'Successfully published assets with GoReleaser!' diff --git a/.config/taskfiles/go/Taskfile-test.yml b/.config/taskfiles/go/Taskfile-test.yml new file mode 100644 index 00000000..94c492ab --- /dev/null +++ b/.config/taskfiles/go/Taskfile-test.yml @@ -0,0 +1,21 @@ +--- +version: '3' + +tasks: + ci: + deps: + - :install:go:gotestsum + cmds: + - gotestsum --junitfile report.xml --format testname + + convey: + deps: + - :install:go:goconvey + desc: Run tests through the browser with GoConvey + summary: | + # Open Go Testing Web UI + + [GoConvey](http://goconvey.co/) allows you to write tests in your IDE + and get live updates in a browser window while writing the tests. + cmds: + - goconvey diff --git a/.config/taskfiles/go/Taskfile.yml b/.config/taskfiles/go/Taskfile.yml new file mode 100644 index 00000000..4f80dd63 --- /dev/null +++ b/.config/taskfiles/go/Taskfile.yml @@ -0,0 +1,109 @@ +--- +version: '3' + +tasks: + build: + deps: + - :install:software:jq + vars: + BUILD_COMMAND: + sh: jq -r '.blueprint.build_command' package.json + BUILD_OUTPUT: + sh: jq -r '.blueprint.build_command_output' package.json + cmds: + - task: build:bin + vars: + BUILD_COMMAND: '{{.BUILD_COMMAND}}' + BUILD_OUTPUT: '{{.BUILD_OUTPUT}}' + status: + - '[[ "{{.BUILD_COMMAND}}" == "null" ]] || [[ "{{.BUILD_OUTPUT}}" == "null" ]]' + + build:bin: + deps: + - :install:software:go + log: + error: Failed to build binary + start: Running build command specified in `package.json` (defined under the `build_command` key in the `blueprint` section) + success: Successfully built the binary + cmds: + - go mod tidy + - '{{.BUILD_COMMAND}}' + sources: + - '**/*.go' + generates: + - '{{.BUILD_OUTPUT}}' + + help: + deps: + - build + - :install:software:jq + vars: + BUILD_COMMAND: + sh: jq -r '.blueprint.build_command' package.json + BUILD_OUTPUT: + sh: jq -r '.blueprint.build_command_output' package.json + log: + error: Failed to query binary help menu output + start: Querying binary help menu output + success: Successfully injected binary help menu output into `.variables.json` + cmds: + - | + TMP_HELP="$(mktemp)" + {{.BUILD_OUTPUT}} --help 2> "$TMP_HELP" + TMP_VARS="$(mktemp)" + jq --arg output "$(cat "$TMP_HELP")" '.help_menu_output = $output' .variables.json > "$TMP_VARS" + mv "$TMP_VARS" .variables.json + status: + - '[[ "{{.BUILD_COMMAND}}" == "null" ]] || [[ "{{.BUILD_OUTPUT}}" == "null" ]]' + + prepare: + cmds: + - task: :go:goreleaser:build + + publish: + cmds: + - task: :go:goreleaser:release + - task: publish:after + - rm -rf dist + + publish:after: + deps: + - publish:after:docker + - publish:after:fury + + publish:after:docker: + summary: | + Unsure why this is here and how it is useful. + cmds: + - .config/log warn 'Unused step go:publish:after:docker - pay attention to this only for debugging purposes' + # cmds: + # - | + # for FILE in build/goreleaserdocker*; do + # [ -e "$FILE" ] && task --dir "$FILE" docker:verify docker:build docker:publish + # break + # done + # - rm -rf build/goreleaserdocker* + + publish:after:fury: + deps: + - :install:software:fury + cmds: + - fury push build/*.deb --account=$FURY_ACCOUNT --api-token=$FURY_DEPLOY_TOKEN + - fury push build/*.rpm --account=$FURY_ACCOUNT --api-token=$FURY_DEPLOY_TOKEN + status: + - '[ -z "$FURY_ACCOUNT" ] || [ -z "$FURY_DEPLOY_TOKEN" ]' + + test: + deps: + - :install:software:go + log: + error: Failed to test Go sources + start: Testing Go sources + success: Successfully finished testing Go sources + cmds: + - go test ./... + + verify: + cmds: + - task: :go:goreleaser:check + - task: :publish:snap:register diff --git a/.config/taskfiles/image/Taskfile.yml b/.config/taskfiles/image/Taskfile.yml new file mode 100644 index 00000000..38583707 --- /dev/null +++ b/.config/taskfiles/image/Taskfile.yml @@ -0,0 +1,170 @@ +--- +version: '3' + +vars: + TINYPNG_API_KEY: z4qhFBLRvNMG9rHmdkSLx1DR1FNZN5gc + +tasks: + compress: + deps: + - :install:npm:tinypng + - :install:software:exiftool + desc: Compress JPG/PNG images via TinyPNG + summary: | + # Compress JPG/PNG images via TinyPNG + + Finding the best stack for compressing images is not easy. All the best stacks seem to + only be available for macOS. This task gets around this shortcoming by using [TinyPNG](https://tinypng.org) + to compress images. Although relying on a third-party service is not ideal, TinyPNG does + a fantastic job compressing images. After an image is compressed, file meta is stored to the + image using 'exiftool'. This helps us avoid compressing the same image twice. + + An API key is embedded in these Taskfiles but it is a free one and has usage limits. If you + would like to use your own credits then you can [sign up for a developer account here] + (https://tinypng.com/developers). After you sign up, save your key to a file at `~/.tinypng` + and this task will use your API key instead. + + **Example of compressing all '**.*.(jpg|png)' files in a project:** + `task image:compress` + + **Example of compressing single image:** + `task image:compress -- ./path/image.png` + cmds: + - task: compress:{{if .CLI_ARGS}}cli{{else}}default{{end}} + + compress:cli: + cmds: + - | + function compressImage() { + if exiftool "$1" &> /dev/null; then + if (exiftool "$1" | grep Comment | grep tinypng-compressed) > /dev/null; then + .config/log info '`'"$1"'` has already been compressed by TinyPNG' + else + if [ -f ~/.tinypng ]; then + tinypng "$1" + else + tinypng -k "{{.TINYPNG_API_KEY}}" "$1" + fi + .config/log "Adding 'tinypng-compressed' as a comment in the EXIF data" + exiftool -overwrite_original -comment="tinypng-compressed" "$1" + .config/log success "Successfully compressed $1" + fi + else + .config/log error 'Failed to extract EXIF data from `$1` so the compression is being skipped' + fi + } + compressImage '{{.CLI_ARGS}}' + + compress:default: + cmds: + - | + function compressImage() { + if exiftool "$1" &> /dev/null; then + if (exiftool "$1" | grep Comment | grep tinypng-compressed) > /dev/null; then + .config/log info '`'"$1"'` has already been compressed by TinyPNG' + else + if [ -f ~/.tinypng ]; then + tinypng "$1" + else + tinypng -k "{{.TINYPNG_API_KEY}}" "$1" + fi + .config/log "Adding 'tinypng-compressed' as a comment in the EXIF data" + exiftool -overwrite_original -comment="tinypng-compressed" "$1" + .config/log success "Successfully compressed $1" + fi + else + .config/log error 'Failed to extract EXIF data from `$1` so the compression is being skipped' + fi + } + while read PATHH; do + compressImage "$PATHH" + done < <(find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.jpg' -o -name '*.png' \)) + sources: + - '**/*.(jpg|png)' + + convert:logo: + deps: + - :install:npm:sharp + - :install:npm:tinypng + cmds: + - sharp -i {{.CLI_ARGS}} -f png -o logo.png resize 200 200 + - tinypng -k "{{.TINYPNG_API_KEY}}" logo.png + - .config/log info 'Generated `logo.png`' + - rm -f {{.CLI_ARGS}} + - .config/log info 'Ensured `{{.CLI_ARGS}}` is removed' + + favicons: + deps: + - :install:npm:real-favicon + cmds: + - task donothing + + resize: + deps: + - :install:npm:sharp + - :install:software:exiftool + desc: Resize an image + summary: | + # Resize images + + This task leverages the NPM package named `sharp` to resize images. To use this task, + specify the width, followed by the height, and then the image path (or just the name + if it is in the same directory). Alternatively, you can open an interactive dialog + to walk you through the process. + + **Example opening an interactive dialog:** + `task image:resize` + + **Example changing the width to 200px and height to 240px of an image named `image.png`:** + `task image:resize -- 200 240 image.png` + cmds: + - task: resize:{{if .CLI_ARGS}}cli{{else}}default{{end}} + + resize:all: + summary: | + # Resize all images in a specific directory + + This task will scan a directory passed in via the CLI and resize the images it finds using arguments + also passed in via the CLI. It will also scan subdirectories. To use this command, follow the following + format: + + **Example for resizing to 250px width and 300px height:** + `task image:resize:all -- path/to/dir ---- 250 300` + cmds: + - | + RESIZE_ALL_ARRAY=({{index (splitList " ---- " .CLI_ARGS) 1}}) + while read IMAGE_PATH; do + task image:resize:cli -- "$RESIZE_ALL_ARRAY[0]" "$RESIZE_ALL_ARRAY[1]" "$IMAGE_PATH" + done < <(find "{{index (splitList " ---- " .CLI_ARGS) 0}}" -type f -name "*.jpg" -or -name "*.jpeg" -or -name "*.png" -or -name "*.bmp" -or -name "*.gif" -or -name "*.tif" -or -name "*.tiff" | sed 's/^.\///') + + resize:cli: + cmds: + - | + ARGS_ARRAY=({{.CLI_ARGS}}) + if ! exiftool "${ARGS_ARRAY[2]}" | grep "Image Size" | grep "${ARGS_ARRAY[0]}x${ARGS_ARRAY[1]}"; then + .config/log info "Resizing "'`'"${ARGS_ARRAY[2]}"'`' + sharp -i "${ARGS_ARRAY[2]}" -o "${ARGS_ARRAY[2]}.resized" resize "${ARGS_ARRAY[0]}" "${ARGS_ARRAY[1]}" + mv "${ARGS_ARRAY[2]}.resized" "${ARGS_ARRAY[2]}" + .config/log success "Successfully resized "'`'"${ARGS_ARRAY[2]}"'`' + else + .config/log info '`'"${ARGS_ARRAY[2]}"'`'" appears to already be the appropriate size" + fi + status: + - 'exiftool "${ARGS_ARRAY[2]}" | grep "Image Size" | grep "${ARGS_ARRAY[0]}x${ARGS_ARRAY[1]}"' + + resize:default: + cmds: + - | + DIRS_TMP="$(mktemp)" + find . -type d -not -path "*/.*" > "$DIRS_TMP" + .config/log prompt 'Select the directory of the image(s) you would like to resize (hidden directories are omitted)' + CHOSEN_DIR="$(.config/log filter "$DIRS_TMP")" + FILES_TMP="$(mktemp)" + find "$CHOSEN_DIR" -type f -name "*.jpg" -or -name "*.jpeg" -or -name "*.png" -or -name "*.bmp" -or -name "*.gif" -or -name "*.tif" -or -name "*.tiff" | sed 's/^.\///' > "$FILES_TMP" + .config/log prompt 'Select the image that you would like to resize' + CHOSEN_IMAGE="$(.config/log filter "$FILES_TMP")" + .config/log prompt 'Enter the desired width' + CHOSEN_WIDTH="$(.config/log input "Enter width..")" + .config/log prompt 'Enter the desired height' + CHOSEN_HEIGHT="$(.config/log input "Enter height..")" + task image:resize:cli -- "$CHOSEN_WIDTH" "$CHOSEN_HEIGHT" "$CHOSEN_IMAGE" diff --git a/.config/taskfiles/install/Taskfile-ansible.yml b/.config/taskfiles/install/Taskfile-ansible.yml new file mode 100644 index 00000000..fb02f8c5 --- /dev/null +++ b/.config/taskfiles/install/Taskfile-ansible.yml @@ -0,0 +1,67 @@ +--- +version: '3' + +vars: + ANSIBLE_VENV: + sh: echo "$HOME/.local/megabytelabs/ansible" + +tasks: + install:ansible: + cmds: + - pip3 install ansible + + main: + deps: + - install:ansible + cmds: + - task: requirements + + requirements: + deps: + - requirements:galaxy + - requirements:python + + requirements:galaxy: + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + ansible-galaxy install -r requirements.yml + status: + - '[ ! -f requirements.yml ]' + + requirements:python: + cmds: + - pip3 install requirements.txt + status: + - '[ ! -f requirements.txt ]' + + role: + deps: + - :install:software:docker + desc: Install an Ansible role by using Docker + summary: | + # Install an Ansible Role + + This task installs a single Ansible role on the localhost. It + will download the role from Ansible Galaxy and then provision + the localhost using a Docker container (so that Ansible dependencies + are not permanently installed). + + **Example:** + `task install:ansible:role -- professormanhattan/androidstudio + cmds: + - | + if [ -d ~/.ansible/roles/{{.CLI_ARGS}} ]; then + rm -rf ~/.ansible/roles/{{.CLI_ARGS}} + fi + - ansible-galaxy install --force --ignore-errors professormanhattan.androidstudio + - echo "TODO - run Docker and provision" + + start: + deps: + - :install:software:python + cmds: + - python3 -m venv {{.ANSIBLE_VENV}} + - | + source {{.ANSIBLE_VENV}}/bin/activate + task install:ansible:main diff --git a/.config/taskfiles/install/Taskfile-apt.yml b/.config/taskfiles/install/Taskfile-apt.yml new file mode 100644 index 00000000..5e276cc0 --- /dev/null +++ b/.config/taskfiles/install/Taskfile-apt.yml @@ -0,0 +1,99 @@ +--- +version: '3' + +tasks: + azure-cli: + env: + AZ_REPO: + sh: lsb_release -cs + cmds: + - sudo apt-get update + - sudo apt-get install -y --no-install-recommends ca-certificates curl apt-transport-https lsb-release gnupg + - curl -sL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/microsoft.gpg > /dev/null + - echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ $AZ_REPO main" | sudo tee /etc/apt/sources.list.d/azure-cli.list + - sudo apt-get update && sudo apt-get install -y --no-install-recommends azure-cli + + clean: + log: + error: Error encountered while cleaning apt-get caches + start: Cleaning apt-get caches + success: Finished cleaning apt-get caches + cmds: + - sudo apt-get autoremove -y + - sudo apt-get clean -y + - sudo rm -rf /var/lib/apt/lists/* /tmp/library-scripts/ + + gcloud: + cmds: + - curl -sSL https://sdk.cloud.google.com | bash + + gitlab-runner: + cmds: + - curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash + - sudo apt-get update && sudo apt-get install -y --no-install-recommends gitlab-runner + + helm: + cmds: + - curl https://baltocdn.com/helm/signing.asc | sudo apt-key add - + - sudo apt-get install apt-transport-https --yes + - echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list + - sudo apt-get update && sudo apt-get install -y --no-install-recommends helm + + kubectl: + cmds: + - sudo apt-get update + - sudo apt-get install -y --no-install-recommends apt-transport-https ca-certificates curl + - sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg + - echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" + | sudo tee /etc/apt/sources.list.d/kubernetes.list + - sudo apt-get update + - sudo apt-get install -y --no-install-recommends kubectl + + node: + cmds: + - curl -sL https://deb.nodesource.com/setup_16.x -o /tmp/node_setup.sh + - sudo bash /tmp/node_setup.sh + - rm /tmp/node_setup.sh + - sudo apt-get update && sudo apt-get install -y --no-install-recommends nodejs + + packer: + cmds: + - curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add - + - sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" + - sudo apt-get update && sudo apt-get install packer + + python: + cmds: + - sudo apt-get update + - sudo apt-get install -y --no-install-recommends software-properties-common + - sudo add-apt-repository -y ppa:deadsnakes/ppa + - sudo apt-get update && sudo apt-get install -y --no-install-recommends python3.10 python3-pip + + sysbench: + cmds: + - curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.deb.sh | sudo bash + - sudo apt-get update && sudo apt-get install -y --no-install-recommends sysbench + + terraform: + cmds: + - curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add - + - sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" + - sudo apt-get update && sudo apt-get install -y --no-install-recommends terraform + + vagrant: + cmds: + - curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add - + - sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" + - sudo apt-get update && sudo apt-get install vagrant + + waypoint: + cmds: + - curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add - + - sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" + - sudo apt-get update && sudo apt-get install -y --no-install-recommends waypoint + + yarn: + cmds: + - curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | sudo tee /usr/share/keyrings/yarnkey.gpg >/dev/null + - echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | sudo tee /etc/apt/sources.list.d/yarn.list + - sudo apt-get update && sudo apt-get install -y --no-install-recommends yarn diff --git a/.config/taskfiles/install/Taskfile-gh.yml b/.config/taskfiles/install/Taskfile-gh.yml new file mode 100644 index 00000000..7e0e840e --- /dev/null +++ b/.config/taskfiles/install/Taskfile-gh.yml @@ -0,0 +1,1366 @@ +--- +version: '3' + +env: + GITHUB_TOKEN: + sh: | + if [ -n "$GITHUB_TOKEN" ]; then + echo "$GITHUB_TOKEN" + else + # Public repository view PAT (user: TimeTravelersHackedMe) + # The gh CLI used to download assets needs a PAT or the user to login so this prevents that requirement + # Split into two variables so GitHub's detect/remove does not mess with anything + FIRST_PART="github_pat_" + SECOND_PART="11ABLI5FQ0RnibLOzu4kY0_SKzixo0dQRKPrniu0vMVabt" + THIRD_PART="mlwrDNBzDaxsWptk3QRA2OIMLFQWKgEGBCgR" + echo "${FIRST_PART}${SECOND_PART}${THIRD_PART}" + fi + +tasks: + act: + cmds: + - task: install:gh + vars: + BIN_NAME: act + PACKAGE: nektos/act + + azurefunctions: + cmds: + - task: azurefunctions:{{OS}} + + azurefunctions:darwin: + cmds: + - echo 'Install azure-functions-core-tools using Homebrew on MacOS' + + azurefunctions:linux: + cmds: + - gh release download -R Azure/azure-functions-core-tools -p "*linux-x64*.zip" + - unzip -q -d azure-functions-cli Azure.Functions.Cli.linux-x64*.zip + - mv azure-functions-cli /opt + - ln -s /opt/azure-functions-cli/func /usr/local/bin/func + - ln -s /opt/azure-functions-cli/gozip /usr/local/bin/gozip + status: + - '[ -f /usr/local/bin/func ]' + + azurefunctions:windows: + cmds: + - echo 'Install azure-functions-core-tools using Chocolatey on Windows' + + bat: + cmds: + - task: install:gh + vars: + BIN_NAME: bat + PACKAGE: sharkdp/bat + TAR_OPTIONS: ' --strip-components 1' + + betwixt: + cmds: + - task: betwixt:{{OS}} + + betwixt:darwin: + cmds: + - gh release download --clobber -R kdzwinel/betwixt -p "*darwin-x64.zip" + - unzip -q Betwixt-darwin-x64.zip + - mv Betwixt-darwin-x64/Betwixt.app /Applications + - rm -f Betwixt-darwin-x64.zip + - rm -rf Betwixt-darwin-x64 + status: + - '[ -d /Applications/Betwixt.app ]' + + betwixt:linux: + cmds: + - gh release download -R kdzwinel/betwixt -p "*linux-x64.zip" + - unzip -q Betwixt-linux-x64.zip + - mv Betwixt-linux-x64 /opt + - ln -s /opt/Betwixt-linux-x64/Betwixt /usr/local/bin/betwixt + status: + - '[ -f /opt/Betwixt-linux-x64/Betwixt ]' + + betwixt:windows: + cmds: + - gh release download -R kdzwinel/betwixt -p "*win32-x64.zip" + - cmd /C powershell 'md "C:\Program Files\Betwixt" -Force' + - tar -xf Betwixt-win32-x64.zip -C "C:\Program Files\Betwixt" --strip-components 1 + - cmd /C del Betwixt-win32-x64.zip + - cmd /C powershell 'New-Item -Value "C:\Program Files\Betwixt\Betwixt.exe" -Path "C:\Users\Public\Desktop\Betwixt" -ItemType SymbolicLink -Force' + status: + - '[ -f "C:\Program Files\Betwixt\Betwixt.exe" ]' + + bitwarden: + cmds: + - task: bw:{{OS}} + status: + - '[ -f /usr/local/bin/bw ]' + + bivac: + cmds: + - task: install:gh + vars: + BIN_NAME: bivac + PACKAGE: camptocamp/bivac + + bw:darwin: + cmds: + - gh release download -R bitwarden/cli -p "bw-macos*.zip" + - mkdir -p /usr/local/bin && unzip -q bw-macos*.zip -d /usr/local/bin && rm -f bw-macos*.zip + - chmod +x /usr/local/bin/bw + + bw:linux: + cmds: + - gh release download -R bitwarden/cli -p "bw-linux*.zip" + - mkdir -p /usr/local/bin && unzip -q bw-linux*.zip -d /usr/local/bin && rm -f bw-linux*.zip + - chmod +x /usr/local/bin/bw + + bw:windows: + cmds: + - gh release download -R bitwarden/cli -p "bw-windows*.zip" + - powershell 'md C:/ProgramData/bin -Force; tar -xf bw-windows*.zip -C C:/ProgramData/bin; del bw-windows*.zip -Force' + + cerebro: + cmds: + - task: cerebro:{{OS}} + + cerebro:darwin: + cmds: + - echo 'Install Cerebro using Homebrew.' + + cerebro:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: Cerebro + PACKAGE: cerebroapp/cerebro + + cerebro:windows: + cmds: + - echo 'Cerebro is not available for Windows.' + + consul-cli: + cmds: + - task: install:gh + vars: + BIN_NAME: consul-cli + PACKAGE: mantl/consul-cli + TAR_OPTIONS: ' --strip-components 1' + + croc: + cmds: + - task: install:gh + vars: + BIN_NAME: croc + PACKAGE: schollz/croc + + ctop: + cmds: + - task: install:gh + vars: + BIN_NAME: ctop + PACKAGE: bcicen/ctop + + cumulus: + cmds: + - echo 'Cumulus is available only for MacOS, install using Homebrew on MacOS.' + + dasel: + cmds: + - task: install:gh + vars: + BIN_NAME: dasel + PACKAGE: TomWright/dasel + + dat: + cmds: + - task: install:gh + vars: + BIN_NAME: dat + PACKAGE: dat-ecosystem-archive/dat + TAR_OPTIONS: ' --strip-components 1' + + ZIP_OPTIONS: ' -j' + delta: + cmds: + - task: install:gh + vars: + BIN_NAME: delta + PACKAGE: dandavison/delta + TAR_OPTIONS: ' --strip-components 1' + + deno: + cmds: + - task: install:gh + vars: + BIN_NAME: deno + PACKAGE: denoland/deno + + deta: + cmds: + - task: install:gh + vars: + BIN_NAME: deta + PACKAGE: deta/deta-cli + + direnv: + cmds: + - task: install:gh + vars: + BIN_NAME: direnv + PACKAGE: direnv/direnv + + dive: + cmds: + - task: install:gh + vars: + BIN_NAME: dive + PACKAGE: wagoodman/dive + + docker-pushrm: + cmds: + - task: install:gh + vars: + BIN_NAME: docker-pushrm + PACKAGE: christian-korneck/docker-pushrm + + dockle: + cmds: + - task: install:gh + vars: + BIN_NAME: dockle + PACKAGE: goodwithtech/dockle + + doctl: + cmds: + - task: install:gh + vars: + BIN_NAME: doctl + PACKAGE: digitalocean/doctl + + dog: + cmds: + - task: install:gh + vars: + BIN_NAME: dog + PACKAGE: ogham/dog + + duf: + cmds: + - task: install:gh + vars: + BIN_NAME: duf + PACKAGE: muesli/duf + + dust: + cmds: + - task: install:gh + vars: + BIN_NAME: dust + PACKAGE: bootandy/dust + TAR_OPTIONS: ' --strip-components 1' + + fd: + cmds: + - task: install:gh + vars: + BIN_NAME: fd + PACKAGE: sharkdp/fd + TAR_OPTIONS: ' --strip-components 1' + # Fails with the error HTTP 404: Not Found (https://api.github.com/repos/henryboldi/felony/releases/latest) because there is no 'latest' release + # felony: + # cmds: + # - task: install:gh + # vars: + # BIN_NAME: felony + # PACKAGE: henryboldi/felony + + ffsend: + cmds: + - task: install:gh + vars: + BIN_NAME: ffsend + PACKAGE: timvisee/ffsend + + filebrowser: + cmds: + - task: install:gh + vars: + BIN_NAME: filebrowser + PACKAGE: filebrowser/filebrowser + + fq: + cmds: + - task: install:gh + vars: + BIN_NAME: fq + PACKAGE: wader/fq + + fuego: + cmds: + - task: install:gh + vars: + BIN_NAME: fuego + PACKAGE: sgarciac/fuego + + fusion: + cmds: + - task: install:gh + vars: + BIN_NAME: fusion + PACKAGE: edgelaboratories/fusion + + ganache: + cmds: + - task: ganache:{{OS}} + + ganache:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: Ganache + PACKAGE: trufflesuite/ganache-ui + + ganache:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: Ganache + PACKAGE: trufflesuite/ganache-ui + + ganache:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: Ganache + EXE_PATTERN: '-p *setup.exe' + + PACKAGE: trufflesuite/ganache-ui + gh: + cmds: + - task: install:gh + vars: + BIN_NAME: gh + PACKAGE: cli/cli + TAR_OPTIONS: ' --strip-components 1' + + gitify: + cmds: + - task: gitify:{{OS}} + + gitify:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: gitify + PACKAGE: manosim/gitify + + gitify:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: Gitify + PACKAGE: manosim/gitify + + gitify:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: gitify + EXE_PATTERN: '-p *Setup*.exe' + + PACKAGE: manosim/gitify + gitleaks: + cmds: + - task: install:gh + vars: + BIN_NAME: gitleaks + PACKAGE: zricethezav/gitleaks + + gitomatic: + cmds: + - task: install:gh + vars: + BIN_NAME: gitomatic + PACKAGE: muesli/gitomatic + + glab: + cmds: + - task: install:gh + vars: + BIN_NAME: glab + PACKAGE: profclems/glab + TAR_OPTIONS: ' --strip-components 1' + + gojq: + cmds: + - task: install:gh + vars: + BIN_NAME: gojq + PACKAGE: itchyny/gojq + TAR_OPTIONS: ' --strip-components 1' + + hey: + cmds: + - task: hey:{{OS}} + + hey:darwin: + cmds: + - curl https://hey-release.s3.us-east-2.amazonaws.com/hey_darwin_amd64 -o /usr/local/bin/hey + - chmod +x /usr/local/bin/hey + status: + - '[ -f /usr/local/bin/hey ]' + + hey:linux: + cmds: + - curl https://hey-release.s3.us-east-2.amazonaws.com/hey_linux_amd64 -o /usr/local/bin/hey + - chmod +x /usr/local/bin/hey + status: + - '[ -f /usr/local/bin/hey ]' + + hey:windows: + cmds: + - curl https://hey-release.s3.us-east-2.amazonaws.com/hey_windows_amd64 -o C:/ProgramData/bin/hey.exe + status: + - '[ -f C:/ProgramData/bin/hey.exe ]' + + install:appimage: + cmds: + - gh release download -R '{{.PACKAGE}}' -p "*.AppImage" + - mv *.AppImage /opt/{{.DEST_APPIMAGE_NAME}}.AppImage + - chmod +x /opt/{{.DEST_APPIMAGE_NAME}}.AppImage + - ln -s /opt/{{.DEST_APPIMAGE_NAME}}.AppImage /usr/local/bin/{{.DEST_APPIMAGE_NAME}} + status: + - '[ -f /opt/{{.DEST_APPIMAGE_NAME}}.AppImage ]' + + install:dmg: + vars: + LOG_SCRIPT: + sh: echo "${PWD}/.config/log" + cmds: + - gh release download --clobber -R '{{.PACKAGE}}' -p "*64*.dmg" -p "*.dmg" + - | + if (type sudo &> /dev/null && sudo -n true); then + COUNT=0 + while read FILE; do + COUNT=$((COUNT + 1)) + echo $COUNT + if [ "$COUNT" == "1" ]; then + hdiutil attach "$FILE" -nobrowse -mountpoint /tmp/{{.DMG_NAME}} + cp -r /tmp/{{.DMG_NAME}}/*.app /Applications/ + hdiutil detach /tmp/{{.DMG_NAME}} + rm "$FILE" + else + {{.LOG_SCRIPT}} warn 'Multiple releases have possibly been downloaded' + fi + done < <(find . -type f -iname "*.dmg") + else + {{.LOG_SCRIPT}} warn 'Run with sudo privileges to install DMG files' + fi + status: + - '[ -d /Applications/{{.DMG_NAME}}.app ]' + + install:gh: + deps: + - :install:software:gh + cmds: + - task: install:gh:{{OS}} + vars: + BIN_NAME: '{{.BIN_NAME}}' + PACKAGE: '{{.PACKAGE}}' + TAR_OPTIONS: '{{.TAR_OPTIONS}}' + ZIP_OPTIONS: '{{.ZIP_OPTIONS}}' + + install:gh:darwin: + deps: + - :install:software:gh + vars: + BIN_FOLDER: + sh: echo "/usr/local/bin" + LOG_SCRIPT: + sh: echo "${PWD}/.config/log" + PATTERNS: + sh: |- + if [[ {{ARCH}} == "arm" ]]; then + echo "-p \"*Darwin_arm64\" -p \"*Darwin_arm64.tar.gz\" -p \"*darwin_arm64\" -p \"*darwin_arm64.tar.gz\" -p \"*darwin-arm64.tar.gz\" \ + -p \"*darwin-arm64\" -p \"*Darwin-64bit.tar.gz\" -p \"*darwin_x64.tar.gz\" -p \"*darwin.tar.gz\" -p \"*macOS-ARM64.tar.gz\" \ + -p \"*aarch64-apple-darwin.zip\" -p \"*-macos\" -p \"*darwin_arm64.zip\" -p \"*arm64-darwin.zip\" -p \"*darwin-amd64.zip\"" + else + echo "-p \"*Darwin_x86_64\" -p \"*Darwin_x86_64.tar.gz\" -p \"*Darwin_amd64.tar.gz\" -p \"*darwin_amd64\" -p \"*darwin_amd64.tar.gz\" \ + -p \"*darwin-amd64.tar.gz\" -p \"*darwin-amd64\" -p \"*Darwin-64bit.tar.gz\" -p \"*darwin-x64\" -p \"*darwin_x64.tar.gz\" -p \"*MacOS_64-bit.tar.gz\" \ + -p \"*darwin-x86_64\" -p \"*darwin_x86_64.tar.gz\" -p \"*darwin.tar.gz\" -p \"*macOS-64bit.tar.gz\" -p \"*x86_64-apple-darwin.zip\" -p \"*-darwin.zip\" \ + -p \"*-macos\" -p \"*macOS_amd64.tar.gz\" -p \"*macOS_x86_64.tar.gz\" -p \"*darwin_amd64.zip\" -p \"*macos-x64.zip\" -p \"*x86_64-darwin.zip\" -p \"*darwin-arm64.zip\"" + fi + env: + TMP: + sh: mktemp -d + cmds: + - gh release download -R "{{.PACKAGE}}" {{.PATTERNS}} -D "$TMP" + - | + cd "$TMP" + COUNT=0 + while read FILE; do + COUNT=$((COUNT + 1)) + if [ "$COUNT" == "1" ]; then + if [[ "$FILE" == *.tar.gz ]]; then + tar -xzf "$FILE"{{.TAR_OPTIONS}} + rm "$FILE" + elif [[ "$FILE" == *.zip ]]; then + echo 'zip' + unzip{{.ZIP_OPTIONS}} "$FILE" + rm "$FILE" + fi + else + {{.LOG_SCRIPT}} warn 'Multiple releases have possibly been downloaded' + fi + done < <(find . -type f) + pwd + ls -la + rm -rf CHANGELOG.md LICENSE LICENSE* README.md autocomplete completion* *.1 *.sh *_autocomplete contrib Changes manpages + mkdir -p {{.BIN_FOLDER}} + if [ "$(ls 2>/dev/null * | wc -l | tr -d ' ')" == "1" ]; then + {{.LOG_SCRIPT}} info 'Moving `{{.BIN_NAME}}` from its default location' + mv * {{.BIN_FOLDER}}/{{.BIN_NAME}} + else + if [ -d bin ]; then + cd bin + if [ "$(ls 2>/dev/null * | wc -l | tr -d ' ')" == "1" ]; then + {{.LOG_SCRIPT}} info 'Moving `{{.BIN_NAME}}` from the `bin` folder' + mv * {{.BIN_FOLDER}}/{{.BIN_NAME}} + else + {{.LOG_SCRIPT}} warn 'Unmatched binary named `{{.BIN_NAME}}` from `*.tar.gz` with a `bin` folder' + {{.COMMANDS}} + fi + cd .. + elif [ "$(ls 2>/dev/null {{.BIN_NAME}} | wc -l | tr -d ' ')" == "1" ]; then + {{.LOG_SCRIPT}} warn 'Moving `{{.BIN_NAME}}` from its default location, but, there are other files that are not moved' + mv {{.BIN_NAME}} {{.BIN_FOLDER}}/{{.BIN_NAME}} + else + echo "$(ls 2>/dev/null {{.BIN_NAME}} | wc -l | tr -d ' ')" + {{.LOG_SCRIPT}} warn 'Unmatched binary named `{{.BIN_NAME}}`' + {{.COMMANDS}} + fi + fi + - chmod +x {{.BIN_FOLDER}}/{{.BIN_NAME}} + status: + - '[ -f /usr/local/bin/{{.BIN_NAME}} ]' + + install:gh:linux: + deps: + - :install:software:gh + vars: + BIN_FOLDER: + sh: echo "/usr/local/bin" + LOG_SCRIPT: + sh: echo "${PWD}/.config/log" + PATTERNS: -p "*Linux_x86_64" -p "*Linux_x86_64.tar.gz" -p "*Linux_amd64.tar.gz" -p "*linux_amd64" -p "*linux_amd64.tar.gz" -p "*linux-amd64.tar.gz" + -p "*linux-amd64" -p "*Linux-64bit.tar.gz" -p "*linux-x64" -p "*linux_x64.tar.gz" -p "*linux-x86_64" -p "*linux_x86_64.tar.gz" -p "*linux_amd64.zip" + -p "*linux-x64.zip" -p "*linux-x64.tar.gz" -p "*x86_64-linux.zip" -p "*Linux_64-bit.tar.gz" -p "*linux-amd64.zip" + PATTERNS_GNU: -p "*x86_64-unknown-linux-gnu.tar.gz" -p "*x86_64-unknown-linux-gnu.zip" + PATTERNS_MUSL: -p "*x86_64-unknown-linux-musl.tar.gz" + env: + TMP: + sh: mktemp -d + cmds: + - gh release download -R "{{.PACKAGE}}" {{.PATTERNS}} {{.PATTERNS_GNU}} -D "$TMP" + - | + cd "$TMP" + COUNT=0 + while read FILE; do + COUNT=$((COUNT + 1)) + if [ "$COUNT" == "1" ]; then + if [[ "$FILE" == *.tar.gz ]]; then + tar -xzf "$FILE"{{.TAR_OPTIONS}} + rm "$FILE" + elif [[ "$FILE" == *.zip ]]; then + unzip{{.ZIP_OPTIONS}} "$FILE" + rm "$FILE" + fi + else + {{.LOG_SCRIPT}} warn 'Multiple releases have possibly been downloaded' + fi + done < <(find . -type f) + ls -la + rm -rf CHANGELOG.md LICENSE LICENSE* README.md autocomplete completion* *.1 *.sh *_autocomplete contrib Changes manpages + mkdir -p {{.BIN_FOLDER}} + if [ "$(ls 2>/dev/null * | wc -l)" == "1" ]; then + {{.LOG_SCRIPT}} info 'Moving `{{.BIN_NAME}}` from its default location' + if ! mv * {{.BIN_FOLDER}}/{{.BIN_NAME}}; then + sudo mv * {{.BIN_FOLDER}}/{{.BIN_NAME}} + fi + else + if [ -d bin ]; then + cd bin + if [ "$(ls 2>/dev/null * | wc -l)" == "1" ]; then + {{.LOG_SCRIPT}} info 'Moving `{{.BIN_NAME}}` from the `bin` folder' + if ! mv * {{.BIN_FOLDER}}/{{.BIN_NAME}}; then + sudo mv * {{.BIN_FOLDER}}/{{.BIN_NAME}} + fi + else + {{.LOG_SCRIPT}} warn 'Unmatched binary named `{{.BIN_NAME}}` from `*.tar.gz` with a `bin` folder' + {{.COMMANDS}} + fi + cd .. + elif [ "$(ls 2>/dev/null {{.BIN_NAME}} | wc -l)" == "1" ]; then + {{.LOG_SCRIPT}} warn 'Moving `{{.BIN_NAME}}` from its default location, but, there are other files that are not moved' + if ! mv {{.BIN_NAME}} {{.BIN_FOLDER}}/{{.BIN_NAME}}; then + sudo mv {{.BIN_NAME}} {{.BIN_FOLDER}}/{{.BIN_NAME}} + fi + else + {{.LOG_SCRIPT}} warn 'Unmatched binary named `{{.BIN_NAME}}`' + {{.COMMANDS}} + fi + fi + - chmod +x {{.BIN_FOLDER}}/{{.BIN_NAME}} + status: + - '[ -f {{.BIN_FOLDER}}/{{.BIN_NAME}} ]' + + install:gh:windows: + deps: + - :install:software:gh + vars: + BIN_FOLDER: C:/ProgramData/bin + PATTERNS: + -p "*x86_64-windows.zip" -p "*Windows_64-bit.tar.gz" -p "*windows-amd64*.zip" -p "*windows_amd64.zip" -p "*windows_amd64.tar.gz" -p "*windows-amd64*" + -p "*win-x64.zip" -p "*Windows-64bit.zip" -p "*64*windows-msvc.zip" -p "*windows_x64.zip" -p "*Windows_x86_64.tar.gz" -p "*win-*.exe" -p "*setup*.exe" -p "*Setup*.exe" + -p "*installer*64*.exe" + env: + TMPD: + sh: powershell '$rnd=Get-Random; while(! (Test-Path $env:tmp/tmp.$rnd) -and ! ($created)){$ret=(md $env:tmp/tmp.$rnd -Force).FullName; $created=$true}$ret' + cmds: + - gh release download -R "{{.PACKAGE}}" {{.PATTERNS}} {{.EXE_PATTERN}} -D "$TMPD" + - cd "$TMPD" && powershell ' + $COUNT = 0; + $installer=$false; + Get-ChildItem -File | %{ + $FILE = $_.Name; + $COUNT = $COUNT + 1; + if($COUNT -eq 1 ){ + if (("$FILE" -like "*.tar.gz") -or ("$FILE" -like "*.zip")){ + tar -xzf "$FILE"{{.TAR_OPTIONS}}; + Remove-Item "$FILE" -Force; + }elseif((! $installer) -and (("$FILE" -like "*setup*64*.exe") -or ("$FILE" -like "*setup*.exe") -or ("$FILE" -like "*installer*64*.exe") -or ("$FILE" -like "*64*installer*.exe"))){ + $installer=$true; + Start-Process "$FILE" /S -NoNewWindow -Wait -PassThru; + Remove-Item "$FILE" -Force; + } + }else{ + Write-Host 'Multiple releases have possibly been downloaded' -ForegroundColor Yellow; + } + }; + Get-ChildItem; + Remove-Item "CHANGELOG.md", "LICENSE", "LICENSE*", "README.md", "autocomplete", "completion*", "*_autocomplete", "contrib", "Changes" -EA SilentlyContinue -Force; + mkdir {{.BIN_FOLDER}} -EA SilentlyContinue; + pwd; + if($installer){ + Write-Host '{{.BIN_NAME}} installed using the installer'; + }elseif((Get-ChildItem -File | Measure).Count -eq 1){ + Write-Host 'Moving {{.BIN_NAME}} from its default location'; + Move-Item -Force * {{.BIN_FOLDER}}/{{.BIN_NAME}}.exe; + }else{ + if(Test-Path .\bin){ + cd bin; + if((Get-ChildItem -File | Measure).Count -eq 1){ + Write-Host 'Moving {{.BIN_NAME}} from the bin folder'; + Move-Item -Force * {{.BIN_FOLDER}}/{{.BIN_NAME}}.exe; + }else{ + Write-Host 'Unmatched binary named {{.BIN_NAME}} from *.tar.gz with a bin folder' -ForegroundColor Yellow; + } + cd ..; + }elseif((Get-ChildItem -File {{.BIN_NAME}}* -ea SilentlyContinue | Measure).Count -gt 0){ + Write-Host 'Moving {{.BIN_NAME}} from its default location, but, there are other files that are not moved' -ForeGroundColor Yellow; + Move-Item -Force {{.BIN_NAME}}*.exe {{.BIN_FOLDER}}/; + }else{ + Write-Host 'Unmatched binary named {{.BIN_NAME}}' -ForegroundColor Yellow; + } + }' + status: + - '[ -f {{.BIN_FOLDER}}/{{.BIN_NAME}}.exe ]' + + jitsi-meet-electron: + cmds: + - task: jitsi-meet-electron:{{OS}} + status: + - '[ -f /usr/local/bin/jitsi-meet-electron ]' + + jitsi-meet-electron:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: jitsi-meet + PACKAGE: jitsi/jitsi-meet-electron + + jitsi-meet-electron:linux: + cmds: + - gh release download -R jitsi/jitsi-meet-electron -p "*.AppImage" + - mv *.AppImage /opt/jitsi-meet.AppImage + - chmod +x /opt/jitsi-meet.AppImage + - ln -s /opt/jitsi-meet.AppImage /usr/local/bin/jitsi-meet-electron + + jitsi-meet-electron:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: jitsi-meet + PACKAGE: jitsi/jitsi-meet-electron + + jq: + cmds: + - task: jq:{{OS}} + status: + - '[ -f /usr/local/bin/jq ]' + + jq:darwin: + cmds: + - gh release download -R stedolan/jq -p "jq-osx-amd64" + - mkdir -p /usr/local/bin && mv jq-osx-amd64 /usr/local/bin/jq + - chmod +x /usr/local/bin/jq + + jq:linux: + cmds: + - gh release download -R stedolan/jq -p "jq-linux64" + - mkdir -p /usr/local/bin && mv jq-linux64 /usr/local/bin/jq + - chmod +x /usr/local/bin/jq + + jq:windows: + cmds: + - gh release download -R stedolan/jq -p "jq-win64.exe" + - powershell 'md C:/ProgramData/bin -Force; Move-Item jq-win64.exe C:/ProgramData/bin/jq.exe -Force' + + kubectx: + cmds: + - task: install:gh + vars: + BIN_NAME: kubectx + PACKAGE: ahmetb/kubectx + + kubenav: + cmds: + - task: kubenav:{{OS}} + + kubenav:darwin: + vars: + PATTERNS: + sh: |- + if [[ {{ARCH}} == "arm" ]]; then + echo "-p \"*darwin-arm64.zip\"" + else + echo "-p \"*darwin-amd64.zip\"" + fi + env: + TMP: + sh: mktemp -d + cmds: + - gh release download -R kubenav/kubenav {{.PATTERNS}} -D "$TMP" + - | + cd "$TMP" + FILE=$(find . -type f -iname *.zip) + unzip -q "$FILE" + rm "$FILE" + mv kubenav.app /Applications/ + status: + - '[ -d /Applications/kubenav.app ]' + + kubenav:linux: + vars: + PATTERNS: + sh: |- + if [[ {{ARCH}} == "arm" ]]; then + echo "-p \"*linux-arm64.zip\"" + else + echo "-p \"*linux-amd64.zip\"" + fi + env: + TMP: + sh: mktemp -d + cmds: + - gh release download -R kubenav/kubenav {{.PATTERNS}} -D "$TMP" + - | + cd "$TMP" + FILE=$(find . -type f -iname *.zip) + unzip -q "$FILE" + rm "$FILE" + mv kubenav /usr/local/bin + chmod +x /usr/local/bin/kubenav + status: + - '[ -f /usr/local/bin/kubenav ]' + + kubenav:windows: + cmds: + - gh release download -R kubenav/kubenav -p "*windows-amd64.zip" + - tar -xf *windows-amd64.zip + - cmd /C move kubenav.exe C:/ProgramData/bin/kubenav.exe + - cmd /C del /Q *windows-amd64.zip + status: + - '[ -f C:/ProgramData/bin/kubenav.exe ]' + + linuxkit: + cmds: + - task: install:gh + vars: + BIN_NAME: linuxkit + PACKAGE: linuxkit/linuxkit + + manta: + cmds: + - task: manta:{{OS}} + + manta:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: Manta + PACKAGE: hql287/manta + + manta:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: Manta + PACKAGE: hql287/manta + + manta:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: Manta + EXE_PATTERN: '*Setup*.exe' + + PACKAGE: hql287/manta + mark-text: + cmds: + - task: marktext:{{OS}} + + marktext:darwin: + cmds: + - echo 'Install Marktext using Homebrew.' + + marktext:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: Marktext + PACKAGE: marktext/marktext + + marktext:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: marktext + PACKAGE: marktext/marktext + + masscode: + cmds: + - task: masscode:{{OS}} + status: + - '[ -f /usr/local/bin/masscode ]' + + masscode:darwin: + cmds: + - echo 'Install MassCode using Homebrew on MacOS.' + + masscode:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: MassCode + PACKAGE: antonreshetov/masscode + + masscode:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: masscode + EXE_PATTERN: '*Setup*.exe' + + PACKAGE: antonreshetov/masscode + mergestat: + cmds: + - task: install:gh + vars: + BIN_NAME: mergestat + PACKAGE: mergestat/mergestat + + mkcert: + cmds: + - task: install:gh + vars: + BIN_NAME: mkcert + PACKAGE: FiloSottile/mkcert + + motrix: + cmds: + - task: motrix:{{OS}} + + motrix:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: Motrix + PACKAGE: agalwood/motrix + + motrix:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: Motrix + PACKAGE: agalwood/motrix + + motrix:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: Motrix + PACKAGE: agalwood/motrix + + mqttx: + cmds: + - task: install:gh + vars: + BIN_NAME: mqttx + EXE_PATTERN: '*Setup*x64.exe' + + PACKAGE: agalwood/mqttx + nebula: + cmds: + - task: install:gh + vars: + BIN_NAME: nebula + PACKAGE: slackhq/nebula + + nfpm: + cmds: + - task: install:gh + vars: + BIN_NAME: nfpm + PACKAGE: goreleaser/nfpm + + oq: + cmds: + - task: install:gh + vars: + BIN_NAME: oq + PACKAGE: Blacksmoke16/oq + + osquery: + cmds: + - task: osquery:{{OS}} + + osquery:darwin: + cmds: + - task: install:gh + vars: + BIN_NAME: osquery + PACKAGE: osquery/osquery + + osquery:linux: + env: + TMP: + sh: mktemp -d + cmds: + - gh release download -R osquery/osquery -p "*linux_x86_64.tar.gz" -D "$TMP" + - | + cd "$TMP" + FILE=$(find . -type f) + tar -xzf "$FILE" + rm "$FILE" + cp -r * / + status: + - '[ -f /usr/bin/osqueryd]' + + osquery:windows: + cmds: + - echo 'osquery is not available for Windows.' + + peco: + cmds: + - task: install:gh + vars: + BIN_NAME: peco + PACKAGE: peco/peco + TAR_OPTIONS: ' --strip-components 1' + ZIP_OPTIONS: ' -j' + + pretzel: + cmds: + - task: pretzel:{{OS}} + + pretzel:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: Pretzel + PACKAGE: amiechen/pretzel + + pretzel:linux: + cmds: + - echo 'Pretzel is not available for Linux' + + pretzel:windows: + cmds: + - echo 'Pretzel is not available for Windows' + + q: + cmds: + - task: install:gh + vars: + BIN_NAME: q + PACKAGE: harelba/q + + raindrop: + cmds: + - task: raindrop:{{OS}} + + raindrop:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: raindrop + PACKAGE: raindropio/raindrop + + raindrop:linux: + cmds: + - echo 'Raindrop is not available for Linux' + + raindrop:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: raindrop + EXE_PATTERN: '*Installer.exe' + + PACKAGE: raindropio/raindrop + responsively: + cmds: + - task: responsively:{{OS}} + + responsively:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: ResponsivelyApp + PACKAGE: responsively-org/responsively-app + + responsively:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: Responsively + PACKAGE: responsively-org/responsively-app + + responsively:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: ResponsivelyApp + PACKAGE: responsively-org/responsively-app + + runjs: + cmds: + - task: runjs:{{OS}} + + runjs:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: runjs + PACKAGE: lukehaas/RunJS + + runjs:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: RunJS + PACKAGE: lukehaas/RunJS + + runjs:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: runjs + PACKAGE: lukehaas/RunJS + + shfmt: + cmds: + - task: install:gh + vars: + BIN_NAME: shfmt + PACKAGE: mvdan/sh + + ssl-proxy: + cmds: + - task: install:gh + vars: + BIN_NAME: ssl-proxy + PACKAGE: suyashkumar/ssl-proxy + + switchhosts: + cmds: + - task: switchhosts:{{OS}} + + switchhosts:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: SwitchHosts + PACKAGE: oldj/SwitchHosts + + switchhosts:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: SwitchHosts + PACKAGE: oldj/SwitchHosts + + switchhosts:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: SwitchHosts + PACKAGE: oldj/SwitchHosts + + tabby: + cmds: + - task: tabby:{{OS}} + + tabby:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: tabby + PACKAGE: Eugeny/tabby + + tabby:linux: + env: + TMP: + sh: mktemp -d + cmds: + - gh release download -R Eugeny/tabby -p "*linux-x64.tar.gz" -D "$TMP" + - | + cd "$TMP" + FILE=$(find . -type f) + mkdir Tabby + tar -xzf "$FILE" -C Tabby --strip-components 1 + rm "$FILE" + mv Tabby /opt/ + ln -s /opt/Tabby/tabby /usr/local/bin/tabby + status: + - '[ -f /usr/local/bin/tabby]' + + tabby:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: tabby + PACKAGE: jackd248/tabby + + task: + cmds: + - task: install:gh + vars: + BIN_NAME: task + PACKAGE: go-task/task + + temps: + cmds: + - task: temps:{{OS}} + + temps:darwin: + env: + TMP: + sh: mktemp -d + cmds: + - gh release download -R jackd248/temps -p "*darwin-x64.zip" -D "$TMP" + - | + cd "$TMP" + unzip -q "Temps-darwin-x64.zip" + rm "Temps-darwin-x64.zip" + mv Temps-darwin-x64/*.app /Applications/ + status: + - '[ -d /Applications/Temps.app ]' + + temps:linux: + env: + TMP: + sh: mktemp -d + cmds: + - gh release download -R jackd248/temps -p "*linux-x64.zip" -D "$TMP" + - | + cd "$TMP" + unzip -q "Temps-linux-x64.zip" + rm "Temps-linux-x64.zip" + mv Temps-linux-x64 /opt/ + ln -s /opt/Temps-linux-x64/Temps /usr/local/bin/Temps + status: + - '[ -f /usr/local/bin/Temps]' + + temps:windows: + cmds: + - echo 'Temps does not work on Windows' + + tokei: + cmds: + - task: install:gh + vars: + BIN_NAME: tokei + PACKAGE: XAMPPRocky/tokei + + transfer: + cmds: + - task: install:gh + vars: + BIN_NAME: transfer + PACKAGE: rinetd/transfer + + trivy: + cmds: + - task: install:gh + vars: + BIN_NAME: trivy + PACKAGE: aquasecurity/trivy + + udemy-downloader-gui: + cmds: + - task: udemy-downloader-gui:{{OS}} + + udemy-downloader-gui:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: Udeler + PACKAGE: FaisalUmair/udemy-downloader-gui + + udemy-downloader-gui:linux: + cmds: + - task: install:appimage + vars: + DEST_APPIMAGE_NAME: UdemyDownloadedGUI + PACKAGE: FaisalUmair/udemy-downloader-gui + + udemy-downloader-gui:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: udemy-downloader-gui + PACKAGE: FaisalUmair/udemy-downloader-gui + + up: + cmds: + - task: up:{{OS}} + status: + - '[ -f /usr/local/bin/up ]' + + up:darwin: + cmds: + - gh release download -R akavel/up -p "up-darwin" + - mkdir -p /usr/local/bin && mv up-darwin /usr/local/bin/up + - chmod +x /usr/local/bin/up + + up:linux: + cmds: + - gh release download -R akavel/up -p "up" + - mkdir -p /usr/local/bin && mv up /usr/local/bin/up + - chmod +x /usr/local/bin/up + + up:windows: + cmds: + - echo 'up is not available for Windows from GitHub Releases' + + ventoy: + cmds: + - task: ventoy:{{OS}} + status: + - '[ -f /opt/ventoy/VentoyGUI.x86_64 ]' + + ventoy:darwin: + cmds: + - echo "Ventoy is not available for MacOS" + + ventoy:linux: + cmds: + - gh release download -R ventoy/Ventoy -p "*linux.tar.gz" + - ls *linux.tar.gz | xargs tar -xvf + - rm -f *.tar.gz + - mkdir -p /opt/ventoy && mv ./ventoy-* /opt/ventoy/ + + ventoy:windows: + cmds: + - gh release download -R ventoy/ventoy -p "ventoy_windows_amd64.exe" + - powershell 'md C:/ProgramData/bin -Force; Move-Item ventoy_windows_amd64.exe C:/ProgramData/bin/ventoy.exe -Force' + + webtorrent: + cmds: + - task: webtorrent:{{OS}} + + webtorrent:darwin: + cmds: + - task: install:dmg + vars: + DMG_NAME: WebTorrent + PACKAGE: webtorrent/webtorrent-desktop + + webtorrent:linux: + env: + TMP: + sh: mktemp -d + cmds: + - gh release download -R webtorrent/webtorrent-desktop -p "*linux-x64.zip" -D "$TMP" + - | + cd "$TMP" + FILE=$(find . -type f) + unzip -q $FILE + rm "$FILE" + mv WebTorrent-linux-x64 /opt/ + ln -s /opt/WebTorrent-linux-x64/WebTorrent /usr/local/bin/webtorrent + status: + - '[ -f /usr/local/bin/webtorrent]' + + webtorrent:windows: + cmds: + - task: install:gh + vars: + BIN_NAME: WebTorrent + PACKAGE: webtorrent/webtorrent-desktop + + whaler: + cmds: + - task: whaler:{{OS}} + status: + - '[ -f /usr/local/bin/whaler ]' + + whaler:darwin: + cmds: + - gh release download -R P3GLEG/whaler -p "Whaler_MacOS" + - mkdir -p /usr/local/bin && mv Whaler_MacOS /usr/local/bin/whaler + - chmod +x /usr/local/bin/whaler + + whaler:linux: + cmds: + - gh release download -R P3GLEG/whaler -p "Whaler_linux_amd64" + - mkdir -p /usr/local/bin && mv Whaler_linux_amd64 /usr/local/bin/whaler + - chmod +x /usr/local/bin/whaler + + whaler:windows: + cmds: + - gh release download -R P3GLEG/whaler -p "Whaler_windows.exe" + - powershell 'md C:/ProgramData/bin -Force; Move-Item Whaler_windows.exe C:/ProgramData/bin/whaler.exe -Force' + + yq: + cmds: + - task: yq:{{OS}} + status: + - '[ -f /usr/local/bin/yq ]' + + yq:darwin: + cmds: + - gh release download -R mikefarah/yq -p "yq_darwin_amd64" + - mkdir -p /usr/local/bin && mv yq_darwin_amd64 /usr/local/bin/yq + - chmod +x /usr/local/bin/yq + + yq:linux: + cmds: + - gh release download -R mikefarah/yq -p "yq_linux_amd64" + - mkdir -p /usr/local/bin && mv yq_linux_amd64 /usr/local/bin/yq + - chmod +x /usr/local/bin/yq + + yq:windows: + cmds: + - gh release download -R mikefarah/yq -p "yq_windows_amd64.exe" + - powershell 'md C:/ProgramData/bin -Force; Move-Item yq_windows_amd64.exe C:/ProgramData/bin/yq.exe -Force' diff --git a/.config/taskfiles/install/Taskfile-github.yml b/.config/taskfiles/install/Taskfile-github.yml new file mode 100644 index 00000000..838c185f --- /dev/null +++ b/.config/taskfiles/install/Taskfile-github.yml @@ -0,0 +1,72 @@ +--- +version: '3' + +tasks: + bundle: + deps: + - docker-pushrm + - fusion + + docker-pushrm: + run: once + tags: + - update + cmds: + - task: install:github + vars: + BIN_NAME: docker-pushrm + PACKAGE: github.com/christian-korneck/docker-pushrm + - mkdir -p "$HOME/.docker/cli-plugins" + - | + if type docker-pushrm &> /dev/null; then + mv "$(which docker-pushrm)" "$HOME/.docker/cli-plugins/docker-pushrm" + else + mv "$HOME/.local/go/bin/docker-pushrm" "$HOME/.docker/cli-plugins/docker-pushrm" + fi + - chmod +x "$HOME/.docker/cli-plugins/docker-pushrm" + status: + - '[ -f "$HOME/.docker/cli-plugins/docker-pushrm" ]' + + fusion: + run: once + tags: + - update + cmds: + - task: install:github + vars: + BIN_NAME: fusion + PACKAGE: github.com/edgelaboratories/fusion + + install:github: + deps: + - :install:software:jq + vars: + BIN: '{{.BIN_NAME}}' + run: when_changed + log: + error: Failed to acquire GitHub release from `{{.PACKAGE}}` + start: Acquiring GitHub binary release from `{{.PACKAGE}}` + success: Installed GitHub release from `{{.PACKAGE}}`, available as `{{.BIN}}` + cmds: + - .config/log info "PATH variable --> $PATH" + - task: :install:go:bin + - mkdir -p "$HOME/.config/bin" + - jq '. | .default_path = "./.bin" | .bins = {}' <<< $(echo '{}') > "$HOME/.config/bin/config.json" + - | + if [ -n "$CI" ]; then echo "*************** GitHub --> {{.BIN}}"; fi + - | + if [ -z "$GOPATH" ]; then + export GOPATH="$HOME/.local/go" + fi + export PATH="$PATH:$HOME/.local/go/bin" + bin install -f {{.PACKAGE}} "$GOPATH/bin/{{.BIN}}" + status: + - type {{.BIN}} > /dev/null || [ -n "$NO_INSTALL_HOMEBREW" ] + + sentry: + run: once + cmds: + - task: install:github + vars: + BIN_NAME: sentry-cli + PACKAGE: github.com/getsentry/sentry-cli diff --git a/.config/taskfiles/install/Taskfile-go.yml b/.config/taskfiles/install/Taskfile-go.yml new file mode 100644 index 00000000..5e613c00 --- /dev/null +++ b/.config/taskfiles/install/Taskfile-go.yml @@ -0,0 +1,151 @@ +--- +version: '3' + +tasks: + bin: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: bin + PACKAGE: github.com/marcosnils/bin@latest + + bundle: + deps: + - bin + - golangci-lint + - goreleaser + - minio + - nfpm + - ots + - pup + + clean: + cmds: + - rm -rf "$GOPATH/pkg" "$GOPATH/src" + status: + - '[ -z "$GOPATH" ]' + + gitomatic: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: gitomatic + PACKAGE: github.com/muesli/gitomatic@latest + + goconvey: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: goconvey + PACKAGE: github.com/smartystreets/goconvey@latest + + golangci-lint: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: golangci-lint + PACKAGE: github.com/golangci/golangci-lint/cmd/golangci-lint@v1.45.0 + + goofys: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: goofys + PACKAGE: github.com/kahing/goofys@latest + + goreleaser: + run: once + tags: + - semantic + cmds: + - task: install:go + vars: + BIN_NAME: goreleaser + PACKAGE: github.com/goreleaser/goreleaser@latest + + gotestsum: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: gotestsum + PACKAGE: gotest.tools/gotestsum@latest + + gum: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: gum + PACKAGE: github.com/charmbracelet/gum@latest + + install:go: + vars: + BIN: '{{.BIN_NAME}}' + run: when_changed + log: + error: Failed to install `{{.PACKAGE}}` with Go + start: Installing Go package named `{{.PACKAGE}}` + success: Successfully installed `{{.PACKAGE}}` + cmds: + - task: :install:software:go + - go install {{.PACKAGE}} + status: + - type {{.BIN}} > /dev/null + + minio: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: mc + PACKAGE: github.com/minio/mc@latest + + misspell: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: misspell + PACKAGE: github.com/client9/misspell/cmd/misspell@latest + + nfpm: + run: once + tags: + - semantic + cmds: + - task: install:go + vars: + BIN_NAME: nfpm + PACKAGE: github.com/goreleaser/nfpm/v2/cmd/nfpm@latest + + node-prune: + run: once + tags: + - semantic + cmds: + - task: install:go + vars: + BIN_NAME: node-prune + PACKAGE: github.com/tj/node-prune@latest + + ots: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: ots + PACKAGE: github.com/sniptt-official/ots@latest + + pup: + run: once + cmds: + - task: install:go + vars: + BIN_NAME: pup + PACKAGE: github.com/ericchiang/pup@latest diff --git a/.config/taskfiles/install/Taskfile-npm.yml b/.config/taskfiles/install/Taskfile-npm.yml new file mode 100644 index 00000000..5d294efb --- /dev/null +++ b/.config/taskfiles/install/Taskfile-npm.yml @@ -0,0 +1,576 @@ +--- +version: '3' + +tasks: + autocannon: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: autocannon + + bitwarden: + run: once + cmds: + - task: install:npm + vars: + BIN_NAME: bw + PACKAGE: '@bitwarden/cli' + + browserslist: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: browserslist + + bundle: + cmds: + - task: :install:modules:global + + clinic: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: clinic + + codecov: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: codecov + + commitizen: + run: once + cmds: + - task: install:npm + vars: + BIN_NAME: git-cz + FORCE_INSTALL: true + PACKAGE: commitizen + status: + - '[ -n "$CI" ] || type git-cz &> /dev/null' + + commitlint: + run: once + cmds: + - task: install:npm + vars: + BIN_NAME: commitlint + PACKAGE: '@commitlint/cli' + status: + - '[ -n "$CI" ]' + + crowdin: + run: once + cmds: + - task: install:npm + vars: + BIN_NAME: crowdin + PACKAGE: '@crowdin/cli' + + cspell: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: cspell + + depcheck: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: depcheck + + devcontainer: + run: once + cmds: + - task: install:npm + vars: + BIN_NAME: devcontainer + PACKAGE: '@vscode/dev-container-cli' + + dockerfilelint: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: dockerfilelint + + esbuild: + run: once + tags: + - semantic + cmds: + - task: install:npm + vars: + PACKAGE: esbuild + + eslint: + run: once + tags: + - update + cmds: + - task: install:npm + vars: + PACKAGE: eslint + + esprint: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: esprint + + fuite: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: fuite + + git-notify: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: git-notify + + global:library: + deps: + - global:package-manager + env: + PACKAGE_EXISTS: + sh: | + PACKAGE_PATH="$(echo $NODE_PATH | sed 's/^://' | sed 's/:.*$//')/{{.PACKAGE}}/package.json" + if [ -f "$PACKAGE_PATH" ]; then + echo 'true' + else + echo 'false' + fi + run: once + log: + error: Error installing `{{.PACKAGE}}` globally + start: Installing NPM global library `{{.PACKAGE}}` which does not have a CLI + success: '`{{.PACKAGE}}` has been installed globally' + cmds: + - | + if [ -n "$CI" ]; then echo "*************** npm global --> {{.PACKAGE}}"; fi + - | + export PATH="$PATH:$HOME/.local/bin:$HOME/.volta/bin" + volta install {{.PACKAGE}}' + status: + - '[[ "$PACKAGE_EXISTS" == "true" ]]' + + global:package-manager: + deps: + - :install:software:node + - :install:software:volta + run: once + cmds: + - .config/log info 'Installing `{{.NPM_PROGRAM}}`' + - | + export PATH="$PATH:$HOME/.local/bin:$HOME/.volta/bin" + volta install {{.NPM_PROGRAM}} + - .config/log info 'Successfully installed `{{.NPM_PROGRAM}}`' + status: + - type {{.NPM_PROGRAM}} > /dev/null + + htmlhint: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: htmlhint + + husky: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: husky + + ifttt: + run: once + cmds: + - task: install:npm + vars: + BIN_NAME: ifttt + PACKAGE: ifttt-cli + + install:npm: + deps: + - global:package-manager + vars: + BIN: '{{if .BIN_NAME}}{{.BIN_NAME}}{{else}}{{.PACKAGE}}{{end}}' + run: when_changed + cmds: + - .config/log start 'Installing NPM package `{{.PACKAGE}}` globally' + - | + PATH="$PATH:$HOME/.local/bin:$HOME/.volta/bin" + volta install {{.PACKAGE}} + - .config/log success 'Installed NPM package `{{.PACKAGE}}` globally' + status: + - type {{.BIN}} > /dev/null + + ionic: + run: once + tags: + - semantic + cmds: + - task: install:npm + vars: + BIN_NAME: ionic + PACKAGE: '@ionic/cli' + + jest: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: jest + + leasot: + run: once + tags: + - update + cmds: + - task: install:npm + vars: + PACKAGE: leasot + + lint-staged: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: lint-staged + + liquidjs: + run: once + tags: + - update + cmds: + - task: install:npm + vars: + BIN_NAME: hbs + PACKAGE: hbs-cli + - task: liquidjs:install + + liquidjs:install: + tags: + - update + cmds: + - task: install:npm + vars: + PACKAGE: liquidjs + + majestic: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: majestic + + markdown-link-check: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: markdown-link-check + + markdown-table-formatter: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: markdown-table-formatter + + modclean: + run: once + tags: + - semantic + cmds: + - task: install:npm + vars: + BIN_NAME: modclean + PACKAGE: modclean@2 + + ndb: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: ndb + + nest: + run: once + tags: + - semantic + cmds: + - task: install:npm + vars: + BIN_NAME: nest + PACKAGE: '@nestjs/cli' + + nodemon: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: nodemon + + npm: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: npm + + ntl: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: ntl + + nx: + run: once + tags: + - semantic + cmds: + - task: install:npm + vars: + PACKAGE: nx + + only-allow: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: only-allow + + open-cli: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: open-cli + + pac: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: pac + + pkg: + run: once + tags: + - semantic + cmds: + - task: install:npm + vars: + PACKAGE: pkg + + pnpm: + run: once + tags: + - update + cmds: + - task: install:npm + vars: + PACKAGE: pnpm + - task: pnpm:import + status: + - type pnpm &> /dev/null + + pnpm-lock-export: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: pnpm-lock-export + + pnpm:import: + run: once + log: + error: Error running `pnpm import` + start: Running `pnpm import` + success: '`pnpm import` finished successfully!' + cmds: + - pnpm import + status: + - '[ ! -f package-lock.json ] && [ ! -f yarn.lock ] && [ ! -f npm-shrinkwrap.json ]' + + prettier: + run: once + tags: + - update + cmds: + - task: install:npm + vars: + PACKAGE: prettier + + quicktype: + run: once + tags: + - update + cmds: + - task: install:npm + vars: + PACKAGE: quicktype + + readme: + run: once + tags: + - update + cmds: + - task: install:npm + vars: + BIN_NAME: readme + PACKAGE: '@appnest/readme' + + real-favicon: + run: once + cmds: + - task: install:npm + vars: + BIN_NAME: real-favicon + PACKAGE: cli-real-favicon + + remark: + run: once + tags: + - update + cmds: + - task: install:npm + vars: + BIN_NAME: remark + PACKAGE: remark-cli + + secretlint: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: secretlint + + semantic-release: + run: once + tags: + - semantic + cmds: + - task: install:npm + vars: + PACKAGE: semantic-release + + sharp: + run: once + cmds: + - task: install:npm + vars: + BIN_NAME: sharp + PACKAGE: sharp-cli + + shellcheck: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: shellcheck + + snyk: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: snyk + + standard: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: standard + + standard-version: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: standard-version + + stylelint: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: stylelint + + synp: + run: once + tags: + - update + cmds: + - task: install:npm + vars: + PACKAGE: synp + + tinypng: + run: once + cmds: + - task: install:npm + vars: + BIN_NAME: tinypng + PACKAGE: tinypng-cli + + ts-node: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: ts-node + + typedoc: + run: once + tags: + - semantic + cmds: + - task: install:npm + vars: + PACKAGE: typedoc + + typescript: + run: once + tags: + - semantic + cmds: + - task: install:npm + vars: + BIN_NAME: tsc + PACKAGE: typescript + + typesync: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: typesync + + wrangler: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: wrangler + + yarnhook: + run: once + cmds: + - task: install:npm + vars: + PACKAGE: yarnhook diff --git a/.config/taskfiles/install/Taskfile-pipx.yml b/.config/taskfiles/install/Taskfile-pipx.yml new file mode 100644 index 00000000..2365245b --- /dev/null +++ b/.config/taskfiles/install/Taskfile-pipx.yml @@ -0,0 +1,237 @@ +--- +version: '3' + +tasks: + add-trailing-comma: + tags: + - update + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: add-trailing-comma + + ansible: + tags: + - update + run: once + cmds: + - task: install:pipx + vars: + BIN_NAME: ansible + PACKAGE: ansible-base + + ansible-lint: + run: once + tags: + - semantic + cmds: + - task: install:pipx + vars: + PACKAGE: ansible-lint + + ansibler: + tags: + - update + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: ansibler + - task: ansible + + black: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: black + + blocklint: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: blocklint + + bundle: + deps: + - add-trailing-comma + - ansible + - ansible-lint + - ansibler + - blocklint + - flake8 + - isort + - latestos + - mod-ansible-autodoc + - molecule + - mypy + - poet + - pre-commit-hooks + - proselint + - pyformat + - pyinstaller + - pyinstrument + - pysnooper + - pytest + - pytest-cov + - toml-sort + - yamllint + + flake8: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: flake8 + + getmac: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: getmac + + install:pipx: + vars: + BIN: '{{if .BIN_NAME}}{{.BIN_NAME}}{{else}}{{.PACKAGE}}{{end}}' + env: + PATH: + sh: echo "$PATH:$HOME/.local/bin" + run: when_changed + cmds: + - task: :install:software:pipx + - .config/log info 'Installing `{{.PACKAGE}}` as a pipx package' + - | + PATH="$PATH:$HOME/.local/bin" + pipx install {{.PACKAGE}} --include-deps --force + - .config/log success 'Successfully installed `{{.PACKAGE}}` with pipx' + status: + - type {{.BIN}} > /dev/null + + isort: + tags: + - update + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: isort + + latestos: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: latestos + + mod-ansible-autodoc: + run: once + tags: + - update + cmds: + - task: install:pipx + vars: + PACKAGE: mod-ansible-autodoc + + molecule: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: molecule + + mypy: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: mypy + + poet: + tags: + - update + run: once + cmds: + - task: install:pipx + vars: + BIN_NAME: poet + PACKAGE: homebrew-pypi-poet + + pre-commit-hooks: + run: once + cmds: + - task: install:pipx + vars: + BIN_NAME: check-toml + PACKAGE: pre-commit-hooks + status: + - type check-toml &> /dev/null + + proselint: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: proselint + + pyformat: + tags: + - update + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: pyformat + + pyinstaller: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: pyinstaller + + pyinstrument: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: pyinstrument + + pysnooper: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: pysnooper + + pytest: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: pytest + + pytest-cov: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: pytest-cov + + toml-sort: + run: once + tags: + - update + cmds: + - task: install:pipx + vars: + PACKAGE: toml-sort + + yamllint: + run: once + cmds: + - task: install:pipx + vars: + PACKAGE: yamllint diff --git a/.config/taskfiles/install/Taskfile-python.yml b/.config/taskfiles/install/Taskfile-python.yml new file mode 100644 index 00000000..4527100f --- /dev/null +++ b/.config/taskfiles/install/Taskfile-python.yml @@ -0,0 +1,193 @@ +--- +version: '3' + +tasks: + pip: + deps: + - :install:software:python + run: when_changed + log: + error: Failed to install pip3 package `{{.PACKAGE}}` + start: Installing pip3 package `{{.PACKAGE}}` + success: Installed pip3 package `{{.PACKAGE}}` + cmds: + - | + if [ -n "$CI" ]; then echo "*************** pip3 --> {{.PACKAGE}}"; fi + - pip3 install {{.PACKAGE}} + status: + - type {{.PACKAGE}} > /dev/null + + pipx: + cmds: + - task: :install:pipx:install + vars: + BIN_NAME: '{{.BIN_NAME}}' + PACKAGE: '{{.PACKAGE}}' + + pytest: + cmds: + - task: :install:pipx:pytest + + pytest-cov: + cmds: + - task: :install:pipx:pytest-cov + + requirements: + run: once + cmds: + - task: :install:software:python + - task: :{{if eq .REPOSITORY_TYPE "ansible"}}install:pipx:ansible{{else}}donothing{{end}} + - task: requirements:poetry + - task: :{{if eq .REPOSITORY_TYPE "ansible"}}ansible:galaxy:requirements{{else}}donothing{{end}} + + requirements:poetry: + cmds: + - task: requirements:poetry:prereqs + - task: requirements:poetry:install + status: + - '[ ! -f pyproject.toml ] || ! type poetry &> /dev/null' + + requirements:poetry:install: + summary: | + # Poetry Requirements + + `poetry publish -r gitlab` will publish to GitLab registry on GitLab CI + env: + CPPFLAGS: -I/usr/local/opt/openssl@3/include + LDFLAGS: -L/usr/local/opt/openssl@3/lib + run: once + log: + error: Failed to configure / install via `poetry` + start: Configuring Poetry and running `poetry install` + success: Successfully ran `poetry install` + cmds: + - task: :install:software:poetry + - poetry env use "$(which python3)" + - poetry config virtualenvs.create {{if .PYTHON_VIRTUALENV}}{{.PYTHON_VIRTUALENV}}{{else}}false{{end}} + - poetry config virtualenvs.in-project true + - | + if [ -n "$PYPI_TOKEN" ]; then + poetry config pypi-token.pypi "$PYPI_TOKEN" || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + .config/log warn 'Failed to run `poetry config pypi-token.pypi`' + fi + fi + - | + if [ -n "$GITLAB_CI" ]; then + poetry config repositories.gitlab "https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/pypi" + fi + - | + if [ -n "$GITLAB_CI" ]; then + poetry config http-basic.gitlab gitlab-ci-token "${CI_JOB_TOKEN}" || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + .config/log warn 'Failed to run `poetry config http-basic.gitlab gitlab-ci-token`' + fi + fi + - | + if [ -n "$AUTO_UPDATE_PIP_PACKAGES" ]; then + .config/log info 'Updating Poetry dependencies{{if .INSTALL_OPTIONS}} with options `{{.INSTALL_OPTIONS}}`{{end}}' + poetry update{{if .INSTALL_OPTIONS}} {{.INSTALL_OPTIONS}}{{end}} + else + .config/log info 'Installing Poetry dependencies{{if .INSTALL_OPTIONS}} with options `{{.INSTALL_OPTIONS}}`{{end}}' + export "CFLAGS=-I/usr/local/include -L/usr/local/lib" + poetry install{{if .INSTALL_OPTIONS}} {{.INSTALL_OPTIONS}}{{end}} || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + .config/log warn 'Running `poetry install{{if .INSTALL_OPTIONS}} {{.INSTALL_OPTIONS}}{{end}}` failed.' + .config/log info 'Attempting to fix by running `poetry update{{if .INSTALL_OPTIONS}} {{.INSTALL_OPTIONS}}{{end}}`' + poetry update{{if .INSTALL_OPTIONS}} {{.INSTALL_OPTIONS}}{{end}} + fi + fi + sources: + - pyproject.toml + + requirements:poetry:prereqs: + tags: + - update + env: + GCC5_MISSING_MESSAGE: | + # Sudo Required for gcc-5 Symlink + + It looks like your system is missing gcc-5 but gcc is available. Select "Yes" if you would + like to symlink your version of gcc to gcc-5 which, for the purposes of this project, can + prevent issues with our Python runner `poetry`. However, there are some circumstances where + you may not want to do this. This will generally be harmless - you can always remove the symlink + by running `sudo rm -rf /usr/local/bin/gcc-5`. + log: + error: Error checking `gcc` + start: Checking if `gcc` should be symlinked to `gcc-5` + success: Checked `gcc` + cmds: + - | + if which gcc &> /dev/null; then + TMP="$(mktemp)" && echo "$GCC5_MISSING_MESSAGE" > "$TMP" && .config/log md "$TMP" + if [[ $- == *i* ]]; then + if [ -w /usr/local/bin ]; then + if .config/log confirm 'Symlink `gcc` to `gcc-5`?'; then + ln -s "$(which gcc)" /usr/local/bin/gcc-5 + fi + else + if .config/log confirm 'Symlink `gcc` to `gcc-5` via sudo?'; then + .config/log info 'Running sudo command `sudo ln -s '"$(which gcc)"' /usr/local/bin/gcc-5`' + sudo ln -s "$(which gcc)" /usr/local/bin/gcc-5 + else + .config/log warn 'Continuing with missing `gcc-5` binary' + fi + fi + else + .config/log info 'Session is non-interactive' + .config/log warn 'Symlinking `gcc` to `gcc-5` since session is non-interactive' + .config/log info 'Remove the symlink later on (if required) by running `sudo rm /usr/local/bin/gcc-5`' + if [ -w /usr/local/bin ]; then + ln -s "$(which gcc)" /usr/local/bin/gcc-5 + else + sudo ln -s "$(which gcc)" /usr/local/bin/gcc-5 + fi + fi + else + .config/log warn '`gcc` is missing.' + fi + status: + - which gcc-5 + + venv:node: + deps: + - :install:software:dasel + - :install:software:python + vars: + POETRY_MANUAL_SETUP_MSG: Adding semantic-release-python plugin Python dependencies by + running `poetry add pip@latest setuptools@latest twine@latest wheel@latest` + log: + error: Encountered error while installing Python dependencies for `semantic-release-python` + start: Installing `semantic-release-python` Python dependencies + success: Successfully installed `semantic-release-python` Python dependencies + cmds: + - | + REQUIREMENTS_PATH="$PWD/lib/requirements.txt" + cd "$INIT_CWD" || exit + if type poetry > /dev/null && test -f pyproject.toml; then + .config/log info 'Poetry installation detected. Using Poetry to install NPM package Python dependencies.' + if dasel select -f pyproject.toml '.tool.poetry.extras.semantic' > /dev/null; then + .config/log info '`semantic` extras bundle detected.. Installing via `poetry install -E semantic`' + poetry install -E semantic + else + .config/log info '{{.POETRY_MANUAL_SETUP_MSG}}' + poetry add pip@latest setuptools@latest twine@latest wheel@latest + fi + else + if [ -f venv/bin/activate ]; then + .config/log info 'Using existing virtualenv found at `venv/`' + . venv/bin/activate + elif [ -f .venv/bin/activate ]; then + .config/log info 'Using existing virtualenv found at `.venv/`' + . .venv/bin/activate + else + .config/log info 'No virtualenv detected.. Creating virtualenv in `.venv/`' + python3 -m venv .venv + . .venv/bin/activate + fi + .config/log info 'Installing Python dependencies into virtualenv' + pip3 install -U pip setuptools + pip3 install -r "$REQUIREMENTS_PATH" + fi + sources: + - lib/requirements.txt diff --git a/.config/taskfiles/install/Taskfile-qubes.yml b/.config/taskfiles/install/Taskfile-qubes.yml new file mode 100644 index 00000000..d40fd8d9 --- /dev/null +++ b/.config/taskfiles/install/Taskfile-qubes.yml @@ -0,0 +1,14 @@ +--- +version: '3' + +tasks: + dom0: + summary: | + # Tasks to Run to Setup dom0 + + 1. https://www.qubes-os.org/news/2021/11/12/new-qubes-application-menu/ + cmds: + - sudo qubes-dom0-update --enablerepo=qubes-dom0-unstable qubes-desktop-linux-menu + - qubes-app-menu & + - qvm-create --class StandaloneVM --label blue --property virt_mode=hvm + - qvm-create --class StandaloneVM --label --property virt_mode=hvm diff --git a/.config/taskfiles/install/Taskfile-requirements.yml b/.config/taskfiles/install/Taskfile-requirements.yml new file mode 100644 index 00000000..953b9f62 --- /dev/null +++ b/.config/taskfiles/install/Taskfile-requirements.yml @@ -0,0 +1,156 @@ +--- +version: '3' + +vars: + INSTALL_GROUP: + sh: | + if type apk &> /dev/null; then + echo 'apk' + elif type apt-get &> /dev/null; then + echo 'apt-get' + elif type dnf &> /dev/null; then + echo 'dnf' + elif type pacman &> /dev/null; then + echo 'pacman' + elif type yum &> /dev/null; then + echo 'yum' + elif [ '{{OS}}' == 'darwin' ]; then + echo 'brew' + fi + REQUIREMENTS_TASKFILE: .config/taskfiles/install/Taskfile-requirements.yml + +gpg: + apt-get: + - gnupg-agent + - gnupg-curl + - gnupg2 + - pcscd + - scdaemon + brew: + - gnupg + - pinentry-mac + dnf: + - gnupg2 + - pinentry-curses + pacman: + - gnupg + yum: + - gnupg2 + - pinentry-curses + +yubikey: + apt-get: + - cryptsetup + - dirmngr + - gnupg2 + - gnupg-agent + - hopenpgp-tools + - libpcsclite-dev + - libssl-dev + - openssl + - pcscd + - python3-gnupg + - python3-pip + - python3-pyscard + - rng-tools + - scdaemon + - secure-delete + - swig + - wget + - yubikey-personalization + brew: + - gnupg + - hopenpgp-tools + - pinentry-mac + - swig + - wget + - ykman + - yubikey-personalization + dnf: + - gnupg2 + - gnupg2-smime + - pcsc-lite + - pcsc-lite-libs + - pinentry-curses + pacman: + - ccid + - gnupg + - hopenpgp-tools + - pcsclite + - yubikey-personalization + python: + - PyOpenSSL + - yubikey-manager + yum: + - gnupg2 + - gnupg2-smime + - pcsc-lite + - pcsc-lite-libs + - pinentry-curses + +tasks: + gpg: + cmds: + - task: install + vars: + REQUIREMENTS_KEY: gpg + + install: + deps: + - install:deps:{{OS}} + vars: + INSTALL_GROUP: '{{if .REQUIREMENTS_GROUP}}{{.REQUIREMENTS_GROUP}}{{else}}{{.INSTALL_GROUP}}{{end}}' + REQUIREMENTS_KEY: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.REQUIREMENTS_KEY}}{{end}}' + cmds: + - task: :{{if .REQUIREMENTS_GROUP_TASK}}{{.REQUIREMENTS_GROUP_TASK}}{{else}}install:software:system{{end}} + vars: + PACKAGE: + sh: yq e '.["{{.REQUIREMENTS_KEY}}"]["{{.INSTALL_GROUP}}"]' {{.REQUIREMENTS_TASKFILE}} | sed 's/- / /' | tr -d '\n' | sed 's/^ //' + + install:deps:darwin: + cmds: + - task: :install:software:brew + - task: :install:software:yq + + install:deps:linux: + cmds: + - task: :install:software:yq + + onlykey: + summary: | + # Preload OnlyKey Installer Files + + Windows + OnlyKey_5.3.6.exe + SHA256 - 22fc0b80d0b11fa5b0f9a566ae11edb8aee41e53905259e2a8a948c71e45e1fe + + MacOS + OnlyKey.App.5.3.6.dmg + SHA256 - 1f7756227af0752bf2d1071bf6f04e5a3282df54ac0125fdfb4abfab7edb115a + + Linux + OnlyKey_5.3.6_amd64.deb + SHA256 - d5e223581b459c869a2f2d4c84cd77f52b851dced5c87dea5f8d93ec1cecc7c4 + GPG Signature verify with debsig-verify + A1D6 4A3B 496C B0F3 6E12 B46F 9A9F 520D 44EA 53D1 + cmds: + - mkdir -p ~/Downloads + - curl -sSL https://github.com/trustcrypto/OnlyKey-App/releases/download/v5.3.6/OnlyKey.App.5.3.6.dmg > ~/Downloads/OnlyKey.App.5.3.6.dmg + - curl -sSL https://github.com/trustcrypto/OnlyKey-App/releases/download/v5.3.6/OnlyKey_5.3.6.exe > ~/Downloads/OnlyKey_5.3.6.exe + - curl -sSL https://github.com/trustcrypto/OnlyKey-App/releases/download/v5.3.6/OnlyKey_5.3.6_amd64.deb > ~/Downloads/OnlyKey_5.3.6_amd64.deb + - | + if type apt &> /dev/null; then + sudo apt install debsig-verify + fi + + yubikey: + cmds: + - task: :install:software:yubikey-agent + - task: install + vars: + REQUIREMENTS_KEY: yubikey + - task: install + vars: + REQUIREMENTS_GROUP: python + REQUIREMENTS_GROUP_TASK: install:python:pip + REQUIREMENTS_KEY: yubikey diff --git a/.config/taskfiles/install/Taskfile-rust.yml b/.config/taskfiles/install/Taskfile-rust.yml new file mode 100644 index 00000000..bd00bd86 --- /dev/null +++ b/.config/taskfiles/install/Taskfile-rust.yml @@ -0,0 +1,61 @@ +--- +version: '3' + +tasks: + bundle: + deps: + - grex + - htmlq + - hyperfine + + catfs: + run: once + cmds: + - task: install:rust + vars: + BIN_NAME: catfs + PACKAGE: catfs + + clean: + cmds: + - rm -rf "$HOME/.cargo/registry" + + grex: + run: once + cmds: + - task: install:rust + vars: + BIN_NAME: grex + PACKAGE: grex + + htmlq: + run: once + cmds: + - task: install:rust + vars: + BIN_NAME: htmlq + PACKAGE: htmlq + + hyperfine: + run: once + cmds: + - task: install:rust + vars: + BIN_NAME: hyperfine + PACKAGE: hyperfine + + install:rust: + vars: + BIN: '{{if .BIN_NAME}}{{.BIN_NAME}}{{else}}{{.PACKAGE}}{{end}}' + run: when_changed + log: + error: Failed to install Cargo crate `{{.PACKAGE}}` + start: Installing Cargo crate `{{.PACKAGE}}` + success: Installed Cargo crate `{{.PACKAGE}}` + cmds: + - task: :install:software:rust + - | + if [ -n "$CI" ]; then echo "*************** cargo --> {{.PACKAGE}}"; fi + - cargo install {{.PACKAGE}} + status: + - type {{.BIN}} > /dev/null || [ -n "$NO_INSTALL_RUST" ] diff --git a/.config/taskfiles/install/Taskfile-service.yml b/.config/taskfiles/install/Taskfile-service.yml new file mode 100644 index 00000000..4e1db5ba --- /dev/null +++ b/.config/taskfiles/install/Taskfile-service.yml @@ -0,0 +1,9 @@ +--- +version: '3' + +tasks: + start: + cmds: + - sudo service {{.SERVICE}} start + status: + - '[ "{{OS}}" == "darwin" ]' diff --git a/.config/taskfiles/install/Taskfile-software.yml b/.config/taskfiles/install/Taskfile-software.yml new file mode 100644 index 00000000..b5e4cd90 --- /dev/null +++ b/.config/taskfiles/install/Taskfile-software.yml @@ -0,0 +1,1412 @@ +--- +version: '3' + +tasks: + act: + summary: | + ## Simulate GitHub Actions Locally + + [Act]((https://github.com/nektos/act) gives you the capability to test + GitHub Actions locally. The program allows you to programmatically run + steps defined in your `.github/workflows/` folder. Just like GitHub, + it runs the actions in Docker containers. You can then simulate + your GitHub Actions with a CLI: + + **Example triggering pull_request flow:** + `act pull_request` + + **Example running a specific job:** + `act -j test` + run: once + cmds: + - task: install:software + vars: + PACKAGE: act + status: + - type act > /dev/null + + allure: + run: once + cmds: + - task: install:software + vars: + PACKAGE: allure + status: + - type allure > /dev/null + + ansifilter: + run: once + tags: + - update + cmds: + - task: install:software + vars: + PACKAGE: ansifilter + status: + - type ansifilter > /dev/null + + axel: + run: once + cmds: + - task: install:software + vars: + PACKAGE: axel + status: + - type axel > /dev/null + + bfg: + run: once + cmds: + - task: install:software + vars: + PACKAGE: bfg + status: + - type bfg > /dev/null + + brew: + run: once + log: + error: Failed to install or load Homebrew + start: Ensuring Homebrew is installed and available + success: Successfully ensured Homebrew is installed + cmds: + - task: common + - task: brew:{{OS}} + status: + - type brew > /dev/null || [ -n "$NO_INSTALL_HOMEBREW" ] + + brew:cask: + run: when_changed + log: + error: Failed to install `{{.CASK}}` + start: Ensuring the `{{.CASK}}` Homebrew cask is installed + success: Successfully installed `{{.CASK}}` + cmds: + - task: brew + - | + if [ -n "$CI" ]; then echo "*************** brew cask --> {{.CASK}}"; fi + - brew install --cask {{.CASK}} + status: + - type {{.CASK}} &> /dev/null + + brew:darwin: + cmds: + - | + if ! type brew &> /dev/null; then + if type sudo &> /dev/null && sudo -n true; then + echo | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + else + .config/log info 'Attempting to install Homebrew - you may be prompted for your `sudo` password' + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + fi + task install:software:exit:notice:reload + fi + - task: brew:utils + status: + - type brew &> /dev/null + + brew:formulae: + cmds: + - task: install:software + vars: + BIN_NAME: '{{.BIN_NAME}}' + PACKAGE: '{{.PACKAGE}}' + + brew:linux: + run: once + cmds: + - | + function ensureSource() { + if ! (grep "/bin/brew shellenv" < "$1" &> /dev/null); then + echo 'eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"' >> "$1" + fi + } + if ! type brew &> /dev/null; then + if type sudo &> /dev/null && sudo -n bash; then + echo | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + else + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + fi + eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + ensureSource "$HOME/.profile" + task install:software:exit:notice:reload + fi + status: + - type brew &> /dev/null + + brew:utils: + run: once + log: + error: Failed to setup GNU-compatibility tools + start: Installing GNU-compatibility tools for macOS via Homebrew + success: Successfully installed GNU-compatibility tools + cmds: + - task: compatibility:coreutils + - task: compatibility:findutils + - task: compatibility:gnu-sed + - task: compatibility:grep + - task: compatibility:gnu-tar + - task: compatibility:gawk + status: + - '[ "{{OS}}" != "darwin" ]' + + brew:windows: + cmds: + - task: common:windows + + bundle: + deps: + - brew + cmds: + - brew tap Homebrew/bundle + + codeclimate: + cmds: + - task: install:software + vars: + INSTALL_TASK: '{{.TASK}}' + PACKAGE: codeclimate/formulae/codeclimate + status: + - type codeclimate > /dev/null + + common: + run: once + log: + error: There was an error ensuring common system tools are present + start: Ensuring common system tools are present + success: Ensured common system tools are present + cmds: + - task: common:{{OS}} + + common:darwin: + cmds: + - task: common:darwin:xcode + + common:darwin:xcode: + vars: + CLT_STATUS: + sh: brew config | grep CLT + log: + error: Failed to run `sudo xcode-select --install` + start: Running `sudo xcode-select --install` to install macOS developer tools + success: Successfully ran `sudo xcode-select --install` + cmds: + - sudo xcode-select --install + status: + - '[ "{{.CLT_STATUS}}" != "CLT: N/A" ]' + + common:linux: + vars: + LINUX_FAMILY: + sh: | + if [ -f "/etc/debian_version" ]; then + echo 'debian' + elif [ -f "/etc/redhat-release" ]; then + echo 'redhat' + elif [ -f "/etc/arch-release" ]; then + echo 'archlinux' + elif [ -f "/etc/alpine-release" ]; then + echo 'alpine' + elif type apk &> /dev/null; then + echo 'alpine' + elif type apt-get &> /dev/null; then + echo 'debian' + elif type dnf &> /dev/null || type yum &> /dev/null; then + echo 'redhat' + elif type pacman &> /dev/null; then + echo 'archlinux' + elif type yum &> /dev/null; then + echo 'redhat' + else + echo 'unknown' + fi + log: + error: Failed to ensure basic system dependencies are installed + start: Ensuring basic system dependencies are installed + success: Basic system dependencies are installed! + cmds: + - task: common:linux:{{.LINUX_FAMILY}} + + common:linux:alpine: + vars: + APK_INFO: + sh: apk info + PACKAGES: bash coreutils curl file g++ grep git libc6-compat make ruby ruby-bigdecimal ruby-etc ruby-irb ruby-json ruby-test-unit sudo + log: + error: Failed to run `apk --no-cache add {{.PACKAGES}}` + start: Installing common dependencies by running `apk --no-cache add {{.PACKAGES}}` + success: Successfully installed common dependencies + cmds: + - | + if type sudo &> /dev/null; then + sudo apk --no-cache add {{.PACKAGES}} + else + apk --no-cache add {{.PACKAGES}} + fi + status: + - type bash &> /dev/null + - type curl &> /dev/null + - type git &> /dev/null + - type grep &> /dev/null + - type gzip &> /dev/null + - type make &> /dev/null + - type ruby &> /dev/null + - type sudo &> /dev/null + - '{{.APK_INFO}} | grep coreutils' + - '{{.APK_INFO}} | grep g++' + - '{{.APK_INFO}} | grep libc6-compat' + - '{{.APK_INFO}} | grep ruby-bigdecimal' + - '{{.APK_INFO}} | grep ruby-etc' + - '{{.APK_INFO}} | grep ruby-irb' + - '{{.APK_INFO}} | grep ruby-json' + - '{{.APK_INFO}} | grep ruby-test-unit' + + common:linux:archlinux: + interactive: true + vars: + PACKAGES: base-devel bash curl file gcc git grep gzip make procps-ng ruby sudo + log: + error: Failed to run `pacman -S {{.PACKAGES}}` + start: Installing common software by running `pacman -S {{.PACKAGES}}` + success: Successfully installed common software dependencies + cmds: + - .config/log warn "Archlinux support for Homebrew is not very well documented.. if this does not work and you can get it working, please open a PR :)" + - | + if type sudo &> /dev/null; then + sudo pacman update + sudo pacman -S {{.PACKAGES}} + else + pacman update + pacman -S base-devel {{.PACKAGES}} + fi + status: + - type bash &> /dev/null + - type curl &> /dev/null + - type git &> /dev/null + - type grep &> /dev/null + - type gzip &> /dev/null + - type make &> /dev/null + - type ruby &> /dev/null + - type sudo &> /dev/null + - ldconfig -p | grep base-devel + - ldconfig -p | grep file + - ldconfig -p | grep procps-ng + + common:linux:debian: + interactive: true + vars: + PACKAGES: bash build-essential curl file gcc git grep gzip make procps ruby sudo + log: + error: Failed to install common software with `apt-get install -y {{.PACKAGES}}` + start: Installing common software by running `apt-get install -y {{.PACKAGES}}` + success: Successfully installed common software dependencies + cmds: + - | + if type sudo &> /dev/null; then + sudo apt-get update + sudo apt-get install -y {{.PACKAGES}} + else + apt-get update + apt-get install -y {{.PACKAGES}} + fi + status: + - type bash &> /dev/null + - type curl &> /dev/null + - type git &> /dev/null + - type grep &> /dev/null + - type gzip &> /dev/null + - type make &> /dev/null + - type ruby &> /dev/null + - type sudo &> /dev/null + - dpkg-query -l build-essential &> /dev/null + - dpkg-query -l file &> /dev/null + - dpkg-query -l procps &> /dev/null + + common:linux:redhat: + interactive: true + cmds: + - task: common:linux:redhat:packages + - task: common:linux:redhat:devtools + - task: common:linux:redhat:fedora + + common:linux:redhat:devtools: + vars: + PACKAGE_MANAGER: + sh: if type dnf &> /dev/null; then echo 'dnf'; else echo 'yum'; fi + log: + error: Failed to install the "Development Tools" group with {{.PACKAGE_MANAGER}} + start: Installing the "Development Tools" group + success: Successfully installed the "Development Tools" group + cmds: + - mkdir -p "$HOME/.config/bodega" + - | + if [ ! -f "$HOME/.config/bodega/yum-devtools-check-ran" ]; then + if type dnf &> /dev/null; then + dnf groupinfo 'Development Tools' &> "$HOME/.config/bodega/dnf-devtools-check-ran" + else + yum grouplist 'Development Tools' &> "$HOME/.config/bodega/yum-devtools-check-ran" + fi + DEV_TOOLS_NOT_INSTALLED="$(grep 'No groups match' < "$HOME/.config/bodega/yum-devtools-check-ran" > /dev/null)" + if [[ "$DEV_TOOLS_NOT_INSTALLED" == '0' ]]; then + if type sudo &> /dev/null; then + if type dnf &> /dev/null; then + sudo dnf groupinfo 'Development Tools' + else + sudo yum groupinstall -y 'Development Tools' + fi + else + if type dnf &> /dev/null; then + dnf group install -y 'Development Tools' + else + yum groupinstall -y 'Development Tools' + fi + fi + fi + touch "$HOME/.config/bodega/yum-devtools-check-ran" + fi + + common:linux:redhat:fedora: + vars: + PACKAGES: libxcrypt-compat + PACKAGE_MANAGER: + sh: if type dnf &> /dev/null; then echo 'dnf'; else echo 'yum'; fi + log: + error: Failed to install Fedora-specific common software with `{{.PACKAGE_MANAGER}} install -y {{.PACKAGES}}` + start: Installing Fedora-specific common software by running `{{.PACKAGE_MANAGER}} install -y {{.PACKAGES}}` + success: Successfully installed Fedora-specific common software dependencies + cmds: + - | + if ! rpm --quiet --query {{.PACKAGES}}; then + if type sudo &> /dev/null; then + sudo {{.PACKAGE_MANAGER}} -y install {{.PACKAGES}} + else + {{.PACKAGE_MANAGER}} install -y {{.PACKAGES}} + fi + fi + status: + - '[ -f /etc/os-release ] && source /etc/os-release && [[ "$ID" == "fedora" ]] && [ "$VERSION_ID" -gt "29" ]' + + common:linux:redhat:packages: + vars: + PACKAGES: bash curl file git grep gzip make procps-ng ruby sudo + PACKAGE_MANAGER: + sh: if type dnf &> /dev/null; then echo 'dnf'; else echo 'yum'; fi + log: + error: Failed to install common software with `{{.PACKAGE_MANAGER}} install -y {{.PACKAGES}}` + start: Installing common software by running `{{.PACKAGE_MANAGER}} install -y {{.PACKAGES}}` + success: Successfully installed common software dependencies + cmds: + - | + if type sudo &> /dev/null; then + sudo {{.PACKAGE_MANAGER}} install -y {{.PACKAGES}} + else + yum install -y {{.PACKAGES}} + fi + status: + - type bash &> /dev/null + - type curl &> /dev/null + - type git &> /dev/null + - type grep &> /dev/null + - type gzip &> /dev/null + - type make &> /dev/null + - type ruby &> /dev/null + - type sudo &> /dev/null + - rpm -q | grep file + - rpm -q | grep procps-ng + + common:linux:unknown: + log: + start: You are using an operating system that we do not directly support. Please make sure + the equivalent of `build-essential`, `curl`, `file`, `git`, and `procps` are installed. + cmds: + - .config/log warn "*** Unknown OS -> $OSTYPE ***" + + common:windows: + log: + error: Windows is not supported. Try using a Windows WSL environment. + cmds: + - exit 1 + + compatibility:findutils: + cmds: + - task: install:software + vars: + PACKAGE: findutils + status: + - type gfind > /dev/null + + compatibility:gawk: + cmds: + - task: install:software + vars: + PACKAGE: gawk + status: + - type gawk > /dev/null + + compatibility:gnu-sed: + cmds: + - task: install:software + vars: + PACKAGE: gnu-sed + status: + - type gsed > /dev/null + + compatibility:gnu-tar: + cmds: + - task: install:software + vars: + PACKAGE: gnu-tar + status: + - type gtar > /dev/null + + compatibility:grep: + cmds: + - task: install:software + vars: + PACKAGE: grep + status: + - type ggrep > /dev/null + + container-structure-test: + run: once + cmds: + - task: install:software + vars: + PACKAGE: container-structure-test + status: + - type container-structure-test > /dev/null + + coreutils: + cmds: + - task: install:software + vars: + PACKAGE: coreutils + status: + - type gcp > /dev/null || [ "{{OS}}" != "darwin" ] + + dasel: + tags: + - update + cmds: + - task: install:software + vars: + PACKAGE: dasel + status: + - type dasel > /dev/null + + deno: + run: once + cmds: + - task: install:software + vars: + PACKAGE: deno + status: + - type deno > /dev/null + + docker: + run: once + cmds: + - task: docker:{{OS}} + - task: :docker:ensure:running + status: + - type docker > /dev/null + + docker-slim: + run: once + tags: + - docker-build + cmds: + - task: install:software + vars: + PACKAGE: docker-slim + status: + - type docker-slim > /dev/null + + docker:darwin: + run: once + cmds: + - task: brew:cask + vars: + CASK: docker + - task: exit:notice:restart + status: + - type docker > /dev/null + + docker:linux: + run: once + cmds: + - task: install:software + vars: + PACKAGE: docker + - task: exit:notice:restart + status: + - type docker > /dev/null + + docker:windows: + cmds: + - task: common:windows + + dockle: + run: once + cmds: + - task: install:software + vars: + BIN_NAME: dockle + PACKAGE: goodwithtech/r/dockle + status: + - type dockle > /dev/null + + exiftool: + run: once + cmds: + - task: system + vars: + PACKAGE: exiftool + - task: install:software + vars: + PACKAGE: exiftool + status: + - type exiftool > /dev/null + + exit:notice:reload: + cmds: + - .config/log warn 'Software was installed that requires a terminal session reload' + - .config/log warn 'Please close and re-open the terminal. Then, re-run the same command to continue if you face any issues.' + status: + - '[ -f /.dockerenv ]' + + exit:notice:restart: + cmds: + - .config/log warn 'Software was installed that requires a system reboot' + - .config/log info 'Please reboot the system and re-run the same command after rebooting' + - exit 1 + status: + - '[ -f /.dockerenv ]' + + expect: + run: once + cmds: + - task: system + vars: + PACKAGE: expect + - task: install:software + vars: + PACKAGE: expect + status: + - type expect > /dev/null + + fury: + run: once + cmds: + - task: install:software + vars: + BIN_NAME: fury + PACKAGE: gemfury/tap/gemfury + status: + - type fury > /dev/null + + gcc: + run: once + cmds: + - task: system + vars: + PACKAGE: gcc + status: + - type gcc > /dev/null + + gcloud: + run: once + cmds: + - task: gcloud:{{OS}} + status: + - type gcloud > /dev/null + + gcloud:darwin: + cmds: + - task: brew:cask + vars: + CASK: google-cloud-sdk + status: + - type gcloud > /dev/null + + gcloud:linux: + cmds: + - curl -sSL https://sdk.cloud.google.com | bash + + gcloud:windows: + cmds: + - task: common:windows + + gh: + run: once + cmds: + - task: install:software + vars: + PACKAGE: gh + status: + - type gh &> /dev/null + + git: + run: once + cmds: + - task: system + vars: + PACKAGE: git + - task: common + status: + - type git > /dev/null + + git-bug: + run: once + cmds: + - task: install:software + vars: + PACKAGE: git-bug + status: + - type git-bug > /dev/null + + git-issue:admin: + run: once + cmds: + - git clone https://github.com/dspinellis/git-issue.git .git-issue + - | + cd .git-issue + sudo make install + - rm -rf .git-issue + + gitlab-runner: + run: once + cmds: + - task: install:software + vars: + PACKAGE: gitlab-runner + status: + - type gitlab-runner > /dev/null + + gitleaks: + run: once + cmds: + - task: install:software + vars: + PACKAGE: gitleaks + status: + - type gitleaks > /dev/null + + glab: + run: once + cmds: + - task: install:software + vars: + PACKAGE: glab + status: + - type glab > /dev/null + + glow: + run: once + cmds: + - task: install:software + vars: + PACKAGE: glow + status: + - type glow > /dev/null + + go: + run: once + cmds: + - task: go:gopath + - task: go:goroot + - task: :install:profile:add + vars: + PROFILE_STRING: export PATH="$PATH:${GOPATH}/bin:${GOROOT}/bin" + - task: go:install + + go:gopath: + run: once + cmds: + - task: :install:profile:add + vars: + PROFILE_STRING: export GOPATH="${HOME}/.local/go" + status: + - '[ -n "$GOPATH" ]' + + go:goroot: + run: once + cmds: + - task: :install:profile:add + vars: + PROFILE_STRING: export GOROOT="$(brew --prefix golang)/libexec" + status: + - '[ -n "$GOROOT" ]' + + go:install: + run: once + cmds: + - task: install:software + vars: + PACKAGE: go + + golangci-lint: + run: once + cmds: + - task: install:software + vars: + PACKAGE: golangci-lint + + grype: + run: once + cmds: + - task: install:software + vars: + PACKAGE: anchore/grype/grype + + gum: + run: once + cmds: + - task: :install:go:gum + + gzip: + run: once + cmds: + - task: system + vars: + PACKAGE: gzip + - task: install:software + vars: + PACKAGE: gzip + status: + - type gzip > /dev/null + + heroku: + run: once + cmds: + - task: install:software + vars: + PACKAGE: heroku/brew/heroku + + hey: + run: once + cmds: + - task: install:software + vars: + PACKAGE: hey + + install:software: + vars: + BIN: '{{if .BIN_NAME}}{{.BIN_NAME}}{{else}}{{last (splitList "/" .PACKAGE)}}{{end}}' + run: when_changed + log: + error: Failed to install `{{.PACKAGE}}` + start: Ensuring the `{{.PACKAGE}}` Homebrew formulae is installed + success: Successfully installed `{{.PACKAGE}}` + cmds: + - task: brew + - | + if [ -n "$CI" ]; then echo "*************** brew formulae --> {{.PACKAGE}}"; fi + - brew install {{.PACKAGE}} + status: + - type {{.BIN}} &> /dev/null || [ -n "$NO_INSTALL_HOMEBREW" ] + + jq: + tags: + - update + run: once + cmds: + - task: system + vars: + PACKAGE: jq + - task: install:software + vars: + PACKAGE: jq + status: + - type jq > /dev/null + + kvm: + run: once + cmds: + - task: :install:install-doctor + vars: + SOFTWARE: kvm + - task: exit:notice:restart + status: + - type qemu-system-x86_64 > /dev/null + + node: + deps: + - common + - volta + run: once + cmds: + - task: node:install + status: + - type node &> /dev/null + + node:install: + cmds: + - | + . "$HOME/.profile" &> /dev/null || true + volta install node + + openssl: + run: once + cmds: + - task: install:software + vars: + PACKAGE: openssl + + p7zip: + run: once + cmds: + - task: install:software + vars: + BIN_NAME: 7z + PACKAGE: p7zip + + packer: + run: once + tags: + - semantic-packer + cmds: + - task: install:software + vars: + PACKAGE: packer + + parallels: + run: once + cmds: + - task: brew:cask + vars: + CASK: parallels + status: + - '[ "{{OS}}" != "darwin" ] || ! mdfind -name "Parallels Desktop.app" &> /dev/null' + + pinentry: + run: once + cmds: + - task: install:software + vars: + PACKAGE: pinentry-mac + status: + - '[ "{{OS}}" != "darwin" ] || [ -f /usr/local/bin/pinentry-mac ]' + + pipx: + tags: + - update + run: once + log: + error: Failed to ensure `pipx` is installed + start: Ensuring `pipx` is installed + success: Successfully ensured `pipx` is installed + cmds: + - task: python + - task: pipx:{{OS}} + status: + - type pipx > /dev/null + + pipx:darwin: + cmds: + - task: brew + - brew install pipx + - pipx ensurepath + + pipx:linux: + deps: + - pipx:linux:brew + - pipx:linux:system + + pipx:linux:brew: + cmds: + - brew install pipx + - pipx ensurepath + status: + - '! type brew > /dev/null' + + pipx:linux:system: + deps: + - python + cmds: + - task: system + vars: + PACKAGE: python3-pip + - task: system + vars: + PACKAGE: python3-venv + - | + if ! type pip3; then + python3 -m ensurepip > /dev/null + fi + - python3 -m pip install --user pipx + - python3 -m pipx ensurepath + status: + - type brew > /dev/null + + pipx:windows: + log: + error: These scripts are not currently compatible with Windows. Try using WSL. + cmds: + - exit 1 + + poetry: + tags: + - update + run: once + cmds: + - task: poetry:{{OS}} + - task: exit:notice:reload + status: + - type poetry > /dev/null || [ -n "$NO_INSTALL_POETRY" ] + + poetry:darwin: + run: once + cmds: + - task: install:software + vars: + PACKAGE: poetry + + poetry:linux: + run: once + cmds: + - task: install:software + vars: + PACKAGE: poetry + + poetry:windows: + log: + error: These scripts are not currently compatible with Windows. Try using WSL. + cmds: + - exit 1 + + python: + deps: + - common + run: once + cmds: + - task: brew + - task: python:{{OS}} + status: + - type python3 > /dev/null + + python:darwin: + run: once + cmds: + - task: install:software + vars: + PACKAGE: python@3.10 + status: + - type python3 > /dev/null + + python:linux: + run: once + cmds: + - task: install:software + vars: + PACKAGE: python@3.10 + status: + - type python3 > /dev/null + + python:pip: + run: once + cmds: + - | + PIP_LOCATION="$(which pip)" + PIP3_LOCATION="$(which pip3)" + if [ "$USER" == "root" ]; then + if type pip &> /dev/null && type pip3 &> /dev/null; then + rm "$PIP_LOCATION" + ln -s "$PIP3_LOCATION" "$PIP_LOCATION" + elif type pip3 &> /dev/null; then + ln -s "$PIP3_LOCATION" /usr/bin/pip + elif type pip &> /dev/null; then + ln -s "$PIP_LOCATION" /usr/bin/pip3 + else + .config/log warn 'Both `pip` and `pip3` appear to be missing' + fi + elif type sudo &> /dev/null && sudo -n true; then + if type pip &> /dev/null && type pip3 &> /dev/null; then + sudo rm "$PIP_LOCATION" + sudo ln -s "$PIP3_LOCATION" "$PIP_LOCATION" + elif type pip3 &> /dev/null; then + sudo ln -s "$PIP3_LOCATION" /usr/bin/pip + elif type pip &> /dev/null; then + sudo ln -s "$PIP_LOCATION" /usr/bin/pip3 + else + .config/log warn 'Both `pip` and `pip3` appear to be missing' + fi + elif type sudo &> /dev/null; then + if type pip &> /dev/null && type pip3 &> /dev/null; then + .config/log info 'Running `sudo rm $(which pip)`' + sudo rm "$PIP_LOCATION" + sudo ln -s "$PIP3_LOCATION" "$PIP_LOCATION" + elif type pip3 &> /dev/null; then + .config/log info 'Running `sudo ln -s $(which pip3) /usr/bin/pip`' + sudo ln -s "$PIP3_LOCATION" /usr/bin/pip + elif type pip &> /dev/null; then + .config/log info 'Running `sudo ln -s $(which pip) /usr/bin/pip3`' + sudo ln -s "$PIP_LOCATION" /usr/bin/pip3 + else + .config/log warn 'Both `pip` and `pip3` appear to be missing' + fi + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - which pip > /dev/null + - which pip3 > /dev/null + - '[ "$(which pip)" == "$(which pip3)" ]' + + python:windows: + log: + error: These scripts are not currently compatible with Windows. Try using WSL. + cmds: + - exit 1 + + rsync: + tags: + - update + run: once + cmds: + - task: system + vars: + PACKAGE: rsync + - task: install:software + vars: + PACKAGE: rsync + status: + - type rsync > /dev/null + + rust: + run: once + cmds: + - task: install:software + vars: + BIN_NAME: cargo + PACKAGE: rust + + s5cmd: + run: once + cmds: + - task: install:software + vars: + BIN_NAME: s5cmd + PACKAGE: peak/tap/s5cmd + + sentry: + run: once + cmds: + - task: install:software + vars: + BIN_NAME: sentry-cli + PACKAGE: getsentry/tools/sentry-cli + + snapcraft: + run: once + tags: + - semantic + cmds: + - task: snapcraft:snap + - task: install:software + vars: + PACKAGE: snapcraft + status: + - type snapcraft > /dev/null + + snapcraft:snap: + log: + error: Failed to run `sudo snap install snapcraft --classic` + start: Running `sudo snap install snapcraft --classic` + success: Successfully installed `snapcraft` + cmds: + - sudo snap install snapcraft --classic + status: + - '! type snap > /dev/null' + + sshfs: + run: once + cmds: + - task: system + vars: + PACKAGE: sshfs + + sshpass: + run: once + cmds: + - task: install:software + vars: + PACKAGE: hudochenkov/sshpass/sshpass + + subrepo: + run: once + cmds: + - task: install:software + vars: + PACKAGE: git-subrepo + status: + - git subrepo --version > /dev/null || [[ "${container:=}" == "docker" ]] + + system: + vars: + PACKAGE: '{{if .PACKAGE}}{{.PACKAGE}}{{else}}{{.CLI_ARGS}}{{end}}' + cmds: + - .config/log start 'Attempting to install `{{.PACKAGE}}` using the system package manager' + - | + if [ -f "/etc/redhat-release" ]; then + if type dnf &> /dev/null; then + task install:software:system:dnf -- '{{.PACKAGE}}' + else + task install:software:system:yum -- '{{.PACKAGE}}' + fi + elif [ -f "/etc/debian_version" ]; then + task install:software:system:apt-get -- '{{.PACKAGE}}' + elif [ -f "/etc/arch-release" ]; then + task install:software:system:pacman -- '{{.PACKAGE}}' + elif [ -f "/etc/alpine-release" ]; then + task install:software:system:apk -- '{{.PACKAGE}}' + elif type dnf &> /dev/null; then + task install:software:system:dnf -- '{{.PACKAGE}}' + elif type yum &> /dev/null; then + task install:software:system:yum -- '{{.PACKAGE}}' + elif type apt-get &> /dev/null; then + task install:software:system:apt-get -- '{{.PACKAGE}}' + elif type pacman &> /dev/null; then + task install:software:system:pacman -- '{{.PACKAGE}}' + elif type apk &> /dev/null; then + task install:software:system:apk -- '{{.PACKAGE}}' + else + if [ '{{OS}}' == 'darwin' ]; then + task install:software:system:macos -- '{{.PACKAGE}}' + else + .config/log error 'No matching system. Cannot continue with installation.' && exit 1 + fi + fi + status: + - type {{.PACKAGE}} > /dev/null || [ '{{.PACKAGE}}' == 'null' ] + + system:apk: + vars: + PACKAGE: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.PACKAGE}}{{end}}' + cmds: + - | + if [ "$USER" == "root" ]; then + apk --no-cache add {{.PACKAGE}} + elif type sudo &> /dev/null && sudo -n true; then + sudo apk --no-cache add {{.PACKAGE}} + elif type sudo &> /dev/null; then + .config/log info 'Running `sudo apk --no-cache add {{.PACKAGE}}`' + sudo apk --no-cache add {{.PACKAGE}} + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - '[ -z "$(which apk)" ]' + + system:apt-get: + vars: + PACKAGE: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.PACKAGE}}{{end}}' + cmds: + - | + if [ "$USER" == "root" ]; then + apt-get update + apt-get install -y {{.PACKAGE}} + elif type sudo &> /dev/null && sudo -n true; then + sudo apt-get update + sudo apt-get install -y {{.PACKAGE}} + elif type sudo &> /dev/null; then + .config/log info 'Running `sudo apt-get update`' + sudo apt-get update + .config/log info 'Running `sudo apt-get install -y {{.PACKAGE}}`' + sudo apt-get install -y {{.PACKAGE}} + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - '[ -z "$(which apt-get)" ]' + + system:dnf: + vars: + PACKAGE: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.PACKAGE}}{{end}}' + cmds: + - | + if [ "$USER" == "root" ]; then + dnf install {{.PACKAGE}} + elif type sudo &> /dev/null && sudo -n true; then + sudo dnf install {{.PACKAGE}} + elif type sudo &> /dev/null; then + .config/log info 'Running `sudo dnf install {{.PACKAGE}}`' + sudo dnf install {{.PACKAGE}} + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - '[ -z "$(which dnf)" ]' + + system:macos: + vars: + PACKAGE: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.PACKAGE}}{{end}}' + cmds: + - brew install {{.PACKAGE}} + status: + - '[ "{{OS}}" != "darwin" ]' + + system:pacman: + vars: + PACKAGE: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.PACKAGE}}{{end}}' + cmds: + - | + if [ "$USER" == "root" ]; then + pacman -S {{.PACKAGE}} + elif type sudo &> /dev/null && sudo -n true; then + sudo pacman -S {{.PACKAGE}} + elif type sudo &> /dev/null; then + .config/log info 'Running `sudo pacman -S {{.PACKAGE}}`' + sudo pacman -S {{.PACKAGE}} + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - '[ -z "$(which pacman)" ]' + + system:yum: + vars: + PACKAGE: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.PACKAGE}}{{end}}' + cmds: + - | + if [ "$USER" == "root" ]; then + yum install -y {{.PACKAGE}} + elif type sudo &> /dev/null && sudo -n true; then + sudo yum install -y {{.PACKAGE}} + elif type sudo &> /dev/null; then + .config/log info 'Running `sudo yum install -y {{.PACKAGE}}`' + sudo yum install -y {{.PACKAGE}} + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - '[ -z "$(which yum)" ]' + + task: + run: once + cmds: + - | + if [[ "$(which task)" == *'.local/bin/task' ]] && command -v brew > /dev/null && ! brew list go-task/tap/go-task; then + brew install go-task/tap/go-task + fi + + tokei: + run: once + cmds: + - task: install:software + vars: + PACKAGE: tokei + + trivy: + run: once + cmds: + - task: install:software + vars: + PACKAGE: aquasecurity/trivy/trivy + + unbuffer: + run: once + cmds: + - task: install:software + vars: + BIN_NAME: unbuffer + PACKAGE: expect + + vagrant: + cmds: + - task: vagrant:{{OS}} + status: + - type vagrant > /dev/null + + vagrant:darwin: + run: once + cmds: + - task: brew:cask + vars: + CASK: vagrant + + vagrant:linux: + run: once + cmds: + - task: :install:install-doctor + vars: + SOFTWARE: vagrant + + virtualbox: + cmds: + - task: virtualbox:{{OS}} + - task: exit:notice:restart + status: + - type vboxmanage > /dev/null + + virtualbox:darwin: + run: once + cmds: + - task: brew:cask + vars: + CASK: virtualbox + + virtualbox:linux: + run: once + cmds: + - task: :install:install-doctor + vars: + SOFTWARE: virtualbox + + vmware: + cmds: + - task: vmware:{{OS}} + - task: exit:notice:restart + status: + - type vmrun > /dev/null + + vmware:darwin: + run: once + cmds: + - task: brew:cask + vars: + CASK: vmware-fusion + + vmware:linux: + run: once + cmds: + - task: :install:install-doctor + vars: + SOFTWARE: vmware + + volta: + run: once + cmds: + - .config/log info 'Installing Volta' + - curl https://get.volta.sh | bash + - | + . "$HOME/.profile" &> /dev/null || true + volta setup + - .config/log success 'Successfully setup Volta' + status: + - type volta > /dev/null + + yarn: + tags: + - update + run: once + cmds: + - task: install:software + vars: + PACKAGE: yarn + + yq: + tags: + - update + run: once + cmds: + - task: install:software + vars: + PACKAGE: yq + + yubikey-agent: + run: once + cmds: + - task: install:software + vars: + PACKAGE: yubikey-agent diff --git a/.config/taskfiles/install/Taskfile-tap.yml b/.config/taskfiles/install/Taskfile-tap.yml new file mode 100644 index 00000000..ceeb24ee --- /dev/null +++ b/.config/taskfiles/install/Taskfile-tap.yml @@ -0,0 +1,49 @@ +--- +version: '3' + +tasks: + scan: + deps: + - :install:npm:leasot + - :install:software:jq + summary: | + ```shell + # @description Processes leasot data and returns .variables.json data including charts written in @appnest/readme format + # + # @arg $1 The file that the leasot JSON was written to + # @arg $2 The tag being processed + function populateChartVar() { + ... + } + ``` + vars: + DOC_IDS: '@binaryBrew,@npmBrew,@pythonBrew' + log: + error: Failed to acquire package information from comments via `leasot` + start: Scanning and acquiring package information in comments via `leasot` + success: Acquired package information from comments + cmds: + - | + function populateChartVar() { + CHART='[["Package", "Description"]' + jq --arg tag "$(echo $2 | tr '[a-z]' '[A-Z]')" -r '.[] | select(.tag == $tag) | . + | del(. ["file", "ref", "line", "tag"]) | .text' "$1" | while read COMMENT; do + if [ "$CHART" != '[' ]; then + CHART="${CHART}," + fi + LINK="$(echo $COMMENT | sed 's/ - .*//')" + DESCRIPTION="$(echo $COMMENT | sed 's/.* - //' | sed 's/\"/\\\"/g')" + CHART="${CHART}[\"**$LINK**\",\"$DESCRIPTION\"]" + done + CHART="${CHART}]" + TMP_CHART="$(mktemp)" + KEY="$(echo $2 | sed 's/^@//')" + jq --arg chart "$CHART" --arg key "${KEY}_var_chart" '.[$key] = ($chart | fromjson)' .variables.json > "$TMP_CHART" + mv "$TMP_CHART" .variables.json + } + TMP="$(mktemp)" + leasot --tags '{{.DOC_IDS}}' --reporter json './Formula/**/*' > "$TMP" || true + VARIABLES_JSON="$(jq '.' .variables.json)" + for ID in {{replace "," " " .DOC_IDS}}; do + populateChartVar "$TMP" "$ID" + done diff --git a/.config/taskfiles/install/Taskfile-ventoy.yml b/.config/taskfiles/install/Taskfile-ventoy.yml new file mode 100644 index 00000000..5df1d447 --- /dev/null +++ b/.config/taskfiles/install/Taskfile-ventoy.yml @@ -0,0 +1,99 @@ +--- +version: '3' + +vars: + VENTOY_DOWNLOAD_CONFIG_URL: https://gitlab.com/megabyte-labs/jumpusb/-/raw/master/local/distros.json + VENTOY_USB_PATH: + sh: | + if [ -d /run/media/user/Ventoy ]; then + echo "/run/media/user/Ventoy" + else + echo "./jumpusb-fs" + fi + +tasks: + clone: + cmds: + - | + mkdir -p {{.VENTOY_USB_PATH}} + cd {{.VENTOY_USB_PATH}} + git init + git remote add origin https://gitlab.com/megabyte-labs/jumpusb.git + git fetch + git pull origin master + + download: + deps: + - :install:software:axel + - :install:software:p7zip + todo: Add validation of md5/sha256 and add update functionality to VENTOY_DOWNLOAD_CONFIG_URL + cmds: + - task: setup:persistence + - curl -sSL '{{.VENTOY_DOWNLOAD_CONFIG_URL}}' > .ventoy.json + - | + jq -c '.distros[]' .ventoy.json | while read VENTOY_OS; do + MIRROR_COUNT="$(jq -r -n --argjson in "$VENTOY_OS" '$in.mirrors | length')" + VENTOY_URL="$(jq -r -n --argjson in "$VENTOY_OS" '$in.mirrors[]')" + if [ "$MIRROR_COUNT" == '1' ]; then + VENTOY_URL_AXEL="$VENTOY_URL" + else + VENTOY_URL_AXEL="$(jq -r -n --argjson in "$VENTOY_OS" '$in.mirrors | join(" ")')" + fi + VENTOY_MD5="$(jq -r -n --argjson in "$VENTOY_OS" '$in.md5')" + VENTOY_PATH="$(jq -r -n --argjson in "$VENTOY_OS" '$in.path')" + VENTOY_PER_BASE="$(jq -r -n --argjson in "$VENTOY_OS" '$in.persistence_base')" + VENTOY_PER_FILE="$(jq -r -n --argjson in "$VENTOY_OS" '$in.persistence_file')" + mkdir -p "$(dirname ./jumpusb-fs/$VENTOY_PATH)" + if [ ! -f "./jumpusb-fs/$VENTOY_PATH" ] || [ -f "./jumpusb-fs/${VENTOY_PATH}.st" ]; then + mkdir -p "$(dirname ./jumpusb-fs/${VENTOY_PATH})" + axel -o "./jumpusb-fs/${VENTOY_PATH}" -n 14 $VENTOY_URL_AXEL + if [[ "$VENTOY_URL" == *.gz ]]; then + mv "./jumpusb-fs/${VENTOY_PATH}" "./jumpusb-fs/${VENTOY_PATH}.gz" + gzip -d "./jumpusb-fs/${VENTOY_PATH}.gz" + elif [[ "$VENTOY_URL" == *.bz2 ]]; then + mv "./jumpusb-fs/${VENTOY_PATH}" "./jumpusb-fs/${VENTOY_PATH}.bz2" + bzip2 -d "./jumpusb-fs/${VENTOY_PATH}.bz2" + fi + fi + if [ "$VENTOY_PER_FILE" != 'null' ] && [ ! -f "./jumpusb-fs/persistence/$VENTOY_PER_FILE" ]; then + 7z -y x "./jumpusb-fs/persistence/$VENTOY_PER_BASE" + UNCOMPRESSED_FILE="$(echo $VENTOY_PER_BASE | sed 's/.7z$//')" + mv "$UNCOMPRESSED_FILE" "./jumpusb-fs/persistence/$VENTOY_PER_FILE" + fi + done + + install: + cmds: + - task: install:disk + + install:detect:usb: + summary: | + # Detects the Drive Path of USB Last Inserted + + This task detects the drive path of the last USB that was inserted. After + it detects the drive path, it writes the path to the file that is passed in + as the ANSWER_FILE Go template variable. + cmds: + - | + # TODO: Add bash script / darwin script that detects the drive path of + + install:disk: + vars: + TARGET_DRIVE: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.TARGET_DRIVE}}{{end}}' + cmds: + - curl -sSL https://github.com/ventoy/Ventoy/releases/download/v1.0.74/ventoy-1.0.74-linux.tar.gz > ventoy.tar.gz + - tar -xzvf ventoy.tar.gz + - .config/log info 'Running sudo command `sudo sh ventoy/Ventoy2Disk.sh -ui {{.TARGET_DRIVE}}`' + - sudo sh ventoy/Ventoy2Disk.sh -ui {{.TARGET_DRIVE}} + - rm -rf ventoy + status: + - '[ -z "{{.TARGET_DRIVE}}" ]' + + setup:persistence: + cmds: + - curl -sSL https://github.com/ventoy/backend/releases/download/v5.0/images.zip > images.zip + - unzip images.zip + - rm images.zip + - mkdir -p {{.VENTOY_USB_PATH}}/persistence + - mv images/* {{.VENTOY_USB_PATH}}/persistence + - rm -rf images diff --git a/.config/taskfiles/install/Taskfile.yml b/.config/taskfiles/install/Taskfile.yml new file mode 100644 index 00000000..90f17c06 --- /dev/null +++ b/.config/taskfiles/install/Taskfile.yml @@ -0,0 +1,391 @@ +--- +version: '3' + +tasks: + brewfile: + deps: + - :install:software:brew + log: + error: Encountered error while installing Homebrew software bundle defined in `.config/Brewfile` + start: Installing Homebrew software bundle defined in `.config/Brewfile` + success: Successfully installed Homebrew software bundle defined in `.config/Brewfile` + cmds: + - brew tap Homebrew/bundle + - mkdir -p local + - cp .config/Brewfile local/Brewfile.common + - brew bundle install --file local/Brewfile.common + - | + if [ -f local/Brewfile ]; then + brew bundle install --file local/Brewfile + fi + + extras: + cmds: + - task: :install:software:gcc + - task: :install:software:sshfs + - task: :install:software:gcloud + + install-doctor: + log: + error: Error encountered while installing {{.SOFTWARE}} via https://install.doctor + start: Ensuring {{.SOFTWARE}} is installed using Install Doctor + success: Successfully installed `{{.SOFTWARE}}` via Install Doctor + cmds: + - | + .config/log info 'Installing `{{.SOFTWARE}}` by running `curl -sS https://install.doctor/{{.SOFTWARE}} | bash`' + curl -sS https://install.doctor/{{.SOFTWARE}} | bash + status: + - type {{.SOFTWARE}} &> /dev/null || [[ '{{.DOCKER_ENVIRONMENT}}' == "true" ]] + + modules:global: + deps: + - :install:software:volta + - :install:software:yq + run: once + log: + error: Error pre-loading NPM global packages + start: Pre-loading NPM global packages + success: Finished pre-loading NPM global packages + cmds: + - | + PKGS="$(yq eval '.tasks[].cmds[0].vars.NPM_PACKAGE' .config/taskfiles/install/Taskfile-npm.yml | tr '\n' ' ')" + for PKG in $PKGS; do + if [ -f "$(echo $NODE_PATH | sed 's/^://' | sed 's/:.*//')/$PKG" ]; then + LIST="$LIST $PKG" + fi + done + if [ -n "$LIST" ]; then + .config/log info "Installing the following NPM packages globally - $LIST" + volta install $LIST + fi + status: + - '[[ "${container:=}" == "docker" ]]' + + modules:local: + cmds: + - task: modules:local:init + status: + - '[ -n "$BUILD_DATE" ] && [ -f /.dockerenv ]' + + modules:local:init: + deps: + - :install:npm:{{.NPM_PROGRAM}} + - :install:software:jq + vars: + PREVIOUS_DEPS: + sh: | + if [ -f .task/npm-deps ]; then + cat .task/npm-deps + else + echo "" + fi + env: + DEPS: + sh: jq -r -S '.dependencies + .devDependencies + .optionalDependencies + .peerDependencies' package.json + PREVIOUS_DEPS: '{{.PREVIOUS_DEPS}}' + SEMANTIC_PYTHON_POST_INSTALL: '{{if eq .SEMANTIC_RELEASE "true"}}true{{else}}false{{end}}' + run: when_changed + log: + error: Encountered error while installing local NPM dependencies + start: Installing local NPM dependencies + success: Successfully installed local NPM dependencies + cmds: + - task: tslib + - | + {{.NPM_PROGRAM}} config set loglevel error --location project + {{.NPM_PROGRAM}} config set strict-peer-dependencies false --location project + - cmd: | + if [ "$DEPS" != "$PREVIOUS_DEPS" ] || [ ! -d node_modules ]; then + if [ -n "$AUTO_UPDATE_NPM_PACKAGES" ]; then + {{.NPM_PROGRAM}} update + else + {{.NPM_PROGRAM}} install {{if eq .NPM_PROGRAM "pnpm"}}--public-hoist-pattern *types* \ + --no-optional --loglevel error --strict-peer-dependencies false \ + --public-hoist-pattern *eslint* --public-hoist-pattern @prettier/plugin-* \ + --public-hoist-pattern *prettier-plugin-* --public-hoist-pattern *jest* {{end}}|| EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + .config/log info 'Running `{{.NPM_PROGRAM}} update` because install had non-zero exit code '"(Code $EXIT_CODE)" + {{.NPM_PROGRAM}} update + fi + fi + fi + mkdir -p .task + echo "$DEPS" > .task/npm-deps + ignore_error: true + sources: + - package.json + + modules:local:lockfiles: + cmds: + - task: :install:npm:pnpm-lock-export + - task: :install:npm:synp + - | + if [ ! -f package-lock.json ]; then + pnpm-lock-export + MOVE_PACKAGE_LOCK=true + fi + if [ ! -f yarn.lock ] && [ -f package-lock.json ]; then + synp --source-file package-lock.json + mkdir -p local + mv yarn.lock local/yarn.lock + fi + if [ -n "$MOVE_PACKAGE_LOCK" ]; then + mkdir -p local + mv package-lock.json local/package-lock.json + fi + sources: + - pnpm-lock.yaml + + modules:local:sync: + deps: + - modules:local + log: + error: Error while synchronizing `NPM_KEEP_UPDATED` packages with the latest version(s) + start: Ensuring `NPM_KEEP_UPDATED` NPM packages are the latest version + success: '`NPM_KEEP_UPDATED` packages are all the latest version' + cmds: + - | + TMP_REFRESH="$(mktemp)" + TMP_LIST="$(mktemp)" + function updateAvailable() { + LATEST="$(npm view $1 version)" + LOCAL="$(jq -r '.version' ./node_modules/$1/package.json)" + if ! printf '%s\n%s\n' "$LATEST" "$LOCAL" | sort -V -c > /dev/null; then + .config/log info "Version $LATEST is available for $1 (currently version $LOCAL)" + echo "true" > "$TMP_REFRESH" + fi + } + for PATTERN in {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.NPM_KEEP_UPDATED}}{{end}}; do + while read PATHH; do + if [ -f "$PATHH/package.json" ]; then + PKG="$(echo $PATHH | sed 's/.\/node_modules\///')" + PACKAGE_LIST="$(cat "$TMP_LIST") ${PKG}@latest" + echo "$PACKAGE_LIST" > "$TMP_LIST" + updateAvailable "$PKG" & + fi + done < <(find ./node_modules/$PATTERN -maxdepth 0) + done + wait + PACKAGE_LIST="$(cat "$TMP_LIST")" + REFRESH_PACKAGES="$(cat $TMP_REFRESH)" + if [[ "$REFRESH_PACKAGES" == 'true' ]]; then + PACKAGE_LIST="$(echo $PACKAGE_LIST | sed 's/^.//')" + .config/log info "Updating NPM packages configured to sync with latest version since one or more of them have an update available" + {{.NPM_PROGRAM}} update $PACKAGE_LIST + .config/log success 'Successfully updated to the following - `'"$PACKAGE_LIST"'`' + else + {{if .CLI_ARGS}} + .config/log info '`{{.CLI_ARGS}}` is already the latest version' + {{else}} + .config/log info "NPM packages configured to sync with latest version are all up-to-date" + {{end}} + fi + + path:add: + vars: + UNAME: + sh: uname + log: + error: Failed to modify PATH + start: Adding `$HOME/{{.PATH_STRING}}` to the PATH in $HOME/.profile + cmds: + - | + if [ '{{OS}}' == 'darwin' ] || [ '{{OS}}' == 'linux' ]; then + # shellcheck disable=SC2016 + PATH_STRING='PATH="$HOME/{{.PATH_STRING}}:$PATH"' + if ! grep "$PATH_STRING" "$HOME/.profile" > /dev/null; then + echo -e "export ${PATH_STRING}\n" >> "$HOME/.profile" + .config/log info "Updated the PATH variable to include ~/{{.PATH_STRING}} in $HOME/.profile" + fi + elif [[ '{{.UNAME}}' == 'CYGWIN'* ]] || [[ '{{.UNAME}}' == 'MINGW'* ]]; then + .config/log error "Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + .config/log error "FreeBSD support not added yet" && exit 1 + else + .config/log error "System type not recognized ($OSTYPE)" + fi + + profile:add: + log: + error: Error modifying $HOME/.profile + start: Adding `{{.PROFILE_STRING}}` to $HOME/.profile + success: Successfully modified $HOME/.profile + cmds: + - | + if [ '{{OS}}' == 'darwin' ] || [ '{{OS}}' == 'linux' ]; then + # shellcheck disable=SC2016 + PROFILE_STRING='{{.PROFILE_STRING}}' + if ! cat "$HOME/.profile" | grep "$PATH_STRING" > /dev/null; then + echo -e "${PROFILE_STRING}\n" >> "$HOME/.profile" + .config/log info 'Added `{{.PROFILE_STRING}}` to '"$HOME/.profile" + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + .config/log error "Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + .config/log error "FreeBSD support not added yet" && exit 1 + else + .config/log error "System type not recognized" + fi + + scoop:scan: + deps: + - :install:software:jq + env: + TMP: + sh: mktemp + log: + error: Error while extracting Scoop information + start: Extracting Scoop information + success: Successfully extracted Scoop information + cmds: + - jq --arg scoops "$(jq -n '[["Project", "Description"], (inputs | ["**[" + .homepage + "](" + .homepage + ")**", .description ])]' Scoops/*.json)" + '.scoop_var_chart = $scoops' .variables.json > "$TMP" && mv "$TMP" .variables.json + + system:upgrade: + log: + error: Encountered error while upgrading system + start: Upgrading system packages + cmds: + - task: system:upgrade:apk + - task: system:upgrade:apt-get + - task: system:upgrade:dnf + - task: system:upgrade:pacman + - task: system:upgrade:yum + - task: system:upgrade:macos + + system:upgrade:apk: + cmds: + - | + if [ "$USER" == "root" ]; then + .config/log info 'TODO' + elif type sudo &> /dev/null && sudo -n true; then + .config/log info 'TODO' + elif type sudo &> /dev/null; then + .config/log info 'TODO' + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - '[ -z "$(which apk)" ]' + + system:upgrade:apt-get: + cmds: + - | + if [ "$USER" == "root" ]; then + apt-get update + apt-get install -y {{.PACKAGE}} + elif type sudo &> /dev/null && sudo -n true; then + sudo apt-get update + sudo apt -y upgrade + elif type sudo &> /dev/null; then + .config/log info 'Running `sudo apt-get update`' + sudo apt-get update + .config/log info 'Running `sudo apt -y upgrade`' + sudo apt -y upgrade + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - '[ -z "$(which apt-get)" ]' + + system:upgrade:dnf: + cmds: + - | + if [ "$USER" == "root" ]; then + dnf clean all + dnf check-update + dnf update + elif type sudo &> /dev/null && sudo -n true; then + sudo dnf clean all + sudo dnf check-update + sudo dnf update + elif type sudo &> /dev/null; then + .config/log info 'Running `sudo dnf clean all`' + sudo dnf clean all + .config/log info 'Running `sudo dnf check-update`' + sudo dnf check-update + .config/log info 'Running `sudo dnf update`' + sudo dnf update + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - '[ -z "$(which dnf)" ]' + + system:upgrade:macos: + cmds: + - echo "TODO Upgrade macOS system version via CLI" + status: + - '[ "{{OS}}" != "darwin" ]' + + system:upgrade:pacman: + cmds: + - | + if [ "$USER" == "root" ]; then + .config/log 'TODO' + elif type sudo &> /dev/null && sudo -n true; then + .config/log 'TODO' + elif type sudo &> /dev/null; then + .config/log 'TODO' + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - '[ -z "$(which pacman)" ]' + + system:upgrade:yum: + cmds: + - | + if [ "$USER" == "root" ]; then + yum clean all + yum update + elif type sudo &> /dev/null && sudo -n true; then + sudo yum clean all + sudo yum update + elif type sudo &> /dev/null; then + .config/log info 'Running `sudo yum clean all`' + sudo yum clean all + .config/log info 'Running `sudo yum update`' + sudo yum update + else + .config/log warn '`sudo` unavailable and user has no permissions' + fi + status: + - '[ -z "$(which yum)" ]' + + tslib: + vars: + TSLIB_MSG: You can potentially optimize your bundle by setting `importHelpers` in compilerOptions + in tsconfig.json to `true`. After importHelpers is set to true, the taskfiles will automatically + install tslib. + run: once + log: + error: Failed to probe `tslib` + start: Checking if `tslib` is being used + cmds: + - | + if [[ "$(jq -r '.dependencies.tslib' package.json)" == 'null' ]]; then + if [ "$(jq -r '.compilerOptions.importHelpers' tsconfig.json)" != 'true' ]; then + .config/log info '{{.TSLIB_MSG}}' + else + .config/log info 'Adding `tslib` to dependencies since `importHelpers` is set to true in tsconfig.json' + TMP="$(mktemp)" + jq '.dependencies.tslib = "latest"' package.json > "$TMP" + mv "$TMP" package.json + fi + fi + status: + - '[ ! -f tsconfig.json ]' + + ventoy: + cmds: + - task: :install:ventoy:install + - task: ventoy:init + preconditions: + - sh: test -d /run/media/user/Ventoy + msg: The `/run/media/user/Ventoy` mounted USB directory must be available! + + ventoy:init: + deps: + - :install:ventoy:clone + - :install:ventoy:download diff --git a/.config/taskfiles/lint/Taskfile-codeclimate.yml b/.config/taskfiles/lint/Taskfile-codeclimate.yml new file mode 100644 index 00000000..f226cf19 --- /dev/null +++ b/.config/taskfiles/lint/Taskfile-codeclimate.yml @@ -0,0 +1,42 @@ +--- +version: '3' + +tasks: + load:custom-engines: + deps: + - :install:software:docker + - :install:software:jq + - :install:software:yq + env: + CUSTOM_ENGINES: + sh: yq e -o=j '.' .codeclimate.yml | jq -r '.plugins | keys[]' + cmds: + - | + function dockerGet() { + if ! docker images {{.DOCKERHUB_PROFILE}}/$1 | grep ' slim-codeclimate ' > /dev/null; then + docker pull "{{.DOCKERHUB_PROFILE}}/$1:slim-codeclimate" + fi + docker tag "{{.DOCKERHUB_PROFILE}}/$1:slim-codeclimate" "codeclimate/codeclimate-$1:latest" + } + for ENGINE in $CUSTOM_ENGINES; do + if [ "$(yq e -o=j '.' .codeclimate.yml | jq --arg engine $ENGINE -r '.plugins[$engine].enabled')" == 'true' ]; then + .config/log info "Loading custom CodeClimate engine ($ENGINE)" + dockerGet "$ENGINE" + fi + done + + taskfiles:add: + cmds: + - rm -rf .config + - cp -rf ../../.config .config + - cp ../../start.sh start.sh + - cp ../../Taskfile.yml Taskfile.yml + - cp ../../package.json package.json + status: + - '[ -z "$CODECLIMATE_INTEGRATION_TEST" ]' + + taskfiles:remove: + cmds: + - rm -rf .config start.sh Taskfile.yml package.json + status: + - '[ -z "$CODECLIMATE_INTEGRATION_TEST" ]' diff --git a/.config/taskfiles/lint/Taskfile-esprint.yml b/.config/taskfiles/lint/Taskfile-esprint.yml new file mode 100644 index 00000000..a770feaf --- /dev/null +++ b/.config/taskfiles/lint/Taskfile-esprint.yml @@ -0,0 +1,8 @@ +--- +version: '3' + +tasks: + esprint: + todo: Remove this file + cmds: + - echo 'Stale file' diff --git a/.config/taskfiles/lint/Taskfile-markdown.yml b/.config/taskfiles/lint/Taskfile-markdown.yml new file mode 100644 index 00000000..87bbacec --- /dev/null +++ b/.config/taskfiles/lint/Taskfile-markdown.yml @@ -0,0 +1,66 @@ +--- +version: '3' + +tasks: + markdown: + deps: + - :install:modules:local + - :install:npm:remark + desc: Lint markdown files with `remark` + summary: | + # Lint markdown files + + This task uses `remark-lint` under the hood to provide markdown style recommendations. + + **Example scanning all markdown files:** + `task lint:markdown` + + **Example scanning single markdown file:** + `task lint:markdown -- TEST.md` + + For more information on `remark`, see the [GitHub page](https://github.com/remarkjs/remark). + + For more information on `remark-lint`, see the [GitHub page](https://github.com/remarkjs/remark-lint). + log: + error: Error running `remark` + start: Filtering markdown files with `remark` + success: Successfully filtered markdown files with `remark` + cmds: + - | + {{if .CLI_ARGS}} + remark {{.CLI_ARGS}} + {{else}} + while read PATHH; do + remark --no-stdout "$PATHH" & + done < <(find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.md' \)) + wait + {{end}} + + markdown:links: + deps: + - :install:npm:markdown-link-check + desc: Scan markdown files for broken links + summary: | + # Report any broken links in the files that end with .md + + This task uses the npm package called `markdown-link-check` to scan all the links + and then report which ones are broken. + + **Example scanning the whole project:** + `task lint:markdown-broken-links` + + **Example scanning single file:** + `task lint:markdown-broken-links -- filename.md` + + For more information on `markdown-link-check`, see their [GitHub page](https://github.com/tcort/markdown-link-check). + log: + error: Errors reported by `markdown-link-check` + start: Checking for broken links in markdown files with `markdown-link-check` + success: Successfully passed `markdown-link-check` + cmds: + - | + {{if .CLI_ARGS}} + markdown-link-check {{.CLI_ARGS}} + {{else}} + find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.md' \) -print0 | xargs -0 -r -n1 markdown-link-check + {{end}} diff --git a/.config/taskfiles/lint/Taskfile-prose.yml b/.config/taskfiles/lint/Taskfile-prose.yml new file mode 100644 index 00000000..7212dee5 --- /dev/null +++ b/.config/taskfiles/lint/Taskfile-prose.yml @@ -0,0 +1,58 @@ +--- +version: '3' + +tasks: + prose: + deps: + - :install:pipx:proselint + desc: Lint text/markdown for English prose with `proselint` + summary: | + # Lint for English Prose + + This task uses `proselint` to analyze markdown files for prose. It will generate recommendations + based on typography, grammar, and wording. + + **Example scanning all markdown files:** + `task lint:prose` + + **Example scanning specific file (markdown or not):** + `task lint:prose -- myfile.js` + + For more information, see [Proselint's GitHub page](https://github.com/amperser/proselint). + log: + error: Error encountered while validating {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}the project{{end}} with Proselint + start: Linting {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}the project{{end}} prose with Proselint + success: Successfully validated {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}the project{{end}} with Proselint + cmds: + - task: prose:config + - | + PATH="$PATH:$HOME/.local/bin" + {{if .CLI_ARGS}} + {{.PYTHON_HANDLE}}proselint {{.CLI_ARGS}} + {{else}} + find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.md' \) -print0 | xargs -0 -r -n1 {{.PYTHON_HANDLE}}proselint + {{end}} + - task: prose:config:restore + + prose:config: + log: + error: Error encountered while ensuring Proselint configuration is in proper location + start: Moving Proselint configuration to proper location + success: Ensured Proselint configuration is in proper location + cmds: + - | + if [ -f ~/.config/proselint/config.json ]; then + .config/log info 'Backing up `proselint` from `~/.config/proselint/config.json` to `~/.config/proselint/config.json.backup`' + mv ~/.config/proselint/config.json ~/.config/proselint/config.json.backup + fi + - mkdir -p ~/.config/proselint + - cp .config/proselint.json ~/.config/proselint/config.json + + prose:config:restore: + log: + error: Error restoring original Proselint configuration + start: Restoring original Proselint configuration + cmds: + - mv ~/.config/proselint/config.json.backup ~/.config/proselint/config + status: + - '[ ! -f ~/.config/proselint/config.json.backup ]' diff --git a/.config/taskfiles/lint/Taskfile.yml b/.config/taskfiles/lint/Taskfile.yml new file mode 100644 index 00000000..597552cd --- /dev/null +++ b/.config/taskfiles/lint/Taskfile.yml @@ -0,0 +1,701 @@ +--- +version: '3' + +vars: + ANSIBLE_LINT_CONFIG: .config/ansible-lint.yml + CSPELL_CONFIG: .config/cspell.json + FLAKE8_CONFIG: .config/flake8.toml + PRETTIERIGNORE_CONFIG: .config/prettierignore + SHELLCHECK_EXCLUDE: SC1091 + YAMLLINT_CONFIG: .config/yamllint.yml + +tasks: + all: + deps: + - '{{.REPOSITORY_TYPE}}' + - eslint + - :lint:markdown:markdown + - :lint:prose:prose + - shellcheck + - yaml + + ansible: + deps: + - :symlink:{{.REPOSITORY_SUBTYPE}} + - :install:pipx:ansible-lint + desc: Lint Ansible projects using Ansible Lint + hide: '{{ne .REPOSITORY_TYPE "ansible"}}' + summary: | + # Lint an Ansible project using Ansible Lint + + This task lints the project using Ansible Lint which will scan the project and report design + patterns that can possibly be improved. It can be used on both playbooks and roles. The + configuration for Ansible Lint is stored in the root of the project in a file titled + `.ansible-lint`. This configuration file is shared between all of our Ansible projects + so any changes to it need to be made to the upstream Ansible common file repository. Because of + this, it probably makes more sense to to disable rules (when absolutely necessary) using the + [syntax described in this link](https://ansible-lint.readthedocs.io/en/latest/rules.html#false-positives-skipping-rules). + + For more information, see [Ansible Lint's GitHub page](https://github.com/ansible-community/ansible-lint). + log: + error: Ansible Lint has detected possible errors! + start: Linting project with Ansible Lint + success: Validated project with Ansible Lint + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + ansible-lint -c {{.ANSIBLE_LINT_CONFIG}} + + any: + deps: + - :install:pipx:blocklint + - :install:pipx:pre-commit-hooks + desc: Generic linting of files for things like destroyed-symlinks, merge conflicts, etc. + log: + error: Errors were reported by the global linters for `{{.CLI_ARGS}}` + start: Linting `{{.CLI_ARGS}}` with global linters + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + blocklint --wordlist blacklist,slave,whitelist {{.CLI_ARGS}} + - PATH="$PATH:$HOME/.local/bin" && check-merge-conflict {{.CLI_ARGS}} + - PATH="$PATH:$HOME/.local/bin" && check-added-large-files {{.CLI_ARGS}} + - PATH="$PATH:$HOME/.local/bin" && check-symlinks {{.CLI_ARGS}} + - PATH="$PATH:$HOME/.local/bin" && destroyed-symlinks {{.CLI_ARGS}} + + codeclimate: + deps: + - :install:software:codeclimate + - :lint:codeclimate:load:custom-engines + desc: Run CodeClimate with all available linters + summary: | + # Run CodeClimate + + CodeClimate provides a birds-eye view of all the various possible issues that there may be + with a project by leveraging various plugins called CodeClimate engines. + + This task will run all available linters defined in `.codeclimate.yml` after substituting + some of the standard linters with custom engines built by Megabyte Labs. + + **Example:** + `task lint:codeclimate` + + You can optionally output the results in HTML format. To do so, pass in the name of the output + HTML file you would like to generate. + + **Example generating HTML results:** + `task lint:codeclimate -- ' -f html > codeclimate.html'` + + You can also specify a specific path for CodeClimate to scan: + + **Example specifying specific path to analyze:** + `task lint:codeclimate -- ' ./src/component' + log: + error: '`codeclimate` has detected errors' + start: Running `codeclimate` + success: Successfully validated the project by running `codeclimate` + cmds: + - | + if [ -z "$CODECLIMATE_INTEGRATION_TEST" ]; then + while read PATHH; do + cd "$(dirname $PATHH)" + CODECLIMATE_DEBUG=1 codeclimate analyze --dev{{if .CLI_ARGS}}{{.CLI_ARGS}}{{end}} + done < <(find test -maxdepth 2 -type f -name .codeclimate.yml) + else + while read PATHH; do + cd "$(dirname $PATHH)" && break + done < <(find test -maxdepth 2 -type f -name .codeclimate.yml) + task lint:codeclimate:taskfiles:add + CODECLIMATE_DEBUG=1 codeclimate analyze --dev + task lint:codeclimate:taskfiles:remove + fi + + commit: + deps: + - :install:modules:local + - :install:npm:commitlint + summary: | + # Lint a commit message + + This task will lint a commit message with `commitlint`. It requires that the commit message be passed as + an argument to this task. + + **Example:** + `task lint:commit -- 'My commit message'` + log: + error: Encountered error while linting commit message ({{.CLI_ARGS}}) + cmds: + - | + if [ ! -z '{{.CLI_ARGS}}' ] && [ '{{.CLI_ARGS}}' != '.git/MERGE_MSG' ] \ + && [ "$(test -f .git/MERGE_MSG && head -c12 < .git/MERGE_MSG)" != 'Merge branch' ]; then + cp '{{.CLI_ARGS}}' '{{.CLI_ARGS}}.bak' + TMP="$(mktemp)" + COMMIT_MSG_NOEMOJI="$(sed 's/^..//' < '{{.CLI_ARGS}}' | xargs)" + .config/log info 'Linting commit message (with emoji stripped)' + echo "$COMMIT_MSG_NOEMOJI" | commitlint || (mv '{{.CLI_ARGS}}.bak' '{{.CLI_ARGS}}' && exit 1) + elif [ '{{.CLI_ARGS}}' == '.git/MERGE_MSG' ]; then + .config/log info 'Bypassing commitlint since the message is a merge message' + fi + status: + - '[[ -z "{{.CLI_ARGS}}" ]]' + preconditions: + - sh: '[ ! -z "{{.CLI_ARGS}}" ]' + msg: 'A CLI argument must be passed to this task. See `task lint:commit --summary` for details.' + + docker: + deps: + - :install:software:docker + desc: Lint Dockerfiles using Hadolint (requires Docker) + summary: | + # Lint Dockerfiles using Hadolint + + Hadolint is a linter for Dockerfiles. This task uses Hadolint to report warnings + and suggestions so that the project is using best practices and is less likely to + have errors. The task uses Docker to run an ultra-compact container that includes + Hadolint. Installing Hadolint is done at runtime. The task scans for all files that + are named either `Dockerfile` or `Dockerfile.j2`. On top of reporting suggestions + for adhereing to Docker best-practices, Hadolint also leverages Shellcheck to report + possible errors in the shell logic used by the Dockerfile. + + **If the Dockerfile is named something other than `Dockerfile` or `Dockerfile.j2`, you + can manually run Hadolint by running:** + `docker run -v "$PWD:/work" -w /work megabytelabs/hadolint:slim CustomDockerfileName` + + **This task will ignore files in the following directories (and possibly more):** + * .cache * .husky * node_modules + * .common * .modules * test + * .git * .task * venv + + **Example scanning the whole project:** + `task lint:docker` + + **Example scanning single file:** + `task lint:docker -- CustomDockerfile` + + For more information, see [Hadolint's GitHub page](https://github.com/hadolint/hadolint). + vars: + PWD: + sh: echo "${PWD}/" + hide: + sh: '[ ! -f Dockerfile ]' + log: + error: Hadolint reported errors + start: Linting {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}the project{{end}} with Hadolint + success: Hadolint reported no errors + cmds: + - | + {{if .CLI_ARGS}} + docker run -v "$PWD:/work" -w /work megabytelabs/hadolint:slim -c .config/hadolint.yml {{.CLI_ARGS | replace .PWD ""}} + {{else}} + find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name Dockerfile -o \ + -name Dockerfile.j2 \) -print0 | xargs -0 -r -n1 docker run -v "$PWD:/work" -w /work \ + megabytelabs/hadolint:slim -c .config/hadolint.yml + {{end}} + + dockerfile: + cmds: + - task: docker + + dockerfilelint: + deps: + - :install:npm:dockerfilelint + desc: Lint a Dockerfile with `dockerfilelint` + hide: + sh: '[ ! -f Dockerfile ]' + log: + error: '`dockerfilelint` reported errors' + start: Linting {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}the project{{end}} with `dockerfilelint` + success: '`dockerfilelint` ran successfully and reported no errors' + cmds: + - | + {{if .CLI_ARGS}} + dockerfilelint {{.CLI_ARGS | replace .PWD ""}} + {{else}} + find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name Dockerfile -o \ + -name Dockerfile.j2 \) -print0 | xargs -0 -r -n1 dockerfilelint + {{end}} + + eslint: + deps: + - :install:modules:local + - :install:npm:eslint + desc: Lint with ESLint + summary: | + # Lint with `eslint` + + This task will lint the project or a specific pattern of files with `eslint`. It is capable of + linting JSON, YML, JS, TS, and HTML as long as the correct `package.json` dependencies are + installed. By default, all projects have the libraries necessary for linting JSON, TOML, and YML. + + **Example linting entire project:** + `task lint:eslint` + + **Example linting specific file:** + `task lint:eslint -- my_file.ts` + + **Example linting specific pattern of files:** + `task lint:eslint -- '**/*.js'` + log: + error: ESLint has detected errors that need to be addressed + start: Linting {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}the project{{end}} with ESLint + success: Successfully passed the ESLint test (please still address warnings) + cmds: + - > + eslint -c package.json --no-eslintrc --format {{.ESLINT_FORMATTER}} --cache + --cache-location .cache/eslintcache {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + + eslint:staged: + deps: + - :install:modules:local + - :install:npm:eslint + desc: Lint only modified files with ESLint + env: + ESLINT_STAGED_ONLY: on + log: + error: Failed to lint modified files with ESLint + start: Linting modified files with ESLint + success: Successfully linted modified files with ESLint + cmds: + - > + eslint -c package.json --no-eslintrc --format {{.ESLINT_FORMATTER}} --cache + --cache-location .cache/eslintcache {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + + eslint:warn-all: + deps: + - :install:modules:local + - :install:npm:eslint + desc: Convert ESLint errors into warnings by saving the modified configuration inside `package.json` + summary: | + # Convert ESLint Errors into Warnings + + This task will run ESLint against the project and then automatically inline + all reported errors as warnings in `package.json`. + log: + error: Encountered error while converting `eslint` errors to warnings + start: Overriding all errors reported from `eslint` as `warnings` in `package.json` + success: Successfully overrode `eslint` errors as warnings + cmds: + - > + ESLINT_TMP="$(mktemp)" + + eslint -c package.json --no-eslintrc --format summary --cache + --cache-location .cache/eslintcache {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} | + tee "$ESLINT_TMP" + + while read LINE; do + TMP="$(mktemp)"; + ESLINT_RULE="$(echo "$LINE" | grep 'errors ' | sed 's/.*rule:\ //')"; + if [ -n "$ESLINT_RULE" ] && [ "$ESLINT_RULE" != 'syntax error' ]; then + jq --arg rule "$ESLINT_RULE" '.eslintConfig.rules[$rule] = "warn"' package.json > "$TMP"; + mv "$TMP" package.json; + fi; + done < "$ESLINT_TMP" + + go: + deps: + - :install:software:go + - :install:software:golangci-lint + desc: Lint Go with `golangci-lint` + summary: | + # Lint Go Files + + This task will lint Go files with `golangci-lint`. The configuration is + stored in `.config/golangci.yml` and uses a handful of linters. + + If you would like to skip linting the whole project and instead lint an + individual file, then you can do so by passing the file path as a CLI + parameter like so: + + **Example linting individual file:** + `task lint:go -- path/filename.go` + log: + error: Detected errors with `golangci-lint` + start: Linting with `golangci-lint` + success: Successfully completed linting with `golangci-lint` + cmds: + - golangci-lint run -c .config/golangci.yml{{if .CLI_ARGS}}{{.CLI_ARGS}}{{end}} + + js: + deps: + - :install:modules:local + - :install:npm:eslint + log: + error: Errors were reported by ESLint for {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}JavaScript/TypeScript files{{end}} + start: Linting {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}JavaScript/TypeScript files{{end}} with ESLint + success: Successfully passed ESLint checks for {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}JavaScript/TypeScript files{{end}} + cmds: + - > + eslint -c package.json --no-eslintrc --format pretty --ext .js,.jsx,.ts,.tsx --cache + --cache-location .cache/eslintcache {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + + lint-staged: + deps: + - :install:modules:local + - :install:npm:lint-staged + log: + error: Errors were encountered when validating the staged files + start: Linting staged files + success: The staged files appear to be good! + cmds: + - | + lint-staged || LINT_EXIT_CODE="$?" + if [ -n "$LINT_EXIT_CODE" ]; then + .config/log error 'Linting failed!' + .config/log info 'If necessary, you can bypass this pre-commit hook by running `git commit --no-verify`. Please try to fix the errors first.' + exit 1 + fi + + packer: + deps: + - :install:software:packer + desc: Validate the Packer templates ending with `template.json` + hide: '{{ne .REPOSITORY_TYPE "packer"}}' + summary: | + # Validate Packer templates + + This task will loop through all the Packer templates ending with `template.json` + in the root of this project and report any errors that the templates might have. + Alternatively, you can scan a single file (see example below). + + **Example scanning for all files ending with `template.json` in the root directory:** + `task lint:packer` + + **Example scanning single file:** + `task lint:packer -- filename.json` + + For more information on `packer validate`, see the [Packer website](https://www.packer.io/docs/commands/validate). + log: + error: Error while running `packer validate` + start: Running {{if .CLI_ARGS}}`packer validate {{.CLI_ARGS}}`{{else}}`packer validate` on all files ending with `template.json`{{end}} + success: '`packer validate` reported no issues' + cmds: + - | + {{if .CLI_ARGS}} + .config/log info 'Validating the {{.CLI_ARGS}} Packer template' + packer validate {{.CLI_ARGS}} + {{else}} + for TEMPLATE in *template.json; do + .config/log info "Validating the $TEMPLATE Packer template" + packer validate "$TEMPLATE" + done + {{end}} + + php: + desc: Lint PHP with all available PHP linters + summary: | + # Lint PHP Files + + This task will lint PHP files with the following linters: + + * [go]() + + If you would like to skip linting the whole project and instead lint an + individual file, then you can do so by passing the file path as a CLI + parameter like so: + + **Example linting individual file:** + `task lint:php -- path/filename.php` + cmds: + - task: :donothing + + prettier: + deps: + - :install:modules:local + - :install:npm:prettier + desc: Lint formatting using Prettier + summary: | + # Report formatting errors with Prettier + + This task will run Prettier on the project and list the possible fixes without automatically + applying the fixes. It will report mistakes like inconsistent indent lengths, trailing spaces, + and more. Prettier will use the configuration specified in the `package.json` file under the + `prettier` key. + + If this command is incompatible with a file then you can add the file to the `.prettierignore` + file. + + For more information, see [Prettier's website](https://prettier.io/). + log: + error: Errors were encountered by Prettier + start: Linting {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}the project{{end}} with Prettier + success: Prettier successfully finished + cmds: + - | + prettier --ignore-path {{.PRETTIERIGNORE_CONFIG}} --list-different {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + .config/log warn 'Failed to lint with `prettier` - falling back to `prettier`' + prettier --ignore-path {{.PRETTIERIGNORE_CONFIG}} --list-different {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + fi + + python: + deps: + - python:deps:flake8 + - python:deps:python + label: lint:python + desc: Lint Python files using Flake8 + hide: '{{ne .REPOSITORY_TYPE "python"}}' + summary: | + # Lint Python files using Flake8 + + Flake8 is a Python library that lints Python projects (or projects that may include Python files). + It is a combination of several Python linters like PyFlakes and pycodestyle. This task will run + `flake8` using the configuration found in the `.flake8` file in the root of this project. + + The `.flake8` file is a common file shared across many of our repositories so if changes are made to + it then the changes need to be made to the appropriate common file repository [here](https://gitlab.com/megabyte-labs/common). + Because of this, it might make more sense to add a comment to lines that you wish to be ignored by flake8. + For instance, you can ignore rule E234 by adding a comment at the end of the line that looks like, "# noqa: E234". + + _NOTE: In order to maintain our strict quality standards, disabling Flake8 rules should only be done + when absolutely necessary._ + + **Example scanning all files:** + `task lint:python` + + **Example scanning specific file:** + `task lint:python -- myfile.py` + + For more information, see [Flake8's GitHub page](https://github.com/PyCQA/flake8). + vars: + CONFIG_COMMAND: + sh: | + if [ -f {{.FLAKE8_CONFIG}} ]; then + echo '--config {{.FLAKE8_CONFIG}} ' + fi + log: + error: flake8 detected some issues + start: Linting {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}the project{{end}} with flake8 + success: flake8 validation was successful + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + flake8 {{.CONFIG_COMMAND}}{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + + python:deps:flake8: + cmds: + - task: :install:pipx:flake8 + status: + - '[ "{{.REPOSITORY_TYPE}}" == "python" ]' + + python:deps:python: + cmds: + - task: :install:python:requirements + status: + - '[ "{{.REPOSITORY_TYPE}}" != "python" ]' + + shell: + cmds: + - task: shellcheck + - task: prettier + + shellcheck: + deps: + - :install:npm:shellcheck + desc: Report possible errors in shell scripts + summary: | + # Report possible errors in shell scripts using Shellcheck + + Shellcheck is a tool that reports warnings and suggestions for shell (e.g. bash) scripts. This task + can scan the project for files ending with `.sh` or `.sh.j2` and runs Shellcheck on them. Files in + the following folders are ignored: + + * .cache * .husky + * .git * node_modules + * .husky * slim_test + + **Example scanning all files:** + `task lint:scripts` + + **Example scanning specific file:** + `task lint:scripts -- myfile.sh` + + For more information, see [Shellcheck's GitHub page](https://github.com/koalaman/shellcheck). + log: + error: Shellcheck reported errors for {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}the project{{end}} + start: Linting {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}the project{{end}} with Shellcheck + success: Linted {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}the project{{end}} with no errors from Shellcheck + cmds: + - | + .config/log info 'Linting with Shellcheck' + {{if .CLI_ARGS}} + shellcheck -e {{.SHELLCHECK_EXCLUDE}} {{.CLI_ARGS}} + {{else}} + find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.sh' -o -name '*.sh.j2' \) \ + -print0 | xargs -0 -r -n1 shellcheck -e {{.SHELLCHECK_EXCLUDE}} + {{end}} + + spelling: + deps: + - :install:npm:cspell + desc: Checks for spelling errors in staged files + summary: | + # Check for spelling errors in staged files + + Use cspell to check for possible spelling errors using the configuration stored in `.config/cspell.json`. + + This task is utilized by the pre-commit hook. For more information about cspell, see the + [cspell NPM page](https://www.npmjs.com/package/cspell). + + **Example scanning all staged files:** + `task lint:spelling` + + **Example scanning specific file:** + `task lint:spelling -- myfile.sh` + + **Although this task only analyzes staged files, you can manually run cspell, for example, on all JavaScript + files by running:** + `npx cspell '**/*.js'` + cmds: + - task: spelling:{{if .CLI_ARGS}}cli{{else}}staged{{end}} + + spelling:cli: + log: + start: Running a spell-check with `cspell` + cmds: + - cmd: | + set -e + EXIT_CODE=0 + cspell --no-progress --show-context --no-must-find-files --config {{.CSPELL_CONFIG}} {{.CLI_ARGS}} || EXIT_CODE=$? + if [ "$EXIT_CODE" != '0' ]; then + .config/log warn 'Possible spelling errors were detected. Take appropriate action before merging changes.' + fi + ignore_errors: true + + spelling:markdown: + deps: + - :install:go:misspell + desc: Run `misspell` (a spell-checker) on all markdown files + log: + error: Encountered errors while running `misspell` + start: Running `misspell` spell-check + success: Successfully ran `misspell` with no errors reported + cmds: + - > + .config/log info 'If errors are found, you can auto-fix them by running `task fix:spelling:markdown`' + {{if .CLI_ARGS}} + misspell {{.CLI_ARGS}} + {{else}} + find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f \( -name '*.md' \) + -print0 | xargs -0 -r -n1 misspell + {{end}} + + spelling:staged: + deps: + - :install:npm:cspell + - :install:software:git + log: + start: Detecting possible spelling errors in staged files + cmds: + - cmd: | + STAGED_FILES="$(git diff --cached --name-only)" + if [ -n "$STAGED_FILES" ]; then + set -e + EXIT_CODE=0 + .config/log info 'Running a spell-check on staged files with `cspell`' + # Must not quote $STAGED_FILES + cspell --no-progress --show-context --no-must-find-files --config {{.CSPELL_CONFIG}} $STAGED_FILES || EXIT_CODE=$? + if [ "$EXIT_CODE" != '0' ]; then + .config/log warn 'Possible spelling errors were detected. Take appropriate action before merging changes.' + fi + fi + ignore_errors: true + + toml: + deps: + - :install:pipx:pre-commit-hooks + summary: | + # Lint TOML files + + This task verifies the syntax of TOML documents using the `check-toml` module from the + Python `pre-commit-hook` library. + + **Example scanning all '**/*.toml' files:** + `task lint:toml` + + **Example scanning specific file:** + `task lint:toml -- myfile.toml` + log: + error: Possible TOML errors were detected + start: Ensuring {{if .CLI_ARGS}}`{{.CLI_ARGS}}` is valid TOML{{else}}the project'\''s TOML files are valid{{end}} + success: The TOML appears to be valid! + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + check-toml {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + + vagrant: + deps: + - :install:software:vagrant + desc: Validate the Vagrantfile + hide: + sh: '[ ! -f Vagrantfile ]' + summary: | + # Validate the Vagrantfile + + This task is an alias for `vagrant validate`. Vagrant's `validate` command + will ensure the Vagrantfile in the root of this repository has no errors and + is using valid syntax. + + For more information on `vagrant validate`, see the [Vagrant website](https://www.vagrantup.com/docs/cli/validate). + log: + error: '`vagrant validate` detected one or more issues with the Vagrantfile' + start: Validating the Vagrantfile + success: The Vagrantfile passed `vagrant validate` + cmds: + - vagrant validate + + xml: + deps: + - :install:pipx:pre-commit-hooks + summary: | + # Lint XML files + + This task verifies the syntax of XML documents using the `check-xml` module from + the Python `pre-commit-hook` library. + + **Example scanning all '**/*.xml' files:** + `task lint:xml` + + **Example scanning specific file:** + `task lint:xml -- myfile.xml` + log: + error: Possible issues were detected with the XML{{if .CLI_ARGS}}(`{{.CLI_ARGS}}`){{end}} + start: Ensuring {{if .CLI_ARGS}}{{.CLI_ARGS}} is valid XML{{else}}the project contains only valid XML{{end}} + success: The XML appears to be valid + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + check-xml {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + + yaml: + deps: + - :install:pipx:yamllint + desc: Lint `.yml` files using YAML Lint + summary: | + # Lint YML files using YAML Lint + + YAML Lint is a general purpose linter tool that reports suggestions for `.yml` + files. It checks for syntax validity as well as cosmetic problems like line + lengths, trailing spaces, and indentation. The configuration file is in the root + of the repository in the file named `.yamllint`. The `.yamllint` file is shared + across all of our projects so if you need to make changes to get rid of warnings + it will generally make more sense to disable YAML Lint for a single line using the + [method described here](https://github.com/adrienverge/yamllint#features). + + _NOTE: Disabling YAML Lint rules should only be done when absolutely necessary._ + + **Example scanning all '**/*.yml' files:** + `task lint:yaml` + + **Example scanning specific file:** + `task lint:yaml -- myfile.sh` + + For more information, see the [YAML Lint GitHub page](https://github.com/adrienverge/yamllint). + log: + error: Possible issues were detected by `yamllint` + start: Linting {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}the project{{end}} with `yamllint` + success: No issues were detected by `yamllint` + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + yamllint -c {{.YAMLLINT_CONFIG}} -s {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} diff --git a/.config/taskfiles/log/Taskfile.yml b/.config/taskfiles/log/Taskfile.yml new file mode 100644 index 00000000..1e8e6073 --- /dev/null +++ b/.config/taskfiles/log/Taskfile.yml @@ -0,0 +1,60 @@ +--- +version: '3' + +tasks: + markdown: + summary: | + # Render Markdown with Glow + + [Glow]() provides pleasently formatted markdown + in the terminal. This task allows you to pass in data to be formatted + by Glow as the MARKDOWN variable or a CLI argument if the MARKDOWN + variable is undefined. In both cases, this task will first check if + the variable is a markdown file. It will render the file in this case. + It can also render markdown passed in as a string. + + **Example with CLI and markdown file:** + task log:markdown -- path/to/markdown.md + + **Example with MARKDOWN variable and inlined markdown:** + ``` + mytask: + cmds: + - task: :log:markdown + vars: + MARKDOWN: | + # Markdown Title + + Paragraph `code` [Link to cool website](https://megabyte.space). + ``` + + If passing in inlined markdown via the MARKDOWN library, you should ensure + that the inlined string is properly escaped so there are no clashes with single + quotes (e.g. ''). You can escape your strings using Task's built-in templating + engine by using the following syntax: + + ``` + {{'{{'}} replace "'" "\'" .MY_VARIABLE {{'}}'}} + ``` + cmds: + - | + TMP="$(mktemp)" + {{if .MARKDOWN}} + if [ -f '{{.MARKDOWN}}' ]; then + cp '{{.MARKDOWN}}' "$TMP" + else + tee "$TMP" < '{{.MARKDOWN}}' + fi + .config/log md "$TMP" + {{else}} + {{if .CLI_ARGS}} + if [ -f '{{.CLI_ARGS}}' ]; then + cp '{{.CLI_ARGS}}' "$TMP" + else + tee "$TMP" < '{{replace "'" "\'" .CLI_ARGS}}' + fi + .config/log md "$TMP" + {{else}} + .config/log error 'A CLI argument or the MARKDOWN variable must be passed in.' + {{end}} + {{end}} diff --git a/.config/taskfiles/nest/Taskfile.yml b/.config/taskfiles/nest/Taskfile.yml new file mode 100644 index 00000000..65c429a3 --- /dev/null +++ b/.config/taskfiles/nest/Taskfile.yml @@ -0,0 +1,28 @@ +--- +version: '3' + +tasks: + benchmark: + deps: + - :install:npm:autocannon + - :install:npm:clinic + desc: Initiate a web server benchmarking session (with `clinic`) + hide: '{{ne (print .REPOSITORY_TYPE "-" .REPOSITORY_SUBTYPE) "npm-app"}}' + summary: | + # Initiate a web server benchmarking session with `clinic` + + This task runs a basic stress test on an endpoint that is `/` by default. + See the examples below for stress testing an endpoint other than the default. + For more information, see the [`clinic` GitHub page](https://github.com/clinicjs/node-clinic). + + **Example usage:** + `task npm:benchmark` + + **Example with custom endpoint:** + `task npm:benchmark -- /custom/endpoint` + log: + error: Error encountered while running clinic with autocannon on `dist/main.js` + start: Running clinic with autocannon on `dist/main.js` + success: Finished running clinic with autocannon on `dist/main.js` + cmds: + - clinic doctor --autocannon [ {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}/{{end}} --method POST ] -- node ./dist/main.js diff --git a/.config/taskfiles/npm/Taskfile-bundle.yml b/.config/taskfiles/npm/Taskfile-bundle.yml new file mode 100644 index 00000000..e9995f9b --- /dev/null +++ b/.config/taskfiles/npm/Taskfile-bundle.yml @@ -0,0 +1,127 @@ +--- +version: '3' + +tasks: + create: + deps: + - :install:software:jq + log: + error: Error creating production `node_modules/` pack + start: Creating production `node_modules/` pack + success: Successfully created production `node_modules/` pack + cmds: + - | + if [ -d node_modules ]; then + mv node_modules node_modules.bak + fi + - task: install + - task: minify + - task: pack + - rm package-lock.json + - | + if [ -d node_modules.bak ]; then + rm -rf node_modules + mv node_modules.bak node_modules + fi + status: + - '[ $(jq -r ".dependencies | length" package.json) == "0" ]' + + install: + deps: + - :install:software:node + log: + error: Encountered error while running `npm install --only=prod` + start: Installing production `node_modules/` + success: Successfully set-up production `node_modules/` folder + cmds: + - SKIP_NPM_START=true npm install --only=prod + + minify: + deps: + - :install:go:node-prune + - :install:npm:modclean + summary: | + ## node-prune + + `node-prune` is a Go project that does a good job at cleaning up projects while + remaining safe. + + ## modclean + + `modclean` does what `node-prune` does and takes it a step further. + + `modclean` provides 3 different patterns by default. They are: + + * `default:safe` - Contains patterns that are considered safe and should not + affect your application. It also removes the most files/folders of all the configurations. + * `default:caution` - Contains patterns that could potentially cause issues with + modules, but includes patterns that will help reduce your modules folder size. + * `default:danger` - Contains patterns that are known to cause issues with certain + modules, but can help reduce files and your modules folder size even further. + + **To clean with `default:safe` and `default:caution`, you can run:** + `task npm:bundle:minify -- default:safe,default:caution + + **To clean with all of them:** + `task npm:bundle:minify -- default:*` + + If CLI arguments are not passed, then the task looks at `.blueprint.modcleanPatterns` in `package.json` + for the patterns. And if that is not present, then `default:safe,default:caution` is used by default. + + You can see the [default patterns here](https://github.com/ModClean/modclean-patterns-default/blob/master/patterns.json). + vars: + MODCLEAN_PATTERNS: + sh: | + {{if .CLI_ARGS}}echo '{{.CLI_ARGS}}'{{else}}PREFERENCE="$(jq -r '.blueprint.modcleanPatterns' package.json)" + if [ "$PREFERENCE" != 'null' ]; then + echo "$PREFERENCE" + else + echo "default:safe,default:caution" + fi{{end}} + log: + error: Encountered error while pruning production `node_modules/` folder + start: Pruning production `node_modules/` folder + success: Successfully pruned production `node_modules/` folder + cmds: + - node-prune + - modclean --patterns="{{.MODCLEAN_PATTERNS}}" --run + + pack: + deps: + - :install:npm:pac + log: + error: Error compressing production `node_modules/` into `.tgz` files stored in the `.modules/` folder + start: Packing production `node_modules/` into `.tgz` files stored in the `.modules/` folder + success: Finished packing production `node_modules/` into the `.modules/` folder + cmds: + - mkdir -p .modules + - .config/log info '`pac` is being replaced by `produndle` - WIP' || produndle pack + + unpack: + deps: + - :install:software:jq + cmds: + - task: unpack:pac + status: + - '[ ! -d .modules ] || ([ -d .modules ] && (! ls .modules | grep .tgz))' + + unpack:pac: + deps: + - :install:npm:pac + log: + error: Error running `unpack:pac` + start: Populating `node_modules/` with `.tgz` files in the `.modules` folder + success: Finished populating `node_modules/` folder + cmds: + - .config/log info '`pac` is being replaced by `produndle` - WIP' || produndle unpack + - | + BASE_DIR="$(PWD)" + for MODULE in node_modules/*; do + cd "$MODULE" + if [ -f Taskfile.yml ]; then + task unpack || EXIT_CODE=$? + if [ -n "$EXIT_CODE" ]; then + "${PWD}/.config/log" warn 'There was an error running `task unpack` in '"$MODULE" + fi + fi + done diff --git a/.config/taskfiles/npm/Taskfile-cov.yml b/.config/taskfiles/npm/Taskfile-cov.yml new file mode 100644 index 00000000..d3ec45b1 --- /dev/null +++ b/.config/taskfiles/npm/Taskfile-cov.yml @@ -0,0 +1,72 @@ +--- +version: '3' + +tasks: + all: + cmds: + - task: :npm:build:all + - task: :npm:test:unit + - task: report + + check: + deps: + - :install:modules:local + - :install:npm:nyc + log: + error: Errors encountered while running `nyc report` and `nyc check-coverage` + start: Running `nyc report` and `nyc check-coverage` + success: Finished running `nyc` + cmds: + - '{{.NPX_HANDLE}}nyc report' + - '{{.NPX_HANDLE}}nyc check-coverage --lines 100 --functions 100 --branches 100' + + html: + deps: + - :install:modules:local + - :install:npm:nyc + log: + error: Error while generating HTML report with `nyc` + start: Reporting with `nyc` in HTML format + success: Report generated by `nyc` in HTML format + cmds: + - '{{.NPX_HANDLE}}nyc report --reporter=html' + + lcov: + deps: + - :install:modules:local + - :install:npm:nyc + log: + error: Encountered error generating `lcov` report with `nyc` + start: Reporting with `nyc` in `lcov` format + success: Finished `lcov` report with `nyc` + cmds: + - '{{.NPX_HANDLE}}nyc report --reporter=lcov' + + open: + deps: + - html + - :npm:install:open-cli + desc: View the code coverage report in the browser + hide: '{{ne .REPOSITORY_TYPE "npm"}}' + log: + start: Opening `coverage/index.html` with the default browser + cmds: + - '{{.NPX_HANDLE}}open-cli coverage/index.html' + + report: + deps: + - html + - lcov + + upload: + deps: + - lcov + - :install:npm:codecov + desc: Upload code coverage report to `codecov.io` + hide: '{{ne .REPOSITORY_TYPE "npm"}}' + log: + error: Error while running `codecov` + start: Running `codecov` + success: Successfully ran `codecov` + cmds: + - '{{.NPX_HANDLE}}codecov' diff --git a/.config/taskfiles/npm/Taskfile-doc.yml b/.config/taskfiles/npm/Taskfile-doc.yml new file mode 100644 index 00000000..cf6ac0ee --- /dev/null +++ b/.config/taskfiles/npm/Taskfile-doc.yml @@ -0,0 +1,46 @@ +--- +version: '3' + +tasks: + html: + deps: + - :npm:install:typedoc + desc: Generate TypeDoc HTML documentation + summary: | + # Generate TypeDoc HTML Documentation + + [TypeDoc](https://typedoc.org/) is a CLI tool that generates documentation from specially crafted + comments in TypeScript code. It also uses type information to generate + the documentation. + + Running this command will scan the `src/` folder, extract the comments and + type information and then generate HTML documentation. + + Be sure to read about how to decorate your comments by reading + [TypeDoc's documentation on comment formatting](https://typedoc.org/guides/doccomments/). + log: + error: Encountered error generating HTML documentation with TypeDoc + start: Generating HTML documentation with TypeDoc + success: Successfully generated HTML technical documentation with TypeDoc + cmds: + - '{{.NPX_HANDLE}}typedoc src/ --exclude "**/*.spec.ts" --out build/docs' + + json: + deps: + - :npm:install:typedoc + desc: Generate TypeDoc JSON documentation + log: + error: Encountered error generating JSON documentation with TypeDoc + start: Generating JSON documentation with TypeDoc + success: Successfully generated JSON technical documentation with TypeDoc + cmds: + - '{{.NPX_HANDLE}}typedoc src/ --exclude "**/*.spec.ts" --json build/docs/typedoc.json' + + open: + deps: + - html + - :npm:install:open-cli + log: + start: Opening TypeDoc documentation (`build/docs/index.html`) in the default browser + cmds: + - '{{.NPX_HANDLE}}open-cli build/docs/index.html' diff --git a/.config/taskfiles/npm/Taskfile.yml b/.config/taskfiles/npm/Taskfile.yml new file mode 100644 index 00000000..2d08a736 --- /dev/null +++ b/.config/taskfiles/npm/Taskfile.yml @@ -0,0 +1,485 @@ +--- +version: '3' + +vars: + BINARY_BUILD_FOLDER: dist/bin + BUILD_FOLDERS: build coverage dist + +tasks: + build: + deps: + - :install:modules:local + - :install:npm:esbuild + - :install:npm:nest + - clean + desc: Build the application + log: + error: Error building the application + start: Building the application + success: Successfully built the application + cmds: + - task: depcheck:dependencies + - mkdir -p dist + - task: build:compile + + build:compile: + deps: + - build:nest + - build:esbuild:bundled + - build:esbuild:minified + - build:fpm + + build:esbuild:bundled: + deps: + - :install:modules:local + - :install:npm:esbuild + log: + error: Error running `esbuild` (config -> ./.config/esbuild/bundled.js) + start: Running `esbuild` with bundle script (config -> ./.config/esbuild/bundled.js) + success: Finished running `esbuild` (config -> ./.config/esbuild/bundled.js) + cmds: + - node .config/esbuild/bundled.js + + build:esbuild:minified: + deps: + - :install:modules:local + - :install:npm:esbuild + log: + error: Error running `esbuild` (config -> ./.config/esbuild/minified.js) + start: Running `esbuild` with minify script (config -> ./.config/esbuild/minified.js) + success: Finished running `esbuild with minify script (config -> ./.config/esbuild/minified.js) + cmds: + - node .config/esbuild/minified.js + + build:fpm: + deps: + - :install:software:jq + vars: + NPM_PACKAGE_NAME: + sh: jq -r '.blueprint.binName' package.json + NPM_PACKAGE_VERSION: + sh: jq -r '.version' package.json + cmds: + - task: build:fpm-build + vars: + NPM_PACKAGE_NAME: '{{.NPM_PACKAGE_NAME}}' + NPM_PACKAGE_VERSION: '{{.NPM_PACKAGE_VERSION}}' + + build:fpm:build: + deps: + - task: build:fpm:compile + vars: + ARCHITECTURE: amd64 + FILE_EXTENSION: deb + FPM_TARGET: deb + NPM_PACKAGE_NAME: '{{.NPM_PACKAGE_NAME}}' + NPM_PACKAGE_VERSION: '{{.NPM_PACKAGE_VERSION}}' + SYSTEM: debian + - task: build:fpm:compile + vars: + ARCHITECTURE: arm64 + FILE_EXTENSION: deb + FPM_TARGET: deb + NPM_PACKAGE_NAME: '{{.NPM_PACKAGE_NAME}}' + NPM_PACKAGE_VERSION: '{{.NPM_PACKAGE_VERSION}}' + SYSTEM: debian + - task: build:fpm:compile + vars: + ARCHITECTURE: amd64 + FILE_EXTENSION: txz + FPM_TARGET: freebsd + NPM_PACKAGE_NAME: '{{.NPM_PACKAGE_NAME}}' + NPM_PACKAGE_VERSION: '{{.NPM_PACKAGE_VERSION}}' + SYSTEM: freebsd + # - task: build:fpm:compile + # vars: + # ARCHITECTURE: amd64 + # FILE_EXTENSION: + # FPM_TARGET: pacman + # NPM_PACKAGE_NAME: '{{.NPM_PACKAGE_NAME}}' + # NPM_PACKAGE_VERSION: '{{.NPM_PACKAGE_VERSION}}' + # SYSTEM: archlinux + - task: build:fpm:compile + vars: + ARCHITECTURE: amd64 + FILE_EXTENSION: rpm + FPM_TARGET: rpm + NPM_PACKAGE_NAME: '{{.NPM_PACKAGE_NAME}}' + NPM_PACKAGE_VERSION: '{{.NPM_PACKAGE_VERSION}}' + SYSTEM: redhat + - task: build:fpm:compile + vars: + ARCHITECTURE: arm64 + FILE_EXTENSION: rpm + FPM_TARGET: rpm + NPM_PACKAGE_NAME: '{{.NPM_PACKAGE_NAME}}' + NPM_PACKAGE_VERSION: '{{.NPM_PACKAGE_VERSION}}' + SYSTEM: redhat + - task: build:fpm:compile + vars: + ARCHITECTURE: all + FILE_EXTENSION: tar + FPM_TARGET: tar + NPM_PACKAGE_NAME: '{{.NPM_PACKAGE_NAME}}' + NPM_PACKAGE_VERSION: '{{.NPM_PACKAGE_VERSION}}' + SYSTEM: source + log: + error: Encountered error while building binaries with FPM + start: Building binaries with FPM + success: Successfully built binaries with FPM + cmds: + - | + .config/log success 'Running gzip on `{{.NPM_PACKAGE_NAME}}-{{.NPM_PACKAGE_VERSION}}-source-all.tar`' + gzip -9 {{.NPM_PACKAGE_NAME}}-{{.NPM_PACKAGE_VERSION}}-source-all.tar + + build:fpm:compile: + deps: + - :install:software:fpm + log: + error: Encountered error while compiling {{.ARCHITECTURE}} {{.FPM_TARGET}} binary with FPM for {{.NPM_PACKAGE_NAME}}, version {{.NPM_PACKAGE_VERSION}} + start: Compiling {{.ARCHITECTURE}} {{.FPM_TARGET}} binary with FPM for {{.NPM_PACKAGE_NAME}}, version {{.NPM_PACKAGE_VERSION}} + success: Finished compiling {{.ARCHITECTURE}} {{.FPM_TARGET}} binary with FPM for {{.NPM_PACKAGE_NAME}}, version {{.NPM_PACKAGE_VERSION}} + cmds: + - > + fpm -s npm -t {{.FPM_TARGET}} + -p {{.BINARY_BUILD_FOLDER}}/{{.NPM_PACKAGE_NAME}}-{{.NPM_PACKAGE_VERSION}}-{{.SYSTEM}}-{{.ARCHITECTURE}}.{{.FILE_EXTENSION}} + --architecture {{.ARCHITECTURE}} . + + build:nest: + deps: + - :install:modules:local + - :install:npm:nest + log: + error: Encountered error while building NestJS project + start: Building NestJS project + success: Finished building NestJS project + cmds: + - nest build + - task: build:nest:after + + build:nest:after: + deps: + - :common:update:man-page + - build:pkg + + build:none: + log: + start: Skipping build step because project does not need building + cmds: + - task: :donothing + + build:pkg: + deps: + - :install:modules:local + - :install:npm:pkg + log: + error: Encountered error while generating standalone executables for macOS, Linux, and Windows using `pkg` + start: Generating standalone executables for macOS, Linux, and Windows using `pkg` + success: Generated standalone executables for macOS, Linux, and Windows using `pkg` + cmds: + - pkg package.json + + build:release: 'true' + + build:tsconfig: + deps: + - :install:modules:local + - :install:npm:typescript + log: + error: Error building with `tsconfig.json` + start: Removing `dist/` folder and running `tsc -p tsconfig.json` + success: Successfully built project + cmds: + - rm -rf dist + - tsc -p tsconfig.json + + bump: + deps: + - :install:modules:local + cmds: + - npm run build + - | + .config/log info 'The current package.json version is `'"$(jq -r '.version' package.json)"'`' + BUMP_VERSION="$(npm version patch --no-scripts --no-commit-hooks --no-git-tag-version --force | sed 's/^v//')" + .config/log info 'Bumped the package.json version with a patch update to version `'"$BUMP_VERSION"'`' + - SKIP_PREPUB=true npm publish + + clean: + deps: + - :install:software:rsync + desc: Remove temporary folders that might conflicts with builds + vars: + RANDOM_STRING: + sh: openssl rand -hex 14 + log: + error: Error removing `{{.BUILD_FOLDERS}}` with rsync + start: Removing `{{.BUILD_FOLDERS}}` with rsync + success: Successfully removed `{{.BUILD_FOLDERS}}` + cmds: + - mkdir -p '/tmp/{{.RANDOM_STRING}}' + - mkdir -p '/tmp/{{.RANDOM_STRING}}-empty' + - | + for TMP_FILE in {{.BUILD_FOLDERS}}; do + if [ -d "$TMP_FILE" ]; then + mv "$TMP_FILE" "/tmp/{{.RANDOM_STRING}}/$TMP_FILE" 2> /dev/null + (rsync -a --delete '/tmp/{{.RANDOM_STRING}}-empty' "/tmp/{{.RANDOM_STRING}}/$TMP_FILE" && rm -rf "/tmp/{{.RANDOM_STRING}}-$TMP_FILE") & + fi + done + wait + + config: + deps: + - :install:software:jq + - :install:software:node + vars: + REGISTRY_ADDRESS: + sh: jq -r '.publishConfig["@gitlab:registry"]' package.json | sed 's/^https://' + cmds: + - npm config set -- '//registry.npmjs.org/:_authToken' '${NPM_TOKEN}' '{{.REGISTRY_ADDRESS}}:_authToken' '${GITLAB_TOKEN}' + + depcheck:dependencies: + deps: + - :install:npm:depcheck + - :install:software:jq + desc: Remove unused dependencies + log: + error: Error uninstalling unused dependencies in package.json + start: Uninstalling unused dependencies in package.json + success: Successfully uninstalled unused dependencies in package.json + cmds: + - depcheck --json | jq -r '.dependencies[]' | xargs -n1 {{.NPM_PROGRAM}} uninstall --save + + preinstall: + deps: + - :install:npm:only-allow + log: + error: '`pnpm` must be used as the package manager' + start: Forcing `pnpm` as the package manager by running `only-allow pnpm` + success: Successfully enforced `pnpm` as the package manager + cmds: + - only-allow pnpm + + prepare: + cmds: + - task: prepare:project + - task: prepare:release + + prepare:project: + cmds: + - task: :common:husky + status: + - '[ -n "$CI" ] || [ -n "$SEMANTIC_RELEASE" ]' + + prepare:prune: + cmds: + - task: prune + status: + - '! type node-prune &> /dev/null' + + prepare:release: + cmds: + - 'true' + status: + - '[ -z "$SEMANTIC_RELEASE" ]' + + prune: + deps: + - :install:go:node-prune + log: + error: Error pruning `node_modules/` + start: Pruning the `node_modules/` folder + success: Successfully pruned the `node_modules/` folder + cmds: + - node-prune + + publish:gitlab: + deps: + - :install:software:jq + - :install:software:node + - :npm:config + vars: + PKG_NAME: + sh: jq -r '.name' package.json + REGISTRY_ADDRESS: + sh: jq -r '.publishConfig["@gitlab:registry"]' package.json + VERSION: + sh: jq -r '.version' package.json + env: + BACKUP_PKG: + sh: mktemp + cmds: + - cp package.json "$BACKUP_PKG" + - TMP="$(mktemp)" && jq '.name = "@gitlab/{{.PKG_NAME}}" | .publishConfig.access = "restricted"' package.json > "$TMP" && mv "$TMP" package.json + - npm publish + - npm dist-tag --registry={{.REGISTRY_ADDRESS}} add {{.PKG_NAME}}@{{.VERSION}} latest + - mv "$BACKUP_PKG" package.json + status: + - '[ "$(jq -r ".publishConfig[\"@gitlab:registry\"] | type" package.json)" != "string" ] || [ -z "$NPM_PUBLISH_GITLAB" ]' + + start: + deps: + - :install:modules:local + - :install:npm:nest + desc: Run the application + log: + error: Encountered error while running `nest start` + start: Running `nest start` + success: Successfully ran `nest start` + cmds: + - nest start + + start:debug: + deps: + - :install:modules:local + - :install:npm:nest + log: + error: Encountered error while running `nest start --watch` + start: Running `nest start --watch` + success: Successfully ran `nest start --watch` + cmds: + - nest start --watch + + start:inspect: + deps: + - :install:modules:local + - :install:npm:ndb + - :install:npm:nodemon + - :install:npm:ts-node + desc: Run the application in `watch` mode and open DevTools + log: + error: Error encountered while running `ndb nest start --watch` + start: Running `ndb nest start --watch` + success: Successfully ran `ndb nest start --watch` + cmds: + - ndb nest start --watch + + start:inspect:legacy: + deps: + - :install:modules:local + - :install:npm:nest + log: + error: Encountered error while running `nest start --debug --watch` + start: Running `nest start --debug --watch` + success: Successfully ran `nest start --debug --watch` + cmds: + - nest start --debug --watch + + start:prod: + deps: + - :install:modules:local + - :install:npm:nest + desc: Run the application in `production` mode + log: + error: Error encountered while building and running `node dist/main` + start: Running `production` build by building the project and then running `node dist/main` + success: Successfully ran `production` build + cmds: + - task: build + - node dist/main + + start:prod:inspect: + deps: + - :install:modules:local + - :install:npm:nest + desc: Run the application in `production` and `watch` mode and open DevTools + log: + error: Error encountered while running `ndb nodemon` + start: Running `ndb nodemon` + success: Successfully ran `ndb nodemon` + cmds: + - ndb nodemon + + test: + deps: + - :install:modules:local + - :install:npm:jest + desc: Run the unit tests for an NPM project + log: + error: Errors were detected by Jest + start: Running `jest` + success: Successfully ran `jest` + cmds: + - jest --silent=false + + test:ci: + deps: + - :install:modules:local + - :install:npm:jest + log: + error: Encountered error while running `jest --collectCoverage --ci --reporters=default --reporters=jest-junit` + start: Running `jest --collectCoverage --ci --reporters=default --reporters=jest-junit` + success: Successfully ran `jest --collectCoverage --ci --reporters=default --reporters=jest-junit` + cmds: + - jest --collectCoverage --ci --reporters=default --reporters=jest-junit + + test:coverage: + deps: + - :install:modules:local + - :install:npm:jest + desc: Generate code coverage assets + log: + error: Error running `jest --coverage` + start: Generating code coverage assets by running `jest --coverage` + success: Successfully ran `jest --coverage` + cmds: + - jest --coverage + + test:dashboard: + deps: + - :install:modules:local + - :install:npm:majestic + desc: Run and manage test cases from a web interface (powered by `majestic`) + log: + error: Error running `majestic --debug` + start: Launching web interface for debugging test cases by running `majestic --debug` + success: Successfully ran `majestic --debug` + cmds: + - majestic --debug + + test:debug: + deps: + - :install:modules:local + - :install:npm:jest + desc: Debug tests in `watch` mode + log: + error: Error running `jest --watch` + start: Running `jest --watch` + success: Successfully ran `jest --watch` + cmds: + - jest --watch + + test:e2e: 'true' + + test:inspect: + deps: + - :install:modules:local + - :install:npm:jest + - :install:npm:ndb + - :install:npm:nodemon + - :install:npm:ts-node + desc: Debug tests with DevTools in `watch` mode + log: + error: Error running `ndb nodemon --config .config/nodemon-jest.json` + start: Running `ndb nodemon --config .config/nodemon-jest.json` to enable debugging with Chrome DevTools in watch mode + success: Successfully ran `ndb nodemon --config .config/nodemon-jest.json` + cmds: + - ndb nodemon --config .config/nodemon-jest.json + + test:none: + log: + start: No tests have been set up for this project + cmds: + - task: :donothing + + typesync: + deps: + - :install:npm:typesync + desc: Install missing type definitions + log: + error: Error running `typesync` + start: Installing missing type definitions by running `typesync` + success: Successfully ran `typesync` + cmds: + - typesync + + verify: 'true' diff --git a/.config/taskfiles/packer/Taskfile-build.yml b/.config/taskfiles/packer/Taskfile-build.yml new file mode 100644 index 00000000..57f4b3a0 --- /dev/null +++ b/.config/taskfiles/packer/Taskfile-build.yml @@ -0,0 +1,123 @@ +--- +version: '3' + +tasks: + kvm: + deps: + - :install:software:kvm + - :install:software:packer + desc: Build a Packer image for KVM + summary: | + # Build a Packer image for KVM + + This task will build a machine image intended to be used by KVM environments. KVM + environments are generally only available on Linux (although, it is supposedly + possible to run KVM on macOS as well). By default, this task assumes the template + file is titled `template.json` and that the file is in the root of the project. If + you would like to use another template file then you can do so by passing the file + name as a parameter (see example below). + + **Example:** + `task build:kvm` + + **Example using a template file named `another_template.json`:** + `task build:kvm -- another_template.json` + hide: + sh: '[ "{{.REPOSITORY_TYPE}}" != "packer" ]' + log: + error: Error running `packer build -only=qemu {{.TEMPLATE_FILE}}` + start: Running `packer build -only=qemu {{.TEMPLATE_FILE}}` + success: Successfully ran `packer build -only=qemu {{.TEMPLATE_FILE}}` + cmds: + - task: :packer:prepare:template + - rm -rf build + - packer build -only=qemu {{.TEMPLATE_FILE}} + + parallels: + deps: + - :install:software:packer + - :install:software:parallels + desc: Build a Packer image for Parallels + summary: | + # Build a Packer image for Parallels + + This task will build a machine image intended to be used by Parallels. Parallels + is only available for macOS. By default, this task assumes the template file is + titled `template.json` and that the file is in the root of the project. If you + would like to use another template file then you can do so by passing the file + name as a parameter (see example below). + + **Example:** + `task build:parallels` + + **Example using a template file named `another_template.json`:** + `task build:parallels -- another_template.json` + hide: + sh: '[ "{{.REPOSITORY_TYPE}}" != "packer" ]' + log: + error: Error running `packer build -only=parallels-iso {{.TEMPLATE_FILE}}` + start: Running `packer build -only=parallels-iso {{.TEMPLATE_FILE}}` + success: Successfully ran `packer build -only=parallels-iso {{.TEMPLATE_FILE}}` + cmds: + - rm -rf build + - packer build -only=parallels-iso {{.TEMPLATE_FILE}} + + virtualbox: + deps: + - :install:software:packer + - :install:software:virtualbox + desc: Build a Packer image for VirtualBox + summary: | + # Build a Packer image for VirtualBox + + This task will build a machine image intended to be used by VirtualBox. By + default, this task assumes the template file is titled `template.json` and + that the file is in the root of the project. If you would like to use another + template file then you can do so by passing the file name as a parameter + (see example below). + + **Example:** + `task build:virtualbox` + + **Example using a template file named `another_template.json`:** + `task build:virtualbox -- another_template.json` + hide: + sh: '[ "{{.REPOSITORY_TYPE}}" != "packer" ]' + log: + error: Error running `packer build -only=virtualbox-iso {{.TEMPLATE_FILE}}` + start: Running `packer build -only=virtualbox-iso {{.TEMPLATE_FILE}}` + success: Successfully ran `packer build -only=virtualbox-iso {{.TEMPLATE_FILE}}` + cmds: + - task: :app:virtualbox:clear + - rm -rf build + - packer build -only=virtualbox-iso {{.TEMPLATE_FILE}} + + vmware: + deps: + - :install:software:packer + - :install:software:vmware + desc: Build a Packer image for VMWare + summary: | + # Build a Packer image for VMWare + + This task will build a machine image intended to be used by VMWare. The image + build can be used by either VMWare Workstation (i.e. Linux, Windows) or by + VMWare Fusion (i.e. macOS). By default, this task assumes the template file is + titled `template.json` and that the file is in the root of the project. If you + would like to use another template file then you can do so by passing the file + name as a parameter (see example below). + + **Example:** + `task packer:vmware` + + **Example using a template file named `another_template.json`:** + `task packer:vmware -- another_template.json` + log: + error: Error running `packer build -only=vmware-iso {{.TEMPLATE_FILE}}` + start: Running `packer build -only=vmware-iso {{.TEMPLATE_FILE}}` + success: Successfully ran `packer build -only=vmware-iso {{.TEMPLATE_FILE}}` + hide: + sh: '[ "{{.REPOSITORY_TYPE}}" != "packer" ]' + cmds: + - rm -rf build + - packer build -only=vmware-iso {{.TEMPLATE_FILE}} diff --git a/.config/taskfiles/packer/Taskfile-update.yml b/.config/taskfiles/packer/Taskfile-update.yml new file mode 100644 index 00000000..94512242 --- /dev/null +++ b/.config/taskfiles/packer/Taskfile-update.yml @@ -0,0 +1,130 @@ +--- +version: '3' + +vars: + TEMPLATE_FILE: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}template.json{{end}}' + +tasks: + descriptions: + deps: + - :install:software:jq + log: + error: Error populating `{{.TEMPLATE_FILE}}` with descriptions + start: Populating `{{.TEMPLATE_FILE}}` with descriptions + success: Populated `{{.TEMPLATE_FILE}}` with descriptions + cmds: + - | + DESCRIPTION_TEMPLATE="$(jq -r '.description_template' '{{.VARIABLES_PATH}}')" + VERSION_DESCRIPTION="$(jq -r '.version_description' '{{.VARIABLES_PATH}}')" + TMP="$(mktemp)" + jq --arg a "$DESCRIPTION_TEMPLATE" --arg b "$VERSION_DESCRIPTION" \ + '.variables.description = $a | .variables.version_description = $b' {{.TEMPLATE_FILE}} > "$TMP" + mv "$TMP" {{.TEMPLATE_FILE}} + sources: + - '{{.TEMPLATE_FILE}}' + - '{{.VARIABLES_PATH}}' + + readme: + log: + error: Encountered error while updating README.md for Packer template + start: Updating README.md for Packer template + success: Updated README.md for Packer template + cmds: + - task: readme:platform + vars: + TYPE: hyperv-iso + - task: readme:platform + vars: + TYPE: parallels-iso + - task: readme:platform + vars: + TYPE: qemu + - task: readme:platform + vars: + TYPE: virtualbox-iso + - task: readme:platform + vars: + TYPE: vmware-iso + + readme:platform: + log: + start: Determining whether to update README.md for `{{.TYPE}}` + cmds: + - task: readme:platform:{{OS}} + vars: + TYPE: '{{.TYPE}}' + status: + - | + grep -q '"type": "{{.TYPE}}"' {{.TEMPLATE_FILE}} + + readme:platform:darwin: + log: + error: Error updating README.md with supported OS information for Packer template + start: Updating README.md with supported OS information for Packer template + success: Updated README.md with supported OS information for Packer template + cmds: + - sed -i .bak '/SUPPORTED_OS_{{.TYPE}}/d' README.md && rm README.md.bak + + readme:platform:linux: + log: + error: Error updating README.md with supported OS information for Packer template + start: Updating README.md with supported OS information for Packer template + success: Updated README.md with supported OS information for Packer template + cmds: + - sed -i '/SUPPORTED_OS_{{.TYPE}}/d' README.md + + variables: + deps: + - :install:software:jq + vars: + ISO_VERSION: + sh: jq -r '.variables.iso_version' {{.TEMPLATE_FILE}} + MAJOR_VERSION: + sh: cut -d '.' -f 1 <<< {{.ISO_VERSION}} + MINOR_VERSION: + sh: cut -d '.' -f 2 <<< {{.ISO_VERSION}} + log: + error: Error updating `{{.VARIABLES_PATH}}` with variables from `{{.TEMPLATE_FILE}}` + start: Updating `{{.VARIABLES_PATH}}` with variables from `{{.TEMPLATE_FILE}}` + success: Updated `{{.VARIABLES_PATH}}` with variables from `{{.TEMPLATE_FILE}}` + cmds: + - | + TEMPLATE_JSON="$(jq -r '.' {{.TEMPLATE_FILE}})" + TMP="$(mktemp)" + jq -S --arg templatejson "$TEMPLATE_JSON" '.template_json = ($templatejson | fromjson)' '{{.VARIABLES_PATH}}' > "$TMP" + mv "$TMP" '{{.VARIABLES_PATH}}' + - task: variables:{{OS}} + vars: + ISO_VERSION: '{{.ISO_VERSION}}' + MAJOR_VERSION: '{{.MAJOR_VERSION}}' + MINOR_VERSION: '{{.MINOR_VERSION}}' + sources: + - '{{.TEMPLATE_FILE}}' + - '{{.VARIABLES_PATH}}' + preconditions: + - sh: test -f {{.TEMPLATE_FILE}} + msg: 'A `{{.TEMPLATE_FILE}}` file is not present. This project uses values stored in `{{.TEMPLATE_FILE}}` to generate certain + meta artifacts. Please add a `{{.TEMPLATE_FILE}}`. You can find an example of one in + [this repository](https://gitlab.com/megabyte-labs/packer/ubuntu-desktop)' + - sh: test -f '{{.VARIABLES_PATH}}' + msg: 'The `{{.VARIABLES_PATH}}` file is missing!' + + variables:darwin: + log: + error: Error updating major version + start: Updating major version + success: Updated major version + cmds: + - sed -i .bak "s^MAJOR_VERSION^{{.MAJOR_VERSION}}^g" '{{.VARIABLES_PATH}}' && rm '{{.VARIABLES_PATH}}.bak' + - sed -i .bak "s^MINOR_VERSION^{{.MINOR_VERSION}}^g" '{{.VARIABLES_PATH}}' && rm '{{.VARIABLES_PATH}}.bak' + - sed -i .bak "s^ISO_VERSION^{{.ISO_VERSION}}^g" '{{.VARIABLES_PATH}}' && rm '{{.VARIABLES_PATH}}.bak' + + variables:linux: + log: + error: Error updating major version + start: Updating major version + success: Updated major version + cmds: + - sed -i "s^MAJOR_VERSION^{{.MAJOR_VERSION}}^g" '{{.VARIABLES_PATH}}' + - sed -i "s^MINOR_VERSION^{{.MINOR_VERSION}}^g" '{{.VARIABLES_PATH}}' + - sed -i "s^ISO_VERSION^{{.ISO_VERSION}}^g" '{{.VARIABLES_PATH}}' diff --git a/.config/taskfiles/packer/Taskfile.yml b/.config/taskfiles/packer/Taskfile.yml new file mode 100644 index 00000000..9b3b05fd --- /dev/null +++ b/.config/taskfiles/packer/Taskfile.yml @@ -0,0 +1,127 @@ +--- +version: '3' + +vars: + TEMPLATE_FILE: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}template.json{{end}}' + VARIABLES_PATH: .variables.json + +tasks: + build: + deps: + - :install:software:packer + desc: Build Packer images for all available platforms + summary: | + # Build Packer images for all available virtualization platforms + + This task begins by removing cached files that may interfere with the Packer build + process. It then runs `packer build template.json` if no arguments are passed. This + command will build Packer images for all the virtualization platforms specified in + the template file. The template file may include instructions for the following + virtualization platforms (and it is possible that it includes other ones that are + not listed below): + + * Hyper-V + * KVM + * Parallels + * VMWare + * VirtualBox + + If you would like to build machine images with another template then you can pass the + template's file name as a parameter (see example below). + + **Example building on all available platforms:** + `task packer:build` + + **Example using a template file named `another_template.json`:** + `task packer:build -- another_template.json` + hide: + sh: '[ "{{.REPOSITORY_TYPE}}" != "packer" ]' + log: + error: Encountered error while running `packer build` + start: Running `packer build` for all supported platforms that are installed + success: Successfully built the Packer images + cmds: + - task: prepare:template + - rm -rf build + - | + IMAGES="," + if type qemu-system-x86_64 &> /dev/null; then + .config/log info 'QEMU is available' + IMAGES="${IMAGES},qemu" + fi + if [ '{{OS}}' == 'darwin' ] && mdfind -name 'Parallels Desktop.app' &> /dev/null; then + .config/log info 'Parallels is installed' + IMAGES="${IMAGES},parallels-iso" + fi + if type vboxmanage &> /dev/null; then + .config/log info 'VirtualBox is installed' + task app:virtualbox:clear + IMAGES="${IMAGES},virtualbox-iso" + fi + if [ '{{OS}}' == 'linux' ] && type vmware &> /dev/null; then + .config/log info 'VMWare Workstation is installed' + IMAGES="${IMAGES},vmware-iso" + fi + if [ '{{OS}}' == 'darwin' ] && type vmrun &> /dev/null; then + .config/log info 'VMWare Fusion is installed' + IMAGES="${IMAGES},vmware-iso" + fi + packer build -only=$(echo $IMAGES | sed 's/^.//') {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}template.json{{end}} + + install:platforms: + desc: Install all available virtualization providers + hide: + sh: '[ "{{.REPOSITORY_TYPE}}" != "packer" ]' + cmds: + - task: :install:software:kvm + - task: :install:software:parallels + - task: :install:software:virtualbox + - task: :install:software:vmware + + latestos: + deps: + - :install:pipx:latestos + - :install:software:jq + vars: + TAG: + sh: jq -r '.variables.latestos_tag' {{.TEMPLATE_FILE}} + hide: + sh: '[ "{{.REPOSITORY_TYPE}}" != "packer" ]' + log: + error: Error running `{{.PYTHON_HANDLE}}latestos {{.TAG}}` + start: Running `{{.PYTHON_HANDLE}}latestos {{.TAG}}` + success: Successfully ran `{{.PYTHON_HANDLE}}latestos {{.TAG}}` + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + {{.PYTHON_HANDLE}}latestos {{.TAG}} + status: + - '[[ "{{.TAG}}}" == "macos" ]] || [ "${container:=}" == "docker" ]' + preconditions: + - sh: test -f {{.TEMPLATE_FILE}} + msg: The `{{.TEMPLATE_FILE}}` file is missing from the root of this project. + + prepare: 'true' + + prepare:template: + deps: + - :install:software:jq + log: + error: Error preparing Packer template + start: Preparing Packer template + success: Successfully prepared Packer template + cmds: + - | + TMP="$(mktemp)" + if [ '{{OS}}' == 'linux' ]; then + jq '.variables.accelerator = "kvm"' {{.TEMPLATE_FILE}} > "$TMP" + elif [ '{{OS}}' == 'darwin' ]; then + jq '.variables.accelerator = "hvf"' {{.TEMPLATE_FILE}} > "$TMP" + else + .config/log warn 'Windows not yet tested' + fi + mv "$TMP" {{.TEMPLATE_FILE}} + + publish: 'true' + + verify: 'true' diff --git a/.config/taskfiles/publish/Taskfile-android.yml b/.config/taskfiles/publish/Taskfile-android.yml new file mode 100644 index 00000000..ee9e9538 --- /dev/null +++ b/.config/taskfiles/publish/Taskfile-android.yml @@ -0,0 +1,24 @@ +--- +version: '3' + +tasks: + beta: + desc: Publishes Android app to Firebase beta channel + log: + error: Encountered error while publishing Android app to Firebase beta channel + start: Publishing Android app to Firebase beta channel + success: Published Android app to Firebase beta channel + cmds: + - fastlane add_plugin firebase_app_distribution + - fastlane run firebase_app_distribution_login + - fastlane beta + + release: + desc: Publishes Android app to Google Play + log: + error: Encountered error while publishing Android app to Google Play + start: Publishing Android app to Google Play + success: Successfully published Android app to Google Play + cmds: + - fastlane add_plugin ionic + - fastlane deploy diff --git a/.config/taskfiles/publish/Taskfile-brew.yml b/.config/taskfiles/publish/Taskfile-brew.yml new file mode 100644 index 00000000..37e4e25d --- /dev/null +++ b/.config/taskfiles/publish/Taskfile-brew.yml @@ -0,0 +1,17 @@ +--- +version: '3' + +tasks: + cask: + desc: Publishes a Homebrew Cask + log: + error: Encountered error while publishing Homebrew Cask + + start: Publishing Homebrew Cask + success: Published Homebrew Cask successfully + formula: + desc: Publishes a Homebrew Formulae + log: + error: Encountered error while publishing Homebrew Formulae + start: Publishing Homebrew Formulae + success: Published Homebrew Formulae diff --git a/.config/taskfiles/publish/Taskfile-chrome.yml b/.config/taskfiles/publish/Taskfile-chrome.yml new file mode 100644 index 00000000..ff8d20fb --- /dev/null +++ b/.config/taskfiles/publish/Taskfile-chrome.yml @@ -0,0 +1,10 @@ +--- +version: '3' + +tasks: + release: + desc: Publishes Chrome extension + log: + error: Encountered error while publishing Chrome extension + start: Publishing Chrome extension + success: Successfully published Chrome extension diff --git a/.config/taskfiles/publish/Taskfile-firefox.yml b/.config/taskfiles/publish/Taskfile-firefox.yml new file mode 100644 index 00000000..d3c7fa67 --- /dev/null +++ b/.config/taskfiles/publish/Taskfile-firefox.yml @@ -0,0 +1,9 @@ +--- +version: '3' + +tasks: + log: + error: Encountered error while publishing Firefox add-on + start: Publishing Firefox add-on + success: Successfully published Firefox add-on + release: Publishes Firefox add-on diff --git a/.config/taskfiles/publish/Taskfile-ios.yml b/.config/taskfiles/publish/Taskfile-ios.yml new file mode 100644 index 00000000..c0c5cada --- /dev/null +++ b/.config/taskfiles/publish/Taskfile-ios.yml @@ -0,0 +1,17 @@ +--- +version: '3' + +tasks: + beta: + desc: Publishes iOS app to Firebase beta channel + log: + error: Encountered error while publishing iOS app to the Firebase beta channel + + start: Publishing iOS app to the Firebase beta channel + success: Successfully published iOS app to the Firebase beta channel + release: + desc: Publishes iOS app to iTunes + log: + error: Encountered error while publishing iOS app to iTunes + start: Publishing iOS app to iTunes + success: Successfully published iOS app to iTunes diff --git a/.config/taskfiles/publish/Taskfile-menubar.yml b/.config/taskfiles/publish/Taskfile-menubar.yml new file mode 100644 index 00000000..b1c6d109 --- /dev/null +++ b/.config/taskfiles/publish/Taskfile-menubar.yml @@ -0,0 +1,5 @@ +--- +version: '3' + +tasks: + release: 'true' diff --git a/.config/taskfiles/publish/Taskfile-opera.yml b/.config/taskfiles/publish/Taskfile-opera.yml new file mode 100644 index 00000000..12022e16 --- /dev/null +++ b/.config/taskfiles/publish/Taskfile-opera.yml @@ -0,0 +1,10 @@ +--- +version: '3' + +tasks: + release: + desc: Publishes Opera extension + log: + error: Encountered error while publishing Opera extension + start: Publishing Opera extension + success: Successfully published Opera extension diff --git a/.config/taskfiles/publish/Taskfile-snap.yml b/.config/taskfiles/publish/Taskfile-snap.yml new file mode 100644 index 00000000..5273027a --- /dev/null +++ b/.config/taskfiles/publish/Taskfile-snap.yml @@ -0,0 +1,58 @@ +--- +version: '3' + +tasks: + export: + deps: + - :install:software:snapcraft + summary: | + # Export Snapcraft Login Credentials + + This task will export an unencrypted file called `snap.login` which you can + use to extract the macaroon/unbound_discharge/email. Once extracted, save + them as environment variables for use with the other tasks. + log: + error: Error encountered while exporting `snap.login` + start: Exporting login credentials to unencrpyted file named `snap.login` + success: Successfully exported `snap.login` (unencrypted) + cmds: + - snapcraft export-login snap.login + + login: + deps: + - :install:software:snapcraft + vars: + SNAP_LOGIN_TEMPLATE: | + [login.ubuntu.com] + macaroon = {{env "SNAPCRAFT_MACAROON"}} + unbound_discharge = {{env "SNAPCRAFT_UNBOUND_DISCHARGE"}} + email = {{env "SNAPCRAFT_EMAIL"}} + log: + error: Error encountered while logging into Snapcraft + start: Logging into Snapcraft + success: Successfully logged into Snapcraft + cmds: + - | + echo '{{.SNAP_LOGIN_TEMPLATE}}' > snap.login + snapcraft login --with snap.login || rm -f snap.login + rm -f snap.login + preconditions: + - '[ -n "$SNAPCRAFT_MACAROON" ]' + - '[ -n "$SNAPCRAFT_UNBOUND_DISCHARGE" ]' + - '[ -n "$SNAPCRAFT_EMAIL" ]' + + register: + deps: + - :install:software:snapcraft + vars: + SNAP_PACKAGE: + sh: jq -r '.blueprint.snapPackage' package.json + log: + error: Error ensuring `{{.SNAP_PACKAGE}}` is registered + start: Ensuring `{{.SNAP_PACKAGE}}` is registered + success: Ensured `{{.SNAP_PACKAGE}}` is registered + cmds: + - task: login + - snapcraft register --yes {{.SNAP_PACKAGE}} + status: + - snapcraft list | grep {{.SNAP_PACKAGE}} || [[ "{{OS}}" != "linux" ]] diff --git a/.config/taskfiles/publish/Taskfile.yml b/.config/taskfiles/publish/Taskfile.yml new file mode 100644 index 00000000..4b02fd78 --- /dev/null +++ b/.config/taskfiles/publish/Taskfile.yml @@ -0,0 +1,109 @@ +--- +version: '3' + +tasks: + app:beta: + deps: + - :publish:android:beta + - :publish:chrome:private + - :publish:firefox:private + - :publish:ios:beta + - :publish:opera:private + desc: Publish a beta release of all the release targets + hide: '{{ne (print .REPOSITORY_TYPE "-" .REPOSITORY_SUBTYPE) "angular-app"}}' + log: + error: Error publishing beta release + start: Publishing beta release + success: Finished publishing beta release + + app:release: + deps: + - :publish:android:release + - :publish:brew:cask + - :publish:chrome:release + - :publish:firefox:release + - :publish:ios:release + - :publish:opera:release + - :publish:menubar:release + desc: Publish a production release of all the targets + hide: '{{ne (print .REPOSITORY_TYPE "-" .REPOSITORY_SUBTYPE) "angular-app"}}' + summary: | + This task is used to release Angular applications. + log: + error: Error publishing `production` release + start: Publishing `production` release + success: Successfully published `production` release + + semantic-release: + deps: + - task: :install:modules:local + vars: + SEMANTIC_RELEASE: + sh: echo true + - :install:npm:semantic-release + - :install:software:git + vars: + RELEASE_PREFIX: + sh: if [ -f setup.cfg ]; then echo 'poetry run '; else echo ''; fi + RUN_OPTIONS: '{{if .CLI_ARGS}} {{.CLI_ARGS}}{{else}} --ci false --debug --dry-run false{{end}}' + env: + GIT_CREDENTIALS: + sh: | + if [ -n "$CI" ]; then + echo "gitlab-ci-token:$CI_BUILD_TOKEN" + fi + HUSKY: '0' + PUBLISHING: 'true' + SEMANTIC_RELEASE: 'true' + log: + error: Error while running `semantic-release` + start: Releasing with `semantic-release`.. + success: Finished running `semantic-release` + cmds: + - git fetch --tags -f + - task: semantic-release:prepare + - | + {{.RELEASE_PREFIX}}semantic-release -r "$(git remote get-url origin)"{{.RUN_OPTIONS}} + - git push all master || git push origin master + + semantic-release:build: + cmds: + - rm -rf artifacts build dist + - task: :release:build + + semantic-release:poetry: + deps: + - :install:software:poetry + cmds: + - poetry install -E semantic + status: + - '[ ! -f setup.cfg ]' + + semantic-release:prepare: + deps: + - task: :install:modules:local:sync + vars: + NPM_KEEP_UPDATED: '{{.SEMANTIC_CONFIG}}' + - :common:update:variables + - :npm:config + - semantic-release:build + - semantic-release:poetry + - version:tag + + version:tag: + deps: + - :ci:commit:config + - :install:software:jq + summary: | + This command is intended to be used before the `semantic-release` task to ensure + that the latest git tag is up-to-date with the latest `package.json` version. + cmds: + - git fetch --all --tags -f + - git tag "v$(jq -r '.version' package.json)" + status: + - git tag | grep "v$(jq -r '.version' package.json)" + preconditions: + - sh: '[ -f package.json ]' + msg: A package.json file must be present! + - sh: '[ "$(jq -r \".version\" package.json)" != "null" ]' + msg: Version must be defined in package.json diff --git a/.config/taskfiles/python/Taskfile-test.yml b/.config/taskfiles/python/Taskfile-test.yml new file mode 100644 index 00000000..a90456da --- /dev/null +++ b/.config/taskfiles/python/Taskfile-test.yml @@ -0,0 +1,49 @@ +--- +version: '3' + +tasks: + ci: + deps: + - :install:python:pytest + - :install:python:pytest-cov + - :install:software:poetry + log: + error: Error encountered while running `pytest` and generating reports + start: Running `pytest` and generating reports + success: Finished running `pytest` and generating reports + cmds: + - poetry config virtualenvs.create false + - | + export "CFLAGS=-I/usr/local/include -L/usr/local/lib" + poetry install --no-dev + - | + {{.PYTHON_HANDLE}}pytest --junitxml=report.xml --cov=src/ + - | + {{.PYTHON_HANDLE}}coverage report + - | + {{.PYTHON_HANDLE}}coverage xml + + mypy: + deps: + - :install:pipx:mypy + log: + error: Encountered error while static type checking with `mypy` + start: Running `mypy` for static type checking + success: Successfully passed type checking with `mypy` + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + {{.PYTHON_HANDLE}}mypy + + pytest: + deps: + - :install:pipx:pytest + - :install:python:requirements + log: + error: '`pytest` encountered errors while running tests' + start: Running `pytest` + success: '`pytest` finished running without any errors to report' + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + {{.PYTHON_HANDLE}}pytest diff --git a/.config/taskfiles/python/Taskfile.yml b/.config/taskfiles/python/Taskfile.yml new file mode 100644 index 00000000..c3f182a6 --- /dev/null +++ b/.config/taskfiles/python/Taskfile.yml @@ -0,0 +1,105 @@ +--- +version: '3' + +tasks: + build: + deps: + - :install:python:requirements + desc: Build the project + log: + error: Encountered error while building the project with `poetry build` + start: Building the project with `poetry build` + success: Successfully built the project + cmds: + - task: clean + - poetry build + + build:binary: + deps: + - :install:python:requirements + desc: Build standalone binary + log: + error: Encountered error while building standalone Python binary with `PyInstaller` + start: Building standalone Python binary with `PyInstaller` + success: Successfully build standalone Python binary with `PyInstaller` + cmds: + - task: clean + - | + {{.PYTHON_HANDLE}}python -OO -m PyInstaller -F run.py + + clean: + log: + error: Encountered error while removing `build/` and `dist/` + start: Removing `build/` and `dist/` + success: Ensured `build/` and `dist/` are removed + cmds: + - rm -rf build + - rm -rf dist + + global:reset: + log: + error: Error removing pip packages + start: Removing pip packages + success: Removed pip packages + cmds: + - cmd: if type pip &> /dev/null; then pip uninstall -y -r <(pip freeze); fi + ignore_error: true + - cmd: if type pip3 &> /dev/null; then pip3 uninstall -y -r <(pip3 freeze); fi + ignore_error: true + + prepare: + cmds: + - .config/log info 'TODO Build the project' + + publish: + deps: + - :install:python:requirements + desc: Publish the project to PyPi.org + log: + error: Error running `poetry publish` + start: Running `poetry publish` + success: '`poetry publish` executed successfully' + cmds: + - poetry publish + + requirementstxt: + deps: + - :install:python:requirements + desc: Generate the requirements.txt fallback file + log: + error: Failed to generate `local/requirements.txt` + start: Generating `local/requirements.txt` + success: Generated `local/requirements.txt` + cmds: + - mkdir -p local + - poetry export -f requirements.txt --output local/requirements.txt --without-hashes + status: + - '[ ! -f pyproject.toml ] || ! type poetry > /dev/null' + + run: + deps: + - :install:python:requirements + desc: Runs the project by calling `run.py` + log: + error: Error running `{{.PYTHON_HANDLE}}python run.py` + start: Running `{{.PYTHON_HANDLE}}python run.py` + success: Successfully ran `{{.PYTHON_HANDLE}}python run.py` + cmds: + - | + {{.PYTHON_HANDLE}}python run.py + + test: + deps: + - :install:python:requirements + desc: Runs tests with `pytest` after fixing and linting + log: + start: Fixing, linting, and testing Python code + success: Successfully fixed, linted, and tested Python code + cmds: + - task: :fix:python + - task: :lint:python + - task: :python:test:pytest + + verify: + cmds: + - .config/log info 'TODO Verify API key works with pypi.org' diff --git a/.config/taskfiles/release/Taskfile.yml b/.config/taskfiles/release/Taskfile.yml new file mode 100644 index 00000000..63d16be4 --- /dev/null +++ b/.config/taskfiles/release/Taskfile.yml @@ -0,0 +1,622 @@ +--- +version: '3' + +env: + RELEASE_ANSIBLE: + sh: | + if [[ '{{.REPOSITORY_TYPE}}' == 'ansible' ]] && [[ '{{.REPOSITORY_SUBTYPE}}' == 'role' ]]; then + echo 'release' + fi + RELEASE_DOCKER: + sh: | + if [ -f Dockerfile ] && ([[ '{{.REPOSITORY_TYPE}}' == 'docker' ]] || [[ "$(jq -r '.blueprint.dockerPublish' package.json)" == 'true' ]]); then + echo 'release' + fi + RELEASE_GO: + sh: | + if [[ '{{.REPOSITORY_TYPE}}' == 'go' ]] && [[ '{{.REPOSITORY_SUBTYPE}}' == 'cli' ]]; then + echo 'release' + fi + RELEASE_NPM: + sh: | + if [[ '{{.REPOSITORY_TYPE}}' == 'npm' ]] || [[ "$(jq -r '.blueprint.npmPublish' package.json)" == 'true' ]]; then + echo 'release' + fi + RELEASE_PACKER: + sh: | + if [[ '{{.REPOSITORY_TYPE}}' == 'packer' ]]; then + echo 'release' + fi + RELEASE_PYTHON: + sh: | + if [[ '{{.REPOSITORY_TYPE}}' == 'python' ]] || [[ "$(jq -r '.blueprint.pythonPublish' package.json)" == 'true' ]]; then + echo 'release' + fi + SNAP_PACKAGE: + sh: jq -r '.blueprint.snapPackage' package.json + +tasks: + add-channel: + deps: + - add-channel:ansible + - add-channel:docker + - add-channel:npm + - add-channel:packer + - add-channel:python + - add-channel:snap + summary: | + Must return a parseable JSON string that resembles the following format: + + ```json + { + type: 'patch', + channel: 'master', + gitHead: 'c7dbb1e87ebff9b177bdb52a326f5b8373b79442', + version: '1.1.26', + gitTag: 'v1.1.26', + name: 'GitLab release', + notes: '## [1.1.26](https://gitlab.com/megabyte-labs/npm/configs/release/compare/v1.1.25...v1.1.26) (2022-04-07)\n' + + '\n' + + '\n' + + '\n', + url: 'https://gitlab.com/megabyte-labs%2Fnpm%2Fconfigs%2Frelease/-/releases/v1.1.26', + pluginName: '@semantic-release/gitlab' + } + ``` + + add-channel:ansible: + vars: + GALAXY_URL: + sh: | + if [ -n "$RELEASE_ANSIBLE" ]; then + echo "https://galaxy.ansible.com/{{.GALAXY_NAMESPACE}}/{{.GALAXY_ROLE_NAME}}" + fi + cmds: + - | + echo '{"name": "Ansible Galaxy role", "url": "{{.GALAXY_URL}}"}' + status: + - '[ -z "$RELEASE_ANSIBLE" ]' + + add-channel:docker: + vars: + DOCKERHUB_URL: + sh: | + if [ -n "$RELEASE_DOCKER" ]; then + echo "https://hub.docker.com/r/{{.DOCKERHUB_PROFILE}}/$(jq -r '.blueprint.slug' package.json)" + fi + cmds: + - | + echo '{"name": "DockerHub", "url": "{{.DOCKERHUB_URL}}"}' + status: + - '[ -z "$RELEASE_DOCKER" ]' + + add-channel:npm: + cmds: + - | + echo '{"name": "Testing", "url": "https://megabyte.space"}' + status: + - '[ -z "$RELEASE_NPM" ]' + + add-channel:packer: + vars: + VAGRANTUP_URL: + sh: | + if [ -n "$RELEASE_PACKER" ]; then + BASENAME="$(jq -r '.variables.box_basename' template.json)" + VAGRANTUP_USER="$(jq -r '.variables.vagrantup_user' template.json)" + if [ "$BASENAME" != 'null' ] && [ "$BASENAME" != 'null' ]; then + echo "https://app.vagrantup.com/$VAGRANTUP_USER/boxes/$BASENAME" + else + echo "https://megabyte.space" + fi + fi + cmds: + - | + echo '{"name": "VagrantUp box", "url": "{{.VAGRANTUP_URL}}"}' + status: + - '[ -z "$RELEASE_PACKER" ]' + + add-channel:python: + vars: + PYPI_URL: + sh: | + if [ -n "$RELEASE_PYTHON" ]; then + PKG_NAME="$(jq -r '.blueprint.customPyPiPackageName' package.json)" + if [ "$PKG_NAME" == 'null' ]; then + PKG_NAME="$(jq -r '.blueprint.customPackageName' package.json)" + fi + if [ "$PKG_NAME" == 'null' ]; then + PKG_NAME="$(jq -r '.name' package.json)" + fi + echo "https://pypi.org/project/$PKG_NAME" + fi + cmds: + - | + echo '{"name": "pip package", "url": "{{.PYPI_URL}}"}' + status: + - '[ -z "$RELEASE_PYTHON" ]' + + add-channel:snap: + vars: + SNAP_PACKAGE: + sh: echo "$SNAP_PACKAGE" + cmds: + - | + echo '{"name": "Snap", "url": "https://snapcraft.io/{{.SNAP_PACKAGE}}"}' + status: + - '[ "$SNAP_PACKAGE" == "null" ]' + + analyze: + summary: | + | Command property | Description | + |------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| + | `exit code` | Any non `0` code is considered as an unexpected error and will stop the `semantic-release` execution with an error. | + | `stdout` | Only the release type (`major`, `minor` or `patch` etc..) can be written to `stdout`. If no release has to be done the command must not write to `stdout`. | + | `stderr` | Can be used for logging. | + cmds: + - | + if [ "$FORCE_SEMANTIC_RELEASE" == 'true' ]; then + echo 'patch' + fi + + build: + deps: + - build:ansible + - build:docker + - build:go + - build:npm + - build:packer + - build:python + + build:ansible: + cmds: + - task: :build + status: + - '[ -z "$RELEASE_ANSIBLE" ]' + + build:docker: + cmds: + - task: :build + status: + - '[ -z "$RELEASE_DOCKER" ]' + + build:go: + cmds: + - task: build:go:continue + status: + - '[ -z "$RELEASE_GO" ]' + + build:go:continue: + deps: + - build:go:deps + cmds: + - task: :build + + build:go:deps: + deps: + - :docker:login + - :install:go:goreleaser + - :install:go:nfpm + - :install:software:go + - :install:software:snapcraft + + build:npm: + deps: + - build:npm:deps + cmds: + - task: :build + status: + - '[ -z "$RELEASE_NPM" ]' + + build:npm:deps: + deps: + - build:npm:deps:go + + build:npm:deps:go: + cmds: + - task: build:go:deps + status: + - '[ "{{.REPOSITORY_SUBTYPE}}" != "cli" ]' + + build:packer: + cmds: + - task: :build + status: + - '[ -z "$RELEASE_PACKER" ]' + + build:python: + cmds: + - task: :build + status: + - '[ -z "$RELEASE_PYTHON" ]' + + fail: + deps: + - :install:software:jq + env: + ARTIFACTS_JSON: + sh: jq -r '.' build/artifacts.json || echo null + METADATA_JSON: + sh: jq -r '.' build/metadata.json || echo null + PACKAGE_JSON: + sh: jq -r '.' package.json + RELEASE_JSON: + sh: jq -r '.' .release.json || echo null + TMP: + sh: mktemp + VARIABLES_JSON: + sh: jq -r '.' .variables.json + cmds: + - jq -n --arg artifacts "$ARTIFACTS_JSON" --arg metadata "$METADATA_JSON" --arg pkg "$PACKAGE_JSON" --arg release "$RELEASE_JSON" --arg vars "$VARIABLES_JSON" --arg job "$CI_JOB_ID" + '.["package.json"] = $pkg | .[".release.json"] = $release | .["build/artifacts.json"] = $artifacts | .["build/metadata.json"] = $metadata | .[".variables.json"] = $vars | .CI_JOB_ID = $job' > "$TMP" + - > + curl -X POST -H "Content-Type: application/json" -d "$(cat "$TMP")" + "https://maker.ifttt.com/trigger/SemanticReleaseFail/json/with/key/$IFTTT_WEBHOOK_TOKEN" + status: + - '[ -z "$IFTTT_WEBHOOK_TOKEN" ]' + + notes: + cmds: + - task: notes:introduction + - task: notes:project-specific + + notes:ansible: + vars: + PKG_NAME: + sh: | + if [ -n "$RELEASE_ANSIBLE" ]; then + echo "{{.GALAXY_NAMESPACE}}.{{.GALAXY_ROLE_NAME}}" + fi + VERSION: + sh: jq -r '.version' package.json + cmds: + - echo -e '```shell\nansible-galaxy install {{.PKG_NAME}},v{{.VERSION}}\n```\n' + status: + - '[ -z "$RELEASE_ANSIBLE" ]' + + notes:docker: + vars: + IMAGE: + sh: | + if [ -n "$RELEASE_DOCKER" ]; then + echo "{{.DOCKERHUB_PROFILE}}/$(jq -r '.blueprint.slug' package.json)" + fi + VERSION: + sh: jq -r '.version' package.json + cmds: + - echo -e '```shell\ndocker pull {{.IMAGE}}:{{.VERSION}}\n```\n' + status: + - '[ -z "$RELEASE_DOCKER" ]' + + notes:go: + vars: + GITHUB_GO_URL: + sh: jq -r '.blueprint.repository.github' package.json | sed 's/https:\/\///' + VERSION: + sh: jq -r '.version' package.json + cmds: + - echo -e '```shell\ngo install {{.GITHUB_GO_URL}}@v{{.VERSION}}\n```\n' + status: + - '[ -z "$RELEASE_GO" ]' + + notes:introduction: + cmds: + - echo -e 'Grab this version by running:\n\n' + + notes:npm: + vars: + FLAG: + sh: | + if [ '{{.REPOSITORY_SUBTYPE}}' == 'cli' ]; then + echo ' -g' + elif [ '{{.REPOSITORY_SUBTYPE}}' == 'config' ] || [ '{{.REPOSITORY_SUBTYPE}}' == 'plugin' ]; then + echo ' --save-dev' + else + echo ' --save' + fi + PKG_NAME: + sh: jq -r '.name' package.json + VERSION: + sh: jq -r '.version' package.json + cmds: + - echo -e '```shell\nnpm i{{.FLAG}} {{.PKG_NAME}}@{{.VERSION}}\n```\n' + status: + - '[ -z "$RELEASE_NPM" ]' + + notes:packer: + vars: + VAGRANTUP_BOX: + sh: | + if [ -n "$RELEASE_PACKER" ]; then + BASENAME="$(jq -r '.variables.box_basename' template.json)" + VAGRANTUP_USER="$(jq -r '.variables.vagrantup_user' template.json)" + if [ "$BASENAME" != 'null' ] && [ "$BASENAME" != 'null' ]; then + echo "$VAGRANTUP_USER/$BASENAME" + fi + fi + VERSION: + sh: jq -r '.version' package.json + cmds: + - echo -e '```shell\nvagrant init --box-version '{{.VERSION}}' {{.VAGRANTUP_BOX}} && vagrant up\n```\n' + status: + - '[ -z "$RELEASE_PACKER" ]' + + notes:project-specific: + deps: + - notes:ansible + - notes:docker + - notes:go + - notes:npm + - notes:packer + - notes:python + + notes:python: + vars: + PKG_NAME: + sh: | + if [ -n "$RELEASE_PYTHON" ]; then + PKG_NAME="$(jq -r '.blueprint.customPyPiPackageName' package.json)" + if [ "$PKG_NAME" == 'null' ]; then + PKG_NAME="$(jq -r '.blueprint.customPackageName' package.json)" + fi + if [ "$PKG_NAME" == 'null' ]; then + PKG_NAME="$(jq -r '.name' package.json)" + fi + echo "$PKG_NAME" + fi + VERSION: + sh: jq -r '.version' package.json + cmds: + - echo -e '```shell\npip3 install {{.PKG_NAME}}=={{.VERSION}}\n```\n' + status: + - '[ -z "$RELEASE_PYTHON" ]' + + prepare: + deps: + - prepare:ansible + - prepare:docker + - prepare:go + - prepare:npm + - prepare:packer + - prepare:python + summary: | + | Command property | Description | + |------------------|---------------------------------------------------------------------------------------------------------------------| + | `exit code` | Any non `0` code is considered as an unexpected error and will stop the `semantic-release` execution with an error. | + | `stdout` | Can be used for logging. | + | `stderr` | Can be used for logging. | + + prepare:ansible: + cmds: + - task: :ansible:prepare + status: + - '[ -z "$RELEASE_ANSIBLE" ]' + + prepare:docker: + cmds: + - task: :docker:prepare + status: + - '[ -z "$RELEASE_DOCKER" ]' + + prepare:go: + cmds: + - task: :go:prepare + status: + - '[ -z "$RELEASE_GO" ]' + + prepare:npm: + cmds: + - task: :npm:prepare + status: + - '[ -z "$RELEASE_NPM" ]' + + prepare:packer: + cmds: + - task: :packer:prepare + status: + - '[ -z "$RELEASE_PACKER" ]' + + prepare:python: + cmds: + - task: :python:prepare + status: + - '[ -z "$RELEASE_PYTHON" ]' + + publish: + deps: + - publish:ansible + - publish:docker + - publish:go + - publish:npm + - publish:packer + - publish:python + + publish:ansible: + cmds: + - task: :ansible:publish + status: + - '[ -z "$RELEASE_ANSIBLE" ]' + + publish:docker: + cmds: + - task: :docker:publish + status: + - '[ -z "$RELEASE_DOCKER" ]' + + publish:go: + cmds: + - task: :go:publish + status: + - '[ -z "$RELEASE_GO" ]' + + publish:npm: + summary: | + Modify the dist tag used (shown in release notes) by modifying `package.json` with the following: + + ``` + { + "publishConfig": { + "registry": "https://registry.npmjs.org/", + "tag": "latest" + } + ``` + cmds: + - task: publish:npm:tasks + status: + - '[ -z "$RELEASE_NPM" ]' + + publish:npm:latest: + vars: + PKG_NAME: + sh: jq -r '.name' package.json + VERSION: + sh: jq -r '.version' package.json + cmds: + - npm config set -- 'https://registry.npmjs.org/:_authToken' '${NPM_TOKEN}' + - npm dist-tag add {{.PKG_NAME}}@{{.VERSION}} latest &> /dev/null + + publish:npm:tasks: + deps: + - publish:npm:latest + - :npm:publish:gitlab + + publish:packer: + cmds: + - task: :packer:publish + status: + - '[ -z "$RELEASE_PACKER" ]' + + publish:python: + cmds: + - task: :python:publish + status: + - '[ -z "$RELEASE_PYTHON" ]' + + success: + deps: + - :install:software:jq + summary: | + | Command property | Description | + |------------------|---------------------------------------------------------------------------------------------------------------------| + | `exit code` | Any non `0` code is considered as an unexpected error and will stop the `semantic-release` execution with an error. | + | `stdout` | Can be used for logging. | + | `stderr` | Can be used for logging. | + env: + PACKAGE_JSON: + sh: jq -r '.' package.json + RELEASE_JSON: + sh: jq -r '.' .release.json + TMP: + sh: mktemp + VARIABLES_JSON: + sh: jq -r '.' .variables.json + cmds: + - jq -n --arg pkg "$PACKAGE_JSON" --arg release "$RELEASE_JSON" --arg vars "$VARIABLES_JSON" + '.["package.json"] = $pkg | .[".release.json"] = $release | .[".variables.json"] = $vars' > "$TMP" + - > + curl -X POST -H "Content-Type: application/json" -d "$(cat "$TMP")" + "https://maker.ifttt.com/trigger/SemanticRelease/json/with/key/$IFTTT_WEBHOOK_TOKEN" + status: + - '[ -z "$IFTTT_WEBHOOK_TOKEN" ]' + + verify:conditions: + deps: + - verify:conditions:ansible + - verify:conditions:docker + - verify:conditions:go + - verify:conditions:npm + - verify:conditions:packer + - verify:conditions:python + summary: | + | Command property | Description | + |------------------|--------------------------------------------------------------------------| + | `exit code` | `0` if the verification is successful, or any other exit code otherwise. | + | `stdout` | Write only the reason for the verification to fail. | + | `stderr` | Can be used for logging. | + + verify:conditions:ansible: + cmds: + - task: :ansible:verify + status: + - '[ -z "$RELEASE_ANSIBLE" ]' + + verify:conditions:docker: + cmds: + - task: :docker:verify + status: + - '[ -z "$RELEASE_DOCKER" ]' + + verify:conditions:go: + cmds: + - task: :go:verify + status: + - '[ -z "$RELEASE_GO" ]' + + verify:conditions:npm: + cmds: + - task: :npm:verify + status: + - '[ -z "$RELEASE_NPM" ]' + + verify:conditions:packer: + cmds: + - task: :packer:verify + status: + - '[ -z "$RELEASE_PACKER" ]' + + verify:conditions:python: + cmds: + - task: :python:verify + status: + - '[ -z "$RELEASE_PYTHON" ]' + + verify:release: + deps: + - verify:release:ansible + - verify:release:docker + - verify:release:go + - verify:release:npm + - verify:release:packer + - verify:release:python + summary: | + | Command property | Description | + |------------------|--------------------------------------------------------------------------| + | `exit code` | `0` if the verification is successful, or any other exit code otherwise. | + | `stdout` | Only the reason for the verification to fail can be written to `stdout`. | + | `stderr` | Can be used for logging. | + + verify:release:ansible: + cmds: + - 'true' + status: + - '[ -z "$RELEASE_ANSIBLE" ]' + + verify:release:docker: + cmds: + - 'true' + status: + - '[ -z "$RELEASE_DOCKER" ]' + + verify:release:go: + cmds: + - 'true' + status: + - '[ -z "$RELEASE_GO" ]' + + verify:release:npm: + cmds: + - 'true' + status: + - '[ -z "$RELEASE_NPM" ]' + + verify:release:packer: + cmds: + - 'true' + status: + - '[ -z "$RELEASE_PACKER" ]' + + verify:release:python: + cmds: + - 'true' + status: + - '[ -z "$RELEASE_PYTHON" ]' diff --git a/.config/taskfiles/security/Taskfile-disk.yml b/.config/taskfiles/security/Taskfile-disk.yml new file mode 100644 index 00000000..9399525a --- /dev/null +++ b/.config/taskfiles/security/Taskfile-disk.yml @@ -0,0 +1,69 @@ +--- +version: '3' + +vars: + CRYPT_LABEL: '{{if .CRYPT_LABEL}}{{.CRYPT_LABEL}}{{else}}secret{{end}}' + DISK_PATH: '{{if .DISK_PATH}}{{.DISK_PATH}}{{else}}{{.CLI_ARGS}}{{end}}' + PARTITION_SIZE: '{{if .PARTITION_SIZE}}{{.PARTITION_SIZE}}{{else}}+25M{{end}}' + +env: + GNUPGHOME: + sh: echo "$HOME/.gnupghome" + +tasks: + encrypt:create: + summary: | + # Encrypt a Disk (USB, etc.) + + This task will encrypt a disk (like a USB drive) as a single partition using + the full disk space. + + **Usage example:** + + ```shell + task security:disk:encrypt -- /dev/mmcblk0 + ``` + + You can find the path of the USB / storage medium to pass to the CLI command + by running `fdisk -l`. + vars: + DISK_LABEL: '{{if .DISK_LABEL}}{{.DISK_LABEL}}{{else}}gpg{{end}}' + PARTITION_NUMBER: '{{if .PARTITION_NUMBER}}{{.PARTITION_NUMBER}}{{else}}1{{end}}' + cmds: + - sudo dd if=/dev/urandom of={{.DISK_PATH}} bs=4M status=progress + - echo -e "o\nn\np\n{{.PARTITION_NUMBER}}\n{{.PARTITION_SIZE}}\nw" | sudo fdisk {{.DISK_PATH}} + - echo -e "${MASTER_KEY}\n${MASTER_KEY}" | sudo cryptsetup -q luksFormat {{.DISK_PATH}}{{.PARTITION_NUMBER}} + - echo -e "${MASTER_KEY}" | sudo cryptsetup -q luksOpen {{.DISK_PATH}}{{.PARTITION_NUMBER}} {{.CRYPT_LABEL}} + - sudo mkfs.ext2 /dev/mapper/{{.CRYPT_LABEL}} -L {{.DISK_LABEL}} + - sudo cryptsetup luksClose {{.CRYPT_LABEL}} + + encrypt:mount: + vars: + PARTITION_NUMBER: '{{if .PARTITION_NUMBER}}{{.PARTITION_NUMBER}}{{else}}1{{end}}' + cmds: + - echo -e "${MASTER_KEY}" | sudo cryptsetup -q luksOpen {{.DISK_PATH}}{{.PARTITION_NUMBER}} {{.CRYPT_LABEL}} + - sudo mkdir /mnt/gpg-encrypted-storage + - sudo mount /dev/mapper/{{.CRYPT_LABEL}} /mnt/gpg-encrypted-storage + + encrypt:unmount: + cmds: + - sudo umount /mnt/gpg-encrypted-storage + - sudo cryptsetup luksClose {{.CRYPT_LABEL}} + + unencrypted:create: + vars: + PARTITION_NUMBER: '{{if .PARTITION_NUMBER}}{{.PARTITION_NUMBER}}{{else}}2{{end}}' + cmds: + - echo -e "o\nn\np\n{{.PARTITION_NUMBER}}\n{{.PARTITION_SIZE}}\nw" | sudo fdisk {{.DISK_PATH}} + - sudo mkfs.ext2 {{.DISK_PATH}}{{.PARTITION_NUMBER}} + + unencrypted:mount: + vars: + PARTITION_NUMBER: '{{if .PARTITION_NUMBER}}{{.PARTITION_NUMBER}}{{else}}2{{end}}' + cmds: + - sudo mkdir /mnt/gpg-public + - sudo mount {{.DISK_PATH}}{{.PARTITION_NUMBER}} /mnt/gpg-public + + unencrypted:unmount: + cmds: + - sudo umount /mnt/gpg-public diff --git a/.config/taskfiles/security/Taskfile-gpg.yml b/.config/taskfiles/security/Taskfile-gpg.yml new file mode 100644 index 00000000..dac9f481 --- /dev/null +++ b/.config/taskfiles/security/Taskfile-gpg.yml @@ -0,0 +1,71 @@ +--- +version: '3' + +tasks: + conf: + vars: + GPG_CONFIG: | + # Source: https://raw.githubusercontent.com/drduh/config/master/gpg.conf + personal-cipher-preferences AES256 AES192 AES + personal-digest-preferences SHA512 SHA384 SHA256 + personal-compress-preferences ZLIB BZIP2 ZIP Uncompressed + default-preference-list SHA512 SHA384 SHA256 AES256 AES192 AES ZLIB BZIP2 ZIP Uncompressed + cert-digest-algo SHA512 + s2k-digest-algo SHA512 + s2k-cipher-algo AES256 + charset utf-8 + fixed-list-mode + no-comments + no-emit-version + keyid-format 0xlong + list-options show-uid-validity + verify-options show-uid-validity + with-fingerprint + require-cross-certification + no-symkey-cache + use-agent + throw-keyids + cmds: + - mkdir -p "{{if .CONFIG_DIR_PATH}}{{.CONFIG_DIR_PATH}}{{else}}$HOME/.gnupg{{end}}" + - echo '{{.GPG_CONFIG}}' > "{{if .CONFIG_DIR_PATH}}{{.CONFIG_DIR_PATH}}{{else}}$HOME/.gnupg{{end}}/gpg.conf" + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + conf:agent: + deps: + - :install:software:pinentry + vars: + GPG_AGENT_CONFIG: | + enable-ssh-support + default-cache-ttl 60 + max-cache-ttl 120 + pinentry-program {{if (eq OS "linux")}}/usr/bin/pinentry-gnome3{{else}}/usr/local/bin/pinentry-mac{{end}} + PROFILE_STRING: | + ### GPG SSH Settings ### + export GPG_TTY="$(tty)" + export SSH_AUTH_SOCK="$(gpgconf --list-dirs agent-ssh-socket)" + gpgconf --launch gpg-agent + cmds: + - mkdir -p "{{if .CONFIG_DIR_PATH}}{{.CONFIG_DIR_PATH}}{{else}}$HOME/.gnupg{{end}}" + - echo '{{.GPG_AGENT_CONFIG}}' > "{{if .CONFIG_DIR_PATH}}{{.CONFIG_DIR_PATH}}{{else}}$HOME/.gnupg{{end}}/gpg-agent.conf" + - task: :install:profile:add + vars: + PROFILE_STRING: '{{.PROFILE_STRING}}' + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + donothing: 'true' + + public:import:file: + todo: Polish + cmds: + - gpg --import /mnt/gpg-public/gpg-$KEYID*.asc + - echo -e "trust\n5\ny" | gpg --command-fd 0 --edit-key "$KEYID" + status: + - '[ ! -f /mnt/gpg-public ]' + + public:import:server: + todo: Add prompt for KEYID + cmds: + - gpg --recv $KEYID + - echo -e "trust\n5\ny" | gpg --command-fd 0 --edit-key "$KEYID" diff --git a/.config/taskfiles/security/Taskfile-ssh.yml b/.config/taskfiles/security/Taskfile-ssh.yml new file mode 100644 index 00000000..a2e959d5 --- /dev/null +++ b/.config/taskfiles/security/Taskfile-ssh.yml @@ -0,0 +1,69 @@ +--- +version: '3' + +tasks: + generate: + vars: + SSH_CIPHER: '{{if .SSH_CIPHER}}{{.SSH_CIPHER}}{{else}}ed25519{{end}}' + SSH_EMAIL_COMMENT: + sh: echo "{{if .SSH_EMAIL}}{{.SSH_EMAIL}}{{else}}$(jq -r '.YUBI_EMAIL' .yubi.json){{end}}" + SSH_KEY_CATEGORY: '{{if .SSH_KEY_CATEGORY}}{{.SSH_KEY_CATEGORY}}{{else}}ssh{{end}}' + cmds: + - mkdir -p "$HOME/.ssh" + - ssh-keygen -t {{.SSH_CIPHER}} -C "{{.SSH_EMAIL_COMMENT}} ({{.SSH_CIPHER}} - {{.SSH_KEY_CATEGORY}})" + -f "$HOME/.ssh/id_gpg_{{.SSH_CIPHER}}_{{.SSH_KEY_CATEGORY}}" -q -P ""{{if (eq .SSH_CIPHER "rsa")}} -b 4096{{end}} + + yubikey: + summary: | + Generates default SSH keys that are intended to be made part of + the keys stored in the ~/.gnupg folder using the `gpg-agent`. + cmds: + - task: generate + vars: + SSH_CIPHER: ed25519 + SSH_KEY_CATEGORY: alt_auto + - task: generate + vars: + SSH_CIPHER: rsa + SSH_KEY_CATEGORY: alt_auto + - task: generate + vars: + SSH_CIPHER: ed25519 + SSH_KEY_CATEGORY: auto + - task: generate + vars: + SSH_CIPHER: rsa + SSH_KEY_CATEGORY: auto + - task: generate + vars: + SSH_CIPHER: ed25519 + SSH_KEY_CATEGORY: local + - task: generate + vars: + SSH_CIPHER: rsa + SSH_KEY_CATEGORY: local + - task: generate + vars: + SSH_CIPHER: ed25519 + SSH_KEY_CATEGORY: private + - task: generate + vars: + SSH_CIPHER: rsa + SSH_KEY_CATEGORY: private + - task: generate + vars: + SSH_CIPHER: ed25519 + SSH_KEY_CATEGORY: web + - task: generate + vars: + SSH_CIPHER: rsa + SSH_KEY_CATEGORY: web + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + yubikey:resident: + notes: + - https://catbaba.com/ssh-authentication-with-a-yubikey-fido2-hardware-token-easy-portable-touch-free/ + - -O no-touch-required for no touch required auth + cmds: + - ssh-keygen -t ed25519 -O resident -O verify-required -C "{{.FILL_ME_IN}}" diff --git a/.config/taskfiles/security/Taskfile-yubikey.yml b/.config/taskfiles/security/Taskfile-yubikey.yml new file mode 100644 index 00000000..8483130d --- /dev/null +++ b/.config/taskfiles/security/Taskfile-yubikey.yml @@ -0,0 +1,323 @@ +--- +version: '3' + +todo: Collect YUBI_USER_PIN, YUBI_ADMIN_PIN, YUBI_LAST_NAME, YUBI_EMAIL, YUBI_FIRST_NAME, YUBI_TITLE (Principal Software Engineer), YUBI_ALT_NAME, YUBI_ALT_EMAIL, YUBI_ALT_TITLE + +vars: + YUBI_MASTER_EMAIL: no-reply@megabyte.space + YUBI_MASTER_NAME: Megabyte Labs + +env: + GNUPGHOME: + sh: echo "$HOME/.gnupghome" + MASTER_KEY: + sh: | + if [ -f .yubi-masterkey ]; then + cat .yubi-masterkey + else + LC_ALL=C tr -dc '[:upper:]' < /dev/urandom | fold -w 30 | head -n1 + fi + +tasks: + add:identities: + env: + KEYID: '{{.KEYID}}' + YUBI_ALT_EMAIL: + sh: jq -r '.YUBI_ALT_EMAIL' .yubi.json + YUBI_ALT_NAME: + sh: jq -r '.YUBI_ALT_NAME' .yubi.json + YUBI_ALT_TITLE: + sh: jq -r '.YUBI_ALT_TITLE' .yubi.json + YUBI_EMAIL: + sh: jq -r '.YUBI_EMAIL' .yubi.json + YUBI_FIRST_NAME: + sh: jq -r '.YUBI_FIRST_NAME' .yubi.json + YUBI_LAST_NAME: + sh: jq -r '.YUBI_LAST_NAME' .yubi.json + YUBI_TITLE: + sh: jq -r '.YUBI_TITLE' .yubi.json + cmds: + - echo -e "${YUBI_FIRST_NAME} ${YUBI_LAST_NAME}\n${YUBI_EMAIL}\n${YUBI_TITLE}\n" | gpg --command-fd 0 --pinentry-mode loopback + --passphrase "$MASTER_KEY" --expert --edit-key "$KEYID" adduid save + - echo -e "${YUBI_ALT_NAME}\n${YUBI_ALT_EMAIL}\n${YUBI_ALT_TITLE}\n" | gpg --command-fd 0 --pinentry-mode loopback + --passphrase "$MASTER_KEY" --expert --edit-key "$KEYID" adduid save + + card: + vars: + KEYID: + sh: gpg --keyid-format long --list-keys {{.YUBI_MASTER_EMAIL}} | grep "pub rsa" | sed 's/pub rsa4096\///' | sed 's/^\([^ ]*\).*/\1/' + env: + KEYID: '{{.KEYID}}' + YUBI_ADMIN_PIN: + sh: jq -r '.YUBI_ADMIN_PIN' .yubi.json + YUBI_EMAIL: + sh: jq -r '.YUBI_EMAIL' .yubi.json + YUBI_FIRST_NAME: + sh: jq -r '.YUBI_FIRST_NAME' .yubi.json + YUBI_LAST_NAME: + sh: jq -r '.YUBI_LAST_NAME' .yubi.json + YUBI_USER_PIN: + sh: jq -r '.YUBI_USER_PIN' .yubi.json + cmds: + - cp -rf ~/.gnupg ~/.gnupg.bak + - task: card:reset + - echo -e "admin\nkdf-setup\n12345678\n" | gpg --command-fd 0 --pinentry-mode loopback --card-edit + - echo -e "admin\npasswd\n1\n123456\n${YUBI_USER_PIN}\n${YUBI_USER_PIN}\nq\n" | gpg --command-fd 0 --pinentry-mode loopback --card-edit + - echo -e "admin\npasswd\n3\n12345678\n${YUBI_ADMIN_PIN}\n${YUBI_ADMIN_PIN}\nq\n" | gpg --command-fd 0 --pinentry-mode loopback --card-edit + - echo -e "admin\nname\n${YUBI_LAST_NAME}\n${YUBI_FIRST_NAME}\n${YUBI_ADMIN_PIN}\nlang\nen\nlogin\n${YUBI_EMAIL}\nquit" | gpg --command-fd 0 --pinentry-mode loopback --card-edit + - task: card:keys + vars: + KEYID: '{{.KEYID}}' + # - gpg --delete-secret-key "$KEYID" + # - mv ~/.gnupg ~/.gnupg-generated # TODO + # - rm -rf ~/.gnupg + # - mv ~/.gnupg.bak ~/.gnupg + + card:keys: + env: + KEYID: '{{.KEYID}}' + YUBI_ADMIN_PIN: + sh: jq -r '.YUBI_ADMIN_PIN' .yubi.json + cmds: + - echo -e "key 1\nkeytocard\n1\n${MASTER_KEY}\n${YUBI_ADMIN_PIN}\n" | gpg --command-fd 0 --pinentry-mode loopback --edit-key "$KEYID" + - echo -e "key 2\nkeytocard\n2\n${MASTER_KEY}\n${YUBI_ADMIN_PIN}\n" | gpg --command-fd 0 --pinentry-mode loopback --edit-key "$KEYID" + - echo -e "key 3\nkeytocard\n3\n${MASTER_KEY}\n${YUBI_ADMIN_PIN}\n" | gpg --command-fd 0 --pinentry-mode loopback --edit-key "$KEYID" + + card:reset: + cmds: + - echo -e "admin\nfactory-reset\ny\nyes\n" | gpg --command-fd 0 --pinentry-mode loopback --card-edit + status: + - '[ -n "$DONT_RESET_YUBIKEY" ]' + + check:entropy: + vars: + ENTROPY_AVAIL: + sh: | + if [ -f /proc/sys/kernel/random/entropy_avail ]; then + cat /proc/sys/kernel/random/entropy_avail + fi + cmds: + - | + if [ '{{.ENTROPY_AVAIL}}' -lt '2000' ]; then + .config/log error 'The entropy pool value is not high enough. It must be greater than 2000!' && exit 1 + fi + status: + - '[ "{{OS}}" != "linux" ]' + + export: + vars: + KEYID: + sh: gpg --keyid-format long --list-keys {{.YUBI_MASTER_EMAIL}} | grep "pub rsa" | sed 's/pub rsa4096\///' | sed 's/^\([^ ]*\).*/\1/' + cmds: + - task: public:export + vars: + KEYID: '{{.KEYID}}' + # - task: revocation:export + # vars: + # KEYID: '{{.KEYID}}' + - task: secrets:export + vars: + KEYID: '{{.KEYID}}' + - task: public:upload + vars: + KEYID: '{{.KEYID}}' + # - task: usb + - task: card + # - task: usb:unmount + + generate:authentication: + env: + KEYID: '{{.KEYID}}' + cmds: + - echo -e "8\nS\nE\nA\nQ\n4096\n1y\n" | gpg --command-fd 0 --pinentry-mode loopback --passphrase "$MASTER_KEY" + --expert --edit-key "$KEYID" addkey save + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + generate:encryption: + env: + KEYID: '{{.KEYID}}' + cmds: + - echo -e "6\n4096\n1y\n" | gpg --command-fd 0 --pinentry-mode loopback --passphrase "$MASTER_KEY" --expert --edit-key "$KEYID" addkey save + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + generate:master: + cmds: + - mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME" + - mkdir -p "$GNUPGHOME/private-keys-v1.d" + - chmod 700 "$GNUPGHOME/private-keys-v1.d" + # Original: echo -e "8\nE\nS\nQ\n4096\n0\ny\n{{.YUBI_MASTER_NAME}}\n{{.YUBI_MASTER_EMAIL}}\n\no\n" -- removed \ny for macOS + - echo -e "8\nE\nS\nQ\n4096\n0\n{{.YUBI_MASTER_NAME}}\n{{.YUBI_MASTER_EMAIL}}\n\no\n" | gpg --expert --command-fd 0 + --pinentry-mode loopback --passphrase "$MASTER_KEY" --full-generate-key + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + generate:signing: + env: + KEYID: '{{.KEYID}}' + cmds: + - echo -e "4\n4096\n1y\n" | gpg --command-fd 0 --pinentry-mode loopback --passphrase "$MASTER_KEY" --expert --edit-key "$KEYID" addkey save + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + generate:subkeys: + vars: + KEYID: + sh: gpg --keyid-format long --list-keys {{.YUBI_MASTER_EMAIL}} | grep "pub rsa" | sed 's/pub rsa4096\///' | sed 's/^\([^ ]*\).*/\1/' + cmds: + - task: generate:signing + vars: + KEYID: '{{.KEYID}}' + - task: generate:encryption + vars: + KEYID: '{{.KEYID}}' + - task: generate:authentication + vars: + KEYID: '{{.KEYID}}' + - task: add:identities + vars: + KEYID: '{{.KEYID}}' + + prepare: + desc: Prepares an environment by provisioning all the required software / preliminary steps + cmds: + - mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME" + - echo '{{.MASTER_KEY}}' > .yubi-masterkey + - task: prepare:init + - task: :install:service:start + vars: + SERVICE: pcscd + - task: check:entropy + - task: generate:master + - task: generate:subkeys + vars: + KEYID: + sh: gpg --keyid-format long --list-keys {{.YUBI_MASTER_EMAIL}} | grep "pub rsa" | sed 's/pub rsa4096\///' | sed 's/^\([^ ]*\).*/\1/' + - task: export + - task: :security:ssh:yubikey + + prepare:init: + cmds: + - task: prepare:init:continue + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + prepare:init:continue: + deps: + - :install:requirements:yubikey + - :security:gpg:conf + - task: :security:gpg:conf + vars: + CONFIG_DIR_PATH: + sh: echo "$GNUPGHOME" + - :security:gpg:conf:agent + - task: :security:gpg:conf:agent + vars: + CONFIG_DIR_PATH: + sh: echo "$GNUPGHOME" + + public:export: + todo: This differs from guide + env: + KEYID: '{{.KEYID}}' + cmds: + - mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME" + - gpg --armor --export "$KEYID" > "$GNUPGHOME/gpg-$KEYID-$(date +%F).asc" + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + public:upload: + cmds: + - gpg --send-key "$KEYID" + - gpg --keyserver pgp.mit.edu --send-key "$KEYID" + - gpg --keyserver keys.gnupg.net --send-key "$KEYID" + - gpg --keyserver hkps://keyserver.ubuntu.com:443 --send-key "$KEYID" + status: + - | + [ "$(echo -e "GET http://google.com HTTP/1.0\n\n" | nc google.com 80 > /dev/null 2>&1)" != "0" ] || [ -n "$YUBIKEY_BACKUP" ] + + revocation:export: + env: + KEYID: '{{.KEYID}}' + cmds: + - mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME" + - gpg --output $GNUPGHOME/revoke.asc --gen-revoke "$KEYID" + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + secrets:export: + env: + KEYID: '{{.KEYID}}' + cmds: + - mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME" + - gpg --armor --export-secret-keys --command-fd 0 --pinentry-mode loopback + --passphrase "$MASTER_KEY" --expert "$KEYID" > $GNUPGHOME/mastersub.key + - gpg --armor --export-secret-subkeys --command-fd 0 --pinentry-mode loopback + --passphrase "$MASTER_KEY" --expert "$KEYID" > $GNUPGHOME/sub.key + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + secure:delete: + env: + KEYID: '{{.KEYID}}' + cmds: + - sudo srm -rf "$GNUPGHOME" || sudo rm -rf "$GNUPGHOME" + - gpg --delete-secret-key "$KEYID" + + stub: + summary: | + Run this to re-stub the GPG files to point to the current YubiKey. + cmds: + - gpg-connect-agent "scd serialno" "learn --force" /bye + + usb: + summary: | + # Backup Keys (Including Master) to USB and Encrypt + + This task will partition a USB with two partitions. One partition will + be encrypted and contain your GPG keys (including the master key). The other + partition will not be encrypted and contain your public key. + cmds: + - task: usb:create + - task: usb:mount + - mkdir ~/.gpg-encrypted-storage && cp -rf $GNUPGHOME ~/.gpg-encrypted-storage # TODO /mnt/gpg-encrypted-storage + - mkdir ~/.gpg-public && cp $GNUPGHOME/gpg-$KEYID* ~/.gpg-public # TODO /mnt/gpg-public + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + usb:create: + deps: + - :security:disk:encrypt:create + - :security:disk:unencrypted:create + + usb:mount: + deps: + - :security:disk:encrypt:mount + - :security:disk:unencrypted:mount + + usb:unmount: + deps: + - :security:disk:encrypt:unmount + - :security:disk:unencrypted:unmount + status: + - '[ -n "$YUBIKEY_BACKUP" ]' + + yubikey-agent: + deps: + - :install:software:yubikey-agent + summary: | + # Sets Up Resident SSH Key + + According to [this guide](https://github.com/jamesog/yubikey-ssh), the yubikey-agent + package is a simplest, best way of utilizing a YubiKey for SSH by leveraging + YubiKey's onboard SSH key generator. By using this method, the private key never leaves + the YubiKey which increases security tremendously. + + See: https://github.com/FiloSottile/yubikey-agent + + A new method is detailed here: https://www.yubico.com/blog/github-now-supports-ssh-security-keys/ + It may be worth migrating to that approach. + cmds: + - brew services start yubikey-agent + - yubikey-agent -setup diff --git a/.config/taskfiles/security/Taskfile.yml b/.config/taskfiles/security/Taskfile.yml new file mode 100644 index 00000000..52c10173 --- /dev/null +++ b/.config/taskfiles/security/Taskfile.yml @@ -0,0 +1,220 @@ +--- +version: '3' + +vars: + DOCKERHUB_PROFILE: megabytelabs + TAGS: latest slim + +tasks: + bandit: + deps: + - :install:python:dependencies + desc: Check for Python code security issues + log: + error: '`bandit` reported some security issues that need to be fixed!' + start: Running `bandit -r run.py` + success: No security issues found with `bandit`! + cmds: + - | + if [ -f run.py ]; then + poetry run bandit -r run.py + fi + - | + if [ -d src ]; then + poetry run bandit -r src + fi + + dockle: + deps: + - :install:software:dockle + - :install:software:jq + desc: Analyze and lint the Docker container using Dockle + summary: | + # Analyze and lint Docker containers with Dockle + + Dockle is a Docker image linter that reports security tips and conflicts with best practices. It does + more than what Hadolint does, according to their GitHub page. Instead of linting the Dockerfile, it + lints the actual image. + + **Example usage for scanning standard Docker repository (with `slim` build):** + `task security:dockle` + + **Example usage for any image:** + `task security:dockle -- namespace/image:tag` + + For more information, see [Dockle's GitHub page](https://github.com/goodwithtech/dockle). + log: + error: Dockle found some errors that need to be fixed! + start: Scanning image(s) with Dockle + success: Successfully completed scan with Dockle + cmds: + - | + {{if .CLI_ARGS}} + dockle {{.CLI_ARGS}} + {{else}} + DOCKER_IMAGE="$(jq -r '.slug' .variables.json)" + for TAG in {{.TAGS}}; do + dockle "{{.DOCKERHUB_PROFILE}}/${DOCKER_IMAGE}:${TAG}" + done + {{end}} + + gitleaks: + deps: + - :install:software:gitleaks + desc: Scans repository (including git history) for possible leaked keys + summary: | + # Scan repository with Gitleaks + + Find accidentally committed passwords, private keys, and API keys by scanning the repository with + Gitleaks. + + **Example of scanning current repository:** + `task lint:gitleaks` + + **Example of scanning a public git repository:** + `task lint:gitleaks -- https://github.com/ProfessorManhattan/Windows12` + + For more information, see the [Gitleaks GitHub page](https://github.com/zricethezav/gitleaks). + log: + error: Possible leak detected by `gitleaks` + start: Scanning repository with `gitleaks` + success: Successfully completed `gitleaks` repository scan for secrets + cmds: + - | + {{if .CLI_ARGS}} + gitleaks --repo-url '{{.CLI_ARGS}}' -v + {{else}} + gitleaks -p . -v + {{end}} + + grype: + deps: + - :install:software:grype + desc: Scan container images and file systems for security issues using Grype + summary: | + # Scan containers and file systems using Grype + + Grype is a container and file system security scanner. This task is simply an alias for + the `grype` command that will first ensure it is installed before running the command. + + **Example usage:** + `task grype -- ubuntu:latest --fail-on medium` + log: + error: Grype found some potential issues! + start: Scanning with Grype + success: Scan completed by Grype + cmds: + - grype {{.CLI_ARGS}} + + private-keys: + deps: + - :install:pipx:pre-commit-hooks + desc: Scan for private keys + summary: | + # Scan for private keys + + This task will scan the project for private keys that might not belong where they are. You + can pass this task a single file or let it loop through the project. If you loop through + the project, common folders like 'node_modules/' and 'venv/' will be ignored. + + **Example scanning the whole project:** + `task lint:private-keys` + + **Example scanning single file:** + `task lint:private-keys -- filename.ext` + log: + error: Private keys were found - make sure you do not commit private keys! + start: Scanning for private keys + success: No private keys were found! + cmds: + - | + PATH="$PATH:$HOME/.local/bin" + {{if .CLI_ARGS}} + {{.PYTHON_HANDLE}}detect-private-key {{.CLI_ARGS}} + {{else}} + find . -type d \( {{.IGNORE_FOLDERS}} \) -prune -o -type f -print0 | xargs -0 -r -n1 {{.PYTHON_HANDLE}}detect-private-key + {{end}} + + snyk: + deps: + - :install:npm:snyk + - :install:software:jq + desc: Analyze the Docker container for security vulnerabilities with Snyk (requires login) + summary: | + # Analyze the Docker container with Snyk + + One of the services Snyk provides is the capability to identify Docker container vulnerabilities. These + vulnerabilities can potentially be used by bad actors. Normally, care should be taken to fix + the vulnerabilities Snyk reports whenever possible. To use Snyk, you must be authenticated with their + service. Signing up is free and easy (albeit, there is a limit to the number of scans you can run for free). + All you have to do is run `snyk auth` with the `snyk` NPM package installed. + + **Example usage for scanning standard Docker repository (with `slim` build and Dockerfile in root directory):** + `task security:snyk` + + **Example usage for any image:** + `task security:snyk -- namespace/image:tag` + + For more information, see [Snyk's website](https://snyk.io/what-is-snyk/). + log: + error: '`snyk` reported security errors!' + start: Scanning {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}all tags of the Dockerfile{{end}} with `snyk` + success: Passed `snyk` security test + cmds: + - task: snyk:login + - | + {{if .CLI_ARGS}} + snyk test --docker {{.CLI_ARGS}} + {{else}} + for TAG in {{.TAGS}}; do + DOCKER_IMAGE="$(jq -r '.slug' .variables.json)" + snyk test --docker {{.DOCKERHUB_PROFILE}}/${DOCKER_IMAGE}:${TAG} --file=Dockerfile + done + {{end}} + + snyk:login: + env: + SNYK_API_TOKEN: + sh: snyk config get api + log: + error: Encountered error while logging in with `snyk auth` + start: Logging in with `snyk auth` + success: Successfully authenticated with `snyk auth` + cmds: + - snyk auth + status: + - '[ ! -z "$SNYK_API_TOKEN" ]' + + trivy: + deps: + - :install:software:jq + - :install:software:trivy + desc: Analyze the Docker container for security vulnerabilities with Trivy + summary: | + # Analyze the Docker container with Trivy + + Trivy is a simple and comprehensive vulnerability and misconfiguration scanner for containers + and other artifacts. This task leverages Trivy's ability to report possible vulnerabilities in + the Docker container. + + **Example usage for scanning both the 'latest' and 'slim' build in a standard Docker repository:** + `task security:trivy` + + **Example usage for scanning any image:** + `task security:trivy -- alpine:latest` + + For more information, see [Trivy's website](https://aquasecurity.github.io/trivy/). + log: + error: Trivy detected one or more significant vulnerabilities in the image(s) + start: Analyzing Docker image with Trivy + success: Trivy reported no major vulnerabilities + cmds: + - | + {{if .CLI_ARGS}} + trivy image {{.CLI_ARGS}} + {{else}} + DOCKER_IMAGE="$(jq -r '.slug' .variables.json)" + for TAG in {{.TAGS}}; do + trivy image {{.DOCKERHUB_PROFILE}}/${DOCKER_IMAGE}:${TAG} + done + {{end}} diff --git a/.config/taskfiles/symlink/Taskfile.yml b/.config/taskfiles/symlink/Taskfile.yml new file mode 100644 index 00000000..572dd8de --- /dev/null +++ b/.config/taskfiles/symlink/Taskfile.yml @@ -0,0 +1,53 @@ +--- +version: '3' + +vars: + ROLE_NAME: '{{.GALAXY_NAMESPACE}}.{{.GALAXY_ROLE_NAME}}' + +tasks: + playbook: + desc: Symlink all the roles in the roles/ folder to ~/.ansible/roles + summary: | + # Symlink each role to `~/.ansible/roles/{{.GALAXY_NAMESPACE}}.role_name` + + In the playbook, roles are sometimes referred to by their folder name and in other cases + they are referred to with their namespace prepended (e.g. `{{.GALAXY_NAMESPACE}}.role_name`). + This can cause issues so, in order for everything to work, each folder needs to be symlinked + to `~/.ansible/roles` with the namespace prepended. + log: + error: Encountered an error while symlinking the roles + start: Symlinking all roles in the `roles/` folder to `$HOME/.ansible/roles/` + success: Successfully symlinked all the roles + cmds: + - mkdir -p "$HOME/.ansible/roles" + - | + while read ROLE_PATH; do + ROLE_FOLDER="{{.GALAXY_NAMESPACE}}.$(basename "$ROLE_PATH")" + if [ ! -d "$HOME/.ansible/roles/$ROLE_FOLDER" ]; then + rm -rf "$HOME/.ansible/roles/$ROLE_FOLDER" + ln -sf "$PWD/$ROLE_PATH" "$HOME/.ansible/roles/$ROLE_FOLDER" + fi + done < <(find ./roles -mindepth 2 -maxdepth 2 -type d) + wait + preconditions: + - sh: test -d roles + msg: The roles folder is missing. Is the project set up right? + + role: + desc: Symlink the current role to ~/.ansible/roles/{{.ROLE_NAME}} + summary: | + # Symlink this role to `~/.ansible/roles/{{.ROLE_NAME}}` + + Roles are sometimes referred to by their folder name (which ideally is also the role_name in + the `{{.META_PATH}}` file) and they are sometimes referred to by their Ansible Galaxy name which + has the namespace prepended (e.g. `{{.ROLE_NAME}}`). In order to make sure Ansible + can find the role in both cases, a symlink in `~/.ansible/roles` that points to the folder needs + to be created. + log: + error: There was an error symlinking `{{.ROLE_NAME}}` to `$HOME/.ansible/roles/{{.ROLE_NAME}}` + start: Symlinking `{{.ROLE_NAME}}` to `$HOME/.ansible/roles/{{.ROLE_NAME}}` + success: Symlinked `{{.ROLE_NAME}}` + cmds: + - mkdir -p "$HOME/.ansible/roles" + - rm -rf "$HOME/.ansible/roles/{{.ROLE_NAME}}" + - ln -sf "$PWD" "$HOME/.ansible/roles/{{.ROLE_NAME}}" diff --git a/.config/taskfiles/ui/Taskfile.yml b/.config/taskfiles/ui/Taskfile.yml new file mode 100644 index 00000000..66300ab4 --- /dev/null +++ b/.config/taskfiles/ui/Taskfile.yml @@ -0,0 +1,7 @@ +--- +version: '3' + +tasks: + launch: + cmds: + - 'true' diff --git a/.config/taskfiles/update/Taskfile.yml b/.config/taskfiles/update/Taskfile.yml new file mode 100644 index 00000000..ac00363d --- /dev/null +++ b/.config/taskfiles/update/Taskfile.yml @@ -0,0 +1,11 @@ +--- +version: '3' + +tasks: + finish: + cmds: + - task: :common:start:update + + init: + cmds: + - task: :common:start:init diff --git a/.config/taskfiles/upstream/Taskfile-common.yml b/.config/taskfiles/upstream/Taskfile-common.yml new file mode 100644 index 00000000..4a3e4484 --- /dev/null +++ b/.config/taskfiles/upstream/Taskfile-common.yml @@ -0,0 +1,289 @@ +--- +version: '3' + +vars: + COMMON_FOLDER: .common + DOCS_URL: https://gitlab.com/megabyte-labs/documentation/{{.REPOSITORY_SUBTYPE}} + SHARED_COMMON_URL: https://gitlab.com/megabyte-labs/common/shared.git + SHARED_FOLDER: .shared + +tasks: + clean: + log: + error: Error running `rm -rf {{.SHARED_FOLDER}}` + start: Running `rm -rf {{.SHARED_FOLDER}}` + success: Removed `{{.SHARED_FOLDER}}` + cmds: + - rm -rf {{.SHARED_FOLDER}} + - rm -f .config/docs/Taskfile.yml + + clone: + cmds: + - task: clone:common + - task: clone:docs + + clone:common: + log: + error: Error cloning Shared Common to `{{.SHARED_FOLDER}}` + start: Cloning Shared Common to `{{.SHARED_FOLDER}}` + success: Cloned Shared Common to `{{.SHARED_FOLDER}}` + cmds: + - rm -rf ./{{.SHARED_FOLDER}} + - git clone --depth=1 {{.SHARED_COMMON_URL}} ./{{.SHARED_FOLDER}} + - rm -rf ./{{.SHARED_FOLDER}}/.git + + clone:docs: + log: + error: Error cloning / cleaning up {{.REPOSITORY_SUBTYPE}} docs repo + start: Cloning {{.REPOSITORY_SUBTYPE}} docs repo and pruning unnecessary files + success: Cloned sub-type documentation repository and pruned unnecessary files + cmds: + - mkdir -p ./{{.SHARED_FOLDER}}/common/.config + - rm -rf ./{{.SHARED_FOLDER}}/.config/docs + - git clone --depth=1 {{.DOCS_URL}} ./{{.SHARED_FOLDER}}/common/.config/docs + - | + cd ./{{.SHARED_FOLDER}}/common/.config/docs + rm -rf .git .config .github .gitlab .vscode .editorconfig .gitignore .gitlab-ci.yml + rm -rf LICENSE Taskfile.yml package-lock.json package.json poetry.lock pyproject.toml + rm -rf docs common.json pnpm-lock.yaml start.sh logo.png yarn.lock local/yarn.lock local/package-lock.json + rm -rf .devcontainer local/slim + + combine: + log: + error: Error combining generated files + start: Combining generated files + success: Combined generated files + cmds: + - mkdir -p _generated_ + - mv .common* _generated_ + - | + mv _generated_/.common _generated_/common + for FOLDER in _generated_/.common-*; do + TARGET="$(echo $FOLDER | sed 's/^_generated_\/.common-//')" + mv "$FOLDER" "_generated_/$TARGET" + done + + copy: + deps: + - :install:software:yq + log: + error: Error removing `.config/` + start: Removing `.config/` + success: Removed `.config/` + cmds: + - mv .gitlab-ci.yml old.gitlab-ci.yml + - task: copy:begin + - | + yq eval-all -i 'select(fileIndex == 0) * select(fileIndex == 1)' old.gitlab-ci.yml .gitlab-ci.yml + mv old.gitlab-ci.yml .gitlab-ci.yml + + copy:begin: + deps: + - copy:common + - copy:project + - copy:project:subtype + + copy:common: + deps: + - :install:software:coreutils + log: + error: Error copying common files + start: Copying common files + success: Copied common files + cmds: + - | + {{if (eq OS "darwin")}}PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH"{{end}} + cp -rT ./{{.SHARED_FOLDER}}/common/ . + - task: :common:husky:permissions + + copy:project: + deps: + - :install:software:coreutils + log: + error: Error copying project files + start: Copying project files + success: Copied project files + cmds: + - | + {{if (eq OS "darwin")}}PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH"{{end}} + cp -rT ./{{.SHARED_FOLDER}}/common ./{{.COMMON_FOLDER}} + if [ ! -d project ]; then mkdir project; fi + cp -rT project ./{{.COMMON_FOLDER}} + + copy:project:subtype: + deps: + - :install:software:coreutils + log: + error: Error copying project sub-type files + start: Copying project sub-type files + success: Copied project sub-type files + cmds: + - | + {{if (eq OS "darwin")}}PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH"{{end}} + for FOLDER in project-*/; do + SUBTYPE="$(echo "$FOLDER" | sed 's/project-\(.*\)\//\1/')" + cp -rT "$FOLDER" "./{{.COMMON_FOLDER}}-$SUBTYPE" + done + + merge: + deps: + - merge:package + - merge:variables + + merge:package: + cmds: + - task: merge:package:project + - task: merge:package:project:subtype + + merge:package:project: + deps: + - :install:software:jq + log: + error: Error merging `package.json.liquid` templates + start: Merging `package.json.liquid` templates + success: Merged `package.json.liquid` templates + cmds: + - | + function ensureKeywords() { + if [ ! -f "$1" ]; then echo "{}" > "$1"; fi + KEYWORDS="$(jq -r '.keywords' "$1")" + if [[ "$KEYWORDS" == 'null' ]] || [[ "$KEYWORDS" == '' ]]; then + TMP="$(mktemp)" + jq -r '.keywords = [] | .' "$1" > "$TMP" + mv "$TMP" "$1" + fi + } + + function mergePackages() { + # Ensure array exists in both JSON files + ensureKeywords "$1" + ensureKeywords "$2" + + # Merge the files + TMP="$(mktemp)" + jq --arg keywords "$(jq '.keywords[]' "$1" "$2" | jq -s '. | unique')" -s -S \ + '.[0] * .[1] | .keywords = ($keywords | fromjson) | .' "$1" "$2" > "$TMP" + mv "$TMP" "$3" + } + + mergePackages "./{{.SHARED_FOLDER}}/common/package.json.liquid" "./project/package.json.liquid" "./{{.COMMON_FOLDER}}/package.json.liquid" + + merge:package:project:subtype: + deps: + - :install:software:jq + log: + error: Error merging `package.json.liquid` templates for sub-type + start: Merging `package.json.liquid` templates for sub-type + success: Merged `package.json.liquid` templates for sub-type + cmds: + - | + function ensureKeywords() { + if [ ! -f "$1" ]; then echo "{}" > "$1"; fi + KEYWORDS="$(jq -r '.keywords' "$1")" + if [[ "$KEYWORDS" == 'null' ]] || [[ "$KEYWORDS" == '' ]]; then + TMP="$(mktemp)" + jq -r '.keywords = [] | .' "$1" > "$TMP" + mv "$TMP" "$1" + fi + } + + function mergePackages() { + # Ensure array exists in both JSON files + ensureKeywords "$1" + ensureKeywords "$2" + + # Merge the files + TMP="$(mktemp)" + jq --arg keywords "$(jq '.keywords[]' "$1" "$2" | jq -s '. | unique')" -s -S '.[0] * .[1] | .keywords = ($keywords | fromjson) | .' "$1" "$2" > "$TMP" + mv "$TMP" "$3" + } + + for FOLDER in project-*/; do + SUBTYPE="$(echo "$FOLDER" | sed 's/project-\(.*\)\//\1/')" + mergePackages "./{{.COMMON_FOLDER}}/package.json.liquid" "project-$SUBTYPE/package.json.liquid" "./{{.COMMON_FOLDER}}-$SUBTYPE/package.json.liquid" & + done + wait + + merge:variables: + cmds: + - task: merge:variables:project + - task: merge:variables:subtype + + merge:variables:project: + deps: + - :install:software:jq + log: + error: Error merging variables files + start: Merging variables.json files + success: Merged variables.json files + cmds: + - | + if [ -f "project/.config/variables.json" ]; then + jq -s -S '.[0] * .[1]' "./{{.COMMON_FOLDER}}/.config/docs/variables.json" \ + "project/.config/variables.json" > "./{{.COMMON_FOLDER}}/.config/variables.json" + else + cp ./{{.COMMON_FOLDER}}/.config/docs/variables.json ./{{.COMMON_FOLDER}}/.config/variables.json + fi + + merge:variables:subtype: + deps: + - :install:software:jq + log: + error: Error merging variables files for sub-type + start: Merging variables.json files for sub-type + success: Merged variables.json files for sub-type + cmds: + - | + GROUP="$(jq -r '.blueprint.group' package.json)" + if [[ "$GROUP" == 'null' ]]; then + GROUP="$(yq e '.vars.REPOSITORY_TYPE' Taskfile.yml)" + if [[ "$GROUP" == 'null' ]]; then + echo "ERROR '.blueprint.group' did not exist in the package.json and the fallback '.vars.REPOSITORY_TYPE' did not exist in the Taskfile.yml" + fi + fi + function handleSubtype() { + FOLDER="$1" + SUBTYPE="$(echo "$FOLDER" | sed 's/project-\(.*\)\//\1/')" + mkdir -p "./{{.COMMON_FOLDER}}-$SUBTYPE/.config" + TMP="$(mktemp)" + jq --arg group "$GROUP" --arg subtype "$SUBTYPE" '.subgroup = $subtype | .group = $group | .' ./{{.COMMON_FOLDER}}/.config/variables.json > "$TMP" + if [ -f "project-$SUBTYPE/.config/variables.json" ]; then + jq -s -S '.[0] * .[1]' "$TMP" "project-$SUBTYPE/.config/variables.json" > "./{{.COMMON_FOLDER}}-$SUBTYPE/.config/variables.json" + else + mv "$TMP" "./{{.COMMON_FOLDER}}-$SUBTYPE/.config/variables.json" + fi + } + for FOLDER in project-*/; do + handleSubtype "$FOLDER" & + done + wait + + reset: + log: + error: Error removing `.common*` and `_generated_` + start: Removing `.common*` and `_generated_` + success: Removed `.common*` and `_generated_` + cmds: + - rm -rf .common* + - rm -rf _generated_ + + template: + deps: + - :install:modules:local + - :install:npm:liquidjs + cmds: + - task: :upstream:variables + vars: + INPUT_FILE: ./{{.COMMON_FOLDER}}/.config/docs/variables.json + OUTPUT_FILE: ./.variables.json + - task: template:files + + template:files: + deps: + - template:files:liquidjs + + template:files:liquidjs: + cmds: + - task: :upstream:template:liquidjs + vars: + ADDITIONAL_IGNORE_FOLDERS: -path './{{.COMMON_FOLDER}}*' -o -path './project*' -o -path './deprecated*' -o diff --git a/.config/taskfiles/upstream/Taskfile-commondocs.yml b/.config/taskfiles/upstream/Taskfile-commondocs.yml new file mode 100644 index 00000000..91bff270 --- /dev/null +++ b/.config/taskfiles/upstream/Taskfile-commondocs.yml @@ -0,0 +1,52 @@ +--- +version: '3' + +vars: + COMMON_FILES_URL: https://gitlab.com/megabyte-labs/common/shared.git + COMMON_FOLDER: .common + +tasks: + clean: + log: + error: Error while running `rm -rf {{.COMMON_FOLDER}}` + start: Running `rm -rf {{.COMMON_FOLDER}}` + success: Successfully ran `rm -rf {{.COMMON_FOLDER}}` + cmds: + - rm -rf {{.COMMON_FOLDER}} + + clone: + log: + error: Error in cloning logic for {{.COMMON_FILES_URL}} + start: Cloning {{.COMMON_FILES_URL}} + success: Successfully cloned {{.COMMON_FILES_URL}} + cmds: + - rm -rf {{.COMMON_FOLDER}} + - git clone --depth=1 {{.COMMON_FILES_URL}} {{.COMMON_FOLDER}} + - rm -rf {{.COMMON_FOLDER}}/.git + + copy: + deps: + - :install:software:coreutils + - :install:software:yq + log: + error: Error copying common files + start: Copying common files + success: Copied common files + cmds: + - mv .gitlab-ci.yml old.gitlab-ci.yml + - | + {{if (eq OS "darwin")}}PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH"{{end}} + cp -rT ./{{.COMMON_FOLDER}}/common/ . + - | + yq eval-all -i 'select(fileIndex == 0) * select(fileIndex == 1)' old.gitlab-ci.yml .gitlab-ci.yml + mv old.gitlab-ci.yml .gitlab-ci.yml + - task: :common:husky:permissions + - mv {{.COMMON_FOLDER}}/.gitlab-ci.yml .gitlab-ci.yml + + template: + cmds: + - task: :upstream:variables + vars: + INPUT_FILE: common.json + OUTPUT_FILE: ./.variables.json + - task: :upstream:template diff --git a/.config/taskfiles/upstream/Taskfile-docs.yml b/.config/taskfiles/upstream/Taskfile-docs.yml new file mode 100644 index 00000000..a0cf37ed --- /dev/null +++ b/.config/taskfiles/upstream/Taskfile-docs.yml @@ -0,0 +1,118 @@ +--- +version: '3' + +vars: + COMMON_FILES_URL: https://gitlab.com/megabyte-labs/common/shared.git + COMMON_FOLDER: .common + SHARED_DOCS_URL: https://gitlab.com/megabyte-labs/documentation/shared.git + SHARED_FOLDER: .shared + +tasks: + clean: + deps: + - clean:common + - clean:shared + + clean:common: + log: + error: Error running `rm -rf {{.COMMON_FOLDER}}` + start: Running `rm -rf {{.COMMON_FOLDER}}` + success: Successfully ran `rm -rf {{.COMMON_FOLDER}}` + cmds: + - rm -rf {{.COMMON_FOLDER}} + + clean:shared: + log: + error: Error running `rm -rf {{.SHARED_FOLDER}}` + start: Running `rm -rf {{.SHARED_FOLDER}}` + success: Successfully ran `rm -rf {{.SHARED_FOLDER}}` + cmds: + - rm -rf {{.SHARED_FOLDER}} + + clone: + deps: + - clone:common + - clone:shared + + clone:common: + log: + error: Error encountered in clone logic for {{.COMMON_FILES_URL}} + start: Cloning {{.COMMON_FILES_URL}} + success: Successfully cloned {{.COMMON_FILES_URL}} + cmds: + - rm -rf {{.COMMON_FOLDER}} + - git clone --depth=1 {{.COMMON_FILES_URL}} {{.COMMON_FOLDER}} + - rm -rf {{.COMMON_FOLDER}}/.git + + clone:shared: + log: + error: Error encountered in clone logic for {{.SHARED_DOCS_URL}} + start: Cloning {{.SHARED_DOCS_URL}} + success: Successfully cloned {{.SHARED_DOCS_URL}} + cmds: + - rm -rf {{.SHARED_FOLDER}} + - git clone --depth=1 {{.SHARED_DOCS_URL}} {{.SHARED_FOLDER}} + - rm -rf {{.SHARED_FOLDER}}/.git + + copy: + deps: + - :install:software:yq + log: + error: Error encountered in copy sequence + start: Beginning copy sequence + success: Completed copy sequence + cmds: + - mv .gitlab-ci.yml old.gitlab-ci.yml + - task: copy:before + - | + yq eval-all -i 'select(fileIndex == 0) * select(fileIndex == 1)' old.gitlab-ci.yml .gitlab-ci.yml + mv old.gitlab-ci.yml .gitlab-ci.yml + - cp {{.SHARED_FOLDER}}/.gitlab-ci.yml .gitlab-ci.yml + + copy:before: + deps: + - copy:common + - copy:shared + + copy:common: + deps: + - :install:software:coreutils + log: + error: Error copying common files + start: Copying common files + success: Copied common files + cmds: + - | + {{if (eq OS "darwin")}}PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH"{{end}} + cp -rT ./{{.COMMON_FOLDER}}/common/ . + - task: :common:husky:permissions + + copy:shared: + log: + error: Error encountered while copying shared files + start: Copying shared files + success: Copied shared files + cmds: + - rm -rf common + - mv {{.SHARED_FOLDER}}/common common + - mv {{.SHARED_FOLDER}}/README.md README.md + - mkdir -p docs + - mv {{.SHARED_FOLDER}}/docs/CONTRIBUTING.md docs/CONTRIBUTING.md + + merge: + deps: + - :install:software:jq + log: + error: Error combining common.json files + start: Combining common.json files + success: Combined common.json files + cmds: + - jq -s -S '.[0] * .[1]' {{.SHARED_FOLDER}}/common.json common.json > variables.json + + template: + cmds: + - task: :upstream:variables + vars: + INPUT_FILE: variables.json + OUTPUT_FILE: ./.variables.json + - task: :upstream:template diff --git a/.config/taskfiles/upstream/Taskfile-project.yml b/.config/taskfiles/upstream/Taskfile-project.yml new file mode 100644 index 00000000..347aaf21 --- /dev/null +++ b/.config/taskfiles/upstream/Taskfile-project.yml @@ -0,0 +1,141 @@ +--- +version: '3' + +vars: + PROJECT_COMMON_URL: https://gitlab.com/megabyte-labs/common/{{.REPOSITORY_TYPE}}.git + SHARED_FOLDER: .shared + +tasks: + boilerplate: + deps: + - :install:software:coreutils + - :install:software:jq + log: + error: There was an error in the boilerplate logic + start: Checking if `.boilerplate/` code should be added + success: Boilerplate logic completed successfully + cmds: + - | + {{if (eq OS "darwin")}}PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH"{{end}} + if [[ "$(jq -r '.blueprint.boilerplate' package.json)" == 'true' ]]; then + for ENTITY in .boilerplate/*; do + TARGET="$(echo $ENTITY | sed 's/^.boilerplate\///')" + rm -rf "$TARGET" + done + fi + GREP="" + for ENTITY in .boilerplate/*; do + TARGET="$(echo $ENTITY | sed 's/^.boilerplate\///')" + GREP="$TARGET"\|"$GREP" + done + GREP="$(echo $GREP | sed 's/\\|$//')" + if ! ls -la | grep "$GREP" &> /dev/null; then + .config/log info "Adding boilerplate code since there are no overlapping files" + cp -rT .boilerplate . + .config/log success "Scaffolded the project with boilerplate code!" + fi + - rm -rf .boilerplate + + clean: + log: + error: Failed to run `rm -rf {{.SHARED_FOLDER}}` + start: Running `rm -rf {{.SHARED_FOLDER}}` + success: Successfully ran `rm -rf {{.SHARED_FOLDER}}` + cmds: + - rm -rf {{.SHARED_FOLDER}} + + clone: + log: + error: Error cloning {{.PROJECT_COMMON_URL}} + start: Cloning {{.PROJECT_COMMON_URL}} + success: Finished cloning {{.PROJECT_COMMON_URL}} + cmds: + - rm -rf {{.SHARED_FOLDER}} + - git clone --depth=1 {{.PROJECT_COMMON_URL}} {{.SHARED_FOLDER}} + - rm -rf ./{{.SHARED_FOLDER}}/.git + + copy: + deps: + - :install:software:coreutils + - :install:software:yq + log: + error: Error copying pre-generated files + start: Copying pre-generated files + success: Copied pre-generated files + cmds: + - | + if [ -f .gitlab-ci.yml ]; then + mv .gitlab-ci.yml old.gitlab-ci.yml + fi + - | + {{if (eq OS "darwin")}}PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH"{{end}} + cp -rT {{.SHARED_FOLDER}}/_generated_/common . + cp -rT {{.SHARED_FOLDER}}/_generated_/{{.REPOSITORY_SUBTYPE}} . + - | + if [ -f old.gitlab-ci.yml ]; then + yq eval-all -i 'select(fileIndex == 0) * select(fileIndex == 1)' old.gitlab-ci.yml .gitlab-ci.yml + mv old.gitlab-ci.yml .gitlab-ci.yml + fi + - task: :common:husky:permissions + - task: save:common:keywords + + merge:package:overrides: + deps: + - :install:software:jq + summary: | + # Merge data from package.json's '.blueprint.jsonOverrides' key + + If you wanted to ensure that the project's package.json file always has its 'xyz' key + equal to 'abc' then you would add the following to the package.json file: + + ```json + { + ... + "blueprint": { + "jsonOverrides": { + "package.json": { + "xyz": "abc" + } + } + } + } + ``` + + In the above example, you could also set the value of package.json equal to the path of some other + JSON file in the project that you would like to override. + + This method is here in case there are any key/values that you would like to freeze. + log: + error: Error processing `.blueprint.jsonOverrides` in `package.json` + start: Processing `.blueprint.jsonOverrides` in `package.json` + success: Finished processing `.blueprint.jsonOverrides` in `package.json` + cmds: + - | + if [ "$(jq -r '.blueprint.jsonOverrides' package.json)" != 'null' ]; then + jq -r '.blueprint.jsonOverrides | keys[]' package.json | while read FILE_PATH; do + VALUE="$(jq --arg filepath "$FILE_PATH" '.blueprint.jsonOverrides[$filepath]' "$FILE_PATH")" + TMP="$(mktemp)" + jq --arg value "$VALUE" -s '.[] + ($value | fromjson)' "$FILE_PATH" > "$TMP" + mv "$TMP" "$FILE_PATH" + done + fi + + save:common:keywords: + deps: + - :install:software:jq + log: + error: Failed to save common keywords + start: Saving common keywords + success: Saved common keywords + cmds: + - | + KEYWORDS="$(jq -r '.keywords' package.json.liquid)" + jq -n --arg keywords "$KEYWORDS" '.keywords = ($keywords | fromjson | unique)' > .config/common-keywords.json.liquid + + template: + cmds: + - task: :upstream:variables + vars: + INPUT_FILE: .config/variables.json + OUTPUT_FILE: .variables.json + - task: :upstream:template diff --git a/.config/taskfiles/upstream/Taskfile-shared.yml b/.config/taskfiles/upstream/Taskfile-shared.yml new file mode 100644 index 00000000..661ef186 --- /dev/null +++ b/.config/taskfiles/upstream/Taskfile-shared.yml @@ -0,0 +1,44 @@ +--- +version: '3' + +vars: + SHARED_COMMON_FOLDER: common + VARIABLES_URL: https://gitlab.com/megabyte-labs/documentation/shared/-/raw/master/common.json + +tasks: + copy: + log: + error: Finished copying common files + start: Copying common files + success: Copied common files + cmds: + - mv .gitlab-ci.yml .gitlab-ci.yml.bak + - cd ./{{.SHARED_COMMON_FOLDER}} && cp -rf . .. && cd .. + - mv .gitlab-ci.yml.bak .gitlab-ci.yml + - task: :common:husky:permissions + + template: + cmds: + - task: template:files + + template:files: + deps: + - template:files:liquidjs + + template:files:liquidjs: + cmds: + - task: :upstream:template:liquidjs + vars: + ADDITIONAL_IGNORE_FOLDERS: -path './{{.SHARED_COMMON_FOLDER}}*' -o -path './deprecated*' -o + + variables: + log: + error: Encountered error in `.variables.json` generation sequence + start: Beginning `.variables.json` generation sequence + success: Completed `.variables.json` generation sequence + cmds: + - curl -s {{.VARIABLES_URL}} > .variables.json + - task: :upstream:variables + vars: + INPUT_FILE: .variables.json + OUTPUT_FILE: .variables.json diff --git a/.config/taskfiles/upstream/Taskfile.yml b/.config/taskfiles/upstream/Taskfile.yml new file mode 100644 index 00000000..0334d295 --- /dev/null +++ b/.config/taskfiles/upstream/Taskfile.yml @@ -0,0 +1,330 @@ +--- +version: '3' + +tasks: + common: + run: once + log: + error: Error updating common repository + start: Updating common repository + success: Finished updating common repository + cmds: + - task: :upstream:common:reset + - task: :upstream:common:clone + - task: :upstream:common:copy + - task: :upstream:common:merge + - task: :upstream:common:clean + - task: :upstream:common:template + - task: :upstream:common:combine + - task: :vscode:generate + - task: :install:modules:local:lockfiles + - task: :fix:eslint + - task: :git:commit:automated + - task: :git:push:all + status: + - '[ "{{.DOCKER_BUILDING}}" == "true" ]' + + commondocs: + run: once + log: + error: Error updating common documents repository + start: Updating common documents repository + success: Finished updating common documents repository + cmds: + - task: :upstream:commondocs:clone + - task: :upstream:commondocs:copy + - task: :upstream:commondocs:clean + - task: :upstream:commondocs:template + - task: :vscode:generate + - task: :install:modules:local:lockfiles + - task: :fix:eslint + - task: :git:commit:automated + - task: :git:push:all + status: + - '[ "{{.DOCKER_BUILDING}}" == "true" ]' + + docs: + run: once + todo: Remove the git:commit:automated tasks + log: + error: Error updating document repository + start: Updating document repository + success: Finished updating document repository + cmds: + - task: :upstream:docs:clone + - task: :upstream:docs:copy + - task: :upstream:docs:merge + - task: :upstream:docs:clean + - task: :upstream:docs:template + - task: :vscode:generate + - task: :install:modules:local:lockfiles + - task: :fix:eslint + - task: :git:commit:automated + - task: :git:push:all + status: + - '[ "{{.DOCKER_BUILDING}}" == "true" ]' + + project: + run: once + log: + error: Error updating project repository + start: Running project update logic + success: Finished running project update logic + cmds: + - task: pull + - task: :upstream:project:clone + - task: :upstream:project:copy + - task: :upstream:project:clean + - task: :upstream:project:template + - task: :upstream:project:boilerplate + - task: :vscode:generate + - task: :upstream:project:merge:package:overrides + - task: :common:update:update + - task: :install:modules:local:lockfiles + - task: :fix:eslint + - task: :git:commit:automated + - task: :git:push:all + - task: :{{if eq .REPOSITORY_SUBTYPE "role"}}ansible:galaxy:import{{else}}donothing{{end}} + status: + - '[ "{{.DOCKER_BUILDING}}" == "true" ]' + + project:scaffold-only: + run: once + log: + error: Error scaffolding project + start: Scaffolding project + success: Finished scaffolding project + cmds: + - task: pull + - task: :upstream:project:clone + - task: :upstream:project:copy + - task: :upstream:project:clean + - task: :upstream:project:template + - task: :upstream:project:boilerplate + - task: :vscode:generate + - task: :upstream:project:merge:package:overrides + - SKIP_UPLOADING=true task common:update:update + - task: :install:modules:local:lockfiles + - task: :fix:eslint + status: + - '[ "{{.DOCKER_BUILDING}}" == "true" ]' + + pull: + deps: + - :install:software:git + run: once + log: + error: Error executing initial `git pull` logic + start: Determining whether or not to `git pull` + success: Finished initial `git pull` logic + cmds: + - | + if ([ -f "$HOME/.ssh/known_hosts" ] && [ ! -n "$(grep "^gitlab.com " "$HOME/.ssh/known_hosts")" ]) || [[ "${container:=}" == "docker" ]]; then + if [ -z "$GITLAB_CI" ]; then + if [ -d "$HOME/.ssh" ]; then + ssh-keyscan gitlab.com >> "$HOME/.ssh/known_hosts" 2> /dev/null + fi + git config url."https://gitlab.com/".insteadOf git@gitlab.com: + fi + fi + - cmd: if git branch -r | grep origin > /dev/null; then git pull --ff-only; fi + ignore_error: true + - cmd: git config --unset url."https://gitlab.com/".insteadOf + ignore_error: true + status: + - '[ ! -d .git ]' + + shared: + run: once + log: + error: Error running shared repository update logic + start: Running shared repository update logic + success: Finished running shared repository update logic + cmds: + - task: pull + - task: :upstream:shared:copy + - task: :upstream:shared:variables + - task: :upstream:shared:template + - task: :vscode:generate + - task: :install:modules:local:lockfiles + - task: :fix:eslint + - task: :git:commit:automated + - task: :git:push:all + status: + - '[ "{{.DOCKER_BUILDING}}" == "true" ]' + + template: + run: once + cmds: + - task: template:liquidjs + status: + - '[ "{{.DOCKER_BUILDING}}" == "true" ]' + + template:liquidjs: + deps: + - :install:modules:local + - :install:npm:liquidjs + - :install:software:jq + - template:liquidjs:python:deps + todo: Update project when Taskfile.yml gets modified + vars: + PYTHON_BREW_NOTICE: In order for this file to be generated, the customPyPiPackageName needs to be specified in the + blueprint section of package.json and the PyPi package needs to be installed on the system. + env: + TMP: + sh: mktemp + run: once + log: + error: Error encountered while generating files from `liquid` template files + start: Generating files from `liquid` template files + success: Successfully generated files from templates + cmds: + - mkdir -p local + - | + if [[ '{{.REPOSITORY_TYPE}}' == 'python' ]] && [ -f '.config/brew/python.rb.liquid' ]; then + BINARY_NAME="$(jq -r '.customPyPiPackageName' .variables.json)" + if [[ "$BINARY_NAME" == 'null' ]] || ! type "$BINARY_NAME" &> /dev/null; then + echo "{{.PYTHON_BREW_NOTICE}}" > .config/brew/python.rb.liquid + fi + fi + - cp package.json package.json.bak + - | + PATH="$PATH:$HOME/.local/bin" + function handlebars() { + FILE="$1" + TMP="$(mktemp)" + .config/log info 'Generating `'"${FILE//.liquid}"'` from `'"$FILE"'`' + hbs --data .variables.json --helper ./.config/hbs.cjs "$FILE" --stdout > "$TMP" + mv "$TMP" "${FILE//.liquid}" + rm "$FILE" + } + while read FILE; do + cp "$FILE" "${FILE//.hbs}" + done < <(find . -type f -not \( {{.ADDITIONAL_IGNORE_FOLDERS}} {{.IGNORE_FOLDERS}} \) -prune -name '*.liquid.hbs') + while read FILE; do + handlebars "$FILE" + done < <(find . -type f -not \( {{.ADDITIONAL_IGNORE_FOLDERS}} {{.IGNORE_FOLDERS}} \) -prune -name '*.liquid') + while read CONFIG_FILE; do + handlebars "$CONFIG_FILE" + done < <(find .config -type f -name '*.liquid') + - | + function ensureKeywords() { + if [ ! -f "$1" ]; then echo "{}" > "$1"; fi + KEYWORDS="$(jq -r '.keywords' "$1")" + if [[ "$KEYWORDS" == 'null' ]] || [[ "$KEYWORDS" == '' ]]; then + TMP="$(mktemp)" + jq -r '.keywords = [] | .' "$1" > "$TMP" + mv "$TMP" "$1" + fi + } + ensureKeywords package.json.bak + - jq -s -S --arg blueprint "$(jq -r '.blueprint' package.json.bak)" + --arg keywords "$(jq '.keywords[]' package.json.bak package.json | jq -s '. | unique')" + --argjson private "$(jq -r '.private' package.json.bak | sed 's/^null$/true/')" + --arg version "$(jq -r '.version' package.json.bak | sed 's/^null$/0.0.1/')" + '.[0] * .[1] | .keywords = ($keywords | fromjson) | .blueprint = ($blueprint | fromjson) | .private = $private | .version = $version | .' + package.json.bak package.json > "$TMP" + - mv "$TMP" package.json + - rm package.json.bak + - task: template:pyproject + + template:liquidjs:python:deps: + cmds: + - task: :install:pipx:poet + status: + - '[ "{{.REPOSITORY_TYPE}}" != "python" ]' + + template:pyproject: + run: once + cmds: + - task: :install:gh:fusion + - task: template:pyproject:config + - task: template:pyproject:local + - task: template:pyproject:fix + status: + - '[ ! -f pyproject.toml ] || [ "{{.DOCKER_BUILDING}}" == "true" ]' + + template:pyproject:config: + log: + error: Encountered error while merging `.config/pyproject.partial.toml` into the regenerated `pyproject.toml` + start: Merging `.config/pyproject.partial.toml` into `pyproject.toml` + success: The `pyproject.toml` file was successfully generated from `.config/pyproject.partial.toml` + cmds: + - fusion toml pyproject.toml .config/pyproject.partial.toml -o pyproject.next.toml + - mv pyproject.next.toml pyproject.toml + status: + - '[ ! -f .config/pyproject.partial.toml ] || [ ! -f pyproject.toml ]' + + template:pyproject:fix: + cmds: + - task: :fix:toml + vars: + CLI_ARGS: pyproject.toml + - task: :fix:prettier + vars: + CLI_ARGS: pyproject.toml + status: + - '[ -n "$NO_INSTALL_HOMEBREW" ]' + + template:pyproject:local: + deps: + - :install:gh:fusion + vars: + PYPROJECT_NOTICE: |- + ##### IMPORTANT DEVELOPER NOTICE ##### + # This `pyproject.toml` file is automatically generated. + # If you want to make changes that persist then you should + # add your changes to `./docs/pyproject.partial.toml`. + # Be sure to include the section's title with your custom + # entries. You can test the generation of this file by running + # `bash start.sh && task init`. + log: + error: Encountered error while merging `local/pyproject.partial.toml` into the regenerated `pyproject.toml` + start: Merging `local/pyproject.partial.toml` into `pyproject.toml` + success: The `pyproject.toml` file was successfully generated from `local/pyproject.partial.toml` + cmds: + - fusion toml pyproject.toml local/pyproject.partial.toml -o pyproject.next.toml + - mv pyproject.next.toml pyproject.toml + - echo '' >> pyproject.toml + - echo '{{.PYPROJECT_NOTICE}}' >> pyproject.toml + - echo '' >> pyproject.toml + status: + - '[ ! -f local/pyproject.partial.toml ] || [ ! -f pyproject.toml ]' + + variables: + deps: + - :install:software:jq + env: + FILE_INPUT: + sh: if [ -f '{{.INPUT_FILE}}' ]; then echo {{.INPUT_FILE}}; else echo ".variables.json"; fi + TMP: + sh: mktemp + run: once + log: + error: Encountered error while injecting `{{.OUTPUT_FILE}}` with variables + start: Injecting `{{.OUTPUT_FILE}}` with variables + success: Successfully injected `{{.OUTPUT_FILE}}` with variables + cmds: + - | + if [[ "$(jq '.keywords' package.json)" == 'null' ]]; then + TMP_KEYWORDS="$(mktemp)" + jq '.keywords = []' package.json > "$TMP_KEYWORDS" + mv "$TMP_KEYWORDS" package.json + fi + - jq --arg blueprint "$(jq -r '.blueprint' package.json | sed 's/^null$/{}/')" + --arg version "$(jq -r '.version' package.json | sed 's/^null$/0.0.1/')" + --arg poetry "$(jq -r '.keywords | join("\", \"")' package.json | sed 's/$/"/' | sed 's/^/"/')" + --arg encoded "$(jq -r '.blueprint.repository.gitlab' package.json | sed 's/https:\/\/gitlab.com\///' | sed 's/\//%252F/g')" + -S '. = (. * ($blueprint | fromjson)) | .version = $version | .poetryKeywords = $poetry | .gitlab_encoded_path = $encoded' + "$FILE_INPUT" > "$TMP" + - mv "$TMP" {{.OUTPUT_FILE}} + - | + if [ '{{.REPOSITORY_TYPE}}' == 'ansible' ] && [ -f meta/main.yml ]; then + task ansible:update:variables:descriptions + fi + - | + if [ '{{.REPOSITORY_TYPE}}' == 'packer' ]; then + task packer:update:variables + fi + status: + - '[ "{{.DOCKER_BUILDING}}" == "true" ]' diff --git a/.config/taskfiles/vagrant/Taskfile-qubes.yml b/.config/taskfiles/vagrant/Taskfile-qubes.yml new file mode 100644 index 00000000..783c7f70 --- /dev/null +++ b/.config/taskfiles/vagrant/Taskfile-qubes.yml @@ -0,0 +1,21 @@ +--- +version: '3' + +tasks: + convert:hvm: + summary: | + # Converts VirtualBox Image to Qubes HVM + + Qubes documentation details the process of converting a VirtualBox image + to an HVM in [this link](https://www.qubes-os.org/doc/standalones-and-hvms/#converting-virtualbox-vms-to-qubes-hvms). + + This task assumes that the VirtualBox image is already built and hosted on + VagrantUp or a similar registry. It downloads the `.box` file, extracts the required disk + image, and then converts it to an HVM to integrate it with QubesOS. + + A sample `.box` file can be found [here](https://app.vagrantup.com/Megabyte/boxes/Ubuntu-Desktop/versions/21.10/providers/virtualbox.box). + + Note: It is also possible to extract the VirtualBox files that QubesOS needs during build time with Packer (the .ova file). However, + it would better to use the `.box` file available on VagrantUp, if possible. + [This guide](https://gist.github.com/aondio/66a79be10982f051116bc18f1a5d07dc) shows how to convert a `.ova` into `.box`. This + script essentially needs to do the opposite. diff --git a/.config/taskfiles/vagrant/Taskfile.yml b/.config/taskfiles/vagrant/Taskfile.yml new file mode 100644 index 00000000..5613d604 --- /dev/null +++ b/.config/taskfiles/vagrant/Taskfile.yml @@ -0,0 +1,60 @@ +--- +version: '3' + +tasks: + up: + deps: + - :install:software:jq + - :install:software:vagrant + desc: Select which virtualization platform to run the image on using an interactive prompt + vars: + PROMPT_OPTIONS: + sh: | + TMP="$(mktemp)" + if type qemu-system-x86_64 &> /dev/null; then + echo 'KVM' > "$TMP" + fi + if [[ '{{OS}}' == 'darwin' ]] && mdfind -name 'Parallels Desktop.app' &> /dev/null; then + echo 'Parallels' > "$TMP" + fi + if type vboxmanage &> /dev/null; then + echo 'VirtualBox' > "$TMP" + fi + if [[ '{{OS}}' == 'linux' ]] && type vmware &> /dev/null; then + echo 'VMWare Workstation' > "$TMP" + fi + if [[ '{{OS}}' == 'darwin' ]] && type vmrun &> /dev/null; then + echo 'VMWare Fusion' > "$TMP" + fi + LIST_LENGTH="$(jq -R -s -c -r 'split("\n") | length' < "$TMP")" + if [ "$LIST_LENGTH" != '0' ]; then + echo "\""$(jq -R -s -c -r 'split("\n") | join("\" \"")' < "$TMP")"\"" + else + echo "None" + fi + cmds: + - | + if [[ '{{.PROMPT_OPTIONS' == 'None' ]]; then + .config/log error 'No virtualization platforms installed. Install a platform (e.g. VirtualBox, VMWare, QEMU) to continue.' && exit 1 + else + .config/log prompt 'Which desktop OS would you like to launch?' + .config/log info 'Select "Other" to launch any OS from VagrantUp.com (TODO Implement feature)' + OS_CHOICE="$(.config/log choose 'ArchLinux' 'CentOS' 'Debian' 'Fedora' 'macOS' 'Ubuntu' 'Windows' 'Other')" + .config/log prompt 'Which virtualization platform would you like to use?' + PLATFORM_CHOICE="$(.config/log choose '{{.PROMPT_OPTIONS}}')" + if [[ "$PLATFORM_CHOICE" == 'KVM' ]]; then + VAGRANT_UP_PROVIDER="libvirt" + elif [[ "$PLATFORM_CHOICE" == 'Parallels' ]]; then + VAGRANT_UP_PROVIDER="parallels" + elif [[ "$PLATFORM_CHOICE" == 'VirtualBox' ]]; then + VAGRANT_UP_PROVIDER="virtualbox" + elif [[ "$PLATFORM_CHOICE" == 'VMWare Workstation' ]]; then + VAGRANT_UP_PROVIDER="vmware_workstation" + elif [[ "$PLATFORM_CHOICE" == 'VMWare Fusion' ]]; then + VAGRANT_UP_PROVIDER="vmware_fusion" + else + .config/log error 'Unrecognized platform selection.' + fi + .config/log warn 'TODO Unfinished task - need to add in OS_CHOICE to vagrant up' + vagrant up --provider="$VAGRANT_UP_PROVIDER" + fi diff --git a/.config/taskfiles/vscode/Taskfile.yml b/.config/taskfiles/vscode/Taskfile.yml new file mode 100644 index 00000000..a1d470b5 --- /dev/null +++ b/.config/taskfiles/vscode/Taskfile.yml @@ -0,0 +1,105 @@ +--- +version: '3' + +vars: + DEVCONTAINER_CONFIG: .devcontainer/devcontainer.json + EXTENSIONS_FILE: .vscode/extensions.json + EXTENSIONS_URL: https://gitlab.com/megabyte-labs/gas-station/-/raw/master/environments/prod/group_vars/desktop/vscode-extensions.yml + TASKS_FILE: .vscode/tasks.json + +tasks: + extensions: + deps: + - :install:npm:prettier + - :install:software:jq + - :install:software:yq + summary: | + # Generates the `{{.EXTENSIONS_FILE}}` file for VS Code auto plugin recommendations + + This task generates the `{{.EXTENSIONS_FILE}}` file which is used to automatically have + VS Code show a popup with a link that leads to a list of plugins that the project recommends. + This list is populated by converting the list of VS Code plugins installed by default by + [Gas Station](https://gitlab.com/megabyte-labs/gas-station) into VS Code syntax. + run: once + log: + error: Failed to generate `{{.EXTENSIONS_FILE}}` + start: Generating `{{.EXTENSIONS_FILE}}` + success: Generated `{{.EXTENSIONS_FILE}}` + cmds: + - mkdir -p .vscode + - >- + curl -s {{.EXTENSIONS_URL}} | yq e -o=j | jq '.[] = [(.[][] | select(.types | index("{{.REPOSITORY_TYPE}}") or index("all"))) + | .name] | . + {"recommendations": .vscode_extensions } | del(.vscode_extensions)' > {{.EXTENSIONS_FILE}} + - task: :fix:prettier + vars: + CLI_ARGS: '{{.EXTENSIONS_FILE}}' + + extensions:devcontainer: + deps: + - :install:npm:prettier + - :install:software:jq + summary: | + # Inject `devcontainer.json` with Extensions + + This task synchronizes the extensions from `.vscode/extensions.json` into the + `.devcontainer/devcontainer.json` extensions field. + run: once + log: + error: Failed to inject `{{.EXTENSIONS_FILE}}` `recommendations` into `{{.DEVCONTAINER_CONFIG}}` + start: Injecting `{{.EXTENSIONS_FILE}}` `recommendations` into `{{.DEVCONTAINER_CONFIG}}` + success: Successfully injected `{{.EXTENSIONS_FILE}}` `recommendations` into `{{.DEVCONTAINER_CONFIG}}` + cmds: + - | + TMP="$(mktemp)" + jq --arg recs "$(jq '.recommendations' {{.EXTENSIONS_FILE}})" '.extensions = ($recs | fromjson)' {{.DEVCONTAINER_CONFIG}} > "$TMP" + mv "$TMP" {{.DEVCONTAINER_CONFIG}} + - task: :fix:prettier + vars: + CLI_ARGS: '{{.DEVCONTAINER_CONFIG}}' + + generate: + deps: + - extensions + - tasks + run: once + cmds: + - task: extensions:devcontainer + + tasks: + deps: + - :install:npm:prettier + - :install:software:jq + summary: | + # Populate the `.vscode/tasks.json` file + + This task populates the `.vscode/tasks.json` file with tasks that are viewable by + running `task --list`. With the file generated using the output from `tasks --list`, + the user can then easily run any of the tasks from within VS Code. + + This is a simple implementation. There are many features provided by VS Code that we + can potentially leverage to make our `tasks.json` better and more useful to developers. + run: when_changed + log: + error: Failed to generate `{{.EXTENSIONS_FILE}}` + start: Generating `.vscode/tasks.json` + success: Generated `{{.EXTENSIONS_FILE}}` + cmds: + - | + TMP="$(mktemp)" + echo '{"version": "2.0.0"}' > "$TMP" + TASKS='[' + while read LINE; do + if [[ "${LINE:0:1}" == '*' ]]; then + TASK_NAME=$(echo "$LINE" | sed "s/'/ADD_QUOTE_BACK_IN/g" | sed 's/^\*.\([^\ ]*\):\(.*\)$/\1/g' | xargs) + TASK_DESC=$(echo "$LINE" | sed "s/'/ADD_QUOTE_BACK_IN/g" | sed 's/^\*.\([^\ ]*\):\(.*\)$/\2/g' | xargs) + LABEL="($TASK_NAME): $TASK_DESC" + LABEL="$(echo "$LABEL" | sed "s/ADD_QUOTE_BACK_IN/'/g")" + TYPE="shell" + COMMAND="bash start.sh && task $TASK_NAME" + TASKS+="{\"label\": \"$LABEL\", \"type\": \"$TYPE\", \"command\": \"$COMMAND\"}," + fi + done <<< "$(task --list)" + jq --arg tasks "${TASKS%?}]" '.tasks = ($tasks | fromjson)' "$TMP" > '{{.TASKS_FILE}}' + - task: :fix:prettier + vars: + CLI_ARGS: '{{.TASKS_FILE}}' diff --git a/.config/taskfiles/web/Taskfile-cloudflare.yml b/.config/taskfiles/web/Taskfile-cloudflare.yml new file mode 100644 index 00000000..41c18b48 --- /dev/null +++ b/.config/taskfiles/web/Taskfile-cloudflare.yml @@ -0,0 +1,13 @@ +--- +version: '3' + +tasks: + deploy: + deps: + - :install:npm:wrangler + + login:wrangler: + deps: + - :install:npm:wrangler + cmds: + - wrangler diff --git a/.config/taskfiles/web/Taskfile-ionic.yml b/.config/taskfiles/web/Taskfile-ionic.yml new file mode 100644 index 00000000..a1cbf973 --- /dev/null +++ b/.config/taskfiles/web/Taskfile-ionic.yml @@ -0,0 +1,54 @@ +--- +version: '3' + +tasks: + build: + deps: + - :install:npm:ionic + log: + error: Error building Ionic project + start: Building Ionic project + success: Successfully built Ionic project + cmds: + - ionic build + + build:prod: + deps: + - :install:npm:ionic + log: + error: Error building Ionic production build + start: Building Ionic project in production mode + success: Successfully built Ionic project in production mode + cmds: + - ionic build --prod + + config: + deps: + - :install:npm:ionic + log: + error: Failed to configure Ionic + start: Configuring Ionic + success: Configured Ionic + cmds: + - ionic config set -g npmClient {{.NPM_PROGRAM}} + + prepare: + deps: + - :install:npm:ionic + log: + error: Failed to prepare with Cordova via Ionic + start: Preparing Cordova targets via Ionic + success: Successfully prepared Ionic Cordova targets + cmds: + - ionic cordova prepare android + - ionic cordova prepare ios + + serve: + deps: + - :install:npm:ionic + log: + error: Failed to run `ionic serve` + start: Running `ionic serve` + success: Successfully finished running `ionic serve` + cmds: + - ionic serve diff --git a/.config/taskfiles/web/Taskfile-nx.yml b/.config/taskfiles/web/Taskfile-nx.yml new file mode 100644 index 00000000..d49cf416 --- /dev/null +++ b/.config/taskfiles/web/Taskfile-nx.yml @@ -0,0 +1,7 @@ +--- +version: '3' + +tasks: + install: + deps: + - :install:npm:nx diff --git a/.config/taskfiles/web/Taskfile-profile.yml b/.config/taskfiles/web/Taskfile-profile.yml new file mode 100644 index 00000000..656c8b47 --- /dev/null +++ b/.config/taskfiles/web/Taskfile-profile.yml @@ -0,0 +1,27 @@ +--- +version: '3' + +tasks: + detect-memory-leaks: + deps: + - :install:npm:fuite + summary: | + # Detect memory leaks + + Detect memory leaks in web applications with [fuite](https://github.com/nolanlawson/fuite). + By default, fuite will assume that the site is a client-rendered web application, and it will + search for internal links on the given page. Then for each link, it will: + + 1. Click the link + 2. Press the browser back button + 3. Repeat to see if the scenario is leaking + + **Example usage:** + `task web:profile:detect-memory-leaks -- https://example.com` + log: + error: Failed to run `fuite {{.CLI_ARGS}}` + start: Running `fuite {{.CLI_ARGS}}` + success: Successfully ran `fuite {{.CLI_ARGS}}` + cmds: + - | + fuite {{.CLI_ARGS}} diff --git a/.config/taskfiles/web/Taskfile.yml b/.config/taskfiles/web/Taskfile.yml new file mode 100644 index 00000000..72570645 --- /dev/null +++ b/.config/taskfiles/web/Taskfile.yml @@ -0,0 +1,5 @@ +--- +version: '3' + +tasks: + donothing: 'true' diff --git a/.config/variables.json b/.config/variables.json new file mode 100644 index 00000000..f22886c1 --- /dev/null +++ b/.config/variables.json @@ -0,0 +1,631 @@ +{ + "SPACE": "", + "alt_badge_style": "flat-square", + "ansible_galaxy_project_id": "", + "author": { + "email": "brian@megabyte.space", + "name": "Brian Zalewski" + }, + "autodoc_actions_description": "", + "autodoc_tags_description": "", + "autodoc_todo_description": "", + "autodoc_variables_description": "", + "badge_style": "for-the-badge", + "blueprint_requirements": [ + ["Variable Name", "Variable Description"], + [ + "`description`", + "Short description of the role, worded in such a way that it makes sense by itself and with 'An Ansible role that ' prepended to it" + ], + ["`group`", "This should always be set to 'ansible' for Ansible roles"], + [ + "`name`", + "This should be the official name for the product that the role installs/configures. It is used in the title of the repository and throughout the documentation to refer to the product." + ], + [ + "`overview`", + "This variable should be a description of what the role installs. You can usually find a good description by Googling, \"What is Android Studio,\" for example if you were populating this variable for the [Android Studio role]({{ repository.group.ansible_roles }}/androidstudio). This text is shown at the top of the README, right below the header section and before the table of contents. Whenever possible, key products/terms should be linked to using markdown. You can see an example of us hyperlinking in this variable by checking out the [Android Studio role]({{ repository.group.ansible_roles }}/androidstudio). The idea is to make it as easy as possible for our users to figure out exactly what the role does." + ], + ["`repository.github`", "The HTTPS URL of the GitHub mirror"], + ["`repository.gitlab`", "The HTTPS URL of the GitLab repository"], + [ + "`slug`", + "This should generally be the ending slug of the GitHub mirror. It is used for things like filling in the package.json name." + ], + ["`subgroup`", "This should always be set to 'role' for Ansible roles"], + ["`title`", "The title of the README.md"] + ], + "commit_help_url": "https://megabyte.space/docs/contributing/commit-guidelines", + "company": "Megabyte LLC", + "copyright": "2020-2021", + "description_emojis": "👨🏻‍💻 🩺", + "docker_label_authors": "Brian Zalewski ", + "docs": { + "header_description_post": "", + "header_description_pre": "Brought to you by ", + "header_title_post": "", + "header_title_pre": "Ansible Playbook: ", + "link": "https://megabyte.space/docs/ansible/playbook" + }, + "downloadLinks": { + "fedora": "https://download.fedoraproject.org/pub/fedora/linux/releases/35/Workstation/x86_64/iso/Fedora-Workstation-Live-x86_64-35-1.2.iso", + "kali": "https://cdimage.kali.org/kali-2022.1/kali-linux-2022.1-installer-amd64.iso", + "qubes": "https://ftp.qubes-os.org/iso/Qubes-R4.1.0-x86_64.iso", + "tails": "https://ftp.osuosl.org/pub/tails/stable/tails-amd64-4.29/tails-amd64-4.29.iso", + "ubuntu": "https://mirror.arizona.edu/ubuntu-releases/21.10/ubuntu-21.10-desktop-amd64.iso", + "windows": "https://software.download.prss.microsoft.com/db/Win11_English_x64.iso?t=c15e0cba-9c8d-4984-b30f-43c42425733d&e=1650582458&h=960d57d2c6a0243e32d3106c0d8f82387966ddf9a9bfce82f89e66866457c014" + }, + "email": { + "help": "help@megabyte.space" + }, + "emoji_beginnings": ["🚀 ", "🔥👉 ", "👉 ", "😉 ", "🆓 ", "🐴 ", "👀 ", "🎉 ", "", "", "", "", "", "", "", ""], + "emoji_endings": [" 🚀", " 🔥🔥🔥", " 👏", " 😉", " 🐙", " 🐴", " 👀", " 🎟", " 🎉🎉", "", "", "", "", "", "", ""], + "github_prefix": "", + "gitlab_pipelines": [ + { + "active": true, + "cron": "0 12 * * 1", + "description": "Weekly Build Test", + "ref": "master", + "variable": { + "WEEKLY_TEST": true + } + }, + { + "active": true, + "cron": "0 12 * * 1", + "description": "Weekly Linux Molecule Test", + "ref": "test/linux", + "variable": { + "WEEKLY_LINUX_TEST": true + } + }, + { + "active": true, + "cron": "0 12 * * 2", + "description": "Weekly macOS Molecule Test", + "ref": "test/darwin", + "variable": { + "WEEKLY_DARWIN_TEST": true + } + }, + { + "active": true, + "cron": "0 12 * * 4", + "description": "Weekly Windows Molecule Test", + "ref": "test/windows", + "variable": { + "WEEKLY_WINDOWS_TEST": true + } + }, + { + "active": true, + "cron": "0 5 1 * *", + "description": "Monthly Repository Update", + "ref": "synchronize", + "variable": { + "REPOSITORY_UPDATE": true + } + } + ], + "gomodProxy": true, + "group": "common", + "groups": { + "angular": ["app", "website"], + "ansible": ["playbook", "role"], + "docker": ["ansible-molecule", "app", "ci-pipeline", "codeclimate", "software"], + "go": ["cli", "library"], + "npm": ["app", "cli", "config", "library", "plugin"], + "packer": ["desktop", "server"], + "python": ["cli", "library"] + }, + "homebrew": { + "folder": "Formula", + "name": "homebrew-tap", + "owner": "installdoc" + }, + "hostapp_var_chart": [ + ["App", "Description", "GitHub            "], + [ + "**[Authelia](https://www.authelia.com/)**", + "An authentication portal that supports SSO and 2FA (_[Homepage](https://www.authelia.com/) | [Documentation](https://www.authelia.com/docs/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/authelia/authelia?style=social)](https://github.com/authelia/authelia)" + ], + [ + "**[Homer](https://github.com/bastienwirtz/homer)**", + "A very simple homepage which is customized by the playbook to automatically include links to the Docker containers you choose to host on the computer (_[Demo](https://homer-demo.netlify.app/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/bastienwirtz/homer?style=social)](https://github.com/bastienwirtz/homer)" + ], + [ + "**[Portainer](https://www.portainer.io/)**", + "A Docker management tool (_[Homepage](https://www.portainer.io/) | [Documentation](https://docs.portainer.io/) | [Demo](https://github.com/portainer/portainer#demo)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/portainer/portainer?style=social)](https://github.com/portainer/portainer)" + ], + [ + "**[Serve](https://github.com/vercel/serve)**", + "Simple interface for viewing files located or symlinked to in the `/var/www/` folder of the machine", + "[![GitHub Repo stars](https://img.shields.io/github/stars/vercel/serve?style=social)](https://github.com/vercel/serve)" + ] + ], + "htpc_var_chart": [ + ["App", "Description", "GitHub            "], + [ + "**[WireGuard](https://docs.linuxserver.io/images/docker-wireguard)**", + "Dedicated WireGuard VPN for the HTPC applications which is configured in *our docker-compose.yml* file to be used as the internet connection for all the containers (_[Homepage](https://www.wireguard.com/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/linuxserver/docker-wireguard?style=social)](https://github.com/linuxserver/docker-wireguard)" + ], + [ + "**[Bazarr](https://docs.linuxserver.io/images/docker-bazarr)**", + "Manages and automatically downloads subtitles (_[Homepage](https://www.bazarr.media/) | [Documentation](https://wiki.bazarr.media/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/morpheus65535/bazarr?style=social)](https://github.com/morpheus65535/bazarr)" + ], + [ + "**[Heimdall](https://docs.linuxserver.io/images/docker-heimdall)**", + "Simple start page for all the HTPC apps (_[Homepage](https://heimdall.site/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/linuxserver/Heimdall?style=social)](https://github.com/linuxserver/Heimdall)" + ], + [ + "**[Jackett](https://docs.linuxserver.io/images/docker-jackett)**", + "Request proxy server for Radarr and Sonarr which helps speed things up", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Jackett/Jackett?style=social)](https://github.com/Jackett/Jackett)" + ], + [ + "**[Kodi Headless](https://hub.docker.com/r/linuxserver/kodi-headless)**", + "Backend for Kodi used to host a centralized database for Kodi instances (_[Homepage](https://kodi.tv/) | [Documentation](https://kodi.wiki/view/Main_Page)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/xbmc/xbmc?style=social)](https://github.com/xbmc/xbmc)" + ], + [ + "**[Lidarr](https://docs.linuxserver.io/images/docker-lidarr)**", + "Music collection manager that automatically downloads from BitTorrent and Usenet (_[Homepage](https://lidarr.audio/) | [Documentation](https://wiki.servarr.com/en/lidarr)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Lidarr/Lidarr?style=social)](https://github.com/Lidarr/Lidarr)" + ], + [ + "**[NZBGet](https://docs.linuxserver.io/images/docker-nzbget)**", + "NZBGet is a Usenet download manager used to download from NewsGroups which are supposedly more secure than torrents. **NOTE: Viruses are still prevalent on both NewsGroups and torrents - make sure you don't run anything with admin / sudo privileges.** (_[Homepage](https://nzbget.net/) | [Documentation](https://nzbget.net/documentation)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/nzbget/nzbget?style=social)](https://github.com/nzbget/nzbget)" + ], + [ + "**[Ombi](https://docs.linuxserver.io/images/docker-ombi)**", + "Plex media request and user management system which can be used to allow users who use your HTPC server to request movies, TV shows, and other media (_[Homepage](https://ombi.io/) | [Documentation](https://docs.ombi.app/) | [Demo](https://app.ombi.io/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Ombi-app/Ombi?style=social)](https://github.com/Ombi-app/Ombi)" + ], + [ + "**[Organizr](https://docs.linuxserver.io/images/docker-htpcmanager)**", + "Front end for HTPC web applications with a full-featured user interface that is full of eye candy (_[Homepage](https://organizr.app/) | [Documentation](https://docs.organizr.app/) | [Demo](https://docs.organizr.app/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/causefx/Organizr?style=social)](https://github.com/causefx/Organizr)" + ], + [ + "**[Radarr](https://docs.linuxserver.io/images/docker-radarr)**", + "Automatic movie downloader that can even be configured to download lists including the Top 250 IMBD movies (_[Homepage](https://radarr.video/) | [Documentation](https://wiki.servarr.com/radarr)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Radarr/Radarr?style=social)](https://github.com/Radarr/Radarr)" + ], + [ + "**[Sonarr](https://docs.linuxserver.io/images/docker-sonarr)**", + "Automatic TV show downloader with tons of ways to easily and automatically download TV shows (_[Homepage](https://sonarr.tv/) | [Documentation](https://wiki.servarr.com/en/sonarr)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Sonarr/Sonarr?style=social)](https://github.com/Sonarr/Sonarr)" + ], + [ + "**[Tautulli](https://docs.linuxserver.io/images/docker-tautulli)**", + "Metrics and monitoring dashboard for Plex (_[Homepage](https://tautulli.com/) | [Documentation](https://wiki.bazarr.media/)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/Tautulli/Tautulli?style=social)](https://github.com/Tautulli/Tautulli)" + ], + [ + "**[Transmission](https://docs.linuxserver.io/images/docker-transmission)**", + "BitTorrent client that can be used in conjunction with or as an alternative to using NewsGroups via NZBGet (_[Homepage](https://transmissionbt.com/) | [Documentation](https://github.com/transmission/transmission/blob/main/docs/README.md)_)", + "[![GitHub Repo stars](https://img.shields.io/github/stars/transmission/transmission?style=social)](https://github.com/transmission/transmission)" + ] + ], + "idPost": "megabyte.space", + "json_top_keys": "", + "license": "MIT", + "link": { + "chat": "https://app.slack.com/client/T01ABCG4NK1/C01NN74H0LW/details/", + "docker_role": "https://github.com/ProfessorManhattan/ansible-docker", + "docs": "https://megabyte.space/docs", + "gitter": "https://gitter.im/megabyte-labs/community", + "home": "https://megabyte.space", + "installdoctor": "https://install.doctor", + "mod_ansible_autodoc": "https://pypi.org/project/mod-ansible-autodoc/", + "privacy": "https://megabyte.space/privacy", + "shield": "https://shields.io", + "terms": "https://megabyte.space/terms" + }, + "misc": { + "appnest": "https://github.com/andreasbm/readme", + "husky": "https://www.npmjs.com/package/husky", + "tinypng": "https://tinypng.com/" + }, + "molecule_descriptions": [ + ["Scenario", "Description"], + ["`default`", "Uses VirtualBox to run tests for all platforms in parallel."], + ["`docker`", "Uses Docker to run tests for all Linux platforms and versions in parallel."], + [ + "`docker-snap`", + "The same as the `docker` scenario except it excludes platforms that have trouble installing snap packages on Docker." + ], + ["`archlinux-desktop`", "Runs the test on the latest version of Archlinux desktop using VirtualBox."], + ["`centos-desktop`", "Runs the test on the latest version of CentOS desktop using VirtualBox."], + ["`debian-desktop`", "Runs the test on the latest version of Debian desktop using VirtualBox."], + ["`fedora-desktop`", "Runs the test on the latest version of Fedora desktop using VirtualBox."], + ["`macos-desktop`", "Runs the test on the latest version of macOS desktop using VirtualBox."], + ["`ubuntu-desktop`", "Runs the test on the latest version of Ubuntu desktop using VirtualBox."], + ["`windows-desktop`", "Runs the test on the latest version of Windows desktop using VirtualBox."], + ["`ci-docker-archlinux`", "Uses Docker to test Archlinux."], + ["`ci-docker-centos`", "Uses Docker to test multiple versions of CentOS."], + ["`ci-docker-debian`", "Uses Docker to test multiple versions of Debian."], + [ + "`ci-docker-debian-snap`", + "Uses Docker to test Debian just like `ci-docker-debian` except it excludes versions that cannot install snap packages." + ], + ["`ci-docker-fedora`", "Uses Docker to test multiple versions of Fedora."], + ["`ci-docker-ubuntu`", "Uses Docker to test multiple versions of Ubuntu."] + ], + "name": "[[ package.json .blueprint.name - See CONTRIBUTING.md ]]", + "newProjectTemplates": { + "angular-app": { + "group": "angular", + "subgroup": "app", + "title": "Angular App" + }, + "angular-website": { + "group": "angular", + "subgroup": "website", + "title": "Angular Website" + }, + "ansible-role": { + "group": "ansible", + "subgroup": "role", + "title": "Ansible Role" + }, + "docker-app": { + "group": "docker", + "subgroup": "app", + "title": "Dockerfile (App)" + }, + "docker-ci-pipeline": { + "group": "docker", + "subgroup": "ci-pipeline", + "title": "CI/CD Pipeline Dockerfile" + }, + "docker-codeclimate": { + "group": "docker", + "subgroup": "codeclimate", + "title": "CodeClimate Engine / Linter Dockerfile" + }, + "docker-docker-compose": { + "group": "docker", + "subgroup": "docker-compose", + "title": "Docker Compose" + }, + "go-cli": { + "group": "go", + "subgroup": "cli", + "title": "Go CLI" + }, + "go-library": { + "group": "go", + "subgroup": "library", + "title": "Go Library" + }, + "misc": { + "group": "misc", + "subgroup": "misc", + "title": "Miscellaneous project" + }, + "npm-app": { + "group": "npm", + "subgroup": "app", + "title": "Node.js App" + }, + "npm-cli": { + "group": "npm", + "subgroup": "cli", + "title": "Node.js CLI" + }, + "npm-configs": { + "group": "npm", + "subgroup": "configs", + "title": "NPM Config Package" + }, + "npm-library": { + "group": "npm", + "subgroup": "library", + "title": "Node.js Library" + }, + "npm-plugin": { + "group": "npm", + "subgroup": "plugin", + "title": "NPM Plugin" + }, + "npm-web-component": { + "group": "npm", + "subgroup": "web-component", + "title": "Web Component" + }, + "packer-desktop": { + "group": "packer", + "subgroup": "desktop", + "title": "Packer (Desktop)" + }, + "packer-server": { + "group": "packer", + "subgroup": "server", + "title": "Packer (Server)" + }, + "python-cli": { + "group": "python", + "subgroup": "cli", + "title": "Python CLI" + }, + "python-library": { + "group": "python", + "subgroup": "library", + "title": "Python Library" + }, + "website": { + "group": "npm", + "subgroup": "website", + "title": "Website" + } + }, + "npm_publish_config_access": "public", + "npm_standard_version_prerelease": "git add --all", + "npm_type": "module", + "organization": "Megabyte Labs", + "overview": "[[ This is a new repository without the details filled in yet. Look for the section about blueprint data in the CONTRIBUTING.md to set up the project. ]]", + "playbook_path": "megabyte-labs/gas-station", + "profile": { + "dockerHubUser": "professormanhattan", + "dockerhub": "megabytelabs", + "galaxy": "professormanhattan", + "github": "ProfessorManhattan", + "githubOrg": "megabyte-labs", + "linkedin": "blzalewski", + "npmjs": "thisismyfirstday", + "npmjs_organization": "installdoc", + "opencollective": "megabytelabs", + "patreon": "ProfessorManhattan", + "pypi": "ProfessorManhattan", + "replit": "ProfessorMegaby", + "stackblitz": "ProfessorManhattan", + "twitter": "MegabyteLabs", + "vagrant": "ProfessorManhattan" + }, + "profile_link": { + "dockerhub": "https://hub.docker.com/u", + "galaxy": "https://galaxy.ansible.com", + "github": "https://github.com", + "linkedin": "https://www.linkedin.com/in/", + "npmjs": "https://www.npmjs.com/~", + "opencollective": "https://opencollective.com", + "patreon": "https://www.patreon.com", + "pypi": "https://pypi.org/user", + "replit": "https://repl.it/@", + "stackblitz": "https://stackblitz.com/@", + "twitter": "MegabyteLabs", + "vagrant": "https://app.vagrantup.com" + }, + "python_role_dependencies": [ + ["Package", "Description", "Required"], + [ + "ansible", + "A configuration management system that can remotely configure computers", + "
✔️
" + ], + [ + "docker", + "Enables the capability of provisioning Docker containers with Ansible", + "
✔️
" + ], + [ + "python-vagrant", + "Required for provisioning Vagrant VMs", + "
✔️
" + ], + [ + "pywinrm", + "Required for provisioning Windows machines that are using WinRM", + "
✔️
" + ], + [ + "ansible-lint", + "Linting tool for Ansible files", + "" + ], + [ + "ansibler", + "Custom tool used to generate advanced documentation (e.g. it generates the compatibility chart and some other charts)", + "" + ], + [ + "black", + "Python file auto-formatter included in case project utilizes Python test scripts", + "" + ], + [ + "blocklint", + "Linting tool that prevents certain words from entering the code base", + "" + ], + [ + "flake8", + "Python linter that reports Python syntax and style errors", + "" + ], + [ + "mod-ansible-autodoc", + "Custom fork of [ansible-autodoc](https://pypi.org/project/ansible-autodoc/0.5.1.1/) which allows us to auto-generate documentation based on comments in the role's YAML files", + "" + ], + [ + "molecule", + "Test framework for Ansible", + "" + ], + [ + "molecule-docker", + "Molecule plugin for provisioning Docker containers", + "" + ], + [ + "molecule-vagrant", + "Molecule plugin for provisioning Vagrant VMs", + "" + ], + [ + "pre-commit-hooks", + "Suite of tools useful for linting", + "" + ], + [ + "proselint", + "Linter used to generate English-language improvements (used to improve documentation)", + "" + ], + [ + "yamllint", + "Linter for YAML files that ensures proper syntax and styling is used", + "" + ] + ], + "redditApplicationId": "O3UxD7HlPpcN88gpEkPIXQ", + "redditUsername": "tsgangster", + "repository": { + "github": "", + "gitlab": "", + "gitlabBaseUrl": "https://gitlab.com/megabyte-labs", + "group": { + "ansible_roles": "https://gitlab.com/megabyte-labs/ansible-roles", + "ansible_roles_path": "megabyte-labs/ansible-roles", + "apps": "https://gitlab.com/megabyte-labs/apps", + "apps_path": "megabyte-labs/apps", + "ci": "https://gitlab.com/megabyte-labs/ci", + "ci_path": "megabyte-labs/ci", + "cloud": "https://gitlab.com/megabyte-labs/cloud", + "cloud_path": "megabyte-labs/cloud", + "common": "https://gitlab.com/megabyte-labs/common", + "common_path": "megabyte-labs/common", + "cryptocurrency": "https://gitlab.com/megabyte-labs/cryptocurrency", + "cryptocurrency_path": "megabyte-labs/cryptocurrency", + "docker_compose": "https://gitlab.com/megabyte-labs/docker-compose", + "docker_compose_path": "megabyte-labs/docker-compose", + "dockerfile": "https://gitlab.com/megabyte-labs/docker", + "dockerfile_path": "megabyte-labs/docker", + "documentation": "https://gitlab.com/megabyte-labs/documentation", + "documentation_path": "megabyte-labs/documentation", + "go": "https://gitlab.com/megabyte-labs/go", + "go_path": "megabyte-labs/go", + "kubernetes": "https://gitlab.com/megabyte-labs/kubernetes", + "kubernetes_path": "megabyte-labs/kubernetes_path", + "npm": "https://gitlab.com/megabyte-labs/npm", + "npm_path": "megabyte-labs/npm", + "packer": "https://gitlab.com/megabyte-labs/packer", + "packer_path": "megabyte-labs/packer", + "python": "https://gitlab.com/megabyte-labs/python", + "python_cli_path": "megabyte-labs/python/cli", + "python_path": "megabyte-labs/python", + "software": "https://gitlab.com/megabyte-labs/software", + "software_path": "megabyte-labs/software", + "web_components": "https://gitlab.com/megabyte-labs/web-components", + "web_components_path": "megabyte-labs/web-components" + }, + "location": { + "commits": { + "github": "/commits/master", + "gitlab": "/-/commits/master", + "gitlab_e2e": "/-/commits/e2e" + }, + "conduct": { + "github": "/blob/master/docs/CODE_OF_CONDUCT.md", + "gitlab": "/-/blob/master/docs/CODE_OF_CONDUCT.md" + }, + "contributing": { + "github": "/blob/master/docs/CONTRIBUTING.md", + "gitlab": "/-/blob/master/docs/CONTRIBUTING.md" + }, + "demo": { + "github": "/raw/master/docs/demo.gif", + "gitlab": "/-/raw/master/docs/demo.gif" + }, + "issues": { + "github": "/issues", + "gitlab": "/-/issues" + }, + "license": { + "github": "/blob/master/LICENSE", + "gitlab": "/-/blob/master/LICENSE" + }, + "logo": { + "github": "/raw/master/logo.png", + "gitlab": "/-/raw/master/logo.png" + }, + "readme": { + "github": "/blob/master/README.md", + "gitlab": "/-/blob/master/README.md" + } + }, + "namespace": "", + "prefix": { + "github": "ansible-" + }, + "project": { + "assets": "https://gitlab.com/megabyte-labs/assets/-/raw/master", + "autodoc": "https://github.com/AndresBott/ansible-autodoc", + "playbooks": "https://github.com/ProfessorManhattan/Gas-Station", + "wrangler": "https://gitlab.com/megabyte-labs/wrangler" + } + }, + "saas_var_chart": [ + ["Service", "Description", "Price"], + [ + "**[CloudFlare](https://www.cloudflare.com/)**", + "CloudFlare is a DNS provider, edge network, and much more. Some day it might be able to replace all the services in this list but until then CloudFlare is the preferred provider for anything it offers a product for. In our configurations, CloudFlare is used for DNS, encrypted tunnels via [cloudflared](https://github.com/cloudflare/cloudflared), [CloudFlare WARP](https://1.1.1.1/), and [CloudFlare Teams](https://blog.cloudflare.com/introducing-cloudflare-for-teams/). On top of that, CloudFlare provides some other great features that can be utilized to make lightning-fast web apps. (_[Documentation](https://developers.cloudflare.com/docs/)_)", + "**Free** for the services we integrate" + ], + [ + "**[Digital Ocean](https://m.do.co/c/751743d45e36)**", + "Digital Ocean is a cloud hosting provider. Anytime CloudFlare's offerings are not enough to satisfy requirements, Digital Ocean is used. The service has a clean and simple web UI, a wide variety of CLIs/SDKs available on GitHub, and the company has been around since 2011. Digital Ocean is primarily used by our stack to host Kubernetes, S3 buckets, and cheap virtual private servers. (_[Documentation](https://docs.digitalocean.com/)_)", + "**~$40/month** for a Kubernetes cluster, S3 bucket, and a general-purpose VPS" + ], + [ + "**[Wasabi](https://wasabi.com/)**", + "Wasabi is the cheapest S3 bucket provider available. It is used as a secondary backup for any data that is backed up / saved to an S3 bucket. (_[Documentation](https://wasabi.com/help/docs/)_)", + "**$5.99/month** for S3 bucket" + ], + [ + "**[Ory](https://www.ory.sh/)**", + "Ory is the only identity platform that can scale indefinitely and is based entirely on open source. Ory is leveraged to provide a feature-rich and programmable single sign-on platform. It includes support for hardware-based tokens. (_[Documentation](https://www.ory.sh/docs/welcome)_)", + "**Free** for the developer edition" + ], + [ + "**[Proton](https://proton.me/)**", + "Proton Mail is an end-to-end encrypted email service founded in 2013 in Geneva, Switzerland. Proton Mail and ProtonVPN are used in our stack to provide secure e-mail and configure VPN profiles using ProtonVPN's unique security features. With the Business plan, you can get custom domain branded e-mail and enough VPN connections to configure your router / VPN profiles on each of your devices. (_[Documentation](https://proton.me/support)_)", + "**$12.99/month** for the Business edition" + ], + [ + "**[GMail](https://mail.google.com)**", + "GMail is a free e-mail service offered by Google. In some cases, we leverage GMail's SMTP capabilities to send notification e-mails. (_[Documentation](https://support.google.com/mail/?hl=en#topic=7065107)_)", + "**Free**" + ] + ], + "scriptsBuild": "task ansible:build:none", + "scriptsHelp": "task --menu", + "scriptsPrepare": "npm run start && (test -f Taskfile.yml && task common:husky) || true", + "scriptsReplaceThis": "\"", + "scriptsReplaceWith": "\\\"", + "scriptsStart": "bash start.sh", + "scriptsTest": "[ -z \"$PS1\" ] && task ansible:test:prompt || task ansible:test:default", + "sharp_instructions": [], + "slackNotificationChannel": "#misc", + "slackNotificationIcon": "https://gitlab.com/megabyte-labs/misc/assets/-/raw/master/logo/megabytelabs-color-icon-350x350.png", + "slackNotificationUsername": "Megabyte Labs Release Notification Bot", + "sponsorship": { + "author": "Brian Zalewski", + "text": "I create open source projects out of love. Although I have a job, shelter, and as much fast food as I can handle, it would still be pretty cool to be appreciated by the community for something I have spent a lot of time and money on. Please consider sponsoring me! Who knows? Maybe I will be able to quit my job and publish open source full time." + }, + "subgroup": "playbook", + "teamsNotificationColor": "#1DA1F2", + "teamsNotificationIcon": "https://gitlab.com/megabyte-labs/misc/assets/-/raw/master/logo/megabytelabs-color-icon-350x350.png", + "title": "[[ package.json .blueprint.title - See CONTRIBUTING.md ]]", + "version": "0.0.1" +} diff --git a/.config/yamllint.yml b/.config/yamllint.yml new file mode 100644 index 00000000..cbb09ebe --- /dev/null +++ b/.config/yamllint.yml @@ -0,0 +1,53 @@ +--- +ignore: | + .autodoc/ + .cache/ + .common/ + .config/ + .git/ + .husky/ + .modules/ + .npm/ + .pnpm-store/ + .shared/ + .task/ + .venv/ + .vscode/ + .tox + .travis.yml + *.hbs.yml + build/ + dist/ + node_modules/ + pnpm-lock.yaml + roles/ + testdata/ + test/output-example/ + venv/ + +extends: default + +rules: + braces: + level: error + max-spaces-inside: 1 + brackets: + level: error + max-spaces-inside: 1 + comments: + min-spaces-from-content: 1 + document-start: + ignore: | + environments/prod/ + indentation: + indent-sequences: consistent + level: error + line-length: + level: error + max: 160 + ignore: | + .config/taskfiles/ + common/.config/taskfiles/ + truthy: + # eslint-disable-next-line yml/plain-scalar + allowed-values: ['false', 'on', 'true'] diff --git a/.config/yarn.lock b/.config/yarn.lock new file mode 100644 index 00000000..4ac7bb69 --- /dev/null +++ b/.config/yarn.lock @@ -0,0 +1,1246 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/eslint-parser@^7.12.16": + version "7.17.0" + resolved "7.17.0" + integrity sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA== + dependencies: + eslint-scope "^5.1.1" + eslint-visitor-keys "^2.1.0" + semver "^6.3.0" + +"@commitlint/config-conventional@15.0.0": + version "16.2.1" + resolved "16.2.1" + integrity sha512-cP9gArx7gnaj4IqmtCIcHdRjTYdRUi6lmGE+lOzGGjGe45qGOS8nyQQNvkNy2Ey2VqoSWuXXkD8zCUh6EHf1Ww== + dependencies: + conventional-changelog-conventionalcommits "^4.3.1" + +"@commitlint/config-conventional@^16.0.0": + version "16.2.1" + resolved "16.2.1" + integrity sha512-cP9gArx7gnaj4IqmtCIcHdRjTYdRUi6lmGE+lOzGGjGe45qGOS8nyQQNvkNy2Ey2VqoSWuXXkD8zCUh6EHf1Ww== + dependencies: + conventional-changelog-conventionalcommits "^4.3.1" + +"@eslint/eslintrc@^1.1.0": + version "1.1.0" + resolved "1.1.0" + integrity sha512-C1DfL7XX4nPqGd6jcP01W9pVM1HYCuUkFk1432D7F0v3JSlUIeOYn9oCoi3eoLZ+iwBSb29BMFxxny0YrrEZqg== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.1" + globals "^13.9.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + +"@octokit/openapi-types@^11.2.0": + version "11.2.0" + resolved "11.2.0" + integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== + +"@prettier/plugin-php@^0.17.6": + version "0.17.6" + resolved "0.17.6" + integrity sha512-Nxkq+Gl8bGbutRV7e3/p5d+Bcftn75oH61RT8xzk44T5ET7fVP50pUdaOdvt704GSNr6wwmfBW8MhBz5IKt+fA== + dependencies: + linguist-languages "^7.5.1" + mem "^8.0.0" + php-parser "https://github.com/glayzzle/php-parser#27abcb2337ac6450c068ef064982dfabf77916a5" + +"@prettier/plugin-pug@^1.19.2": + version "1.19.2" + resolved "1.19.2" + integrity sha512-Izo+m3pb15Ym7t3J+u22kun7bQXZOj+lAqkIc5ifxwd9En9OqcCShZz7eABORmzmY276IjSfY92Ijd23nLR68Q== + dependencies: + pug-lexer "^5.0.0" + +"@prettier/plugin-ruby@^2.0.0": + version "2.0.0" + resolved "2.0.0" + integrity sha512-pA0rjTi5J7cT86XPNhXp7CpdV2Tlyj5oqDIsnQRxMu2P7LY2KJI/pyOSM8pzTH8BgRyQfe1P1NPCcTwxUnUWtQ== + dependencies: + prettier ">=2.3.0" + +"@prettier/plugin-xml@^1.0.2": + version "1.2.0" + resolved "1.2.0" + integrity sha512-bFvVAZKs59XNmntYjyefn3K4TBykS6E+d6ZW8IcylAs88ZO+TzLhp0dPpi0VKfPzq1Nb+kpDnPRTiwb4zY6NgA== + dependencies: + "@xml-tools/parser" "^1.0.11" + prettier ">=2.3" + +"@types/eslint@^7.2.13": + version "7.29.0" + resolved "7.29.0" + integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "0.0.51" + resolved "0.0.51" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + +"@types/http-cache-semantics@*": + version "4.0.1" + resolved "4.0.1" + integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + +"@types/istanbul-lib-coverage@*": + version "2.0.4" + resolved "2.0.4" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-coverage@^2.0.0": + version "2.0.4" + resolved "2.0.4" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "3.0.0" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "3.0.1" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/json-schema@*": + version "7.0.9" + resolved "7.0.9" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + +"@types/json-schema@^7.0.9": + version "7.0.9" + resolved "7.0.9" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + +"@types/keyv@*": + version "3.1.3" + resolved "3.1.3" + integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "17.0.18" + resolved "17.0.18" + integrity sha512-eKj4f/BsN/qcculZiRSujogjvp5O/k4lOW5m35NopjZM/QwLOR075a8pJW5hD+Rtdm2DaCVPENS6KtSQnUD6BA== + +"@types/responselike@*": + version "1.0.0" + resolved "1.0.0" + integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + dependencies: + "@types/node" "*" + +"@types/unist@*": + version "2.0.6" + resolved "2.0.6" + integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== + +"@types/yargs-parser@*": + version "20.2.1" + resolved "20.2.1" + integrity sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw== + +"@types/yargs@^15.0.0": + version "17.0.8" + resolved "17.0.8" + integrity sha512-wDeUwiUmem9FzsyysEwRukaEdDNcwbROvQ9QGRKaLI6t+IltNzbn4/i4asmB10auvZGQCzSQ6t0GSczEThlUXw== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.0.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-fwCMkDimwHVeIOKeBHiZhRUfJXU8n6xW1FL9diDxAyGAFvKcH4csy0v7twivOQdQdA0KC8TDr7GGRd3L4Lv0rQ== + dependencies: + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/type-utils" "5.12.0" + "@typescript-eslint/utils" "5.12.0" + debug "^4.3.2" + functional-red-black-tree "^1.0.1" + ignore "^5.1.8" + regexpp "^3.2.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/eslint-plugin@^5.11.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-fwCMkDimwHVeIOKeBHiZhRUfJXU8n6xW1FL9diDxAyGAFvKcH4csy0v7twivOQdQdA0KC8TDr7GGRd3L4Lv0rQ== + dependencies: + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/type-utils" "5.12.0" + "@typescript-eslint/utils" "5.12.0" + debug "^4.3.2" + functional-red-black-tree "^1.0.1" + ignore "^5.1.8" + regexpp "^3.2.0" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@^2.27.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-iFVADWH2CmiDF+E9kFK2r474BO2JILDKw1NVD5ytqHrM3ezsfdu5uo6B+77DH0suM7iUC/yOayHNziuiI9BPbQ== + dependencies: + "@typescript-eslint/utils" "5.12.0" + +"@typescript-eslint/experimental-utils@^5.0.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-iFVADWH2CmiDF+E9kFK2r474BO2JILDKw1NVD5ytqHrM3ezsfdu5uo6B+77DH0suM7iUC/yOayHNziuiI9BPbQ== + dependencies: + "@typescript-eslint/utils" "5.12.0" + +"@typescript-eslint/parser@^5.11.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-MfSwg9JMBojMUoGjUmX+D2stoQj1CBYTCP0qnnVtu9A+YQXVKNtLjasYh+jozOcrb/wau8TCfWOkQTiOAruBog== + dependencies: + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/typescript-estree" "5.12.0" + debug "^4.3.2" + +"@typescript-eslint/scope-manager@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-GAMobtIJI8FGf1sLlUWNUm2IOkIjvn7laFWyRx7CLrv6nLBI7su+B7lbStqVlK5NdLvHRFiJo2HhiDF7Ki01WQ== + dependencies: + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/visitor-keys" "5.12.0" + +"@typescript-eslint/type-utils@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-9j9rli3zEBV+ae7rlbBOotJcI6zfc6SHFMdKI9M3Nc0sy458LJ79Os+TPWeBBL96J9/e36rdJOfCuyRSgFAA0Q== + dependencies: + "@typescript-eslint/utils" "5.12.0" + debug "^4.3.2" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-JowqbwPf93nvf8fZn5XrPGFBdIK8+yx5UEGs2QFAYFI8IWYfrzz+6zqlurGr2ctShMaJxqwsqmra3WXWjH1nRQ== + +"@typescript-eslint/typescript-estree@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-Dd9gVeOqt38QHR0BEA8oRaT65WYqPYbIc5tRFQPkfLquVEFPD1HAtbZT98TLBkEcCkvwDYOAvuSvAD9DnQhMfQ== + dependencies: + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/visitor-keys" "5.12.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-k4J2WovnMPGI4PzKgDtQdNrCnmBHpMUFy21qjX2CoPdoBcSBIMvVBr9P2YDP8jOqZOeK3ThOL6VO/sy6jtnvzw== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.12.0" + "@typescript-eslint/types" "5.12.0" + "@typescript-eslint/typescript-estree" "5.12.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.12.0": + version "5.12.0" + resolved "5.12.0" + integrity sha512-cFwTlgnMV6TgezQynx2c/4/tx9Tufbuo9LPzmWqyRC3QC4qTGkAG1C6pBr0/4I10PAI/FlYunI3vJjIcu+ZHMg== + dependencies: + "@typescript-eslint/types" "5.12.0" + eslint-visitor-keys "^3.0.0" + +"@washingtondc/development@latest": + version "1.0.10" + resolved "1.0.10" + integrity sha512-rO/ROg5jBNjx3HQIqsVDBWwOFD9sa6UwhsUphfwTcPCJLQ+bu0NFb3yGOpHvmbpU9AWtFS6ZcG2UHRvdPed01Q== + dependencies: + "@commitlint/cli" "15.0.0" + "@commitlint/config-conventional" "15.0.0" + commitizen "4.2.4" + cspell "5.13.1" + cz-emoji-conventional "1.0.1" + git-notify "0.2.3" + husky "7.0.4" + leasot "12.0.0" + lint-staged "12.1.2" + liquidjs "9.28.5" + only-allow "1.0.0" + open-cli "7.0.1" + shellcheck "1.0.0" + yarnhook "0.5.1" + +create-eslint-index@^1.0.0: + version "1.0.0" + resolved "1.0.0#d954372d86d5792fcd67e9f2b791b1ab162411bb" + integrity sha1-2VQ3LYbVeS/NZ+nyt5GxqxYkEbs= + dependencies: + lodash.get "^4.3.0" + +cz-emoji-conventional@1.0.1: + version "1.0.1" + resolved "1.0.1" + integrity sha512-jY+jmmbQ9n671gLWSSI34a7efDz1YPfzM3QZC5T+r8CJCCo1/myW8Ik09NV9KQ5hVSc2BBfvcq7e5IsBOZjyjg== + dependencies: + chalk "^4.1.1" + commitizen "^4.2.4" + word-wrap "^1.2.3" + +cz-emoji-conventional@^1.0.1: + version "1.0.1" + resolved "1.0.1" + integrity sha512-jY+jmmbQ9n671gLWSSI34a7efDz1YPfzM3QZC5T+r8CJCCo1/myW8Ik09NV9KQ5hVSc2BBfvcq7e5IsBOZjyjg== + dependencies: + chalk "^4.1.1" + commitizen "^4.2.4" + word-wrap "^1.2.3" + +eslint-ast-utils@^1.0.0: + version "1.1.0" + resolved "1.1.0" + integrity sha512-otzzTim2/1+lVrlH19EfQQJEhVJSu0zOb9ygb3iapN6UlyaDtyRq4b5U1FuW0v1lRa9Fp/GJyHkSwm6NqABgCA== + dependencies: + lodash.get "^4.4.2" + lodash.zip "^4.2.0" + +eslint-config-prettier@^8.3.0: + version "8.3.0" + resolved "8.3.0" + integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== + +eslint-config-strict-mode@latest: + version "1.0.41" + resolved "1.0.41" + integrity sha512-dodnJvuUF3LEwF+7GJai6PMBB/Qqgd2CYJqwD1TCRkkS72zYZ2vJWkMfpQ/KPUH4kbXRnsiZUAlBOY5/c7ThFA== + dependencies: + "@typescript-eslint/eslint-plugin" "^5.11.0" + "@typescript-eslint/parser" "^5.11.0" + eslint "^8.9.0" + eslint-config-prettier "^8.3.0" + eslint-formatter-git-log "^0.5.3" + eslint-formatter-gitlab "^3.0.0" + eslint-formatter-pretty "^4.1.0" + eslint-formatter-summary "^1.1.0" + eslint-plugin-angular "^4.1.0" + eslint-plugin-array-func "^3.1.7" + eslint-plugin-editorconfig "^3.2.0" + eslint-plugin-eslint-comments "^3.2.0" + eslint-plugin-etc "^2.0.2" + eslint-plugin-ext "^0.1.0" + eslint-plugin-filenames "^1.3.2" + eslint-plugin-fp "^2.3.0" + eslint-plugin-functional "^4.2.0" + eslint-plugin-html "^6.2.0" + eslint-plugin-import "^2.25.4" + eslint-plugin-jest "^25.7.0" + eslint-plugin-jest-async "^1.0.3" + eslint-plugin-jest-dom "^3.9.4" + eslint-plugin-jest-formatting "^3.1.0" + eslint-plugin-jsdoc "^37.9.0" + eslint-plugin-json-schema-validator "^1.2.49" + eslint-plugin-jsonc "^1.7.0" + eslint-plugin-no-constructor-bind "^2.0.4" + eslint-plugin-no-explicit-type-exports "^0.12.1" + eslint-plugin-no-secrets "^0.8.9" + eslint-plugin-no-unsanitized "^3.2.0" + eslint-plugin-no-use-extend-native "^0.5.0" + eslint-plugin-node "^11.1.0" + eslint-plugin-optimize-regex "^1.2.1" + eslint-plugin-prefer-arrow "^1.2.3" + eslint-plugin-prettier "^4.0.0" + eslint-plugin-promise "^5.2.0" + eslint-plugin-regexp "^1.5.1" + eslint-plugin-rxjs "^4.0.4" + eslint-plugin-security "^1.4.0" + eslint-plugin-sonarjs "^0.10.0" + eslint-plugin-sort-class-members "^1.14.1" + eslint-plugin-sort-keys-fix "^1.1.2" + eslint-plugin-switch-case "^1.1.2" + eslint-plugin-toml "^0.2.0" + eslint-plugin-tsdoc "^0.2.14" + eslint-plugin-typescript-sort-keys "^2.1.0" + eslint-plugin-unicorn "^37.0.1" + eslint-plugin-unused-imports "^1.1.5" + eslint-plugin-woke "^1.0.1" + eslint-plugin-yml "^0.10.1" + parse-gitignore "^1.0.1" + tslib "^2.3.1" + yaml "^1.10.2" + +eslint-etc@^5.1.0: + version "5.1.0" + resolved "5.1.0" + integrity sha512-Rmjl01h5smi5cbsFne2xpTuch2xNnwXiX2lbS4HttXUN5FwXKAwG1UEFBVGO1nC091YO/QyVahyfNPJSX2ae+g== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + tsutils "^3.17.1" + tsutils-etc "^1.4.1" + +eslint-formatter-git-log@^0.5.3: + version "0.5.3" + resolved "0.5.3" + integrity sha512-lwYPyg6fq6IQyNwLHkls1sjIXNWvY6RQx8S8hLTcgC+ldKYHd8Dc0G1qBpbQXoBFBrUmz73ZNyP1lMsAP8A33A== + dependencies: + chalk "2.4.2" + +eslint-formatter-gitlab@^3.0.0: + version "3.0.0" + resolved "3.0.0" + integrity sha512-fqZ2G45rgbrHcFunqmwuG5Qo6QAOlxEsR+KdOP08T1Xegw5tJhHh9KFWMSct8q6x8xCMUyYGHypZd342bLUttA== + dependencies: + chalk "^4.0.0" + js-yaml "^4.0.0" + +eslint-formatter-pretty@^4.1.0: + version "4.1.0" + resolved "4.1.0" + integrity sha512-IsUTtGxF1hrH6lMWiSl1WbGaiP01eT6kzywdY1U+zLc0MP+nwEnUiS9UI8IaOTUhTeQJLlCEWIbXINBH4YJbBQ== + dependencies: + "@types/eslint" "^7.2.13" + ansi-escapes "^4.2.1" + chalk "^4.1.0" + eslint-rule-docs "^1.1.5" + log-symbols "^4.0.0" + plur "^4.0.0" + string-width "^4.2.0" + supports-hyperlinks "^2.0.0" + +eslint-formatter-summary@^1.1.0: + version "1.1.0" + resolved "1.1.0" + integrity sha512-teOh471ZYeEShXhBFb16X87buUjNZa2TMNn3CSf//DIKLNneqbk7u5i9hDDiIaQVvZbBXJHkgYxj8urcNKWbXw== + dependencies: + chalk "^4.1.0" + core-js "^3.9.1" + optionalDependencies: + np "^7.4.0" + +eslint-import-resolver-node@^0.3.2: + version "0.3.6" + resolved "0.3.6" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== + dependencies: + debug "^3.2.7" + resolve "^1.20.0" + +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "0.3.6" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== + dependencies: + debug "^3.2.7" + resolve "^1.20.0" + +eslint-module-utils@^2.4.1: + version "2.7.3" + resolved "2.7.3" + integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== + dependencies: + debug "^3.2.7" + find-up "^2.1.0" + +eslint-module-utils@^2.7.2: + version "2.7.3" + resolved "2.7.3" + integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== + dependencies: + debug "^3.2.7" + find-up "^2.1.0" + +eslint-plugin-angular@^4.1.0: + version "4.1.0" + resolved "4.1.0" + integrity sha512-dacledMPxVOZA3T0xcYFuvrMCy5dHxg0ZTMWUaHqSBQef3/XLyXJ9s1LNj0NikJ/dYx6OhqlnnNpKmrJhEUB+Q== + +eslint-plugin-array-func@^3.1.7: + version "3.1.7" + resolved "3.1.7" + integrity sha512-fB5TBICjHSTGToNTbCCgR8zsngpUkoCM31EMh/M/NEAyNg90i5rUuG0dnNNBML2n0BzM0nBE3sPvo2SEWf6jlA== + +eslint-plugin-editorconfig@^3.2.0: + version "3.2.0" + resolved "3.2.0" + integrity sha512-XiUg69+qgv6BekkPCjP8+2DMODzPqtLV5i0Q9FO1v40P62pfodG1vjIihVbw/338hS5W26S+8MTtXaAlrg37QQ== + dependencies: + "@typescript-eslint/eslint-plugin" "^5.0.0" + editorconfig "^0.15.0" + eslint "^8.0.1" + klona "^2.0.4" + +eslint-plugin-es@^3.0.0: + version "3.0.1" + resolved "3.0.1" + integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + +eslint-plugin-eslint-comments@^3.2.0: + version "3.2.0" + resolved "3.2.0" + integrity sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ== + dependencies: + escape-string-regexp "^1.0.5" + ignore "^5.0.5" + +eslint-plugin-etc@^2.0.2: + version "2.0.2" + resolved "2.0.2" + integrity sha512-g3b95LCdTCwZA8On9EICYL8m1NMWaiGfmNUd/ftZTeGZDXrwujKXUr+unYzqKjKFo1EbqJ31vt+Dqzrdm/sUcw== + dependencies: + "@phenomnomnominal/tsquery" "^4.0.0" + "@typescript-eslint/experimental-utils" "^5.0.0" + eslint-etc "^5.1.0" + requireindex "~1.2.0" + tslib "^2.0.0" + tsutils "^3.0.0" + +eslint-plugin-ext@^0.1.0: + version "0.1.0" + resolved "0.1.0" + integrity sha512-CbZgte+kC8u6uymkwtgDPHLgA3IRbhermH88o9VXDh4Pa1ds1QIo0ojJc+mvq5zjf3mm4GT/pTTFYZT9nQORyg== + +eslint-plugin-filenames@^1.3.2: + version "1.3.2" + resolved "1.3.2" + integrity sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w== + dependencies: + lodash.camelcase "4.3.0" + lodash.kebabcase "4.1.1" + lodash.snakecase "4.1.1" + lodash.upperfirst "4.3.1" + +eslint-plugin-fp@^2.3.0: + version "2.3.0" + resolved "2.3.0#376d2a108710e981980bdc3875e3b9920da0489c" + integrity sha1-N20qEIcQ6YGYC9w4deO5kg2gSJw= + dependencies: + create-eslint-index "^1.0.0" + eslint-ast-utils "^1.0.0" + lodash "^4.13.1" + req-all "^0.1.0" + +eslint-plugin-functional@^4.2.0: + version "4.2.0" + resolved "4.2.0" + integrity sha512-3v1DuKQTGwJo93UQ5SKzEjvJTaMGfznzwgGjWEBhLXxJfOMhcW7O6QUO1pmb5aLou9hoh7r31lkPvWmbIbIbew== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + deepmerge-ts "^2.0.1" + escape-string-regexp "^4.0.0" + +eslint-plugin-html@^6.2.0: + version "6.2.0" + resolved "6.2.0" + integrity sha512-vi3NW0E8AJombTvt8beMwkL1R/fdRWl4QSNRNMhVQKWm36/X0KF0unGNAY4mqUF06mnwVWZcIcerrCnfn9025g== + dependencies: + htmlparser2 "^7.1.2" + +eslint-plugin-import@^2.25.4: + version "2.25.4" + resolved "2.25.4" + integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== + dependencies: + array-includes "^3.1.4" + array.prototype.flat "^1.2.5" + debug "^2.6.9" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.6" + eslint-module-utils "^2.7.2" + has "^1.0.3" + is-core-module "^2.8.0" + is-glob "^4.0.3" + minimatch "^3.0.4" + object.values "^1.1.5" + resolve "^1.20.0" + tsconfig-paths "^3.12.0" + +eslint-plugin-jest-async@^1.0.3: + version "1.0.3" + resolved "1.0.3#69196702d8af85655b21a789a6fd75ca0a37800f" + integrity sha1-aRlnAtivhWVbIaeJpv11ygo3gA8= + dependencies: + requireindex "~1.1.0" + +eslint-plugin-jest-dom@^3.9.4: + version "3.9.4" + resolved "3.9.4" + integrity sha512-VRkaALGIhyxinnewZFHe2WJsRWp3TONpXysVXK1IUNJHCpJAIM9yRrI7fQ8i5F6UYE7+DAnvNhSSJZesLTonug== + dependencies: + "@babel/runtime" "^7.16.3" + "@testing-library/dom" "^7.31.2" + requireindex "^1.2.0" + +eslint-plugin-jest-formatting@^3.1.0: + version "3.1.0" + resolved "3.1.0" + integrity sha512-XyysraZ1JSgGbLSDxjj5HzKKh0glgWf+7CkqxbTqb7zEhW7X2WHo5SBQ8cGhnszKN+2Lj3/oevBlHNbHezoc/A== + +eslint-plugin-jest@^25.7.0: + version "25.7.0" + resolved "25.7.0" + integrity sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + +eslint-plugin-jsdoc@^37.9.0: + version "37.9.4" + resolved "37.9.4" + integrity sha512-VxCyGgUNNnj2T4bb1OqltkbsPp3ehRzR5onIfh6zGrAvISmvgX/sbxUlh3YyGqWtjOTSBCURdKdmelSXEIHnlA== + dependencies: + "@es-joy/jsdoccomment" "~0.20.1" + comment-parser "1.3.0" + debug "^4.3.3" + escape-string-regexp "^4.0.0" + esquery "^1.4.0" + regextras "^0.8.0" + semver "^7.3.5" + spdx-expression-parse "^3.0.1" + +eslint-plugin-json-schema-validator@^1.2.49: + version "1.2.49" + resolved "1.2.49" + integrity sha512-Ib7PVrho5a08OI05VyVEkwb2u1jDWeDoHsIngaZFVi4OsnLObcW6gb3xO6VSYHUDvC5CTQans7vVjLUslcCMiA== + dependencies: + ajv "^8.0.0" + debug "^4.3.1" + eslint-utils "^3.0.0" + json-schema-migrate "^2.0.0" + jsonc-eslint-parser "^1.0.0" + minimatch "^3.0.4" + toml-eslint-parser "^0.2.1" + tunnel-agent "^0.6.0" + yaml-eslint-parser "^0.4.0" + +eslint-plugin-jsonc@^1.7.0: + version "1.7.0" + resolved "1.7.0" + integrity sha512-pb3CAD9B0zhv3r9Bg9AdzswL50I3mbIq1ys+tNeuaDeibFlweo84SBNm22oqaFx/Dka+YZw2SLukAkQlJzSHMQ== + dependencies: + eslint-utils "^2.1.0 || ^3.0.0" + jsonc-eslint-parser "^1.4.1" + natural-compare "^1.4.0" + +eslint-plugin-no-constructor-bind@^2.0.4: + version "2.0.4" + resolved "2.0.4" + integrity sha512-r0CGAE5SrRYt1OdACNiZGiOcBbFslKIPnMrFo3kPmX3iKZOm8HRD2eIbqhlc9lSSiBWcPZxXErXnroqgt+dKBg== + dependencies: + requireindex "~1.2.0" + +eslint-plugin-no-explicit-type-exports@^0.12.1: + version "0.12.1" + resolved "0.12.1" + integrity sha512-m1v/f+LYVygCY735KfCovkoXYPbZH5zxEj/tuLOnMwX/qbJEJoRb9evul88Ois5HidvKbiMdMg/tXU55Ki++jg== + dependencies: + "@typescript-eslint/experimental-utils" "^2.27.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.1" + +eslint-plugin-no-secrets@^0.8.9: + version "0.8.9" + resolved "0.8.9" + integrity sha512-CqaBxXrImABCtxMWspAnm8d5UKkpNylC7zqVveb+fJHEvsSiNGJlSWzdSIvBUnW1XhJXkzifNIZQC08rEII5Ng== + +eslint-plugin-no-unsanitized@^3.2.0: + version "3.2.0" + resolved "3.2.0" + integrity sha512-92opuXbjWmXcod94EyCKhp36V1QHLM/ArAST2ssgKOojALne0eZvSPfrg4oyr0EwTXvy0RJNe/Tkm33VkDUrKQ== + +eslint-plugin-no-use-extend-native@^0.5.0: + version "0.5.0" + resolved "0.5.0" + integrity sha512-dBNjs8hor8rJgeXLH4HTut5eD3RGWf9JUsadIfuL7UosVQ/dnvOKwxEcRrXrFxrMZ8llUVWT+hOimxJABsAUzQ== + dependencies: + is-get-set-prop "^1.0.0" + is-js-type "^2.0.0" + is-obj-prop "^1.0.0" + is-proto-prop "^2.0.0" + +eslint-plugin-node@^11.1.0: + version "11.1.0" + resolved "11.1.0" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== + dependencies: + eslint-plugin-es "^3.0.0" + eslint-utils "^2.0.0" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-optimize-regex@^1.2.1: + version "1.2.1" + resolved "1.2.1" + integrity sha512-fUaU7Tj1G/KSTDTABJw4Wp427Rl7RPl9ViYTu1Jrv36fJw4DFhd4elPdXiuYtdPsNsvzn9GcVlKEssGIVjw0UQ== + dependencies: + regexp-tree "^0.1.21" + +eslint-plugin-prefer-arrow@^1.2.3: + version "1.2.3" + resolved "1.2.3" + integrity sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ== + +eslint-plugin-prettier@^4.0.0: + version "4.0.0" + resolved "4.0.0" + integrity sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-promise@^5.2.0: + version "5.2.0" + resolved "5.2.0" + integrity sha512-SftLb1pUG01QYq2A/hGAWfDRXqYD82zE7j7TopDOyNdU+7SvvoXREls/+PRTY17vUXzXnZA/zfnyKgRH6x4JJw== + +eslint-plugin-regexp@^1.5.1: + version "1.5.1" + resolved "1.5.1" + integrity sha512-5v0rQIi54m2KycQHqmOAHrZhvI56GHmI2acr6zEffAqfeifTtobAEapv9Uf4o8//lGvwVkHKyjLoSbBNEFcfOA== + dependencies: + comment-parser "^1.1.2" + eslint-utils "^3.0.0" + grapheme-splitter "^1.0.4" + jsdoctypeparser "^9.0.0" + refa "^0.9.0" + regexp-ast-analysis "^0.3.0" + regexpp "^3.2.0" + scslre "^0.1.6" + +eslint-plugin-rxjs@^4.0.4: + version "4.0.4" + resolved "4.0.4" + integrity sha512-7JopYQRqS5TYBNXioTLtS+W6+IKC1rsC7q8Dtou8E5gMuPxuEFqxU1x2161bwadaK3+h6sR+xiGjEhiE6JwjUA== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + common-tags "^1.8.0" + decamelize "^5.0.0" + eslint-etc "^5.1.0" + requireindex "~1.2.0" + rxjs-report-usage "^1.0.4" + tslib "^2.0.0" + tsutils "^3.0.0" + tsutils-etc "^1.4.1" + +eslint-plugin-security@^1.4.0: + version "1.4.0" + resolved "1.4.0" + integrity sha512-xlS7P2PLMXeqfhyf3NpqbvbnW04kN8M9NtmhpR3XGyOvt/vNKS7XPXT5EDbwKW9vCjWH4PpfQvgD/+JgN0VJKA== + dependencies: + safe-regex "^1.1.0" + +eslint-plugin-sonarjs@^0.10.0: + version "0.10.0" + resolved "0.10.0" + integrity sha512-FBRIBmWQh2UAfuLSnuYEfmle33jIup9hfkR0X8pkfjeCKNpHUG8qyZI63ahs3aw8CJrv47QJ9ccdK3ZxKH016A== + +eslint-plugin-sort-class-members@^1.14.1: + version "1.14.1" + resolved "1.14.1" + integrity sha512-/Q/cm3h4N9DBNYvJMQMhluucSmr3Yydr9U0BgGcXUQe/rgWdXKSymZ5Ewcf4vmAG0bbTmAYmekuMnYYrqlu9Rg== + +eslint-plugin-sort-keys-fix@^1.1.2: + version "1.1.2" + resolved "1.1.2" + integrity sha512-DNPHFGCA0/hZIsfODbeLZqaGY/+q3vgtshF85r+YWDNCQ2apd9PNs/zL6ttKm0nD1IFwvxyg3YOTI7FHl4unrw== + dependencies: + espree "^6.1.2" + esutils "^2.0.2" + natural-compare "^1.4.0" + requireindex "~1.2.0" + +eslint-plugin-switch-case@^1.1.2: + version "1.1.2" + resolved "1.1.2#a622db0c4c440828526b6f26f000d71e74850032" + integrity sha1-piLbDExECChSa28m8ADXHnSFADI= + dependencies: + lodash.last "^3.0.0" + lodash.zipobject "^4.0.0" + +eslint-plugin-toml@^0.2.0: + version "0.2.0" + resolved "0.2.0" + integrity sha512-Y4eGb9q7i0i+UyZAXl3QAqcxkds2X7tfctXPllL7X+PpBXD/3wiqq1RNeoiLmHI9evpO1BjyuakffJZuGM7ElA== + dependencies: + debug "^4.1.1" + lodash "^4.17.19" + toml-eslint-parser "^0.2.1" + +eslint-plugin-tsdoc@^0.2.14: + version "0.2.14" + resolved "0.2.14" + integrity sha512-fJ3fnZRsdIoBZgzkQjv8vAj6NeeOoFkTfgosj6mKsFjX70QV256sA/wq+y/R2+OL4L8E79VVaVWrPeZnKNe8Ng== + dependencies: + "@microsoft/tsdoc" "0.13.2" + "@microsoft/tsdoc-config" "0.15.2" + +eslint-plugin-typescript-sort-keys@^2.1.0: + version "2.1.0" + resolved "2.1.0" + integrity sha512-ET7ABypdz19m47QnKynzNfWPi4CTNQ5jQQC1X5d0gojIwblkbGiCa5IilsqzBTmqxZ0yXDqKBO/GBkBFQCOFsg== + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + json-schema "^0.4.0" + natural-compare-lite "^1.4.0" + +eslint-plugin-unicorn@^37.0.1: + version "37.0.1" + resolved "37.0.1" + integrity sha512-E1jq5u9ojnadisJcPi+hMXTGSiIzkIUMDvWsBudsCGXvKUB2aNSU2TcfyW2/jAS5A4ryBXfzxLykMxX1EdluSQ== + dependencies: + "@babel/helper-validator-identifier" "^7.14.9" + ci-info "^3.2.0" + clean-regexp "^1.0.0" + eslint-template-visitor "^2.3.2" + eslint-utils "^3.0.0" + esquery "^1.4.0" + indent-string "4" + is-builtin-module "^3.1.0" + lodash "^4.17.21" + pluralize "^8.0.0" + read-pkg-up "^7.0.1" + regexp-tree "^0.1.23" + safe-regex "^2.1.1" + semver "^7.3.5" + strip-indent "^3.0.0" + +eslint-plugin-unused-imports@^1.1.5: + version "1.1.5" + resolved "1.1.5" + integrity sha512-TeV8l8zkLQrq9LBeYFCQmYVIXMjfHgdRQLw7dEZp4ZB3PeR10Y5Uif11heCsHRmhdRIYMoewr1d9ouUHLbLHew== + dependencies: + eslint-rule-composer "^0.3.0" + +eslint-plugin-woke@^1.0.1: + version "1.0.1" + resolved "1.0.1" + integrity sha512-cDRyZMNJkrbycju6OKhGnF29j0kbzV9AnLk3IPz6Kn0vWrSf8RyUdS5at97o/CJ8SEN2e6furQch1o/0LIrfMg== + dependencies: + requireindex "~1.1.0" + +eslint-plugin-yml@^0.10.1: + version "0.10.1" + resolved "0.10.1" + integrity sha512-af0WgO3qaH+RW6jv1s6RzXKlg2NZLisN95lqGUf1KqBT6rEJyGSCpM49QYaSTvzmMaB/gcdbrnAfNoYwUn0Yig== + dependencies: + debug "^4.1.1" + lodash "^4.17.19" + natural-compare "^1.4.0" + yaml-eslint-parser "^0.4.0" + +eslint-rule-composer@^0.3.0: + version "0.3.0" + resolved "0.3.0" + integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg== + +eslint-rule-docs@^1.1.5: + version "1.1.231" + resolved "1.1.231" + integrity sha512-egHz9A1WG7b8CS0x1P6P/Rj5FqZOjray/VjpJa14tMZalfRKvpE2ONJ3plCM7+PcinmU4tcmbPLv0VtwzSdLVA== + +eslint-scope@^5.1.1: + version "7.1.1" + resolved "7.1.1" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-scope@^7.1.1: + version "7.1.1" + resolved "7.1.1" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-template-visitor@^2.3.2: + version "2.3.2" + resolved "2.3.2" + integrity sha512-3ydhqFpuV7x1M9EK52BPNj6V0Kwu0KKkcIAfpUhwHbR8ocRln/oUHgfxQupY8O1h4Qv/POHDumb/BwwNfxbtnA== + dependencies: + "@babel/core" "^7.12.16" + "@babel/eslint-parser" "^7.12.16" + eslint-visitor-keys "^2.0.0" + esquery "^1.3.1" + multimap "^1.1.0" + +eslint-utils@^2.0.0: + version "3.0.0" + resolved "3.0.0" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-utils@^2.1.0: + version "3.0.0" + resolved "3.0.0" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +"eslint-utils@^2.1.0 || ^3.0.0": + version "3.0.0" + resolved "3.0.0" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "3.0.0" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^1.3.0: + version "3.3.0" + resolved "3.3.0" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint-visitor-keys@^2.0.0: + version "3.3.0" + resolved "3.3.0" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint-visitor-keys@^2.1.0: + version "3.3.0" + resolved "3.3.0" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint-visitor-keys@^3.0.0: + version "3.3.0" + resolved "3.3.0" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "3.3.0" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.0.1: + version "8.9.0" + resolved "8.9.0" + integrity sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q== + dependencies: + "@eslint/eslintrc" "^1.1.0" + "@humanwhocodes/config-array" "^0.9.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.6.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +eslint@^8.9.0: + version "8.9.0" + resolved "8.9.0" + integrity sha512-PB09IGwv4F4b0/atrbcMFboF/giawbBLVC7fyDamk5Wtey4Jh2K+rYaBhCAbUyEI4QzB1ly09Uglc9iCtFaG2Q== + dependencies: + "@eslint/eslintrc" "^1.1.0" + "@humanwhocodes/config-array" "^0.9.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.6.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +git-cz-emoji@latest: + version "1.1.24" + resolved "1.1.24" + integrity sha512-m9XItxRoL6k/NCAeWubMJW2m3r29V6JkQSpZk5vtFY6IVJ8S4l6l7AVreOsRemKoWiVzxtb2I4xBbJNt1vlrNw== + dependencies: + chalk "^4.1.2" + commitizen "^4.2.4" + tslib "^2.3.1" + word-wrap "^1.2.3" + +glob@^7.2.0: + version "7.2.0" + resolved "7.2.0" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +handlebars-helpers@^0.10.0: + version "0.10.0" + resolved "0.10.0" + integrity sha512-QiyhQz58u/DbuV41VnfpE0nhy6YCH4vB514ajysV8SoKmP+DxU+pR+fahVyNECHj+jiwEN2VrvxD/34/yHaLUg== + dependencies: + arr-flatten "^1.1.0" + array-sort "^0.1.4" + create-frame "^1.0.0" + define-property "^1.0.0" + "falsey" "^0.3.2" + for-in "^1.0.2" + for-own "^1.0.0" + get-object "^0.2.0" + get-value "^2.0.6" + handlebars "^4.0.11" + handlebars-helper-create-frame "^0.1.0" + handlebars-utils "^1.0.6" + has-value "^1.0.0" + helper-date "^1.0.1" + helper-markdown "^1.0.0" + helper-md "^0.2.2" + html-tag "^2.0.0" + is-even "^1.0.0" + is-glob "^4.0.0" + is-number "^4.0.0" + kind-of "^6.0.0" + lazy-cache "^2.0.2" + logging-helpers "^1.0.0" + micromatch "^3.1.4" + relative "^3.0.2" + striptags "^3.1.0" + to-gfm-code-block "^0.1.1" + year "^0.2.1" + +jsonc-eslint-parser@^1.0.0: + version "1.4.1" + resolved "1.4.1" + integrity sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg== + dependencies: + acorn "^7.4.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^1.3.0" + espree "^6.0.0" + semver "^6.3.0" + +jsonc-eslint-parser@^1.4.1: + version "1.4.1" + resolved "1.4.1" + integrity sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg== + dependencies: + acorn "^7.4.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^1.3.0" + espree "^6.0.0" + semver "^6.3.0" + +prettier-config-sexy-mode@latest: + version "1.0.1" + resolved "1.0.1" + integrity sha512-6ZdAv6LW2vq++c5fqoh2MWrIZk9fi67o608xizEPYKPMjGUNDsqjJ/pYiJLp7yDHgGerEERc4bRQqlh8a9W6YA== + dependencies: + "@prettier/plugin-php" "^0.17.6" + "@prettier/plugin-pug" "^1.19.2" + "@prettier/plugin-ruby" "^2.0.0" + "@prettier/plugin-xml" "^1.0.2" + prettier "^2.4.1" + prettier-plugin-go-template "^0.0.11" + prettier-plugin-ini "^0.3.1" + prettier-plugin-java "^1.6.1" + prettier-plugin-jsdoc "^0.3.30" + prettier-plugin-organize-imports "^2.3.4" + prettier-plugin-package-perfection "^1.0.1" + prettier-plugin-properties "^0.1.0" + prettier-plugin-sh "^0.7.1" + prettier-plugin-solidity "^1.0.0-beta.19" + prettier-plugin-sql "^0.4.1" + tslib "^2.3.1" + +prettier-plugin-go-template@^0.0.11: + version "0.0.11" + resolved "0.0.11" + integrity sha512-qtgoEjvbgmcDp9TOqYNgrPrA41s6S1UMyzMqjcxdxQahTX0webWfbamyA/x3XeBFEEJmgXrRAirzJrIVzImsMg== + dependencies: + ulid "^2.3.0" + +prettier-plugin-ini@^0.3.1: + version "0.3.1" + resolved "0.3.1" + integrity sha512-p2MPOpkf0ebBme4n8U3TD7/uC3azlEyPgbfgU/l5SCgAvSKmXmRMSxKxonhSoQAGtdwQWcEADJbmzl+X72yN4Q== + dependencies: + prettier ">=2.0" + +prettier-plugin-java@^1.6.1: + version "1.6.1" + resolved "1.6.1" + integrity sha512-kSY17V/P88nILlILb5iMp16TVJy6Ls9Jy4zAzI4+GsEuRDZH5VqRuLd8aJS1ImWxVgRjNBmoFOjxYyxkRM0SRA== + dependencies: + java-parser "2.0.1" + lodash "4.17.21" + prettier "2.3.1" + +prettier-plugin-jsdoc@^0.3.30: + version "0.3.30" + resolved "0.3.30" + integrity sha512-BTBojOMmrUA1qsWLpJN5whUfU/E72WBUQAB5AvrDkha+O8TxmqaAivnuW+87ItYGRPBFWWzj2r5iWELhBml1Ag== + dependencies: + binary-search-bounds "^2.0.5" + comment-parser "^1.1.4" + linguist-languages "^7.13.0" + mdast-util-from-markdown "^0.8.5" + +prettier-plugin-organize-imports@^2.3.4: + version "2.3.4" + resolved "2.3.4" + integrity sha512-R8o23sf5iVL/U71h9SFUdhdOEPsi3nm42FD/oDYIZ2PQa4TNWWuWecxln6jlIQzpZTDMUeO1NicJP6lLn2TtRw== + +prettier-plugin-package-perfection@^1.0.1: + version "1.0.6" + resolved "1.0.6" + integrity sha512-7nxpRYdAdda6xiwrVDYDxcnxPfBu58JJPGtoJOwh2uohE6RfnUJOOjV76woXG8kO+mBptHAQtqoT6lsLRvatqA== + dependencies: + prettier-package-json "2.6.0" + +prettier-plugin-properties@^0.1.0: + version "0.1.0" + resolved "0.1.0" + integrity sha512-lObSgVaTVWSyYWxKMOzRGmvQp64S1qumu5vS91ZlMc198ay8EGUuDH+Tc019iMJXc2KNpdAYif2qAJA6mjTkgA== + dependencies: + dot-properties "^1.0.0" + linguist-languages "^7.9.0" + +prettier-plugin-sh@^0.7.1: + version "0.7.1" + resolved "0.7.1" + integrity sha512-2MWRdGOSz0yf/z2kTKF1AqxDuH9MZD8faoDAz5ySGphxssi9oyM3Ys+jp7AfqsCXvGUDbRA4EJOlKS0yZKAW6w== + dependencies: + mvdan-sh "^0.5.0" + +prettier-plugin-solidity@^1.0.0-beta.19: + version "1.0.0-beta.19" + resolved "1.0.0-beta.19" + integrity sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g== + dependencies: + "@solidity-parser/parser" "^0.14.0" + emoji-regex "^10.0.0" + escape-string-regexp "^4.0.0" + semver "^7.3.5" + solidity-comments-extractor "^0.0.7" + string-width "^4.2.3" + +prettier-plugin-sql@^0.4.1: + version "0.4.1" + resolved "0.4.1" + integrity sha512-HSBn1f1ZbFIqWm9vVJcdQl1tzdh8qYZbBNK7z3iWhDO/DXM7gZH2HnS5r99e3tYqf34WIdo2nSCWaqylo+5fPw== + dependencies: + node-sql-parser "^4.0.0" + sql-formatter "^4.0.2" + +semantic-release-config@latest: + version "1.1.18" + resolved "1.1.18" + integrity sha512-NtEpAQ7eOJD5kdv2EqdLWuH04vzNR2DVYNqVQgDnzDqM7X2RVeszSo10mLUyY8GpTgqOQTYqRfE4MmX+RVU2gA== + dependencies: + "@semantic-release/changelog" "^6.0.1" + "@semantic-release/commit-analyzer" "^9.0.2" + "@semantic-release/exec" "^6.0.3" + "@semantic-release/git" "^10.0.1" + "@semantic-release/gitlab" "^7.0.4" + "@semantic-release/npm" "^9.0.0" + "@semantic-release/release-notes-generator" "^10.0.3" + conventional-changelog-emoji-config latest + git-cz-emoji latest + semantic-release "^19.0.2" + semantic-release-gh latest + semantic-release-npm-deprecate-old-versions "^1.3.2" + semantic-release-python latest + tslib "^2.3.1" + yaml "^1.10.2" + +toml-eslint-parser@^0.2.1: + version "0.2.1" + resolved "0.2.1" + integrity sha512-lAESSx47NjCe/O9Y5dbAmP+U/EFvdxPRgA0Hp4TxCZP3Bs6hxRfLHxZvBuXvTGkVoK0DpYamPz/rCqYBzoFWVQ== + dependencies: + eslint-visitor-keys "^2.0.0" + +typescript@^4.5.5: + version "4.5.5" + resolved "4.5.5" + integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA== + +yaml-eslint-parser@^0.4.0: + version "0.4.1" + resolved "0.4.1" + integrity sha512-GoJ/p1EW8O2tbTbuhfxjo1XhfUFU3uX3kwvfEQoOaZjO2Lubx8POjlsSqB+18b3SxkujAdQYT9r9nURaUWNYWQ== + dependencies: + eslint-visitor-keys "^2.1.0" + lodash "^4.17.20" + yaml "^1.10.0"