diff --git a/.config/docs/start.sh b/.config/docs/start.sh new file mode 100644 index 00000000..c380a48b --- /dev/null +++ b/.config/docs/start.sh @@ -0,0 +1,334 @@ +#!/usr/bin/env bash + +# @file .config/scripts/start.sh +# @brief Ensures Task is installed and up-to-date and then runs `task start` +# @description +# This script will ensure [Task](https://github.com/go-task/task) is up-to-date +# and then run the `start` task which is generally a good entrypoint for any repository +# that is using the Megabyte Labs templating/taskfile system. The `start` task will +# ensure that the latest upstream changes are retrieved, that the project is +# properly generated with them, and that all the development dependencies are installed. + +set -eo pipefail + +# @description Ensure .config/log is executable +if [ -f .config/log ]; then + chmod +x .config/log +fi + +# @description Installs package when user is root on Linux +# +# @example +# ensureRootPackageInstalled "sudo" +# +# @arg $1 string The name of the package that must be present +# +# @exitcode 0 The package was successfully installed +# @exitcode 1+ If there was an error, the package needs to be installed manually, or if the OS is unsupported +function ensureRootPackageInstalled() { + if ! type "$1" &> /dev/null; then + if [[ "$OSTYPE" == 'linux'* ]]; then + if [ -f "/etc/redhat-release" ]; then + yum update + yum install -y "$1" + elif [ -f "/etc/lsb-release" ]; then + apt update + apt install -y "$1" + elif [ -f "/etc/arch-release" ]; then + pacman update + pacman -S "$1" + elif [ -f "/etc/alpine-release" ]; then + apk update + apk add -y "$1" + fi + fi + fi +} + +# @description If the user is running this script as root, then create a new user named +# megabyte and restart the script with that user. This is required because Homebrew +# can only be invoked by non-root users. +if [ "$EUID" -eq 0 ] && [ -z "$INIT_CWD" ] && type useradd &> /dev/null; then + # shellcheck disable=SC2016 + echo 'INFO: Running as root - creating seperate user named `megabyte` to run script with' + echo "megabyte ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + useradd -m -s "$(which bash)" -c "Megabyte Labs Homebrew Account" megabyte + ensureRootPackageInstalled "sudo" + # shellcheck disable=SC2016 + echo 'INFO: Reloading the script with the `megabyte` user' + exec su megabyte "$0" -- "$@" +fi + +# @description Detect script paths +BASH_SRC="$(dirname "${BASH_SOURCE[0]}")" +SOURCE_PATH="$( + cd "$BASH_SRC" + pwd -P +)" +PROJECT_BASE_DIR="$SOURCE_PATH/../.." + +# @description Ensures ~/.local/bin is in the PATH variable on *nix machines and +# exits with an error on unsupported OS types +# +# @example +# ensureLocalPath +# +# @set PATH string The updated PATH with a reference to ~/.local/bin +# +# @noarg +# +# @exitcode 0 If the PATH was appropriately updated or did not need updating +# @exitcode 1+ If the OS is unsupported +function ensureLocalPath() { + if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux'* ]]; then + # shellcheck disable=SC2016 + PATH_STRING='PATH="$HOME/.local/bin:$PATH"' + mkdir -p "$HOME/.local/bin" + if grep -L "$PATH_STRING" "$HOME/.profile" > /dev/null; then + echo -e "${PATH_STRING}\n" >> "$HOME/.profile" + echo "INFO: Updated the PATH variable to include ~/.local/bin in $HOME/.profile" + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "FreeBSD support not added yet" && exit 1 + else + echo "System type not recognized" + fi +} + +# @description Ensures given package is installed on a system. +# +# @example +# ensurePackageInstalled "curl" +# +# @arg $1 string The name of the package that must be present +# +# @exitcode 0 The package(s) were successfully installed +# @exitcode 1+ If there was an error, the package needs to be installed manually, or if the OS is unsupported +function ensurePackageInstalled() { + if ! type "$1" &> /dev/null; then + if [[ "$OSTYPE" == 'darwin'* ]]; then + brew install "$1" + elif [[ "$OSTYPE" == 'linux'* ]]; then + if [ -f "/etc/redhat-release" ]; then + sudo yum update + sudo yum install -y "$1" + elif [ -f "/etc/lsb-release" ]; then + sudo apt update + sudo apt install -y "$1" + elif [ -f "/etc/arch-release" ]; then + sudo pacman update + sudo pacman -S "$1" + elif [ -f "/etc/alpine-release" ]; then + apk update + apk add -y "$1" + else + echo "ERROR: $1 is missing. Please install $1 to continue." && exit 1 + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "ERROR: System type not recognized" + fi + fi +} + +# @description Ensures the latest version of Task is installed to `/usr/local/bin` (or `~/.local/bin`, as +# a fallback. +# +# @example +# ensureTaskInstalled +# +# @noarg +# +# @exitcode 0 If the package is already present and up-to-date or if it was installed/updated +# @exitcode 1+ If the OS is unsupported or if there was an error either installing the package or setting the PATH +function ensureTaskInstalled() { + # @description Release API URL used to get the latest release's version + TASK_RELEASE_API="https://api.github.com/repos/go-task/task/releases/latest" + if ! type task &> /dev/null; then + if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl' ]]; then + installTask + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "ERROR: System type not recognized. You must install task manually." && exit 1 + fi + else + echo "INFO: Checking for latest version of Task" + CURRENT_VERSION="$(task --version | cut -d' ' -f3 | cut -c 2-)" + LATEST_VERSION="$(curl -s "$TASK_RELEASE_API" | grep tag_name | cut -c 17- | sed 's/\",//')" + if printf '%s\n%s\n' "$LATEST_VERSION" "$CURRENT_VERSION" | sort -V -c &> /dev/null; then + echo "INFO: Task is already up-to-date" + else + echo "INFO: A new version of Task is available (version $LATEST_VERSION)" + echo "INFO: The current version of Task installed is $CURRENT_VERSION" + # Replace with rm "$(which task)" &> /dev/null when ready + if ! type task &> /dev/null; then + installTask + else + echo "WARNING: Unable to remove previous version of Task" + fi + fi + fi +} + +# @description Helper function for ensureTaskInstalled that performs the installation of Task. +# +# @see ensureTaskInstalled +# +# @example +# installTask +# +# @noarg +# +# @exitcode 0 If Task installs/updates properly +# @exitcode 1+ If the installation fails +function installTask() { + # @description Release URL to use when downloading [Task](https://github.com/go-task/task) + TASK_RELEASE_URL="https://github.com/go-task/task/releases/latest" + CHECKSUM_DESTINATION=/tmp/megabytelabs/task_checksums.txt + CHECKSUMS_URL="$TASK_RELEASE_URL/download/task_checksums.txt" + DOWNLOAD_DESTINATION=/tmp/megabytelabs/task.tar.gz + TMP_DIR=/tmp/megabytelabs + if [[ "$OSTYPE" == 'darwin'* ]]; then + DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_darwin_amd64.tar.gz" + else + DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_linux_amd64.tar.gz" + fi + mkdir -p "$(dirname "$DOWNLOAD_DESTINATION")" + echo "INFO: Downloading latest version of Task" + curl -sSL "$DOWNLOAD_URL" -o "$DOWNLOAD_DESTINATION" + curl -sSL "$CHECKSUMS_URL" -o "$CHECKSUM_DESTINATION" + DOWNLOAD_BASENAME="$(basename "$DOWNLOAD_URL")" + DOWNLOAD_SHA256="$(grep "$DOWNLOAD_BASENAME" < "$CHECKSUM_DESTINATION" | cut -d ' ' -f 1)" + sha256 "$DOWNLOAD_DESTINATION" "$DOWNLOAD_SHA256" > /dev/null + echo "SUCCESS: Validated checksum" + mkdir -p "$TMP_DIR/task" + tar -xzvf "$DOWNLOAD_DESTINATION" -C "$TMP_DIR/task" > /dev/null + if type task &> /dev/null && [ -w "$(which task)" ]; then + TARGET_DEST="$(which task)" + else + if [ -w /usr/local/bin ]; then + TARGET_BIN_DIR='/usr/local/bin' + else + TARGET_BIN_DIR="$HOME/.local/bin" + fi + TARGET_DEST="$TARGET_BIN_DIR/task" + mkdir -p "$TARGET_BIN_DIR" + fi + mv "$TMP_DIR/task/task" "$TARGET_DEST" + echo "SUCCESS: Installed Task to $TARGET_DEST" + rm "$CHECKSUM_DESTINATION" + rm "$DOWNLOAD_DESTINATION" +} + +# @description Verifies the SHA256 checksum of a file +# +# @example +# sha256 myfile.tar.gz 5b30f9c042553141791ec753d2f96786c60a4968fd809f75bb0e8db6c6b4529b +# +# @arg $1 string Path to the file +# @arg $2 string The SHA256 signature +# +# @exitcode 0 The checksum is valid or the system is unrecognized +# @exitcode 1+ The OS is unsupported or if the checksum is invalid +function sha256() { + echo "$2" + echo "$1" + if [[ "$OSTYPE" == 'darwin'* ]]; then + if type brew &> /dev/null && ! type sha256sum &> /dev/null; then + brew install coreutils + else + echo "WARNING: Brew is not installed - this may cause issues" + fi + if type brew &> /dev/null; then + PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH" + fi + if type sha256sum &> /dev/null; then + echo "$2 $1" | sha256sum -c + else + echo "WARNING: Checksum validation is being skipped for $1 because the sha256sum program is not available" + fi + elif [[ "$OSTYPE" == 'linux-gnu'* ]]; then + if ! type shasum &> /dev/null; then + echo "WARNING: Checksum validation is being skipped for $1 because the shasum program is not installed" + else + echo "$2 $1" | shasum -s -a 256 -c + fi + elif [[ "$OSTYPE" == 'linux-musl' ]]; then + if ! type sha256sum &> /dev/null; then + echo "WARNING: Checksum validation is being skipped for $1 because the sha256sum program is not available" + else + echo "$2 $1" | sha256sum -c + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "WARNING: System type not recognized. Skipping checksum validation." + fi +} + +##### Main Logic ##### + +if [ ! -f "$HOME/.profile" ]; then + touch "$HOME/.profile" +fi + +# @description Ensures ~/.local/bin is in PATH +ensureLocalPath + +# @description Ensures base dependencies are installed +if [[ "$OSTYPE" == 'darwin'* ]]; then + if ! type curl &> /dev/null && type brew &> /dev/null; then + brew install curl + else + echo "ERROR: Neither curl nor brew are installed. Install one of them manually and try again." + fi + if ! type git &> /dev/null; then + # shellcheck disable=SC2016 + echo 'INFO: Git is not present. A password may be required to run `sudo xcode-select --install`' + sudo xcode-select --install + fi +elif [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then + if ! type curl &> /dev/null || ! type git &> /dev/null; then + ensurePackageInstalled "curl" + ensurePackageInstalled "git" + fi +fi + +# @description Ensures Homebrew and Poetry are installed +if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then + if ! type brew &> /dev/null && [ -z "$INIT_CWD" ]; then + echo "WARNING: Homebrew is not installed. The script will attempt to install Homebrew and you might be prompted for your password." + bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + fi +fi + +# @description Attempts to pull the latest changes if the folder is a git repository +cd "$PROJECT_BASE_DIR" || exit +if [ -d .git ] && type git &> /dev/null; then + HTTPS_VERSION="$(git remote get-url origin | sed 's/git@gitlab.com:/https:\/\/gitlab.com\//')" + git pull "$HTTPS_VERSION" master --ff-only + git submodule update --init --recursive +fi + +# @description Ensures Task is installed and properly configured +ensureTaskInstalled + +# @description Run the start logic, if appropriate +cd "$PROJECT_BASE_DIR" || exit +if [ -z "$GITLAB_CI" ] && [ -z "$INIT_CWD" ]; then + # shellcheck disable=SC1091 + . "$HOME/.profile" + task start + # shellcheck disable=SC2028 + echo 'INFO: There may have been changes to your PATH variable. You may have to reload your terminal or run:\n\n`. '"$HOME/.profile"'`' +fi diff --git a/.config/docs/update-init.sh b/.config/docs/update-init.sh new file mode 100644 index 00000000..c380a48b --- /dev/null +++ b/.config/docs/update-init.sh @@ -0,0 +1,334 @@ +#!/usr/bin/env bash + +# @file .config/scripts/start.sh +# @brief Ensures Task is installed and up-to-date and then runs `task start` +# @description +# This script will ensure [Task](https://github.com/go-task/task) is up-to-date +# and then run the `start` task which is generally a good entrypoint for any repository +# that is using the Megabyte Labs templating/taskfile system. The `start` task will +# ensure that the latest upstream changes are retrieved, that the project is +# properly generated with them, and that all the development dependencies are installed. + +set -eo pipefail + +# @description Ensure .config/log is executable +if [ -f .config/log ]; then + chmod +x .config/log +fi + +# @description Installs package when user is root on Linux +# +# @example +# ensureRootPackageInstalled "sudo" +# +# @arg $1 string The name of the package that must be present +# +# @exitcode 0 The package was successfully installed +# @exitcode 1+ If there was an error, the package needs to be installed manually, or if the OS is unsupported +function ensureRootPackageInstalled() { + if ! type "$1" &> /dev/null; then + if [[ "$OSTYPE" == 'linux'* ]]; then + if [ -f "/etc/redhat-release" ]; then + yum update + yum install -y "$1" + elif [ -f "/etc/lsb-release" ]; then + apt update + apt install -y "$1" + elif [ -f "/etc/arch-release" ]; then + pacman update + pacman -S "$1" + elif [ -f "/etc/alpine-release" ]; then + apk update + apk add -y "$1" + fi + fi + fi +} + +# @description If the user is running this script as root, then create a new user named +# megabyte and restart the script with that user. This is required because Homebrew +# can only be invoked by non-root users. +if [ "$EUID" -eq 0 ] && [ -z "$INIT_CWD" ] && type useradd &> /dev/null; then + # shellcheck disable=SC2016 + echo 'INFO: Running as root - creating seperate user named `megabyte` to run script with' + echo "megabyte ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + useradd -m -s "$(which bash)" -c "Megabyte Labs Homebrew Account" megabyte + ensureRootPackageInstalled "sudo" + # shellcheck disable=SC2016 + echo 'INFO: Reloading the script with the `megabyte` user' + exec su megabyte "$0" -- "$@" +fi + +# @description Detect script paths +BASH_SRC="$(dirname "${BASH_SOURCE[0]}")" +SOURCE_PATH="$( + cd "$BASH_SRC" + pwd -P +)" +PROJECT_BASE_DIR="$SOURCE_PATH/../.." + +# @description Ensures ~/.local/bin is in the PATH variable on *nix machines and +# exits with an error on unsupported OS types +# +# @example +# ensureLocalPath +# +# @set PATH string The updated PATH with a reference to ~/.local/bin +# +# @noarg +# +# @exitcode 0 If the PATH was appropriately updated or did not need updating +# @exitcode 1+ If the OS is unsupported +function ensureLocalPath() { + if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux'* ]]; then + # shellcheck disable=SC2016 + PATH_STRING='PATH="$HOME/.local/bin:$PATH"' + mkdir -p "$HOME/.local/bin" + if grep -L "$PATH_STRING" "$HOME/.profile" > /dev/null; then + echo -e "${PATH_STRING}\n" >> "$HOME/.profile" + echo "INFO: Updated the PATH variable to include ~/.local/bin in $HOME/.profile" + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "FreeBSD support not added yet" && exit 1 + else + echo "System type not recognized" + fi +} + +# @description Ensures given package is installed on a system. +# +# @example +# ensurePackageInstalled "curl" +# +# @arg $1 string The name of the package that must be present +# +# @exitcode 0 The package(s) were successfully installed +# @exitcode 1+ If there was an error, the package needs to be installed manually, or if the OS is unsupported +function ensurePackageInstalled() { + if ! type "$1" &> /dev/null; then + if [[ "$OSTYPE" == 'darwin'* ]]; then + brew install "$1" + elif [[ "$OSTYPE" == 'linux'* ]]; then + if [ -f "/etc/redhat-release" ]; then + sudo yum update + sudo yum install -y "$1" + elif [ -f "/etc/lsb-release" ]; then + sudo apt update + sudo apt install -y "$1" + elif [ -f "/etc/arch-release" ]; then + sudo pacman update + sudo pacman -S "$1" + elif [ -f "/etc/alpine-release" ]; then + apk update + apk add -y "$1" + else + echo "ERROR: $1 is missing. Please install $1 to continue." && exit 1 + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "ERROR: System type not recognized" + fi + fi +} + +# @description Ensures the latest version of Task is installed to `/usr/local/bin` (or `~/.local/bin`, as +# a fallback. +# +# @example +# ensureTaskInstalled +# +# @noarg +# +# @exitcode 0 If the package is already present and up-to-date or if it was installed/updated +# @exitcode 1+ If the OS is unsupported or if there was an error either installing the package or setting the PATH +function ensureTaskInstalled() { + # @description Release API URL used to get the latest release's version + TASK_RELEASE_API="https://api.github.com/repos/go-task/task/releases/latest" + if ! type task &> /dev/null; then + if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl' ]]; then + installTask + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "ERROR: System type not recognized. You must install task manually." && exit 1 + fi + else + echo "INFO: Checking for latest version of Task" + CURRENT_VERSION="$(task --version | cut -d' ' -f3 | cut -c 2-)" + LATEST_VERSION="$(curl -s "$TASK_RELEASE_API" | grep tag_name | cut -c 17- | sed 's/\",//')" + if printf '%s\n%s\n' "$LATEST_VERSION" "$CURRENT_VERSION" | sort -V -c &> /dev/null; then + echo "INFO: Task is already up-to-date" + else + echo "INFO: A new version of Task is available (version $LATEST_VERSION)" + echo "INFO: The current version of Task installed is $CURRENT_VERSION" + # Replace with rm "$(which task)" &> /dev/null when ready + if ! type task &> /dev/null; then + installTask + else + echo "WARNING: Unable to remove previous version of Task" + fi + fi + fi +} + +# @description Helper function for ensureTaskInstalled that performs the installation of Task. +# +# @see ensureTaskInstalled +# +# @example +# installTask +# +# @noarg +# +# @exitcode 0 If Task installs/updates properly +# @exitcode 1+ If the installation fails +function installTask() { + # @description Release URL to use when downloading [Task](https://github.com/go-task/task) + TASK_RELEASE_URL="https://github.com/go-task/task/releases/latest" + CHECKSUM_DESTINATION=/tmp/megabytelabs/task_checksums.txt + CHECKSUMS_URL="$TASK_RELEASE_URL/download/task_checksums.txt" + DOWNLOAD_DESTINATION=/tmp/megabytelabs/task.tar.gz + TMP_DIR=/tmp/megabytelabs + if [[ "$OSTYPE" == 'darwin'* ]]; then + DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_darwin_amd64.tar.gz" + else + DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_linux_amd64.tar.gz" + fi + mkdir -p "$(dirname "$DOWNLOAD_DESTINATION")" + echo "INFO: Downloading latest version of Task" + curl -sSL "$DOWNLOAD_URL" -o "$DOWNLOAD_DESTINATION" + curl -sSL "$CHECKSUMS_URL" -o "$CHECKSUM_DESTINATION" + DOWNLOAD_BASENAME="$(basename "$DOWNLOAD_URL")" + DOWNLOAD_SHA256="$(grep "$DOWNLOAD_BASENAME" < "$CHECKSUM_DESTINATION" | cut -d ' ' -f 1)" + sha256 "$DOWNLOAD_DESTINATION" "$DOWNLOAD_SHA256" > /dev/null + echo "SUCCESS: Validated checksum" + mkdir -p "$TMP_DIR/task" + tar -xzvf "$DOWNLOAD_DESTINATION" -C "$TMP_DIR/task" > /dev/null + if type task &> /dev/null && [ -w "$(which task)" ]; then + TARGET_DEST="$(which task)" + else + if [ -w /usr/local/bin ]; then + TARGET_BIN_DIR='/usr/local/bin' + else + TARGET_BIN_DIR="$HOME/.local/bin" + fi + TARGET_DEST="$TARGET_BIN_DIR/task" + mkdir -p "$TARGET_BIN_DIR" + fi + mv "$TMP_DIR/task/task" "$TARGET_DEST" + echo "SUCCESS: Installed Task to $TARGET_DEST" + rm "$CHECKSUM_DESTINATION" + rm "$DOWNLOAD_DESTINATION" +} + +# @description Verifies the SHA256 checksum of a file +# +# @example +# sha256 myfile.tar.gz 5b30f9c042553141791ec753d2f96786c60a4968fd809f75bb0e8db6c6b4529b +# +# @arg $1 string Path to the file +# @arg $2 string The SHA256 signature +# +# @exitcode 0 The checksum is valid or the system is unrecognized +# @exitcode 1+ The OS is unsupported or if the checksum is invalid +function sha256() { + echo "$2" + echo "$1" + if [[ "$OSTYPE" == 'darwin'* ]]; then + if type brew &> /dev/null && ! type sha256sum &> /dev/null; then + brew install coreutils + else + echo "WARNING: Brew is not installed - this may cause issues" + fi + if type brew &> /dev/null; then + PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH" + fi + if type sha256sum &> /dev/null; then + echo "$2 $1" | sha256sum -c + else + echo "WARNING: Checksum validation is being skipped for $1 because the sha256sum program is not available" + fi + elif [[ "$OSTYPE" == 'linux-gnu'* ]]; then + if ! type shasum &> /dev/null; then + echo "WARNING: Checksum validation is being skipped for $1 because the shasum program is not installed" + else + echo "$2 $1" | shasum -s -a 256 -c + fi + elif [[ "$OSTYPE" == 'linux-musl' ]]; then + if ! type sha256sum &> /dev/null; then + echo "WARNING: Checksum validation is being skipped for $1 because the sha256sum program is not available" + else + echo "$2 $1" | sha256sum -c + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "WARNING: System type not recognized. Skipping checksum validation." + fi +} + +##### Main Logic ##### + +if [ ! -f "$HOME/.profile" ]; then + touch "$HOME/.profile" +fi + +# @description Ensures ~/.local/bin is in PATH +ensureLocalPath + +# @description Ensures base dependencies are installed +if [[ "$OSTYPE" == 'darwin'* ]]; then + if ! type curl &> /dev/null && type brew &> /dev/null; then + brew install curl + else + echo "ERROR: Neither curl nor brew are installed. Install one of them manually and try again." + fi + if ! type git &> /dev/null; then + # shellcheck disable=SC2016 + echo 'INFO: Git is not present. A password may be required to run `sudo xcode-select --install`' + sudo xcode-select --install + fi +elif [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then + if ! type curl &> /dev/null || ! type git &> /dev/null; then + ensurePackageInstalled "curl" + ensurePackageInstalled "git" + fi +fi + +# @description Ensures Homebrew and Poetry are installed +if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then + if ! type brew &> /dev/null && [ -z "$INIT_CWD" ]; then + echo "WARNING: Homebrew is not installed. The script will attempt to install Homebrew and you might be prompted for your password." + bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + fi +fi + +# @description Attempts to pull the latest changes if the folder is a git repository +cd "$PROJECT_BASE_DIR" || exit +if [ -d .git ] && type git &> /dev/null; then + HTTPS_VERSION="$(git remote get-url origin | sed 's/git@gitlab.com:/https:\/\/gitlab.com\//')" + git pull "$HTTPS_VERSION" master --ff-only + git submodule update --init --recursive +fi + +# @description Ensures Task is installed and properly configured +ensureTaskInstalled + +# @description Run the start logic, if appropriate +cd "$PROJECT_BASE_DIR" || exit +if [ -z "$GITLAB_CI" ] && [ -z "$INIT_CWD" ]; then + # shellcheck disable=SC1091 + . "$HOME/.profile" + task start + # shellcheck disable=SC2028 + echo 'INFO: There may have been changes to your PATH variable. You may have to reload your terminal or run:\n\n`. '"$HOME/.profile"'`' +fi diff --git a/.config/docs/variables.json b/.config/docs/variables.json index 7b919cf9..5a4514d4 100644 --- a/.config/docs/variables.json +++ b/.config/docs/variables.json @@ -64,6 +64,11 @@ } ], "group": "ansible", + "homebrew": { + "folder": "Formula", + "name": "homebrew-tap", + "owner": "installdoc" + }, "hostapp_var_chart": [ ["App", "Description"], ["**[Authelia](https://www.authelia.com/)**", "An authentication portal that supports SSO and 2FA"], @@ -96,6 +101,7 @@ ["**[Tautulli](https://docs.linuxserver.io/images/docker-tautulli)**", "Metrics and monitoring for Plex"], ["**[Transmission](https://docs.linuxserver.io/images/docker-transmission)**", "BitTorrent client"] ], + "idPost": "megabyte.space", "json_top_keys": "", "license": "MIT", "link": { @@ -150,6 +156,7 @@ "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", @@ -261,6 +268,52 @@ "" ] ], + "releaseRules": [ + { + "release": "patch", + "type": "build" + }, + { + "release": "patch", + "type": "ci" + }, + { + "release": false, + "type": "chore" + }, + { + "release": false, + "type": "docs" + }, + { + "release": "patch", + "type": "fix" + }, + { + "release": "minor", + "type": "feat" + }, + { + "release": "patch", + "type": "refactor" + }, + { + "release": "patch", + "type": "revert" + }, + { + "release": "patch", + "type": "perf" + }, + { + "release": false, + "type": "style" + }, + { + "release": false, + "type": "test" + } + ], "repository": { "github": "", "gitlab": "", diff --git a/.config/log b/.config/log index aade526a..9abe2c39 100755 --- a/.config/log +++ b/.config/log @@ -14,7 +14,7 @@ if [ "${container:=}" != 'docker' ] && type node > /dev/null && [ -f './node_modules/chalk/package.json' ] \ && [ -f './node_modules/string-break/package.json' ]; then - ENHANCED_LOGGING=true + echo "Disabled enhanced logging temporarily" fi # @description Logs using Node.js @@ -26,7 +26,7 @@ logger() { # @description Logs an error message # @example .config/log error "Something happened!" error() { - if [ "$ENHANCED_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then logger error "$1" else echo "ERROR: $1" @@ -36,7 +36,7 @@ error() { # @description Logs an info message # @example .config/log info "Here is some information" info() { - if [ "$ENHANCED_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then logger info "$1" else echo "INFO: $1" @@ -46,7 +46,7 @@ info() { # @description Logs a message that starts with a star emoji # @example .config/log star "Congratulations" star() { - if [ "$ENHANCED_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then logger star "$1" else echo "STAR: $1" @@ -56,7 +56,7 @@ star() { # @description Logs a success message # @example .config/log success "Yay!" success() { - if [ "$ENHANCED_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then logger success "$1" else echo "SUCCESS: $1" @@ -66,7 +66,7 @@ success() { # @description Logs a warning message # @example .config/log warn "Just so you know.." warn() { - if [ "$ENHANCED_LOGGING" ]; then + if [ -n "$ENHANCED_LOGGING" ]; then logger warn "$1" else echo "WARNING: $1" 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/scripts/prompts/environment.js b/.config/prompts/environment.js similarity index 100% rename from .config/scripts/prompts/environment.js rename to .config/prompts/environment.js diff --git a/.config/scripts/prompts/gitlab-group.js b/.config/prompts/gitlab-group.js similarity index 100% rename from .config/scripts/prompts/gitlab-group.js rename to .config/prompts/gitlab-group.js diff --git a/.config/scripts/prompts/lib/decorate-files.js b/.config/prompts/lib/decorate-files.js similarity index 100% rename from .config/scripts/prompts/lib/decorate-files.js rename to .config/prompts/lib/decorate-files.js diff --git a/.config/scripts/prompts/lib/decorate-system.js b/.config/prompts/lib/decorate-system.js similarity index 100% rename from .config/scripts/prompts/lib/decorate-system.js rename to .config/prompts/lib/decorate-system.js diff --git a/.config/scripts/prompts/lib/log.js b/.config/prompts/lib/log.js similarity index 100% rename from .config/scripts/prompts/lib/log.js rename to .config/prompts/lib/log.js diff --git a/.config/scripts/prompts/molecule/desktop.js b/.config/prompts/molecule/desktop.js similarity index 100% rename from .config/scripts/prompts/molecule/desktop.js rename to .config/prompts/molecule/desktop.js diff --git a/.config/scripts/prompts/molecule/docker.js b/.config/prompts/molecule/docker.js similarity index 100% rename from .config/scripts/prompts/molecule/docker.js rename to .config/prompts/molecule/docker.js diff --git a/.config/scripts/prompts/molecule/local.js b/.config/prompts/molecule/local.js similarity index 100% rename from .config/scripts/prompts/molecule/local.js rename to .config/prompts/molecule/local.js diff --git a/.config/scripts/prompts/molecule/molecule.js b/.config/prompts/molecule/molecule.js similarity index 100% rename from .config/scripts/prompts/molecule/molecule.js rename to .config/prompts/molecule/molecule.js diff --git a/.config/scripts/prompts/molecule/playbook.js b/.config/prompts/molecule/playbook.js similarity index 100% rename from .config/scripts/prompts/molecule/playbook.js rename to .config/prompts/molecule/playbook.js diff --git a/.config/scripts/prompts/molecule/ssh.js b/.config/prompts/molecule/ssh.js similarity index 100% rename from .config/scripts/prompts/molecule/ssh.js rename to .config/prompts/molecule/ssh.js diff --git a/.config/scripts/prompts/molecule/virtualbox.js b/.config/prompts/molecule/virtualbox.js similarity index 100% rename from .config/scripts/prompts/molecule/virtualbox.js rename to .config/prompts/molecule/virtualbox.js diff --git a/.config/scripts/prompts/package.js b/.config/prompts/package.js similarity index 100% rename from .config/scripts/prompts/package.js rename to .config/prompts/package.js diff --git a/.config/scripts/prompts/vagrant-up.js b/.config/prompts/vagrant-up.js similarity index 100% rename from .config/scripts/prompts/vagrant-up.js rename to .config/prompts/vagrant-up.js diff --git a/.config/requirements.txt b/.config/requirements.txt index a225c817..93551b59 100644 --- a/.config/requirements.txt +++ b/.config/requirements.txt @@ -2,25 +2,24 @@ ansible-core==2.11.7; python_version >= "2.7" and python_full_version < "3.0.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.9; python_full_version >= "3.6.0" and python_version >= "3.6" +charset-normalizer==2.0.10; 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" -junit-xml==1.9 markupsafe==2.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" 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.6; 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.26.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" +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.7; 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" +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/scripts/prompts/shell.js b/.config/scripts/prompts/shell.js deleted file mode 100644 index fa9aa7a3..00000000 --- a/.config/scripts/prompts/shell.js +++ /dev/null @@ -1,57 +0,0 @@ -import inquirer from 'inquirer' -import { execSync } from 'node:child_process' -import { decorateSystem } from './lib/decorate-system.js' -import { logInstructions, LOG_DECORATOR_REGEX } from './lib/log.js' - -/** - * Prompts the user for the operating system they wish to launch a shell session with. - * - * @returns {string} The selected operating system, lowercased, in the format the Taskfile is expecting - */ -async function promptForShell() { - const choices = [ - 'Archlinux', - 'CentOS 7', - 'CentOS 8', - 'Debian 9', - 'Debian 10', - 'Fedora 33', - 'Fedora 34', - 'Ubuntu 18.04', - 'Ubuntu 20.04', - 'Ubuntu 21.04' - ] - const choicesDecorated = choices.map((choice) => decorateSystem(choice)) - const response = await inquirer.prompt([ - { - choices: choicesDecorated, - message: 'Which operating system would you like to open up a terminal session with?', - name: 'operatingSystem', - type: 'list' - } - ]) - - const DECORATION_LENGTH = 2 - - return response.operatingSystem - .replace(LOG_DECORATOR_REGEX, '') - .toLowerCase() - .slice(DECORATION_LENGTH) - .replace(' ', '-') -} - -/** - * Main script logic - */ -async function run() { - logInstructions( - '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.' - ) - const choice = await promptForShell() - execSync(`task common:shell:cli -- ${choice}`, { stdio: 'inherit' }) -} - -run() diff --git a/.config/scripts/semantic/prepare.sh b/.config/scripts/semantic/prepare.sh deleted file mode 100644 index 48be93f6..00000000 --- a/.config/scripts/semantic/prepare.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -# @file .config/scripts/semantic/prepare.sh -# @brief Executes during the publish step of a `semantic-release` cycle -# @description -# This is a placeholder for now. - -.config/log info "Preparing release.." diff --git a/.config/scripts/semantic/verify.sh b/.config/scripts/semantic/verify.sh deleted file mode 100644 index 597f9f68..00000000 --- a/.config/scripts/semantic/verify.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -# @file .config/scripts/semantic/verify.sh -# @brief Executes code that runs during the `semantic-release` verify step -# @description -# This is a placeholder for now. - -.config/log info "Doing some verifying.." diff --git a/.config/scripts/start.sh b/.config/scripts/start.sh index 58d9390b..c380a48b 100644 --- a/.config/scripts/start.sh +++ b/.config/scripts/start.sh @@ -16,6 +16,49 @@ if [ -f .config/log ]; then chmod +x .config/log fi +# @description Installs package when user is root on Linux +# +# @example +# ensureRootPackageInstalled "sudo" +# +# @arg $1 string The name of the package that must be present +# +# @exitcode 0 The package was successfully installed +# @exitcode 1+ If there was an error, the package needs to be installed manually, or if the OS is unsupported +function ensureRootPackageInstalled() { + if ! type "$1" &> /dev/null; then + if [[ "$OSTYPE" == 'linux'* ]]; then + if [ -f "/etc/redhat-release" ]; then + yum update + yum install -y "$1" + elif [ -f "/etc/lsb-release" ]; then + apt update + apt install -y "$1" + elif [ -f "/etc/arch-release" ]; then + pacman update + pacman -S "$1" + elif [ -f "/etc/alpine-release" ]; then + apk update + apk add -y "$1" + fi + fi + fi +} + +# @description If the user is running this script as root, then create a new user named +# megabyte and restart the script with that user. This is required because Homebrew +# can only be invoked by non-root users. +if [ "$EUID" -eq 0 ] && [ -z "$INIT_CWD" ] && type useradd &> /dev/null; then + # shellcheck disable=SC2016 + echo 'INFO: Running as root - creating seperate user named `megabyte` to run script with' + echo "megabyte ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + useradd -m -s "$(which bash)" -c "Megabyte Labs Homebrew Account" megabyte + ensureRootPackageInstalled "sudo" + # shellcheck disable=SC2016 + echo 'INFO: Reloading the script with the `megabyte` user' + exec su megabyte "$0" -- "$@" +fi + # @description Detect script paths BASH_SRC="$(dirname "${BASH_SOURCE[0]}")" SOURCE_PATH="$( @@ -39,11 +82,11 @@ PROJECT_BASE_DIR="$SOURCE_PATH/../.." function ensureLocalPath() { if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux'* ]]; then # shellcheck disable=SC2016 - local PATH_STRING='PATH="$HOME/.local/bin:$PATH"' + PATH_STRING='PATH="$HOME/.local/bin:$PATH"' mkdir -p "$HOME/.local/bin" if grep -L "$PATH_STRING" "$HOME/.profile" > /dev/null; then echo -e "${PATH_STRING}\n" >> "$HOME/.profile" - .config/log info "Updated the PATH variable to include ~/.local/bin in $HOME/.profile" + echo "INFO: Updated the PATH variable to include ~/.local/bin in $HOME/.profile" fi elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then echo "Windows is not directly supported. Use WSL or Docker." && exit 1 @@ -54,23 +97,23 @@ function ensureLocalPath() { fi } -# @description Ensures given packages are installed on a system. +# @description Ensures given package is installed on a system. # # @example -# ensurePackagesInstalled "curl git" +# ensurePackageInstalled "curl" # # @arg $1 string The name of the package that must be present # # @exitcode 0 The package(s) were successfully installed # @exitcode 1+ If there was an error, the package needs to be installed manually, or if the OS is unsupported -function ensurePackagesInstalled() { +function ensurePackageInstalled() { if ! type "$1" &> /dev/null; then if [[ "$OSTYPE" == 'darwin'* ]]; then brew install "$1" elif [[ "$OSTYPE" == 'linux'* ]]; then if [ -f "/etc/redhat-release" ]; then sudo yum update - sudo yum install "$1" + sudo yum install -y "$1" elif [ -f "/etc/lsb-release" ]; then sudo apt update sudo apt install -y "$1" @@ -79,16 +122,16 @@ function ensurePackagesInstalled() { sudo pacman -S "$1" elif [ -f "/etc/alpine-release" ]; then apk update - apk add "$1" + apk add -y "$1" else - .config/log error "$1 is missing. Please install $1 to continue." && exit 1 + echo "ERROR: $1 is missing. Please install $1 to continue." && exit 1 fi elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then - .config/log error "Windows is not directly supported. Use WSL or Docker." && exit 1 + echo "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 + echo "ERROR: FreeBSD support not added yet" && exit 1 else - .config/log error "System type not recognized" + echo "ERROR: System type not recognized" fi fi } @@ -110,27 +153,27 @@ function ensureTaskInstalled() { if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl' ]]; then installTask elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then - .config/log error "Windows is not directly supported. Use WSL or Docker." && exit 1 + echo "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 + echo "ERROR: FreeBSD support not added yet" && exit 1 else - .config/log error "System type not recognized. You must install task manually." && exit 1 + echo "ERROR: System type not recognized. You must install task manually." && exit 1 fi else + echo "INFO: Checking for latest version of Task" CURRENT_VERSION="$(task --version | cut -d' ' -f3 | cut -c 2-)" LATEST_VERSION="$(curl -s "$TASK_RELEASE_API" | grep tag_name | cut -c 17- | sed 's/\",//')" - if printf '%s\n%s\n' "$LATEST_VERSION" "$CURRENT_VERSION" | sort -V -C; then - .config/log info "Task is already up-to-date" + if printf '%s\n%s\n' "$LATEST_VERSION" "$CURRENT_VERSION" | sort -V -c &> /dev/null; then + echo "INFO: Task is already up-to-date" else - .config/log info "A new version of Task is available (version $LATEST_VERSION)" - if [ ! -w "$(which task)" ]; then - local MSG_A - MSG_A="ERROR: Task is currently installed in a location the current user does not have write permissions for." - local MSG_B - MSG_B="Manually remove Task from its current location ($(which task)) and then run this script again." - .config/log error """$MSG_A"" ""$MSG_B""" && exit 1 + echo "INFO: A new version of Task is available (version $LATEST_VERSION)" + echo "INFO: The current version of Task installed is $CURRENT_VERSION" + # Replace with rm "$(which task)" &> /dev/null when ready + if ! type task &> /dev/null; then + installTask + else + echo "WARNING: Unable to remove previous version of Task" fi - installTask fi fi } @@ -148,42 +191,39 @@ function ensureTaskInstalled() { # @exitcode 1+ If the installation fails function installTask() { # @description Release URL to use when downloading [Task](https://github.com/go-task/task) - local TASK_RELEASE_URL="https://github.com/go-task/task/releases/latest" - local CHECKSUM_DESTINATION=/tmp/megabytelabs/task_checksums.txt - local CHECKSUMS_URL="$TASK_RELEASE_URL/download/task_checksums.txt" - local DOWNLOAD_DESTINATION=/tmp/megabytelabs/task.tar.gz - local TMP_DIR=/tmp/megabytelabs + TASK_RELEASE_URL="https://github.com/go-task/task/releases/latest" + CHECKSUM_DESTINATION=/tmp/megabytelabs/task_checksums.txt + CHECKSUMS_URL="$TASK_RELEASE_URL/download/task_checksums.txt" + DOWNLOAD_DESTINATION=/tmp/megabytelabs/task.tar.gz + TMP_DIR=/tmp/megabytelabs if [[ "$OSTYPE" == 'darwin'* ]]; then - local DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_darwin_amd64.tar.gz" + DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_darwin_amd64.tar.gz" else - local DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_linux_amd64.tar.gz" + DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_linux_amd64.tar.gz" fi mkdir -p "$(dirname "$DOWNLOAD_DESTINATION")" - .config/log info 'Downloading latest version of Task' + echo "INFO: Downloading latest version of Task" curl -sSL "$DOWNLOAD_URL" -o "$DOWNLOAD_DESTINATION" curl -sSL "$CHECKSUMS_URL" -o "$CHECKSUM_DESTINATION" - local DOWNLOAD_BASENAME DOWNLOAD_BASENAME="$(basename "$DOWNLOAD_URL")" - local DOWNLOAD_SHA256 DOWNLOAD_SHA256="$(grep "$DOWNLOAD_BASENAME" < "$CHECKSUM_DESTINATION" | cut -d ' ' -f 1)" - sha256 "$DOWNLOAD_DESTINATION" "$DOWNLOAD_SHA256" - .config/log success 'Validated checksum' + sha256 "$DOWNLOAD_DESTINATION" "$DOWNLOAD_SHA256" > /dev/null + echo "SUCCESS: Validated checksum" mkdir -p "$TMP_DIR/task" - tar -xzvf "$DOWNLOAD_DESTINATION" -C "$TMP_DIR/task" + tar -xzvf "$DOWNLOAD_DESTINATION" -C "$TMP_DIR/task" > /dev/null if type task &> /dev/null && [ -w "$(which task)" ]; then - local TARGET_DEST TARGET_DEST="$(which task)" else if [ -w /usr/local/bin ]; then - local TARGET_BIN_DIR='/usr/local/bin' + TARGET_BIN_DIR='/usr/local/bin' else - local TARGET_BIN_DIR="$HOME/.local/bin" + TARGET_BIN_DIR="$HOME/.local/bin" fi - local TARGET_DEST="$TARGET_BIN_DIR/task" + TARGET_DEST="$TARGET_BIN_DIR/task" + mkdir -p "$TARGET_BIN_DIR" fi - mkdir -p "$TARGET_BIN_DIR" - mv "$TMP_DIR/task/task" "$TARGET_BIN_DIR/" - .config/log success "Successfully installed Task to $TARGET_DEST" + mv "$TMP_DIR/task/task" "$TARGET_DEST" + echo "SUCCESS: Installed Task to $TARGET_DEST" rm "$CHECKSUM_DESTINATION" rm "$DOWNLOAD_DESTINATION" } @@ -199,11 +239,13 @@ function installTask() { # @exitcode 0 The checksum is valid or the system is unrecognized # @exitcode 1+ The OS is unsupported or if the checksum is invalid function sha256() { + echo "$2" + echo "$1" if [[ "$OSTYPE" == 'darwin'* ]]; then if type brew &> /dev/null && ! type sha256sum &> /dev/null; then brew install coreutils else - .config/log warn "Brew is not installed - this may cause issues" + echo "WARNING: Brew is not installed - this may cause issues" fi if type brew &> /dev/null; then PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH" @@ -211,26 +253,26 @@ function sha256() { if type sha256sum &> /dev/null; then echo "$2 $1" | sha256sum -c else - .config/log warn "Checksum validation is being skipped for $1 because the sha256sum program is not available" + echo "WARNING: Checksum validation is being skipped for $1 because the sha256sum program is not available" fi elif [[ "$OSTYPE" == 'linux-gnu'* ]]; then if ! type shasum &> /dev/null; then - .config/log warn "Checksum validation is being skipped for $1 because the shasum program is not installed" + echo "WARNING: Checksum validation is being skipped for $1 because the shasum program is not installed" else echo "$2 $1" | shasum -s -a 256 -c fi elif [[ "$OSTYPE" == 'linux-musl' ]]; then if ! type sha256sum &> /dev/null; then - .config/log warn "Checksum validation is being skipped for $1 because the sha256sum program is not available" + echo "WARNING: Checksum validation is being skipped for $1 because the sha256sum program is not available" else - echo "$2 $1" | sha256sum -c + echo "$2 $1" | sha256sum -c fi elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then - .config/log error "Windows is not directly supported. Use WSL or Docker." && exit 1 + echo "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 + echo "ERROR: FreeBSD support not added yet" && exit 1 else - .config/log warn "System type not recognized. Skipping checksum validation." + echo "WARNING: System type not recognized. Skipping checksum validation." fi } @@ -245,26 +287,27 @@ ensureLocalPath # @description Ensures base dependencies are installed if [[ "$OSTYPE" == 'darwin'* ]]; then - if ! type curl > /dev/null && type brew > /dev/null; then + if ! type curl &> /dev/null && type brew &> /dev/null; then brew install curl else - .config/log error 'Neither curl nor brew are installed. Install one of them manually and try again.' + echo "ERROR: Neither curl nor brew are installed. Install one of them manually and try again." fi - if ! type git > /dev/null; then + if ! type git &> /dev/null; then # shellcheck disable=SC2016 - .config/log info 'Git is not present. A password may be required to run `sudo xcode-select --install`' + echo 'INFO: Git is not present. A password may be required to run `sudo xcode-select --install`' sudo xcode-select --install fi elif [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then - if ! type curl > /dev/null || ! type git > /dev/null; then - ensurePackagesInstalled "curl git" + if ! type curl &> /dev/null || ! type git &> /dev/null; then + ensurePackageInstalled "curl" + ensurePackageInstalled "git" fi fi # @description Ensures Homebrew and Poetry are installed if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then - if ! type brew > /dev/null; then - .config/log warn 'Homebrew is not installed. The script will attempt to install Homebrew and you might be prompted for your password.' + if ! type brew &> /dev/null && [ -z "$INIT_CWD" ]; then + echo "WARNING: Homebrew is not installed. The script will attempt to install Homebrew and you might be prompted for your password." bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" fi fi @@ -282,9 +325,10 @@ ensureTaskInstalled # @description Run the start logic, if appropriate cd "$PROJECT_BASE_DIR" || exit -if [ -z "$GITLAB_CI" ]; then +if [ -z "$GITLAB_CI" ] && [ -z "$INIT_CWD" ]; then # shellcheck disable=SC1091 . "$HOME/.profile" task start - .config/log info 'There may have been changes to your PATH variable. You may have to reload your terminal or run:\n\n`. '"$HOME/.profile"'`' + # shellcheck disable=SC2028 + echo 'INFO: There may have been changes to your PATH variable. You may have to reload your terminal or run:\n\n`. '"$HOME/.profile"'`' fi diff --git a/.config/taskfiles/ansible/Taskfile-ansibler.yml b/.config/taskfiles/ansible/Taskfile-ansibler.yml index 7a90b830..9c0f315e 100644 --- a/.config/taskfiles/ansible/Taskfile-ansibler.yml +++ b/.config/taskfiles/ansible/Taskfile-ansibler.yml @@ -20,6 +20,10 @@ tasks: 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 @@ -29,10 +33,6 @@ tasks: jq -s -S '.[0] + .[1]' '{{.VARIABLES_PATH}}' .cache/compatibility-chart.json > "$TMP" mv "$TMP" '{{.VARIABLES_PATH}}' - log: - error: Failed to generate operating system compatibility chart - start: Generating operating system compatibility chart - success: Successfully generated operating system compatibility chart compatibility-chart:generate: deps: - :install:python:requirements @@ -48,12 +48,12 @@ tasks: populate-platforms: deps: - :install:python:requirements - cmds: - - '{{.PYTHON_HANDLE}}ansibler --populate-platforms --json-file .cache/compatibility-chart.json' 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: + - '{{.PYTHON_HANDLE}}ansibler --populate-platforms --json-file .cache/compatibility-chart.json' sources: - .cache/compatibility-chart.json - meta/main.yml @@ -61,6 +61,10 @@ tasks: 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 @@ -71,10 +75,6 @@ tasks: jq -s -S '.[0] + .[1]' '{{.VARIABLES_PATH}}' .cache/role-dependencies.json > "$TMP" mv "$TMP" '{{.VARIABLES_PATH}}' - log: - error: Failed to acquire role dependency information - start: Gathering information about role dependencies - success: Acquired role dependency information role-dependencies:generate: deps: - :install:python:requirements diff --git a/.config/taskfiles/ansible/Taskfile-playbook.yml b/.config/taskfiles/ansible/Taskfile-playbook.yml index e93b86a0..7b8bc452 100644 --- a/.config/taskfiles/ansible/Taskfile-playbook.yml +++ b/.config/taskfiles/ansible/Taskfile-playbook.yml @@ -17,6 +17,10 @@ tasks: ``` vars: DOC_IDS: '@binary,@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() { @@ -42,7 +46,6 @@ tasks: for ID in {{replace "," " " .DOC_IDS}}; do populateChartVar "$TMP" "$ID" done - .config/log success 'Finished scanning for documentation comments' environment: # @desc_when [ansible/Taskfile-playbook.yml] ansible/playbook @@ -64,6 +67,12 @@ tasks: - task: environment:{{if .CLI_ARGS}}cli{{else}}prompt{{end}} environment:cli: + log: + error: + default: Encountered an error while switching environments to `{{.CLI_ARGS}}` + 69: A CLI argument must be passed in! + start: Switching environment to `{{.CLI_ARGS}}` + success: Successfully switched environment to `{{.CLI_ARGS}}` cmds: - | {{if .CLI_ARGS}} @@ -73,6 +82,8 @@ tasks: ln -s "./environments/{{.CLI_ARGS}}/$ITEM" "$ITEM" .config/log success "Successfully symlinked `$ITEM` from ./environments/{{.CLI_ARGS}}/$ITEM" done + {{else}} + exit 69 {{end}} environment:prompt: @@ -81,7 +92,6 @@ tasks: interactive: true cmds: - node .config/scripts/prompts/environment.js - preconditions: - sh: test -d node_modules msg: This task has dependencies in the `node_modules` folder which is missing. @@ -100,6 +110,10 @@ tasks: 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) @@ -124,6 +138,10 @@ tasks: 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 - | @@ -141,7 +159,6 @@ tasks: .config/log warn "${ROLE_RELATIVE_PATH}/package.json is missing!" fi done - .config/log success 'Finished adding git remotes for the roles in the ./roles folder' run: cmds: @@ -151,6 +168,10 @@ tasks: deps: - :install:python:requirements - :symlink:playbook + 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: - ansible-playbook -i inventories/{{.CLI_ARGS}} --ask-vault-pass main.yml diff --git a/.config/taskfiles/ansible/Taskfile-populate.yml b/.config/taskfiles/ansible/Taskfile-populate.yml index 12937273..a2400749 100644 --- a/.config/taskfiles/ansible/Taskfile-populate.yml +++ b/.config/taskfiles/ansible/Taskfile-populate.yml @@ -9,6 +9,9 @@ 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}}')" @@ -24,6 +27,9 @@ tasks: 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}}')" @@ -56,6 +62,9 @@ tasks: * 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: @@ -116,6 +125,9 @@ tasks: 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}}')" @@ -136,11 +148,16 @@ tasks: 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)" diff --git a/.config/taskfiles/ansible/Taskfile-test.yml b/.config/taskfiles/ansible/Taskfile-test.yml index cfc04eb5..1f0a5671 100644 --- a/.config/taskfiles/ansible/Taskfile-test.yml +++ b/.config/taskfiles/ansible/Taskfile-test.yml @@ -11,6 +11,10 @@ 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 @@ -33,8 +37,11 @@ tasks: deps: - :symlink:{{.REPOSITORY_SUBTYPE}} - :install:python:requirements + log: + error: Encountered error while testing the Ansible playbook locally + start: Testing the Ansible playbook locally + success: Successfully tested the Ansible playbook locally cmds: - - .config/log info "Testing Ansible playbook locally" - cp test/{{OS}}/inventory inventory - if [ ! -f ansible.cfg.bak ]; then cp ansible.cfg ansible.cfg.bak; fi - cp test/{{OS}}/ansible.cfg ansible.cfg @@ -47,6 +54,10 @@ tasks: molecule:dependencies: deps: - :install:python:requirements + 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: - '{{.PYTHON_HANDLE}} ansible-galaxy install --ignore-errors -r requirements.yml' - task: :symlink:{{.REPOSITORY_SUBTYPE}} @@ -75,17 +86,13 @@ tasks: - molecule:dependencies - :install:software:docker - :install:software:sshpass + 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: - | - .config/log info 'Running Docker Molecule test on containers in the `{{.CLI_ARGS}}` group' - EXIT_CODE=0 - {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="{{.CLI_ARGS}}" {{.PYTHON_HANDLE}}molecule test -s docker -- --skip-tags skipdockertest || EXIT_CODE=$? - if [ "$EXIT_CODE" != '0' ]; then - .config/log error 'The `{{.CLI_ARGS}}` Docker Molecule test finished with errors' - exit 1 - else - .config/log success 'Successfully ran the `{{.CLI_ARGS}}` Docker Molecule test' - fi + {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="{{.CLI_ARGS}}" {{.PYTHON_HANDLE}}molecule test -s docker -- --skip-tags skipdockertest molecule:docker:matrix: deps: @@ -95,6 +102,10 @@ tasks: 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}} - | @@ -102,19 +113,9 @@ tasks: 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`' - else - .config/log info 'Running full Docker Molecule test' fi - .config/log info 'Piping results to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt` for compatibility chart data' - EXIT_CODE=0 PY_COLORS=0 {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="$SCENARIO" {{.PYTHON_HANDLE}}molecule test -s docker -- --skip-tags skipdockertest 2>&1 | \ - tee "{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt" || EXIT_CODE=$? - if [ "$EXIT_CODE" != '0' ]; then - .config/log error 'There were errors while running the test - results are available at `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt`' - exit 1 - else - .config/log success 'Finished running the test - results are available at `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt`' - fi + tee "{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-$SCENARIO.txt" molecule:docker:prompt: interactive: true @@ -128,18 +129,14 @@ tasks: 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 - | - .config/log info 'Running Google Cloud Platform Molecule test' - EXIT_CODE=0 - {{.PYTHON_HANDLE}}molecule test -s gcp || EXIT_CODE=$? - if [ "$EXIT_CODE" != '0' ]; then - .config/log error 'Finished running the Google Cloud Platform Molecule test (with errors)' - exit 1 - else - .config/log success 'Finished running Google Cloud Platform Molecule test' - fi + {{.PYTHON_HANDLE}}molecule test -s gcp molecule:gcp:matrix: deps: @@ -149,18 +146,20 @@ tasks: 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}} - | - .config/log info 'Piping results to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt` for compatibility chart data' EXIT_CODE=0 PY_COLORS=0 {{.PYTHON_HANDLE}}molecule test -s gcp 2>&1 | tee "{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt" || EXIT_CODE=$? if [ "$EXIT_CODE" != '0' ]; then - .config/log error 'Errors encountered while running the test - results are available at `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt`' exit 1 else - .config/log success 'Finished running the test - results are available at `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt`' + .config/log success 'Finished running the test (results were logged to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt`)' fi - | RESULTS="{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-gcp.txt" @@ -210,18 +209,14 @@ tasks: molecule:local:test: deps: - molecule:dependencies + 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: - | - .config/log info 'Running Molecule test locally' - EXIT_CODE=0 PATH="$(poetry env info | grep 'Python: /' | sed 's/Python: //' | sed 's/$/\/bin/'):$PATH" \ {{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_HANDLE}}molecule test -s local || EXIT_CODE=$? - if [ "$EXIT_CODE" != '0' ]; then - .config/log error 'There was an error while running the Molecule test locally' - exit 1 - else - .config/log success 'The local Molecule test was successful!' - fi molecule:ssh: desc: Runs a Molecule test over SSH @@ -239,17 +234,12 @@ tasks: 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: - - | - .config/log info 'Running Molecule test over SSH' - EXIT_CODE=0 - {{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_HANDLE}}molecule test -s remote || EXIT_CODE=$? - if [ "$EXIT_CODE" != '0' ]; then - .config/log error 'Errors encountered while running the remote Molecule test' - exit 1 - else - .config/log success 'Successfully ran the Molecule test on the SSH target' - fi + - '{{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_HANDLE}}molecule test -s remote' molecule:ssh:prompt: interactive: true @@ -285,17 +275,12 @@ tasks: env: # yamllint disable-line rule:truthy 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: - - | - .config/log info 'Running VirtualBox Molecule test on platforms in the `{{.CLI_ARGS}}` group' - EXIT_CODE=0 - {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="{{.CLI_ARGS}}" {{.PYTHON_HANDLE}}molecule test || EXIT_CODE=$? - if [ "$EXIT_CODE" != '0' ]; then - .config/log error 'Errors encountered while running the `{{.CLI_ARGS}}` VirtualBox Molecule test' - exit 1 - else - .config/log success 'Finished running `{{.CLI_ARGS}}` VirtualBox Molecule test' - fi + - '{{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP="{{.CLI_ARGS}}" {{.PYTHON_HANDLE}}molecule test' molecule:virtualbox:converge: desc: Provisions a desktop VirtualBox VM and then runs a Molecule test @@ -332,17 +317,12 @@ tasks: env: # yamllint disable-line rule:truthy 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: - - | - .config/log info 'Running `{{.CLI_ARGS}}` VirtualBox Molecule converge play - this will leave the VirtualBox instance open for inspection' - EXIT_CODE=0 - {{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP={{.CLI_ARGS}} {{.PYTHON_HANDLE}}molecule converge -s desktop || EXIT_CODE=$? - if [ "$EXIT_CODE" != '0' ]; then - .config/log error 'Errors were encountered while running the `{{.CLI_ARGS}}` VirtualBox Molecule converge play' - exit 1 - else - .config/log success 'Finished running the `{{.CLI_ARGS}}` VirtualBox Molecule converge play - you are encouraged to inspect the VM' - fi + - '{{.MOLECULE_TEST_OPTIONS}} MOLECULE_GROUP={{.CLI_ARGS}} {{.PYTHON_HANDLE}}molecule converge -s desktop' molecule:virtualbox:converge:prompt: interactive: true @@ -363,18 +343,14 @@ tasks: sh: date '+%Y-%m-%d' # yamllint disable-line rule:truthy OBJC_DISABLE_INITIALIZE_FORK_SAFETY: YES + 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}} - | - .config/log info 'Running full E2E test with VirtualBox - results will be saved to `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt`' - EXIT_CODE=0 - PY_COLORS=0 {{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_HANDLE}}molecule test | tee '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt' || EXIT_CODE=$? - if [ "$EXIT_CODE" != '0' ]; then - .config/log error 'Errors were encountered while running the full E2E test - see `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt` for details' - exit 1 - else - .config/log success 'Finished running full E2E test - `{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt` contains the results' - fi + PY_COLORS=0 {{.MOLECULE_TEST_OPTIONS}} {{.PYTHON_HANDLE}}molecule test | tee '{{.MOLECULE_LOGS_PATH}}/{{.MOLECULE_DATE}}-default.txt' molecule:virtualbox:prompt: interactive: true @@ -431,10 +407,12 @@ tasks: - :install:software:sshpass - :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: - - | - .config/log info 'Running `vagrant up {{.CLI_ARGS}}`' - vagrant up {{.CLI_ARGS}} + - vagrant up {{.CLI_ARGS}} vagrant:prompt: interactive: true diff --git a/.config/taskfiles/ansible/Taskfile.yml b/.config/taskfiles/ansible/Taskfile.yml index 21798485..09c0c112 100644 --- a/.config/taskfiles/ansible/Taskfile.yml +++ b/.config/taskfiles/ansible/Taskfile.yml @@ -25,6 +25,10 @@ tasks: -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" @@ -42,6 +46,10 @@ tasks: 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}}' - | @@ -57,12 +65,16 @@ tasks: - '{{.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: - | - if [ -n "$ANSIBLE_GALAXY_TOKEN" ]; then - 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" - fi + 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: run: once @@ -75,6 +87,10 @@ tasks: msg: The requirements.yml file is missing! It should be present even if it is empty (which should almost never be the case). galaxy:requirements:install: + 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: - cmd: '{{.PYTHON_HANDLE}} ansible-galaxy install -r requirements.yml --ignore-errors' ignore_error: true @@ -96,9 +112,12 @@ tasks: 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: - | - .config/log info 'Syncing the keywords in package.json with the keywords in meta/main.yml' GALAXY_INFO="$(yq e -o=j meta/main.yml)" OPTIONAL_TAGS="$(jq '.keywords' .config/common-keywords.json)" TMP="$(mktemp)" @@ -120,7 +139,9 @@ tasks: 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 - '{{.NPX_HANDLE}}prettier --write meta/main.yml > /dev/null' - - task fix:yaml:dashes -- meta/main.yml + - task: :fix:yaml:dashes + vars: + CLI_ARGS: meta/main.yml mod-ansible-autodoc: cmds: @@ -152,16 +173,16 @@ tasks: 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: - > - .config/log info 'Compiling `mod-ansible-autodoc` documentation from comments in the play source files' - {{.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 '#💬' - - .config/log success 'Successfully generated documentation partials with `mod-ansible-autodoc`' - mkdir -p .autodoc - mv ansible_actions.md ansible_tags.md ansible_todo.md ansible_variables.json ansible_variables.md .autodoc sources: @@ -173,6 +194,10 @@ tasks: 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)" @@ -187,8 +212,11 @@ tasks: vars: INSTALLING: sh: if test -f install_in_progress; then echo 'true'; else echo 'false'; fi + log: + error: Error occurred while running the Ansible play + start: Running the Ansible play locally + success: Successfully ran the Ansible play locally cmds: - - .config/log info "Running the Ansible play locally" - cp test/{{OS}}/inventory inventory - if [ ! -f ansible.cfg.bak ]; then cp ansible.cfg ansible.cfg.bak; fi - cp test/{{OS}}/ansible.cfg ansible.cfg @@ -203,6 +231,10 @@ tasks: 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}}')" @@ -216,6 +248,10 @@ tasks: 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)" @@ -249,6 +285,10 @@ tasks: 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" @@ -278,11 +318,14 @@ tasks: 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 - .config/log error '`{{.CLI_ARGS}}` is not encrypted. All files matching `**/*vault.yml` should be encrypted by `ansible-vault`.' exit 1 fi } diff --git a/.config/taskfiles/boilerplate/Taskfile.yml b/.config/taskfiles/boilerplate/Taskfile.yml index ba551023..335e9ac4 100644 --- a/.config/taskfiles/boilerplate/Taskfile.yml +++ b/.config/taskfiles/boilerplate/Taskfile.yml @@ -9,6 +9,10 @@ tasks: - :install:software:jq vars: BLUEPRINT_REQUIRED_FIELDS: title description group name overview slug subgroup + 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: - | if [ "$(jq -r '.type' package.json)" != 'module' ]; then @@ -44,6 +48,10 @@ tasks: clean: deps: - :install:software:jq + log: + error: Failed to clean `package.json` + start: Cleaning `package.json` + success: Cleaned `package.json` cmds: - | TMP="$(mktemp)" @@ -57,6 +65,10 @@ tasks: prime:package: deps: - :install:software:jq + 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 - | @@ -78,9 +90,12 @@ tasks: update:taskfile: deps: - :install:software:yq + 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: - | - .config/log info 'Ensuring Taskfile.yml has correct variables' GROUP="$(jq -r '.blueprint.group' package.json)" SUBGROUP="$(jq -r '.blueprint.subgroup' package.json)" TASK_GROUP="$(yq eval '.vars.REPOSITORY_TYPE' Taskfile.yml)" @@ -102,4 +117,3 @@ tasks: UPSTREAM='upstream:docs' fi yq e -i ".tasks.start.cmds[0].task = \"$UPSTREAM\"" Taskfile.yml - .config/log success 'Successfully ensured Taskfile.yml has correct variables' diff --git a/.config/taskfiles/ci/Taskfile-github.yml b/.config/taskfiles/ci/Taskfile-github.yml index 4492c34e..594f57fb 100644 --- a/.config/taskfiles/ci/Taskfile-github.yml +++ b/.config/taskfiles/ci/Taskfile-github.yml @@ -7,5 +7,9 @@ tasks: - :install:software:act - :install:software:docker desc: Locally test the on-push GitHub Action event (only works for Linux containers) + 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 diff --git a/.config/taskfiles/ci/Taskfile.yml b/.config/taskfiles/ci/Taskfile.yml index 92fb1c31..b1dcafca 100644 --- a/.config/taskfiles/ci/Taskfile.yml +++ b/.config/taskfiles/ci/Taskfile.yml @@ -12,11 +12,19 @@ tasks: before:npm: deps: - - :install:software:node + - :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: + log: + error: Failed to pull latest changes + start: Pulling latest changes + success: Successfully pulled latest changes cmds: - | if [[ "$CI_COMMIT_REF_NAME" == 'synchronize' ]]; then @@ -30,7 +38,11 @@ tasks: commit: deps: - :install:software:git + log: + error: Encountered error while pushing changes to master + start: Bypassing git hooks and pushing changes to master (if there are any changes) cmds: + - task donothing - task: commit:config - git add --all - git diff --cached "*" @@ -60,12 +72,20 @@ tasks: 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"; git checkout -q master; git pull -q origin master --ff-only' synchronize: + 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 checkout -b synchronize || git checkout synchronize - git reset --hard HEAD @@ -73,8 +93,10 @@ tasks: - git push -q -o ci.skip origin synchronize --force - | if [ '{{.REPOSITORY_TYPE}}' == 'deprecated_common' ] && [ '{{.REPOSITORY_SUBTYPE}}' == 'shared' ]; then - echo 'Not triggering any pipelines because this is the shared common repository' + .config/log info 'Bypassing the `master` branch pipeline trigger because the repository is not of the right type' else + .config/log info 'Triggering the `master` branch pipeline' 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" fi + - git checkout master diff --git a/.config/taskfiles/common/Taskfile-code.yml b/.config/taskfiles/common/Taskfile-code.yml index 43bed0de..d7b58901 100644 --- a/.config/taskfiles/common/Taskfile-code.yml +++ b/.config/taskfiles/common/Taskfile-code.yml @@ -6,6 +6,9 @@ tasks: 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 @@ -15,5 +18,8 @@ tasks: desc: Scan code base for TODOs and FIXMEs vars: LEASOT_IGNORE: .common .modules .venv node_modules venv + log: + error: Encountered error while running `{{.NPX_HANDLE}}leasot --ignore {{.LEASOT_IGNORE}} {{.CLI_ARGS}}` + start: Scanning project for TODOs and FIXMEs cmds: - '{{.NPX_HANDLE}}leasot --ignore {{.LEASOT_IGNORE}} {{.CLI_ARGS}}' diff --git a/.config/taskfiles/common/Taskfile-update.yml b/.config/taskfiles/common/Taskfile-update.yml index e9ec5a86..79973cdc 100644 --- a/.config/taskfiles/common/Taskfile-update.yml +++ b/.config/taskfiles/common/Taskfile-update.yml @@ -19,10 +19,7 @@ tasks: all:docs: cmds: - - | - {{if eq .REPOSITORY_TYPE "common"}}{{else}}{{if eq .REPOSITORY_TYPE "documentation"}}{{else}} - task common:update:all:docs:generate - {{end}}{{end}} + - task: '{{if eq .REPOSITORY_TYPE "common"}}:donothing{{else}}{{if eq .REPOSITORY_TYPE "documentation"}}:donothing{{else}}all:docs:generate{{end}}{{end}}' all:docs:generate: deps: @@ -41,9 +38,12 @@ tasks: - :install:npm:readme vars: CONTRIB_TEMPLATE: .config/docs/blueprint-contributing.md + 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 - - .config/log info 'Generating `docs/CONTRIBUTING.md` using document partials' - > {{.NPX_HANDLE}}readme generate --silent --headingPrefix '{}' --config .variables.json --input "{{.CONTRIB_TEMPLATE}}" --output docs/CONTRIBUTING.md @@ -51,7 +51,6 @@ tasks: vars: SCRUB_FILE: docs/CONTRIBUTING.md - '{{.NPX_HANDLE}}prettier --write docs/CONTRIBUTING.md > /dev/null' - - .config/log success 'Successfully generated `docs/CONTRIBUTING.md`' sources: - docs/CONTRIBUTING.md - .config/docs/**/* @@ -70,6 +69,10 @@ tasks: sh: if [[ "${container:=}" == "docker" ]]; then echo "docker"; fi FIRST_LINE: sh: if [ -f Dockerfile ]; then head -n 1 Dockerfile; fi + log: + error: Encountered error while updating miscellaneous files + start: Updating miscellaneous files + success: Updated miscellaneous files cmds: - | {{if eq .REPOSITORY_SUBTYPE "ci-pipeline"}} @@ -82,6 +85,9 @@ tasks: - task: :{{if eq .REPOSITORY_SUBTYPE "role"}}ansible:keywords:sync{{else}}donothing{{end}} init: + log: + error: Encountered error while initializing project + start: Ensuring project is initialized cmds: - | if ! test -f package.json; then @@ -136,7 +142,6 @@ tasks: modules: deps: - :install:software:git - label: update:modules summary: | # Ensure all submodules in `.modules/` are updated @@ -145,6 +150,10 @@ tasks: 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: - | if ls .modules/*/ > /dev/null 2>&1; then @@ -161,6 +170,8 @@ tasks: - '! 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 @@ -175,6 +186,10 @@ tasks: - :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: - > {{.NPX_HANDLE}}readme generate --headingPrefix '{}' --silent --config .variables.json @@ -184,7 +199,6 @@ tasks: SCRUB_FILE: README.md - task: ':{{if eq .REPOSITORY_TYPE "packer"}}packer:update:readme{{else}}donothing{{end}}' - '{{.NPX_HANDLE}}prettier --write README.md > /dev/null' - - .config/log success 'Generated README.md successfully' sources: - .autodoc/* - .config/docs/**/* @@ -222,7 +236,6 @@ tasks: - ':{{if eq .REPOSITORY_TYPE "packer"}}packer:latestos{{else}}donothing{{end}}' - init - repositories - label: update summary: | # Refresh project with latest upstream code and ensure project files are up-to-date diff --git a/.config/taskfiles/common/Taskfile.yml b/.config/taskfiles/common/Taskfile.yml index 1bb0bbaa..0a215192 100644 --- a/.config/taskfiles/common/Taskfile.yml +++ b/.config/taskfiles/common/Taskfile.yml @@ -17,6 +17,10 @@ tasks: CLEAN_TARGETS: .autodoc .task .venv node_modules 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' @@ -48,9 +52,10 @@ tasks: It is very important that you use this task to commit rather than the conventional approach using `git commit -m`. However, if you really need to, you can add the flag `--no-verify` to your regular `git commit -m` command to bypass the pre-commit hook. + log: + start: Initiating commit dialog cmds: - . ./.config/husky/pre-commit - - .config/log info "Initiating the commit dialog" - exec < /dev/tty && git cz --hook || true husky: @@ -74,11 +79,14 @@ tasks: 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 {{.NPX_HANDLE}}husky install .config/husky > /dev/null - .config/log success 'Installed husky git hooks' + .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 @@ -86,6 +94,10 @@ tasks: - '[ ! -d .git ] || [[ "${container:=} == "docker" ]]' 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 @@ -154,6 +166,10 @@ tasks: If using `task common:reset` does not fix your issues, you can run this task to completely wipe out uncommitted work, clear all the caches, and sync with the master branch of both this project and its upstream repositories. + 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 @@ -170,16 +186,52 @@ tasks: 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: - - cmd: docker run -it -v "$PWD:/{{.WORKDIR}}" -w /{{.WORKDIR}} --rm megabytelabs/ansible-molecule-{{.CLI_ARGS}}:latest /bin/bash + - .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: - deps: - - :install:modules:local + vars: + HELP_MESSAGE: | + # 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: + HELP_MESSAGE: '{{.HELP_MESSAGE}}' + - task: shell:prompt:question + + shell:prompt:question: interactive: true + 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:]' + env: WORKDIR: sh: basename $PWD diff --git a/.config/taskfiles/docker/Taskfile-build.yml b/.config/taskfiles/docker/Taskfile-build.yml index d9fc22b9..8cbde432 100644 --- a/.config/taskfiles/docker/Taskfile-build.yml +++ b/.config/taskfiles/docker/Taskfile-build.yml @@ -23,12 +23,13 @@ tasks: sh: jq -r '.blueprint.slug' package.json VERSION: sh: jq -r '.version' package.json + log: + error: Error building `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:{{.VERSION}}` + start: Building Docker container `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:{{.VERSION}}` (also tagged as latest) + success: Successfully built Docker container named `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:{{.VERSION}}` cmds: - - | - .config/log info 'Building Docker container `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:{{.VERSION}}` (also tagged as latest)' - docker build --build-arg BUILD_DATE="{{.BUILD_DATE}}" --build-arg REVISION="{{.REVISION}}" --build-arg VERSION="{{.VERSION}}" + - docker build --build-arg BUILD_DATE="{{.BUILD_DATE}}" --build-arg REVISION="{{.REVISION}}" --build-arg VERSION="{{.VERSION}}" --tag {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest --tag {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:{{.VERSION}} . - .config/log success 'Successfully built Docker container named `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:{{.VERSION}}`' sources: - Dockerfile - package.json @@ -61,11 +62,12 @@ tasks: sh: jq -r '.blueprint.dockerslim_command' package.json SLUG: sh: jq -r '.blueprint.slug' package.json + log: + error: Error building `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim` with `docker-slim` + start: Building Docker container named `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim` + success: Successfully built Docker container named `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim` cmds: - - | - .config/log info 'Building Docker container named `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim`' - docker-slim build --tag '{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim' {{.SLIM_COMMAND}} '{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest' - .config/log success 'Successfully built Docker container named `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim`' + - docker-slim build --tag '{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim' {{.SLIM_COMMAND}} '{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest' sources: - Dockerfile - package.json @@ -78,6 +80,10 @@ tasks: slim:prettier: deps: - :install:npm:prettier + 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: - '{{.NPX_HANDLE}}prettier --write slim.report.json > /dev/null' sources: @@ -96,6 +102,9 @@ tasks: SLIM_SIZE: sh: 'docker manifest inspect -v "{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim" | grep size | awk -F ":" "{sum+=$NF} END {print sum}" | awk "{$1=$1/(1024^2); print $1,\"MB\";}"' + 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)" diff --git a/.config/taskfiles/docker/Taskfile-test.yml b/.config/taskfiles/docker/Taskfile-test.yml index 5d302c0d..55efe836 100644 --- a/.config/taskfiles/docker/Taskfile-test.yml +++ b/.config/taskfiles/docker/Taskfile-test.yml @@ -11,21 +11,23 @@ tasks: container-structure-test:latest: deps: - :install:software:container-structure-test + log: + error: '`container-structure-test` reported error(s) when testing `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest`' + start: Testing the `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest` Docker image with `container-structure-test` + success: '`{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest` was successfully validated by `container-structure-test`' cmds: - - | - if [[ "$(docker images -q {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest 2>/dev/null)" != "" ]]; then - .config/log info 'Testing the `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest` Docker image' - container-structure-test test --image {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest --config test.yml - .config/log success 'Successfully tested Docker image named `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest`' - fi + - container-structure-test test --image {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest --config test.yml + status: + - '[[ "$(docker images -q {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:latest 2>/dev/null)" == "" ]]' container-structure-test:slim: deps: - :install:software:container-structure-test + log: + error: '`container-structure-test` reported error(s) when testing `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim`' + start: Testing the `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim` Docker image with `container-structure-test` + success: '`{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim` was successfully validated by `container-structure-test`' cmds: - - | - if [[ "$(docker images -q {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim 2> /dev/null)" != "" ]]; then - .config/log info 'Testing the `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim` Docker image' - container-structure-test test --image {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim --config test.yml - .config/log success 'Successfully tested Docker image named `{{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim`' - fi + - container-structure-test test --image {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim --config test.yml + status: + - '[[ "$(docker images -q {{.DOCKERHUB_PROFILE}}/{{.SLUG}}:slim 2> /dev/null)" == "" ]]' diff --git a/.config/taskfiles/docker/Taskfile-update.yml b/.config/taskfiles/docker/Taskfile-update.yml index ae79a9cf..1efeecc9 100644 --- a/.config/taskfiles/docker/Taskfile-update.yml +++ b/.config/taskfiles/docker/Taskfile-update.yml @@ -8,6 +8,9 @@ tasks: 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: update:labels:add - task: update:labels:{{OS}} @@ -35,28 +38,29 @@ tasks: sh: jq -r '.organization' .variables.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: - | - if ! grep 'org.opencontainers.image.documentation' Dockerfile; then - 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="[[ Injected by running `task update` ]]"' >> Dockerfile - echo 'LABEL org.opencontainers.image.documentation="[[ Injected by running `task update` ]]"' >> Dockerfile - echo 'LABEL org.opencontainers.image.licenses="{{.LICENSE}}"' >> Dockerfile - echo 'LABEL org.opencontainers.image.revision=$REVISION' >> Dockerfile - echo 'LABEL org.opencontainers.image.source="[[ Injected by running `task update` ]]"' >> 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 - .config/log success 'Successfully injected org.opencontainers labels into Dockerfile' - fi + 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="[[ Injected by running `task update` ]]"' >> Dockerfile + echo 'LABEL org.opencontainers.image.documentation="[[ Injected by running `task update` ]]"' >> Dockerfile + echo 'LABEL org.opencontainers.image.licenses="{{.LICENSE}}"' >> Dockerfile + echo 'LABEL org.opencontainers.image.revision=$REVISION' >> Dockerfile + echo 'LABEL org.opencontainers.image.source="[[ Injected by running `task update` ]]"' >> 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 @@ -64,6 +68,8 @@ tasks: vars: GROUP_URL: sh: jq -r '.repository.group.dockerfile' .variables.json + log: + error: Encountered error while updating `Dockerfile` labels cmds: # yamllint disable rule:line-length # eslint-disable max-len @@ -81,6 +87,8 @@ tasks: sh: jq -r '.repository.group.dockerfile' .variables.json SLUG: sh: jq -r '.blueprint.slug' package.json + log: + error: Encountered error while updating `Dockerfile` labels cmds: # yamllint disable rule:line-length # eslint-disable max-len diff --git a/.config/taskfiles/docker/Taskfile.yml b/.config/taskfiles/docker/Taskfile.yml index 624b5a1d..4cb9bab5 100644 --- a/.config/taskfiles/docker/Taskfile.yml +++ b/.config/taskfiles/docker/Taskfile.yml @@ -1,6 +1,12 @@ --- version: '3' +vars: + DOCKER_IMAGE: + sh: jq -r '.name' package.json | sed 's/^@//' + SLIM_ENABLED: + sh: jq -r '.blueprint.slimBuildEnabled' package.json | sed 's/null/false/' + tasks: build: desc: Build a regular Docker image and then generate a slim build from it @@ -8,11 +14,78 @@ tasks: - task: :docker:build:fat - task: :docker:build:slim - standard-version:prebump: + 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: + - echo "$DOCKERHUB_REGISTRY_PASSWORD" | docker login -u {{.DOCKERHUB_USER}} --password-stdin + preconditions: + - test -n "$DOCKERHUB_REGISTRY_PASSWORD" + + prepare: + cmds: + - task: build + + publish: + vars: + MAJOR_VERSION: + sh: '{{if .CLI_ARGS}}echo "{{.CLI_ARGS}}"{{else}}jq -r ".version" package.json{{end}} | sed "s/\..*\..*$//"' + VERSION: + sh: '{{if .CLI_ARGS}}echo "{{.CLI_ARGS}}"{{else}}jq -r ".version" package.json{{end}}' + 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 + TARGET_TAG: '{{.VERSION}}' + - task: publish:image + vars: + SOURCE_TAG: latest + TARGET_TAG: '{{.MAJOR_VERSION}}-latest' + - task: publish:image + vars: + SOURCE_TAG: latest + TARGET_TAG: latest + - task: publish:image + vars: + SOURCE_TAG: slim + TARGET_TAG: '{{.VERSION}}-slim' + - task: publish:image + vars: + SOURCE_TAG: slim + TARGET_TAG: '{{.MAJOR_VERSION}}-slim' + - task: publish:image + vars: + SOURCE_TAG: slim + TARGET_TAG: slim + + publish:image: + 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: + - if [[ '{{.SOURCE_TAG}}' == 'slim' ]]; then [[ '{{.SLIM_ENABLED}}' == 'false' ]]; fi + + 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 megabytelabs/shellcheck:latest | perl \ + 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 @@ -20,15 +93,3 @@ tasks: echo $VERSION.0 fi fi - - standard-version:prerelease: git add --all - - standard-version:pretag: - vars: - PACKAGE_VERSION: - sh: grep version < package.json | head -1 | awk -F':' '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]' - cmds: - - | - if git show-ref --tags 'v{{.PACKAGE_VERSION}}' --quiet; then - git tag -d 'v{{.PACKAGE_VERSION}}' - fi diff --git a/.config/taskfiles/fix/Taskfile.yml b/.config/taskfiles/fix/Taskfile.yml index 6054d4e9..df1a9c2f 100644 --- a/.config/taskfiles/fix/Taskfile.yml +++ b/.config/taskfiles/fix/Taskfile.yml @@ -14,6 +14,8 @@ tasks: - '{{if .REPOSITORY_TYPE eq "python"}}python{{else}}:donothing{{end}}' - toml - xml + log: + start: Running all stable fixers in parallel cmds: - task: yaml:dashes @@ -34,6 +36,10 @@ tasks: **Example fixing all JS/TS files in a project:** `task fix:js` + 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: - '{{.NPX_HANDLE}}eslint -c package.json --no-eslintrc --format pretty --cache --cache-location .cache/eslintcache --fix {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}}' @@ -58,6 +64,10 @@ tasks: **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: - '{{.NPX_HANDLE}}eslint -c package.json --no-eslintrc --cache --cache-location .cache/eslintcache --fix --format pretty --ext .json {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}}' @@ -80,6 +90,10 @@ tasks: **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: - | function misc() { @@ -113,6 +127,10 @@ tasks: `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() { @@ -152,6 +170,8 @@ tasks: `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: - '{{.NPX_HANDLE}}prettier --ignore-path {{.PRETTIERIGNORE_CONFIG}} --write {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}}' @@ -173,6 +193,10 @@ tasks: `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: - | {{if .CLI_ARGS}} @@ -214,6 +238,10 @@ tasks: `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' @@ -239,6 +267,10 @@ tasks: **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: - '{{.NPX_HANDLE}}eslint -c package.json --no-eslintrc --cache --cache-location .cache/eslintcache --fix --format pretty --ext .xml {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}}' @@ -280,6 +312,10 @@ tasks: **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() { @@ -317,6 +353,10 @@ tasks: **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: - '{{.NPX_HANDLE}}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-github.yml b/.config/taskfiles/git/Taskfile-github.yml index c8d917f9..6a7281fb 100644 --- a/.config/taskfiles/git/Taskfile-github.yml +++ b/.config/taskfiles/git/Taskfile-github.yml @@ -17,21 +17,19 @@ tasks: 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: | - TMP="$(mktemp)" - if [ ! -z "$GITHUB_TOKEN" ] && test -e .git && ! gh repo view {{.GITHUB_ORG}}/{{.GITHUB_SLUG}} > /dev/null; then - gh repo create "{{.GITHUB_SLUG}}" --enable-wiki={{.GITHUB_WIKI}} -y --description "{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}" \ - --homepage "{{.HOMEPAGE}}" {{.PROJECT_TYPE}} > /dev/null - .config/log success 'Created a GitHub repository - `https://github.com/{{.GITHUB_ORG}}/{{.GITHUB_SLUG}}`' - git add --all - git commit --quiet -m "🎂 Birth" -n - git push --quiet -u --no-progress github master - fi + - cmd: gh repo create "{{.GITHUB_SLUG}}" --enable-wiki={{.GITHUB_WIKI}} -y --description "{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}" + --homepage "{{.HOMEPAGE}}" {{.PROJECT_TYPE}} > /dev/null ignore_error: true - sources: - - .variables.json - - package.json + - git add --all + - git commit --quiet -m "🎂 Birth" -n + - git push --quiet -u --no-progress github master + status: + - '[ -z "$GITHUB_TOKEN" ] || ! test -e .git || gh repo view {{.GITHUB_ORG}}/{{.GITHUB_SLUG}} > /dev/null' preconditions: - sh: '[ "{{.DESCRIPTION}}" != "null" ]' msg: The `.description` in `package.json` must be set. @@ -45,12 +43,19 @@ tasks: - :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: - - | - PROJECT_ID="$(gh repo view --json id | jq -r '.id')" - TMP="$(mktemp)" && jq --arg projectId "$PROJECT_ID" '.blueprint.github_id = $projectId' package.json > "$TMP" - mv "$TMP" package.json - .config/log success 'Added GitHub project ID to package.json' + - TMP="$(mktemp)" && jq --arg projectId "{{.PROJECT_ID}}" '.blueprint.github_id = $projectId' package.json > "$TMP" && mv "$TMP" package.json + status: + - '[[ "{{.CURRENT_PROJECT_ID}}" == "{{.PROJECT_ID}}" ]]' update: deps: @@ -72,6 +77,10 @@ tasks: 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)" @@ -115,11 +124,11 @@ tasks: 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: - - | - if [ ! -z "$GITHUB_TOKEN" ] && [ ! -z "$ANSIBLE_GALAXY_TOKEN" ] && [ '{{.REPOSITORY_TYPE}}' == 'ansible' ]; then - gh secret set ANSIBLE_GALAXY_TOKEN -b "$ANSIBLE_GALAXY_TOKEN" - .config/log success 'Updated GitHub Actions `ANSIBLE_GALAXY_TOKEN` environment variable successfully' - fi + - 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 index d9ddf8d6..e5a9ed27 100644 --- a/.config/taskfiles/git/Taskfile-gitlab.yml +++ b/.config/taskfiles/git/Taskfile-gitlab.yml @@ -17,6 +17,10 @@ tasks: TOKEN_STATUS: sh: glab api projects/:fullpath/access_tokens | jq -r '.[] | select(.name=="PROJECT_CI_ACCESS_TOKENX") | .id' 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 \ @@ -33,6 +37,10 @@ tasks: 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: - | if [[ "$(glab api projects/:fullpath/protected_branches/master | jq '.code_owner_approval_required')" == 'false' ]]; then @@ -54,11 +62,14 @@ tasks: 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 & wait - .config/log success "Successfully set up GitLab protected branches" create: deps: @@ -79,22 +90,21 @@ tasks: 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: | - KEYWORDS="$(jq -r '.keywords | tostring' package.json | sed 's/\[//' | sed 's/\]//')" - if [ ! -z "$GITLAB_TOKEN" ] && test -e .git && ! glab repo view '{{.GITLAB_PATH}}' > /dev/null; then - NO_PROMPT=1 glab repo create '{{.GITLAB_PATH}}' --group '{{.GITLAB_GROUP}}' \ - --description '{{.EMOJI_START}}{{.DESCRIPTION}}{{.EMOJI_END}}' --name '{{.NAME}}' \ - {{.PROJECT_TYPE}} --tag "$KEYWORDS" - git add --all - git commit --quiet -m "🎂 Birth" -n - git push --quiet -u --no-progress gitlab master - .config/log success 'Created new GitLab repository - `https://gitlab.com/{{.GITLAB_PATH}}`' - fi + 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/\]//')" + git commit --quiet -m "🎂 Birth" -n + git add --all + git push --quiet -u --no-progress gitlab master ignore_error: true - sources: - - .variables.json - - package.json + status: + - '[ -z "$GITLAB_TOKEN" ] || ! test -e .git && glab repo view "{{.GITLAB_PATH}}" > /dev/null' preconditions: - sh: '[ "{{.DESCRIPTION}}" != "null" ]' msg: The `.description` in `package.json` must be set. @@ -115,6 +125,10 @@ tasks: - 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}} - | @@ -142,7 +156,7 @@ tasks: } cat {{.REPOSITORY_LIST}} | (while IFS= read -r REPO_DETAILS; do .config/log info "Executing logic on $REPO_DETAILS" - execRepo "$REPO_DETAILS" & + execRepo "$REPO_DETAILS"{{.CLI_ARGS}} done wait) @@ -165,6 +179,10 @@ tasks: **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}} @@ -188,16 +206,20 @@ tasks: - :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="$(mktemp)" && jq --arg groupId "$GROUP_ID" '.blueprint.gitlab_group_id = $groupId' package.json > "$TMP" mv "$TMP" package.json - .config/log success 'Added GitLab project ID to package.json' integrations: deps: @@ -210,13 +232,15 @@ tasks: 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: - - | - GITHUB_REPO="$(jq -r '.blueprint.repository.github' package.json)" - if [ ! -z "$GITLAB_TOKEN" ] && [ ! -z "$GITHUB_TOKEN" ]; then - glab api projects/:fullpath/integrations/github -X PUT -f token="$GITHUB_TOKEN" -f repository_url="$GITHUB_REPO" --silent - .config/log success 'Ensured GitLab'\''s GitHub integration is activated' - fi + - 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: @@ -231,22 +255,24 @@ tasks: 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 [ -n "$GITLAB_TOKEN" ] && [ -n "$GITHUB_TOKEN" ]; then - 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 - .config/log success 'Ensured pull mirroring from GitHub to GitLab is set up on GitLab' + - | + 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 - sources: - - package.json + - > + 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: @@ -256,35 +282,38 @@ tasks: 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)" - if [ ! -z "$GITLAB_TOKEN" ]; then - 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 - else - .config/log warn 'The `'"GITLAB_TOKEN"'` environment variable is not set so the GitLab pipelines cannot be updated via the API.' - fi - sources: - - .variables.json - - package.json + 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)" @@ -292,7 +321,6 @@ tasks: for PIPELINE_ID in $(jq -r '.[].id' "$TMP"); do glab api projects/:fullpath/pipeline_schedules/"$PIPELINE_ID" -X DELETE done - .config/log info 'Cleared pipeline schedules on GitLab' preconditions: - sh: '[ ! -z "$GITLAB_TOKEN" ]' msg: The `GITLAB_TOKEN` environment variable must be set to run this task @@ -332,22 +360,20 @@ tasks: 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')" - if [ ! -z "$GITLAB_TOKEN" ]; then - 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 - .config/log success 'Ensured GitLab metadata is up-to-date' - else - .config/log warn 'The `GITLAB_TOKEN` environment variable is not set so the GitLab repository cannot be updated via the API.' - fi - sources: - - .variables.json - - package.json + 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. @@ -363,16 +389,14 @@ tasks: 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: - - | - if [ ! -z "$GITLAB_TOKEN" ]; then - glab api projects/:fullpath/services/external-wiki -X PUT -f external_wiki_url="{{.DOCS_URL}}" --silent - .config/log success 'Ensured GitLab'\''s wiki settings are up-to-date' - else - .config/log warn 'The `GITLAB_TOKEN` environment variable is not set so the GitLab repository external wiki cannot be updated via the API.' - fi - sources: - - .variables.json + - 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. diff --git a/.config/taskfiles/git/Taskfile-hook.yml b/.config/taskfiles/git/Taskfile-hook.yml index ab12dfed..6f691f2e 100644 --- a/.config/taskfiles/git/Taskfile-hook.yml +++ b/.config/taskfiles/git/Taskfile-hook.yml @@ -8,21 +8,35 @@ 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: 'true' + 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 @@ -31,6 +45,9 @@ tasks: 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 @@ -41,5 +58,13 @@ tasks: - :fix:misc - :security:gitleaks - :security:private-keys + log: + error: '`pre-commit` hook encountered an error!' - pre-push: 'true' + start: '`pre-commit` hook running..' + pre-push: + log: + error: '`pre-push` hook encountered an error!' + start: '`pre-push` hook running..' + cmds: + - 'true' diff --git a/.config/taskfiles/git/Taskfile.yml b/.config/taskfiles/git/Taskfile.yml index 0621ad3f..eb9bdf4f 100644 --- a/.config/taskfiles/git/Taskfile.yml +++ b/.config/taskfiles/git/Taskfile.yml @@ -40,6 +40,8 @@ env: tasks: commit:automated: + log: + start: Running automated commit cmds: - cmd: | if [[ "$FULLY_AUTOMATED_TASKS" == 'true' ]]; then @@ -57,6 +59,10 @@ tasks: 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}}" @@ -72,7 +78,7 @@ tasks: git add {{.BASENAME}} git commit --quiet -m "refactor(convert-dir-to-submodule) Adding new submodule which was previously a directory." git push --quiet -u --no-progress origin HEAD - - .config/log success 'Converted the `{{.BASENAME}}` directory to a submodule' + - .config/log success '' 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. @@ -85,6 +91,10 @@ tasks: 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 - | @@ -95,6 +105,8 @@ tasks: HUSKY=0 git subrepo clone {{.BASENAME}} "$RELATIVE_DIR" -b master push:all: + log: + start: Running `git push all master` cmds: - | if [[ "$FULLY_AUTOMATED_TASKS" == 'true' ]]; then @@ -123,6 +135,10 @@ tasks: 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 - | @@ -164,6 +180,16 @@ tasks: - '[[ "$(git config remote.github.url)" == "${GITHUB_REPO}.git" ]] || [[ "${container:=}" == "docker" ]]' - '[[ "$(git config remote.gitlab.url)" == "${GITLAB_REPO}.git" ]] || [[ "${container:=}" == "docker" ]]' + 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 @@ -194,6 +220,10 @@ tasks: # /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 diff --git a/.config/taskfiles/go/Taskfile-goreleaser.yml b/.config/taskfiles/go/Taskfile-goreleaser.yml new file mode 100644 index 00000000..ed41e0ec --- /dev/null +++ b/.config/taskfiles/go/Taskfile-goreleaser.yml @@ -0,0 +1,37 @@ +--- +version: '3' + +vars: + GORELEASER_CONFIG: + sh: if [ -f .goreleaser.yml ]; then echo ".goreleaser.yml"; else echo ".config/goreleaser.yml"; fi + +tasks: + build: + deps: + - :install:go:goreleaser + log: + error: GoReleaser encountered an error (config file -> `{{.GORELEASER_CONFIG}}`) + start: Building with project with GoReleaser + success: Completed building the project with GoReleaser + cmds: + - goreleaser build --config {{.GORELEASER_CONFIG}} + + 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 + log: + error: Encountered error while releasing with GoReleaser (config file -> `{{.GORELEASER_CONFIG}}) + start: Publishing compiled assets with GoReleaser + success: Successfully published assets with GoReleaser! + cmds: + - goreleaser release --skip-publish --config {{.GORELEASER_CONFIG}} diff --git a/.config/taskfiles/go/Taskfile.yml b/.config/taskfiles/go/Taskfile.yml index bd70bc83..91f57459 100644 --- a/.config/taskfiles/go/Taskfile.yml +++ b/.config/taskfiles/go/Taskfile.yml @@ -19,12 +19,12 @@ tasks: build:bin: deps: - :install:software:go - cmds: - - '{{.BUILD_COMMAND}}' 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: + - '{{.BUILD_COMMAND}}' sources: - '**/*.go' generates: @@ -39,6 +39,10 @@ tasks: 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)" @@ -46,9 +50,5 @@ tasks: TMP_VARS="$(mktemp)" jq --arg output "$(cat "$TMP_HELP")" '.help_menu_output = $output' .variables.json > "$TMP_VARS" mv "$TMP_VARS" .variables.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` status: - '[[ "{{.BUILD_COMMAND}}" == "null" ]] || [[ "{{.BUILD_OUTPUT}}" == "null" ]]' diff --git a/.config/taskfiles/install/Taskfile-go.yml b/.config/taskfiles/install/Taskfile-go.yml index 0ceb9e8c..2ad1e6f1 100644 --- a/.config/taskfiles/install/Taskfile-go.yml +++ b/.config/taskfiles/install/Taskfile-go.yml @@ -2,22 +2,46 @@ version: '3' tasks: - goget: - deps: - - :install:software:go + get: run: when_changed + log: + error: Failed to acquire Go package named `{{.PACKAGE}}` + start: Getting Go package named `{{.PACKAGE}}` + success: Retrieved Go packaged named `{{.PACKAGE}}` cmds: - - | - .config/log info 'Installing the `{{.PACKAGE}}` Go-lang package' - go get {{.PACKAGE}} - .config/log success 'Successfully installed the `{{.PACKAGE}}` Go-lang package' + - task: :install:software:go + - go get {{.PACKAGE}} status: - type {{.BIN}} &> /dev/null + goreleaser: + run: once + cmds: + - task: install + vars: + BIN: goreleaser + PACKAGE: github.com/goreleaser/goreleaser@latest + status: + - type goreleaser > /dev/null + gotestsum: run: once cmds: - - task: goget + - task: get vars: BIN: gotestsum PACKAGE: gotest.tools/gotestsum + status: + - type gotestsum > /dev/null + + install: + 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 diff --git a/.config/taskfiles/install/Taskfile-npm.yml b/.config/taskfiles/install/Taskfile-npm.yml index 4d0a0dee..da039e2f 100644 --- a/.config/taskfiles/install/Taskfile-npm.yml +++ b/.config/taskfiles/install/Taskfile-npm.yml @@ -102,13 +102,12 @@ tasks: vars: PACKAGE_BIN: '{{if index . "NPM_PACKAGE_BIN"}}{{.NPM_PACKAGE_BIN}}{{else}}{{.NPM_PACKAGE}}{{end}}' run: when_changed + log: + error: Failed to install NPM package `{{.NPM_PACKAGE}}` globally + start: Installing NPM package `{{.NPM_PACKAGE}}` globally + success: Installed NPM package `{{.NPM_PACKAGE}}` globally cmds: - - | - if ! type {{.PACKAGE_BIN}} > /dev/null; then - .config/log info 'Installing _NPM CLI_ package `{{.NPM_PACKAGE}}` globally' - {{.NPM_PROGRAM}} install -g {{.NPM_PACKAGE}} - .config/log success 'Successfully installed `{{.NPM_PACKAGE}}` globally' - fi + - '{{.NPM_PROGRAM}} install -g {{.NPM_PACKAGE}}' status: - type {{.PACKAGE_BIN}} > /dev/null @@ -125,27 +124,24 @@ tasks: echo 'false' fi run: once + log: + error: Error installing `{{.NPM_PACKAGE}}` globally + start: Installing NPM global library `{{.NPM_PACKAGE}}` which does not have a CLI + success: '`{{.NPM_PACKAGE}}` has been installed globally' cmds: - - | - if [[ "$PACKAGE_EXISTS" == 'false' ]]; then - .config/log info "Installing _NPM library_ package `{{.NPM_PACKAGE}}` globally" - {{.NPM_PROGRAM}} install -g {{.NPM_PACKAGE}} - .config/log success 'Successfully installed `{{.NPM_PACKAGE}}` globally' - fi + - '{{.NPM_PROGRAM}} install -g {{.NPM_PACKAGE}}' status: - '[[ "$PACKAGE_EXISTS" == "true" ]]' global:package-manager: - deps: - - :install:software:node run: once + log: + error: Failed to install `{{.NPM_PROGRAM}}` + start: Installing `{{.NPM_PROGRAM}}` + success: Successfully installed `{{.NPM_PROGRAM}}` cmds: - - | - if ! type {{.NPM_PROGRAM}} > /dev/null; then - .config/log info 'Installing `{{.NPM_PROGRAM}}' - npm install -g {{.NPM_PROGRAM}} - .config/log success 'Successfully installed `{{.NPM_PROGRAM}}`' - fi + - task: :install:software:node + - npm install -g {{.NPM_PROGRAM}} status: - type {{.NPM_PROGRAM}} > /dev/null @@ -280,10 +276,21 @@ tasks: - task: global vars: NPM_PACKAGE: pnpm - - if [ -f package-lock.json ] || [ -f yarn.lock ] || [ -f npm-shrinkwrap.json ]; then pnpm import; fi + - task: pnpm:import status: - type pnpm &> /dev/null + 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 cmds: @@ -307,6 +314,13 @@ tasks: NPM_PACKAGE: remark-cli NPM_PACKAGE_BIN: remark + semantic-release: + run: once + cmds: + - task: global + vars: + NPM_PACKAGE: semantic-release + sharp: run: once cmds: diff --git a/.config/taskfiles/install/Taskfile-python.yml b/.config/taskfiles/install/Taskfile-python.yml index b1b0cf11..ef3776f9 100644 --- a/.config/taskfiles/install/Taskfile-python.yml +++ b/.config/taskfiles/install/Taskfile-python.yml @@ -3,21 +3,23 @@ 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: - - | - .config/log info 'Installing `{{.PACKAGE}}` with pip3' - pip3 install {{.PACKAGE}} - .config/log success 'Successfully installed pip3 package named `{{.PACKAGE}}`' + - task: :install:software:python + - pip3 install {{.PACKAGE}} status: - type {{.PACKAGE}} > /dev/null pipx: - deps: - - :install:software:python run: when_changed + log: + error: Failed to install `{{.PACKAGE}}` with pipx + start: Installing `{{.PACKAGE}}` with pipx + success: Successfully installed `{{.PACKAGE}}` with pipx cmds: - task: :install:software:pipx - | @@ -42,10 +44,9 @@ tasks: PACKAGE: pytest-cov requirements: - deps: - - :install:software:python run: once cmds: + - task: :install:software:python - task: requirements:poetry:prereqs - task: requirements:poetry - task: :{{if eq .REPOSITORY_TYPE "ansible"}}ansible:galaxy:requirements{{else}}donothing{{end}} @@ -53,19 +54,17 @@ tasks: - '[[ "$OPTIMIZED_IMAGE" == "true" ]]' requirements:poetry: - deps: - - :install:software:poetry run: once + log: + error: Failed to configure / install via `poetry` + start: Configuring Poetry and running `poetry install` + success: Successfully ran `poetry install` cmds: - - | - .config/log info 'Running `poetry install`' - if [ '{{.PYTHON_VIRTUALENV}}' != 'true' ]; then - poetry config virtualenvs.create false - else - poetry config --local virtualenvs.in-project true - fi - poetry install - .config/log success '`poetry install` completed successfully' + - task: :install:software:poetry + - poetry config virtualenvs.create {{.PYTHON_VIRTUALENV}} + - poetry config virtualenvs.in-project true + - poetry update {{.PYTHON_KEEP_UPDATED}} + - poetry install sources: - pyproject.toml preconditions: @@ -74,20 +73,43 @@ tasks: requirements:poetry:prereqs: vars: - GCC5_MISSING_MESSAGE: 'It looks like your system is missing gcc-5 but gcc is available. You will be prompted - for a password to run:' + GCC5_MISSING_MESSAGE: 'It looks like your system is missing gcc-5 but gcc is available. You might be prompted + for your sudo password to run the following command:' + log: + error: Error checking `gcc` + start: Checking if `gcc` should be symlinked to `gcc-5` + success: Checked `gcc` cmds: - | - if ! which gcc-5 > /dev/null; then - if which gcc; then - if [ -w /usr/local/bin ]; then - ln -s "$(which gcc)" /usr/local/bin/gcc-5 - else - .config/log info '{{.GCC5_MISSING_MESSAGE}}' - .config/log info '`sudo ln -s '"$(which gcc)"' /usr/local/bin/gcc-5`' - sudo ln -s "$(which gcc)" /usr/local/bin/gcc-5 - fi + if which gcc; then + if [ -w /usr/local/bin ]; then + ln -s "$(which gcc)" /usr/local/bin/gcc-5 else - .config/log warn '`gcc` is missing.' + .config/log info '{{.GCC5_MISSING_MESSAGE}}' + .config/log info '`sudo ln -s '"$(which gcc)"' /usr/local/bin/gcc-5`' + sudo ln -s "$(which gcc)" /usr/local/bin/gcc-5 fi + else + .config/log warn '`gcc` is missing.' fi + status: + - which gcc-5 + + venv:node: + log: + error: Encountered error while setting up `.venv` environment.. Is Python 3 installed? + start: Setting up a Python 3 `.venv` + success: Finished setting up `.venv`. Run `. .venv/bin/activate` to load the environment into your shell. + cmds: + - task: :install:software:python + - | + REQUIREMENTS_PATH="$PWD/.config/requirements.txt" + cd "$INIT_CWD" || exit + if [ ! -f venv/bin/activate ]; then + python3 -m venv venv || exit 0 + fi + . .venv/bin/activate || exit 0 + pip3 install -U pip setuptools || exit 0 + pip3 install -r "$REQUIREMENTS_PATH" || exit 0 + sources: + - requirements.txt diff --git a/.config/taskfiles/install/Taskfile-software.yml b/.config/taskfiles/install/Taskfile-software.yml index 243c72a5..ceae4934 100644 --- a/.config/taskfiles/install/Taskfile-software.yml +++ b/.config/taskfiles/install/Taskfile-software.yml @@ -9,6 +9,8 @@ tasks: - task: brew:formulae vars: FORMULAE: act + status: + - type act > /dev/null allure: run: once @@ -16,28 +18,30 @@ tasks: - task: brew:formulae vars: FORMULAE: allure + status: + - type allure > /dev/null brew: - deps: - - common run: once - cmds: - - task: brew:{{OS}} - 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 + brew:cask: - deps: - - brew run: when_changed - cmds: - - brew install --cask {{.CASK}} log: error: Failed to install `{{.CASK}}` start: Ensuring the `{{.CASK}}` Homebrew cask is installed success: Successfully installed `{{.CASK}}` + cmds: + - task: brew + - brew install --cask {{.CASK}} status: - type {{.CASK}} &> /dev/null @@ -59,15 +63,14 @@ tasks: - type brew &> /dev/null brew:formulae: - deps: - - brew run: when_changed - cmds: - - brew install {{.FORMULAE}} log: error: Failed to install `{{.FORMULAE}}` start: Ensuring the `{{.FORMULAE}}` Homebrew formulae is installed success: Successfully installed `{{.FORMULAE}}` + cmds: + - task: brew + - brew install {{.FORMULAE}} status: - type {{.FORMULAE}} &> /dev/null @@ -97,6 +100,10 @@ tasks: 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 @@ -104,10 +111,6 @@ tasks: - task: compatibility:grep - task: compatibility:gnu-tar - task: compatibility:gawk - log: - error: Failed to setup GNU-compatibility tools - start: Installing GNU-compatibility tools for macOS via Homebrew - success: Successfully installed GNU-compatibility tools status: - '[ "{{OS}}" != "darwin" ]' @@ -125,13 +128,13 @@ tasks: common: run: once - cmds: - - task: common:{{OS}} - 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 @@ -140,12 +143,12 @@ tasks: vars: CLT_STATUS: sh: brew config | grep CLT - cmds: - - sudo xcode-select --install 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" ]' @@ -162,6 +165,10 @@ tasks: 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}} status: @@ -172,7 +179,9 @@ tasks: 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 :)" - | + .config/log info 'Attempting to run `sudo pacman update` (for Homebrew dependencies)' sudo pacman update + .config/log info 'Attempting to run `sudo pacman -S base-devel curl file git procps-ng` (for Homebrew dependencies)' sudo pacman -S base-devel curl file git procps-ng status: - type curl &> /dev/null @@ -185,8 +194,9 @@ tasks: interactive: true cmds: - | - .config/log info 'Attempting to install Homebrew dependencies (sudo password required)' + .config/log info 'Attempting to run `sudo apt-get -y update` (for Homebrew dependencies)' sudo apt-get -y update + .config/log info 'Attempting to run `sudo apt-get install -y build-essential curl file git procps` (Homebrew dependencies)' sudo apt-get install -y build-essential curl file git procps status: - type curl &> /dev/null @@ -204,35 +214,38 @@ tasks: yum grouplist 'Development Tools' &> "$HOME/.config/bodega/yum-devtools-check-ran" DEV_TOOLS_NOT_INSTALLED="$(grep 'No groups match' < "$HOME/.config/bodega/yum-devtools-check-ran" > /dev/null)" if [[ "$DEV_TOOLS_NOT_INSTALLED" == '0' ]]; then + .config/log info 'Attempting to run `sudo yum groupinstall -y "Development Tools"`' sudo yum groupinstall -y 'Development Tools' fi touch "$HOME/.config/bodega/yum-devtools-check-ran" fi if ! rpm --quiet --query curl file git procps-ng; then - .config/log info 'Attempting to install Homebrew dependencies (sudo password required)' + .config/log info 'Attempting to run `sudo yum install -y curl file git procps-ng`' sudo yum install -y curl file git procps-ng fi if [ -f '/etc/os-release' ]; then source /etc/os-release if [[ "$ID" == 'fedora' ]] && [ "$VERSION_ID" -gt "29" ]; then if ! rpm --quiet --query libxcrypt-compat; then - .config/log info 'Attempting to install Fedora-specific Homebrew dependency (sudo password required)' + .config/log info 'Attempting to run `sudo yum -y install libxcrypt-compat` (Homebrew dependencies)' sudo yum -y install libxcrypt-compat fi fi fi 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 '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.' + - echo "*** Unknown OS is $OSTYPE ***" common:windows: + log: + error: Windows is not supported. Try using a Windows WSL environment. cmds: - exit 1 - log: - error: Windows is not supported. Try using a Windows WSL environment. compatibility:findutils: cmds: - task: brew:formulae @@ -246,6 +259,8 @@ tasks: - task: brew:formulae vars: FORMULAE: gawk + status: + - type gawk > /dev/null compatibility:gnu-sed: cmds: @@ -290,6 +305,8 @@ tasks: run: once cmds: - task: docker:{{OS}} + status: + - type docker > /dev/null docker:darwin: run: once @@ -330,6 +347,8 @@ tasks: - task: brew:formulae vars: FORMULAE: exiftool + status: + - type exiftool > /dev/null exit:notice:reload: cmds: @@ -355,6 +374,8 @@ tasks: - task: brew:cask vars: CASK: google-cloud-sdk + status: + - type gcloud > /dev/null gcloud:linux: cmds: @@ -372,11 +393,15 @@ tasks: - task: brew:formulae vars: FORMULAE: gh + status: + - type gh > /dev/null git: - deps: - - common run: once + cmds: + - task: common + status: + - type git > /dev/null gitleaks: run: once @@ -384,6 +409,8 @@ tasks: - task: brew:formulae vars: FORMULAE: gitleaks + status: + - type gitleaks > /dev/null glab: run: once @@ -391,6 +418,8 @@ tasks: - task: brew:formulae vars: FORMULAE: glab + status: + - type glab > /dev/null go: run: once @@ -398,6 +427,8 @@ tasks: - task: brew:formulae vars: FORMULAE: go + status: + - type go > /dev/null grype: run: once @@ -414,6 +445,8 @@ tasks: - task: brew:formulae vars: FORMULAE: jq + status: + - type jq > /dev/null node: run: once @@ -421,38 +454,38 @@ tasks: - task: brew:formulae vars: FORMULAE: node + status: + - type node > /dev/null pipx: run: once - cmds: - - task: pipx:{{OS}} 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: - deps: - - brew cmds: + - task: brew - brew install pipx - pipx ensurepath pipx:linux: - deps: - - :install:software:python cmds: - python3 -m pip install --user pipx - python3 -m pipx ensurepath pipx:windows: + log: + error: These scripts are not currently compatible with Windows. Try using WSL. cmds: - exit 1 - log: - error: These scripts are not currently compatible with Windows. Try using WSL. poetry: run: once cmds: @@ -476,17 +509,18 @@ tasks: FORMULAE: poetry poetry:windows: + log: + error: These scripts are not currently compatible with Windows. Try using WSL. cmds: - exit 1 - log: - error: These scripts are not currently compatible with Windows. Try using WSL. python: - deps: - - brew # bug fix run: once cmds: + - task: brew - task: python:{{OS}} + status: + - type python3 > /dev/null python:darwin: run: once @@ -505,17 +539,19 @@ tasks: SOFTWARE: python python:windows: + log: + error: These scripts are not currently compatible with Windows. Try using WSL. cmds: - exit 1 - log: - error: These scripts are not currently compatible with Windows. Try using WSL. rsync: run: once cmds: - task: brew:formulae vars: FORMULAE: rsync + status: + - type rsync &> /dev/null sshpass: run: once @@ -541,6 +577,8 @@ tasks: - task: brew:formulae vars: FORMULAE: tokei + status: + - type tokei > /dev/null trivy: run: once @@ -554,6 +592,8 @@ tasks: vagrant: cmds: - task: vagrant:{{OS}} + status: + - type vagrant > /dev/null vagrant:darwin: run: once @@ -617,3 +657,5 @@ tasks: - task: brew:formulae vars: FORMULAE: yq + status: + - type yq > /dev/null diff --git a/.config/taskfiles/install/Taskfile.yml b/.config/taskfiles/install/Taskfile.yml index e9664a02..c50cca07 100644 --- a/.config/taskfiles/install/Taskfile.yml +++ b/.config/taskfiles/install/Taskfile.yml @@ -5,24 +5,26 @@ 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: - | - .config/log info 'Installing software bundle defined in `.config/Brewfile`' brew tap Homebrew/bundle cd .config brew bundle - .config/log success 'Successfully installed common dependencies defined in `.config/Brewfile`' .config/log warn 'A reboot is recommended' - .config/log info 'Some of the software you may have installed might require a reboot' 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: - | - if ! type {{.SOFTWARE}} &> /dev/null; then - .config/log info 'Installing `{{.SOFTWARE}}` via `curl -sS https://install.doctor/{{.SOFTWARE}} | bash`' - curl -sS https://install.doctor/{{.SOFTWARE}} | bash - .config/log success 'Successfully installed `{{.SOFTWARE}}`' - fi + .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 || [[ "${container:=}" == "docker" ]] @@ -31,6 +33,10 @@ tasks: - :install:npm:{{.NPM_PROGRAM}} - :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' ' ')" @@ -43,7 +49,6 @@ tasks: .config/log info "Installing the following NPM packages globally - $LIST" npm install -g $LIST fi - .config/log success 'Finished preloading global NPM packages' status: - '[[ "${container:=}" == "docker" ]]' @@ -52,16 +57,22 @@ tasks: - :install:npm:{{.NPM_PROGRAM_LOCAL}} - :install:npm:cz-emoji run: once + log: + error: Encountered error while installing local NPM dependencies + start: Installing local NPM dependencies + success: Successfully installed local NPM dependencies cmds: - - .config/log info 'Installing local NPM dependencies' - cmd: '{{.NPM_PROGRAM_LOCAL}} i --shamefully-hoist --no-frozen-lockfile' ignore_error: true - task: modules:local:sync - - .config/log success 'Successfully installed local NPM dependencies' sources: - package.json modules:local:sync: + 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)" @@ -69,7 +80,7 @@ tasks: 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; then + 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 @@ -113,6 +124,9 @@ tasks: vars: UNAME: sh: uname + log: + error: Failed to modify PATH + start: Adding `$HOME/{{.PATH_STRING}}` to the PATH in $HOME/.profile cmds: - | if [[ '{{.UNAME}}' == 'Darwin' ]] || [[ '{{.UNAME}}' == 'Linux' ]]; then @@ -127,7 +141,7 @@ tasks: elif [[ "$OSTYPE" == 'freebsd'* ]]; then .config/log error "FreeBSD support not added yet" && exit 1 else - .config/log error "System type not recognized" + .config/log error "System type not recognized ($OSTYPE)" fi pipx:global: @@ -138,6 +152,10 @@ tasks: PIPX_PACKAGES: ansible-base ansible-lint ansibler black blocklint docker flake8 mod-ansible-autodoc molecule molecule-docker molecule-vagrant pre-commit-hooks proselint python-vagrant pywinrm + log: + error: Error encountered while pre-loading common pipx packages + start: Pre-loading common pipx packages + success: Finished pre-loading common pipx packages cmds: - | for PKG in {{.PIPX_PACKAGES}}; do @@ -149,9 +167,12 @@ tasks: fi done wait - .config/log success 'Successfully preloaded commonly used Python packages' profile:add: + log: + error: Error modifying $HOME/.profile + start: Adding `{{.PROFILE_STRING}}` to $HOME/.profile + success: Successfully modified $HOME/.profile cmds: - | if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]]; then @@ -175,21 +196,21 @@ tasks: 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 [ -f 'tsconfig.json' ]; then - if [[ "$(jq -r '.dependencies.tslib' package.json)" == 'null' ]]; then - if [ "$(jq -r '.compilerOptions.importHelpers')" != 'true' ]; then - .config/log info '{{.TSLIB_MSG}}' - else - .config/log info 'Automatically installing `tslib` since `importHelpers` is set to true in tsconfig.json' - {{.NPM_PROGRAM}} install --save tslib@latest - .config/log success 'Successfully installed `tslib`' - fi + if [[ "$(jq -r '.dependencies.tslib' package.json)" == 'null' ]]; then + if [ "$(jq -r '.compilerOptions.importHelpers')" != 'true' ]; then + .config/log info '{{.TSLIB_MSG}}' else - task install:modules:local:sync -- "tslib" + .config/log info 'Automatically installing `tslib` since `importHelpers` is set to true in tsconfig.json' + {{.NPM_PROGRAM}} install --save tslib@latest + .config/log success 'Successfully installed `tslib`' fi + else + task install:modules:local:sync -- "tslib" fi - sources: - - package.json - - tsconfig.json + status: + - '[ ! -f tsconfig.json ]' diff --git a/.config/taskfiles/lint/Taskfile-esprint.yml b/.config/taskfiles/lint/Taskfile-esprint.yml index 7f62951e..6bb5b3ea 100644 --- a/.config/taskfiles/lint/Taskfile-esprint.yml +++ b/.config/taskfiles/lint/Taskfile-esprint.yml @@ -6,10 +6,11 @@ tasks: deps: - :install:modules:local - :install:npm:esprint - desc: Lint with `eslint` across multiple threads summary: | # Lint with `eslint` across multiple threads + DEVELOPER NOTE: This feature might not work. I was unable to get this working. + This task will lint the project using `esprint`. Using `esprint` allows you to run `eslint` across multiple threads which improves performance in most cases (especially for larger projects). It is capable of linting JSON, YML, JS, TS, and HTML as long as the correct diff --git a/.config/taskfiles/lint/Taskfile-markdown.yml b/.config/taskfiles/lint/Taskfile-markdown.yml index 798e907c..9455f784 100644 --- a/.config/taskfiles/lint/Taskfile-markdown.yml +++ b/.config/taskfiles/lint/Taskfile-markdown.yml @@ -21,6 +21,10 @@ tasks: 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}} @@ -49,6 +53,10 @@ tasks: `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}} diff --git a/.config/taskfiles/lint/Taskfile-prose.yml b/.config/taskfiles/lint/Taskfile-prose.yml index 9ac04447..cbd5877a 100644 --- a/.config/taskfiles/lint/Taskfile-prose.yml +++ b/.config/taskfiles/lint/Taskfile-prose.yml @@ -19,6 +19,10 @@ tasks: `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 - | @@ -30,6 +34,10 @@ tasks: - 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 @@ -40,9 +48,10 @@ tasks: - cp .config/proselint.json ~/.config/proselint/config.json prose:config:restore: + log: + error: Error restoring original Proselint configuration + start: Restoring original Proselint configuration cmds: - - | - if [ -f ~/.config/proselint/config.json.backup ]; then - .config/log info "Restoring original Proselint configuration" - mv ~/.config/proselint/config.json.backup ~/.config/proselint/config - fi + - 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 index 7fdc9cf2..e2fb94db 100644 --- a/.config/taskfiles/lint/Taskfile.yml +++ b/.config/taskfiles/lint/Taskfile.yml @@ -36,6 +36,10 @@ tasks: [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: - | .config/log info 'Linting the project with ansible-lint' @@ -45,6 +49,9 @@ tasks: deps: - :install:python:requirements 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: - '{{.PYTHON_HANDLE}}blocklint --wordlist blacklist,slave,whitelist {{.CLI_ARGS}}' - '{{.PYTHON_HANDLE}}check-merge-conflict {{.CLI_ARGS}}' @@ -55,6 +62,10 @@ tasks: codeclimate: deps: - :install:software:codeclimate + log: + error: '`codeclimate` has detected errors' + start: Running `codeclimate` + success: Successfully validated the project by running `codeclimate` cmds: - codeclimate @@ -70,6 +81,8 @@ tasks: **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' ] && [ "$(head -c12 < .git/MERGE_MSG)" != 'Merge branch' ]; then @@ -118,6 +131,10 @@ tasks: `task lint:docker -- CustomDockerfile` For more information, see [Hadolint's GitHub page](https://github.com/hadolint/hadolint). + log: + error: Hadolint reported errors + start: Linting {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}the project{{end}} with Hadolint + success: Hadolint reported no errors cmds: - | .config/log info 'Linting {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}all Dockerfiles{{end}} with a Hadolint Docker image' @@ -153,21 +170,56 @@ tasks: **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: - > - .config/log info 'Linting {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}the project{{end}} with ESLint' - - {{.NPX_HANDLE}}eslint -c package.json --no-eslintrc --format pretty --cache + {{.NPX_HANDLE}}eslint -c package.json --no-eslintrc --format {{.ESLINT_FORMATTER}} --cache --cache-location .cache/eslintcache {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} - js: + eslint:warn-all: deps: - :install:modules:local - :install:npm:eslint + desc: Convert ESLint errors into warnings in `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: - > - .config/log info 'Linting {{if .CLI_ARGS}}`{{.CLI_ARGS}}`{{else}}JavaScript/TypeScript files{{end}} with ESLint' + ESLINT_TMP="$(mktemp)" + + {{.NPX_HANDLE}}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" ]; then + jq --arg rule "$ESLINT_RULE" '.eslintConfig.rules[$rule] - "warn"' package.json > "$TMP"; + mv "$TMP" package.json; + fi; + done < "$ESLINT_TMP" + 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: + - > {{.NPX_HANDLE}}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}} @@ -176,10 +228,12 @@ tasks: - :install:modules:global - :install:modules:local - :install:python:requirements + log: + error: Errors were encountered when validating the staged files + start: Linting staged files + success: The staged files appear to be good! cmds: - - | - .config/log info 'Linting changed files' - {{.NPX_HANDLE}}lint-staged + - '{{.NPX_HANDLE}}lint-staged' packer: deps: @@ -199,6 +253,10 @@ tasks: `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}} @@ -228,10 +286,12 @@ tasks: 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: - - | - .config/log info 'Linting {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}the project{{end}} with Prettier' - {{.NPX_HANDLE}}prettier --ignore-path {{.PRETTIERIGNORE_CONFIG}} --list-different {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + - '{{.NPX_HANDLE}}prettier --ignore-path {{.PRETTIERIGNORE_CONFIG}} --list-different {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}}' python: deps: @@ -260,6 +320,10 @@ tasks: `task lint:python -- myfile.py` For more information, see [Flake8's GitHub page](https://github.com/PyCQA/flake8). + 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: - | {{if .CLI_ARGS}} @@ -297,6 +361,10 @@ tasks: `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' @@ -332,11 +400,12 @@ tasks: - 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 - .config/log info 'Running a spell-check with `cspell`' {{.NPX_HANDLE}}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.' @@ -347,6 +416,8 @@ tasks: 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)" @@ -377,10 +448,12 @@ tasks: **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: - - | - .config/log info 'Ensuring {{if .CLI_ARGS}}{{.CLI_ARGS}} is valid TOML{{else}}the project'\''s TOML files are valid{{end}}' - {{.PYTHON_HANDLE}}check-toml {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + - '{{.PYTHON_HANDLE}}check-toml {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}}' vagrant: deps: @@ -394,10 +467,12 @@ tasks: 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: - - | - .config/log info 'Validating the Vagrantfile' - vagrant validate + - vagrant validate xml: deps: @@ -414,10 +489,12 @@ tasks: **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: - - | - .config/log info 'Ensuring {{if .CLI_ARGS}}{{.CLI_ARGS}} is valid XML{{else}}the project contains only valid XML{{end}}' - {{.PYTHON_HANDLE}}check-xml {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + - '{{.PYTHON_HANDLE}}check-xml {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}}' yaml: deps: @@ -443,7 +520,9 @@ tasks: `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: - - | - .config/log info 'Linting {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}the project{{end}} with `yamllint`' - {{.PYTHON_HANDLE}}yamllint -c {{.YAMLLINT_CONFIG}} -s {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}} + - '{{.PYTHON_HANDLE}}yamllint -c {{.YAMLLINT_CONFIG}} -s {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.{{end}}' diff --git a/.config/taskfiles/nest/Taskfile.yml b/.config/taskfiles/nest/Taskfile.yml index 9189ea59..093c0f2e 100644 --- a/.config/taskfiles/nest/Taskfile.yml +++ b/.config/taskfiles/nest/Taskfile.yml @@ -19,7 +19,7 @@ tasks: **Example with custom endpoint:** `task npm:benchmark -- /custom/endpoint` + log: + start: Running clinic with autocannon on dist/main.js cmds: - - | - .config/log info 'Running clinic with autocannon on dist/main.js' - {{.NPX_HANDLE}}clinic doctor --autocannon [ {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}/{{end}} --method POST ] -- node ./dist/main.js + - '{{.NPX_HANDLE}}clinic doctor --autocannon [ {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}/{{end}} --method POST ] -- node ./dist/main.js' diff --git a/.config/taskfiles/npm/Taskfile-cov.yml b/.config/taskfiles/npm/Taskfile-cov.yml index f86e3936..ff6e3157 100644 --- a/.config/taskfiles/npm/Taskfile-cov.yml +++ b/.config/taskfiles/npm/Taskfile-cov.yml @@ -12,28 +12,34 @@ tasks: 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: - - | - .config/log info 'Running `nyc report`' - {{.NPX_HANDLE}}nyc report - - | - .config/log info 'Checking coverage with `nyc`' - {{.NPX_HANDLE}}nyc check-coverage --lines 100 --functions 100 --branches 100 + - '{{.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: - - .config/log info 'Reporting with nyc in html format' - '{{.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: - - .config/log info 'Reporting with nyc in lcov format' - '{{.NPX_HANDLE}}nyc report --reporter=lcov' open: @@ -41,8 +47,9 @@ tasks: - html - :npm:install:open-cli desc: Ensures the code coverage report is generated and opens it in a browser + log: + start: Opening `coverage/index.html` with the default browser cmds: - - .config/log info 'Opening coverage/index.html in the default browser' - '{{.NPX_HANDLE}}open-cli coverage/index.html' report: @@ -55,6 +62,9 @@ tasks: - lcov - :install:npm:codecov desc: Uploads code coverage report to `codecov.io` + log: + error: Error while running `codecov` + start: Running `codecov` + success: Successfully ran `codecov` cmds: - - .config/log info 'Running `codecov`' - '{{.NPX_HANDLE}}codecov' diff --git a/.config/taskfiles/npm/Taskfile-doc.yml b/.config/taskfiles/npm/Taskfile-doc.yml index f610cc0d..56b104c4 100644 --- a/.config/taskfiles/npm/Taskfile-doc.yml +++ b/.config/taskfiles/npm/Taskfile-doc.yml @@ -6,27 +6,29 @@ tasks: deps: - :npm:install:typedoc desc: Generate TypeDoc HTML documentation + log: + error: Encountered error generating HTML documentation with TypeDoc + start: Generating HTML documentation with TypeDoc + success: Successfully generated HTML technical documentation with TypeDoc cmds: - - | - .config/log info 'Generating documentation with TypeDoc' - {{.NPX_HANDLE}}typedoc src/ --exclude "**/*.spec.ts" --out build/docs - .config/log success 'Successfully generated technical documentation with TypeDoc' + - '{{.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: - - | - .config/log info 'Generating TypeDoc documentation in JSON format' - {{.NPX_HANDLE}}typedoc src/ --exclude "**/*.spec.ts" --json build/docs/typedoc.json - .config/log success 'Successfully generated JSON TypeDoc documentation' + - '{{.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: - - | - .config/log info 'Opening `build/docs/index.html` in the default browser' - {{.NPX_HANDLE}}open-cli build/docs/index.html + - '{{.NPX_HANDLE}}open-cli build/docs/index.html' diff --git a/.config/taskfiles/npm/Taskfile.yml b/.config/taskfiles/npm/Taskfile.yml index df782963..a3dca70e 100644 --- a/.config/taskfiles/npm/Taskfile.yml +++ b/.config/taskfiles/npm/Taskfile.yml @@ -318,10 +318,11 @@ tasks: - :install:npm:nodemon - :install:npm:ts-node desc: Debug tests with DevTools in `watch` mode + log: + start: Running `{{.NPX_HANDLE}}ndb nodemon --config .config/nodemon-jest.json` to enable debugging with Chrome DevTools in watch mode cmds: - | - .config/log info 'Running `{{.NPX_HANDLE}}ndb nodemon --config .common/nodemon-jest.json` to enable debugging with DevTools in watch mode' - {{.NPX_HANDLE}}ndb nodemon --config .common/nodemon-jest.json + {{.NPX_HANDLE}}ndb nodemon --config .config/nodemon-jest.json typesync: deps: diff --git a/.config/taskfiles/release/Taskfile.yml b/.config/taskfiles/release/Taskfile.yml new file mode 100644 index 00000000..01d3896f --- /dev/null +++ b/.config/taskfiles/release/Taskfile.yml @@ -0,0 +1,14 @@ +--- +version: '3' + +tasks: + semantic-release: + deps: + - :install:npm:semantic-release + - :install:software:poetry + log: + error: Error while running `semantic-release` + start: Releasing.. + success: Finished running `semantic-release` + cmds: + - semantic-release diff --git a/.config/taskfiles/security/Taskfile.yml b/.config/taskfiles/security/Taskfile.yml index 24e6f90a..532f7b12 100644 --- a/.config/taskfiles/security/Taskfile.yml +++ b/.config/taskfiles/security/Taskfile.yml @@ -10,6 +10,10 @@ tasks: 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: - poetry run bandit -r run.py - poetry run bandit -r src @@ -33,6 +37,10 @@ tasks: `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}} @@ -61,6 +69,10 @@ tasks: `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}} @@ -81,6 +93,10 @@ tasks: **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}} @@ -100,6 +116,10 @@ tasks: **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: - | {{if .CLI_ARGS}} diff --git a/.config/taskfiles/symlink/Taskfile.yml b/.config/taskfiles/symlink/Taskfile.yml index 2c30e804..bf8c96a9 100644 --- a/.config/taskfiles/symlink/Taskfile.yml +++ b/.config/taskfiles/symlink/Taskfile.yml @@ -15,6 +15,7 @@ tasks: This can cause issues so, in order for everything to work, each folder needs to be symlinked to `~/.ansible/roles` with the namespace prepended. cmds: + - mkdir -p "$HOME/.ansible/roles" - | while read ROLE_PATH; do ROLE_FOLDER="{{.GALAXY_NAMESPACE}}.$(basename "$ROLE_PATH")" diff --git a/.config/taskfiles/upstream/Taskfile.yml b/.config/taskfiles/upstream/Taskfile.yml index c0079dfe..f8df2a5b 100644 --- a/.config/taskfiles/upstream/Taskfile.yml +++ b/.config/taskfiles/upstream/Taskfile.yml @@ -125,7 +125,7 @@ tasks: handlebars "$FILE" & done < <(find . -type f -not \( {{.ADDITIONAL_IGNORE_FOLDERS}} {{.IGNORE_FOLDERS}} \) -prune -name '*.liquid') while read CONFIG_FILE; do - handlebars "$CONFIG_FILE" & + handlebars "$CONFIG_FILE" done < <(find .config -type f -name '*.liquid') wait - | @@ -151,6 +151,7 @@ tasks: variables: deps: - :install:software:jq + - :install:software:yq env: FILE_INPUT: sh: if [ -f '{{.INPUT_FILE}}' ]; then echo {{.INPUT_FILE}}; else echo ".variables.json"; fi diff --git a/.config/taskfiles/vscode/Taskfile.yml b/.config/taskfiles/vscode/Taskfile.yml index dfb4828b..b7ce8a48 100644 --- a/.config/taskfiles/vscode/Taskfile.yml +++ b/.config/taskfiles/vscode/Taskfile.yml @@ -19,8 +19,11 @@ tasks: 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. + log: + error: Failed to generate `.vscode/extensions.json` + start: Generating `.vscode/extensions.json` + success: Generated `.vscode/extensions.json` cmds: - - .config/log info 'Generating `.vscode/extensions.json`' - mkdir -p .vscode - | TMP="$(mktemp)" @@ -32,7 +35,6 @@ tasks: sed 's/,""//' < "$TMP_JQ" > '{{.EXTENSIONS_FILE}}' - | {{.NPX_HANDLE}}prettier --write '{{.EXTENSIONS_FILE}}' > /dev/null - - .config/log success 'Generated `.vscode/extensions.json`' generate: deps: @@ -53,8 +55,11 @@ tasks: 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 `.vscode/extensions.json` + start: Generating `.vscode/tasks.json` + success: Generated `.vscode/extensions.json` cmds: - - .config/log info 'Generating `.vscode/tasks.json`' - | TMP="$(mktemp)" echo '{"version": "2.0.0"}' > "$TMP" @@ -72,4 +77,3 @@ tasks: jq --arg tasks "${TASKS%?}]" '.tasks = ($tasks | fromjson)' "$TMP" > '{{.TASKS_FILE}}' - | {{.NPX_HANDLE}}prettier --write '{{.TASKS_FILE}}' > /dev/null - - .config/log success 'Generated `.vscode/tasks.json`' diff --git a/.config/taskfiles/web/Taskfile-ionic.yml b/.config/taskfiles/web/Taskfile-ionic.yml index 702a769c..faff06af 100644 --- a/.config/taskfiles/web/Taskfile-ionic.yml +++ b/.config/taskfiles/web/Taskfile-ionic.yml @@ -5,5 +5,9 @@ tasks: 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_LOCAL}} diff --git a/.config/taskfiles/web/Taskfile-profile.yml b/.config/taskfiles/web/Taskfile-profile.yml index 3cfc8164..1c6feca2 100644 --- a/.config/taskfiles/web/Taskfile-profile.yml +++ b/.config/taskfiles/web/Taskfile-profile.yml @@ -18,6 +18,10 @@ tasks: **Example usage:** `task web:profile:detect-memory-leaks -- https://example.com` + log: + error: Failed to run `{{.NPX_HANDLE}}fuite {{.CLI_ARGS}}` + start: Running `{{.NPX_HANDLE}}fuite {{.CLI_ARGS}}` + success: Successfully ran `{{.NPX_HANDLE}}fuite {{.CLI_ARGS}}` cmds: - | {{.NPX_HANDLE}}fuite {{.CLI_ARGS}} diff --git a/.config/variables.json b/.config/variables.json index 414d005b..2fbd45e0 100644 --- a/.config/variables.json +++ b/.config/variables.json @@ -101,6 +101,11 @@ } ], "group": "common", + "homebrew": { + "folder": "Formula", + "name": "homebrew-tap", + "owner": "installdoc" + }, "hostapp_var_chart": [ ["App", "Description"], ["**[Authelia](https://www.authelia.com/)**", "An authentication portal that supports SSO and 2FA"], @@ -133,6 +138,7 @@ ["**[Tautulli](https://docs.linuxserver.io/images/docker-tautulli)**", "Metrics and monitoring for Plex"], ["**[Transmission](https://docs.linuxserver.io/images/docker-transmission)**", "BitTorrent client"] ], + "idPost": "megabyte.space", "json_top_keys": "", "license": "MIT", "link": { @@ -187,6 +193,7 @@ "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", @@ -298,6 +305,52 @@ "" ] ], + "releaseRules": [ + { + "release": "patch", + "type": "build" + }, + { + "release": "patch", + "type": "ci" + }, + { + "release": false, + "type": "chore" + }, + { + "release": false, + "type": "docs" + }, + { + "release": "patch", + "type": "fix" + }, + { + "release": "minor", + "type": "feat" + }, + { + "release": "patch", + "type": "refactor" + }, + { + "release": "patch", + "type": "revert" + }, + { + "release": "patch", + "type": "perf" + }, + { + "release": false, + "type": "style" + }, + { + "release": false, + "type": "test" + } + ], "repository": { "github": "", "gitlab": "", diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 162511b4..f1d61ab6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,18 +9,18 @@ variables: ANSIBLE_CONFIG: ${CI_PROJECT_DIR}/ansible.cfg include: - - local: .gitlab/ci/lint/ansible-lint.gitlab-ci.yml - - local: .gitlab/ci/lint/eslint.gitlab-ci.yml - - local: .gitlab/ci/lint/python/flake8.gitlab-ci.yml - - local: .gitlab/ci/lint/hadolint.gitlab-ci.yml - - local: .gitlab/ci/lint/prettier.gitlab-ci.yml - - local: .gitlab/ci/lint/shellcheck.gitlab-ci.yml - - local: .gitlab/ci/lint/yamllint.gitlab-ci.yml - - local: .gitlab/ci/publish/ansible/galaxy.gitlab-ci.yml - - local: .gitlab/ci/test/ansible/role.gitlab-ci.yml - - local: .gitlab/ci/test/ansible/role.macos.gitlab-ci.yml - - local: .gitlab/ci/test/ansible/role.windows.gitlab-ci.yml - - local: .gitlab/ci/update/project.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/lint/ansible-lint.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/lint/eslint.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/lint/python/flake8.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/lint/hadolint.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/lint/prettier.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/lint/shellcheck.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/lint/yamllint.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/publish/ansible/galaxy.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/test/ansible/role.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/test/ansible/role.macos.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/test/ansible/role.windows.gitlab-ci.yml + - remote: https://gitlab.com/megabyte-labs/gitlab-ci/-/raw/master/update/project.gitlab-ci.yml - template: Dependency-Scanning.gitlab-ci.yml - template: Security/License-Scanning.gitlab-ci.yml - template: Security/Secret-Detection.gitlab-ci.yml diff --git a/.gitlab/ci/scripts/update-init.sh b/.gitlab/ci/scripts/update-init.sh index 445b3504..0e7c0191 100644 --- a/.gitlab/ci/scripts/update-init.sh +++ b/.gitlab/ci/scripts/update-init.sh @@ -15,6 +15,10 @@ if [ -n "$GITLAB_CI" ]; then git checkout master fi +rm -rf start.sh +curl -s https://gitlab.com/megabyte-labs/common/shared/-/raw/master/common/start.sh > update-init.sh +curl -s https://gitlab.com/megabyte-labs/common/shared/-/raw/master/common/.config/taskfiles/install/Taskfile-python.yml > .config/taskfiles/install/Taskfile-python.yml + # @description Clone shared files repository rm -rf common-shared git clone --depth=1 https://gitlab.com/megabyte-labs/common/shared.git common-shared @@ -52,33 +56,35 @@ fi TMP="$(mktemp)" && sed 's/.*cz-conventional-changelog.*//' < package.json > "$TMP" && mv "$TMP" package.json TMP="$(mktemp)" && sed 's/.*config-conventional.*//' < package.json > "$TMP" && mv "$TMP" package.json rm -f temp.json -TMP="$(mktemp)" && jq 'del(.devDependencies["@mblabs/prettier-config"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.devDependencies["@commitlint/config-conventional"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.devDependencies["@mblabs/eslint-config"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.devDependencies["@mblabs/release-config"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.devDependencies["@typescript-eslint/eslint-plugin"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.devDependencies["commitlint-config-gitmoji"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.devDependencies["cz-conventional-changelog"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.devDependencies["glob"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.devDependencies["handlebars-helpers"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.devDependencies["semantic-release"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.optionalDependencies["chalk"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.optionalDependencies["inquirer"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.optionalDependencies["signale"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.optionalDependencies["string-break"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq 'del(.dependencies["tslib"])' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq '.private = false' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq '.devDependencies["@washingtondc/development"] = "^1.0.2"' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq '.devDependencies["@washingtondc/prettier"] = "^1.0.0"' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq '.devDependencies["@washingtondc/release"] = "^0.0.2"' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq '.devDependencies["eslint-config-strict-mode"] = "^1.0.0"' package.json > "$TMP" && mv "$TMP" package.json -TMP="$(mktemp)" && jq '.devDependencies["sleekfast"] = "^0.0.1"' package.json > "$TMP" && mv "$TMP" package.json - -if [ "$(jq -r '.blueprint.group' package.json)" == 'documentation' ]; then - TMP="$(mktemp)" && jq '.eslintConfig.rules["import/no-extraneous-dependencies"] = "off"' package.json > "$TMP" && mv "$TMP" package.json +if type jq &> /dev/null; then + TMP="$(mktemp)" && jq 'del(.devDependencies["@mblabs/prettier-config"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.devDependencies["@commitlint/config-conventional"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.devDependencies["@mblabs/eslint-config"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.devDependencies["@mblabs/release-config"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.devDependencies["@typescript-eslint/eslint-plugin"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.devDependencies["commitlint-config-gitmoji"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.devDependencies["cz-conventional-changelog"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.devDependencies["glob"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.devDependencies["handlebars-helpers"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.devDependencies["semantic-release"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.optionalDependencies["chalk"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.optionalDependencies["inquirer"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.optionalDependencies["signale"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.optionalDependencies["string-break"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq 'del(.dependencies["tslib"])' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq '.private = false' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq '.devDependencies["@washingtondc/development"] = "^1.0.2"' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq '.devDependencies["@washingtondc/prettier"] = "^1.0.0"' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq '.devDependencies["@washingtondc/release"] = "^0.0.2"' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq '.devDependencies["eslint-config-strict-mode"] = "^1.0.0"' package.json > "$TMP" && mv "$TMP" package.json + TMP="$(mktemp)" && jq '.devDependencies["sleekfast"] = "^0.0.1"' package.json > "$TMP" && mv "$TMP" package.json + + if [ "$(jq -r '.blueprint.group' package.json)" == 'documentation' ]; then + TMP="$(mktemp)" && jq '.eslintConfig.rules["import/no-extraneous-dependencies"] = "off"' package.json > "$TMP" && mv "$TMP" package.json + fi fi -if [ -f meta/main.yml ]; then +if [ -f meta/main.yml ] && type yq &> /dev/null; then yq eval -i '.galaxy_info.min_ansible_version = 2.10' meta/main.yml fi @@ -103,7 +109,6 @@ rm -f .prettierignore rm -f .start.sh rm -f .update.sh rm -f .yamllint -rm -f requirements.txt rm -f .config/eslintcache rm -f CODE_OF_CONDUCT.md rm -f CONTRIBUTING.md diff --git a/.gitlab/ci/update/project.gitlab-ci.yml b/.gitlab/ci/update/project.gitlab-ci.yml index 34ffe38d..09520c5c 100644 --- a/.gitlab/ci/update/project.gitlab-ci.yml +++ b/.gitlab/ci/update/project.gitlab-ci.yml @@ -23,6 +23,7 @@ update:project: - task ci:before script: - . "$HOME/.profile" + - poetry --version - task ci:submodules - task start - if [ "$REPOSITORY_UPDATE" == 'true' ]; then task services; fi diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 1a7ff5dd..efbbaa9f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -61,6 +61,11 @@ "type": "shell", "command": "bash .config/scripts/start.sh && task ci:github:actions:test" }, + { + "label": "(commit): Add all and commit", + "type": "shell", + "command": "bash .config/scripts/start.sh && task commit" + }, { "label": "(common:clean): Removes optional folders that are cached during various tasks", "type": "shell", @@ -237,9 +242,9 @@ "command": "bash .config/scripts/start.sh && task lint:eslint" }, { - "label": "(lint:esprint:esprint): Lint with `eslint` across multiple threads", + "label": "(lint:eslint:warn-all): Convert ESLint errors into warnings in `package.json`", "type": "shell", - "command": "bash .config/scripts/start.sh && task lint:esprint:esprint" + "command": "bash .config/scripts/start.sh && task lint:eslint:warn-all" }, { "label": "(lint:esprint:esprint:stop): Stop the `esprint` process that is currently running", @@ -306,6 +311,11 @@ "type": "shell", "command": "bash .config/scripts/start.sh && task lint:yaml" }, + { + "label": "(livereload): Start the project with live-reloading (i.e. watch mode)", + "type": "shell", + "command": "bash .config/scripts/start.sh && task livereload" + }, { "label": "(nest:benchmark): Initiate a web server benchmarking session (with `clinic`)", "type": "shell", diff --git a/README.md b/README.md index 165deadb..2b97d35a 100644 --- a/README.md +++ b/README.md @@ -93,18 +93,18 @@ This repository is the home of an [Ansible](https://www.ansible.com/) role that ## Features +**Ensures Rust shell completions are set-up:** + +- Sets up shell completions for PowerShell on Windows +- Sets up bash and zsh completions on macOS/Linux + **Ensures Rust is installed:** -- Installs Rust via `brew` on macOS sudolessly - Installs Rust via `pacman` on Archlinux -- Installs Rust on macOS, Linux, and Windows +- Installs Rust via `brew` on macOS sudolessly - Installs Rust via `snap` on Debian-flavored systems - Installs Rust via `snap` on RedHat-flavored systems - -**Ensures Rust shell completions are set-up:** - -- Sets up bash and zsh completions on macOS/Linux -- Sets up shell completions for PowerShell on Windows +- Installs Rust on macOS, Linux, and Windows @@ -183,7 +183,10 @@ Although most of our roles do not have dependencies, there are some cases where 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 +| Dependency | Description | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | +| professormanhattan.snapd | Ensures Snap is installed and properly configured on Linux systems | +| professormanhattan.homebrew | Installs Homebrew (a package management system) on macOS and Linux systems | ### Galaxy Collections diff --git a/Taskfile.yml b/Taskfile.yml index 92d3e68a..9ef0ac76 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -11,6 +11,9 @@ includes: ci: ./.config/taskfiles/ci/Taskfile.yml ci:github: ./.config/taskfiles/ci/Taskfile-github.yml common: ./.config/taskfiles/common/Taskfile.yml + project: + taskfile: Taskfile-project.yml + optional: true common:code: ./.config/taskfiles/common/Taskfile-code.yml common:update: ./.config/taskfiles/common/Taskfile-update.yml docker: ./.config/taskfiles/docker/Taskfile.yml @@ -23,6 +26,7 @@ includes: git:gitlab: ./.config/taskfiles/git/Taskfile-gitlab.yml git:hook: ./.config/taskfiles/git/Taskfile-hook.yml go: ./.config/taskfiles/go/Taskfile.yml + go:goreleaser: ./.config/taskfiles/go/Taskfile-goreleaser.yml go:test: ./.config/taskfiles/go/Taskfile-test.yml image: ./.config/taskfiles/image/Taskfile.yml install: ./.config/taskfiles/install/Taskfile.yml @@ -51,6 +55,7 @@ includes: publish:opera: ./.config/taskfiles/publish/Taskfile-opera.yml python: ./.config/taskfiles/python/Taskfile.yml python:test: ./.config/taskfiles/python/Taskfile-test.yml + release: ./.config/taskfiles/release/Taskfile.yml security: ./.config/taskfiles/security/Taskfile.yml symlink: ./.config/taskfiles/symlink/Taskfile.yml upstream: ./.config/taskfiles/upstream/Taskfile.yml @@ -69,6 +74,9 @@ output: interleaved vars: DOCKERHUB_PROFILE: megabytelabs + DOCKERHUB_USER: professormanhattan + ESLINT_FORMATTER: pretty + ESLINT_FORMATTER_OPTIONS: git-log gitlab pretty summary GALAXY_NAMESPACE: sh: "if [ -f meta/main.yml ]; then grep namespace < meta/main.yml | sed 's/.*namespace: \\(.*\\)$/\\1/g'; fi" GALAXY_ROLE_NAME: @@ -83,35 +91,37 @@ vars: INIT_SCRIPT: https://gitlab.com/megabyte-labs/common/shared/-/raw/master/common/.gitlab/ci/scripts/update-init.sh LOG_FIX: sh: chmod +x .config/log && echo "heyhey" - NPM_KEEP_UPDATED: '@washingtondc/* eslint-config-strict-mode sleekfast' + NPM_KEEP_UPDATED: '@washingtondc/* eslint-config-strict-mode semantic-release-config' NPM_PROGRAM: npm NPM_PROGRAM_LOCAL: pnpm NPX_HANDLE: 'pnpx ' NPX_PACKAGE: pnpx PYTHON_HANDLE: sh: if [ -z "$GITLAB_CI" ] || [[ "$OPTIMIZED_IMAGE" == 'false' ]]; then echo 'poetry run '; fi + PYTHON_KEEP_UPDATED: ansibler PYTHON_VIRTUALENV: true REPOSITORY_SUBTYPE: role REPOSITORY_TYPE: ansible TIMEZONE: America/New_York profile: | - PATH="$PATH:$HOME/.local/bin" + PATH="$PATH:$HOME/.local/bin:$HOME/.poetry/bin" if [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then if [ -f /home/linuxbrew/.linuxbrew/bin/brew ] && ! type brew > /dev/null; then eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" fi fi if [[ "$OSTYPE" == 'darwin'* ]] && type brew > /dev/null; then - PATH="$PATH:$(brew --prefix)/opt/coreutils/libexec/gnubin" - PATH="$PATH:$(brew --prefix)/opt/findutils/libexec/gnubin" - PATH="$PATH:$(brew --prefix)/opt/gnu-sed/libexec/gnubin" - PATH="$PATH:$(brew --prefix)/opt/grep/libexec/gnubin" - PATH="$PATH:$(brew --prefix)/opt/gnu-tar/libexec/gnubin" - PATH="$PATH:$(brew --prefix)/opt/gawk/libexec/gnubin" + BREW_PREFIX="$(brew --prefix)" + PATH="$PATH:$BREW_PREFIX/opt/coreutils/libexec/gnubin" + PATH="$PATH:$BREW_PREFIX/opt/findutils/libexec/gnubin" + PATH="$PATH:$BREW_PREFIX/opt/gnu-sed/libexec/gnubin" + PATH="$PATH:$BREW_PREFIX/opt/grep/libexec/gnubin" + PATH="$PATH:$BREW_PREFIX/opt/gnu-tar/libexec/gnubin" + PATH="$PATH:$BREW_PREFIX/opt/gawk/libexec/gnubin" fi - if [ -d "$HOME/.poetry/bin" ] && ! type poetry > /dev/null; then - PATH="$PATH:$HOME/.poetry/bin" + if [ -f .venv/bin/activate ]; then + . .venv/bin/activate fi tasks: @@ -129,8 +139,39 @@ tasks: cmds: - task: go:build - donothing: 'true' + cmds: + - | + {{if .CLI_ARGS}} + git add --all + HUSKY=0 git commit -m "{{.CLI_ARGS}}" --no-verify + {{else}} + git add --all + git commit + {{end}} + + commit: + deps: + - install:software:git + desc: Add all and commit + summary: | + # Commit the code + + By default, this task is an alias for: + + ``` + git add --all + git commit + ``` + + If you pass a commit message as a CLI argument, then the pre-commit + logic will be bypassed. By bypassing the hooks, the commit message will be + excluded from the automatic CHANGELOG.md entry generated during commits. + Passing the commit message as a CLI argument can be done like so: + ``` + task commit -- "Added new files that do not need to show up in CHANGELOG.md" + ``` + donothing: 'true' environment: interactive: true desc: Symlink to an environment in the `environments/` folder @@ -201,7 +242,7 @@ tasks: if [ -n "$GITLAB_CI" ]; then if [ -n "$UPDATE_INIT_SCRIPT" ]; then curl -s "$UPDATE_INIT_SCRIPT" | bash; fi else - bash <(curl -s {{.INIT_SCRIPT}}) + bash <(curl -s https://gitlab.com/megabyte-labs/common/shared/-/raw/master/common/.gitlab/ci/scripts/update-init.sh) fi lint: @@ -218,6 +259,46 @@ tasks: cmds: - task: lint:all + livereload: + deps: + - install:npm:nodemon + desc: Start the project with live-reloading (i.e. watch mode) + summary: | + # LiveReload the project + + Using LiveReload, you can automatically run the project any time you make + changes to the project. The LiveReload feature uses [nodemon](https://github.com/remy/nodemon) + with the configuration stored in `.config/nodemon.json`. + + Whenever you make a change to the project, `task project:livereload` will run. You can + define the command that runs everytime you make a change in the `Taskfile-project.yml` + `livereload` section. This file is kept intact even across upstream propagation updates, + unlike the regular `Taskfile.yml`. + + **Example usage:** + `task livereload` + + The default configuration (in `.config/nodemon.json`) is generic and makes assumptions + about the file types to watch based on what type project it is. If you would like to + use a different configuration, you can pass in the location of the configuration like so: + + **Example with custom configuration:** + `task livereload -- nodemon.custom.json` + + The task program also provides a `--watch` feature. However, it relies on the `sources` attribute + being defined so it might not work in all cases. + + **Example using `task --watch`:** + `task --watch mytask` + log: + start: Starting `nodemon` live-reload session + cmds: + - | + if [ ! -f Taskfile-project.yml ]; then + .config/log error '`Taskfile-project.yml` must exist and have a `livereload` task to use with `nodemon`' && exit 1 + fi + - nodemon --config {{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}.config/nodemon.json{{end}} + preload: deps: - install:brewfile diff --git a/package.json b/package.json index bba86de8..b2f758e9 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "@washingtondc/prettier": "^1.0.0", "@washingtondc/release": "^0.0.2", "eslint-config-strict-mode": "^1.0.0", + "semantic-release-config": "^0.0.2", "sleekfast": "^0.0.1" }, "keywords": [ @@ -71,7 +72,7 @@ "ansible_galaxy_project_id": "57300", "description": "Installs Rust on nearly any platform", "github_id": "R_kgDOGjKr7Q", - "gitlab_group_id": "12261670", + "gitlab_group_id": "", "gitlab_project_id": "32221421", "group": "ansible", "name": "Rust", @@ -91,7 +92,12 @@ "helpUrl": "https://megabyte.space/docs/contributing/commits" }, "eslintConfig": { - "extends": "eslint-config-strict-mode" + "extends": "eslint-config-strict-mode", + "rules": { + "import/no-extraneous-dependencies": "warn", + "max-len": "warn", + "max-lines": "warn" + } }, "funding": [ { @@ -157,9 +163,17 @@ "prettier": "@washingtondc/prettier", "release": { "branches": [ - "master", - "next" + "main", + "next", + { + "name": "beta", + "prerelease": true + }, + { + "name": "alpha", + "prerelease": true + } ], - "extends": "@washingtondc/release" + "extends": "semantic-release-config" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4cb21f3b..5d909e82 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,14 +4,14 @@ specifiers: '@washingtondc/development': ^1.0.2 '@washingtondc/prettier': ^1.0.0 '@washingtondc/release': ^0.0.2 - eslint-config-strict-mode: ^1.0.0 + eslint-config-strict-mode: ^1.0.28 sleekfast: ^0.0.1 devDependencies: '@washingtondc/development': 1.0.10 '@washingtondc/prettier': 1.0.0 '@washingtondc/release': 0.0.2 - eslint-config-strict-mode: 1.0.6 + eslint-config-strict-mode: 1.0.28 sleekfast: 0.0.1 packages: @@ -352,16 +352,16 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/core/7.16.10: - resolution: {integrity: sha512-pbiIdZbCiMx/MM6toR+OfXarYix3uz0oVsnNtfdAGTcCTu3w/JGF8JhirevXLBJUu0WguSZI12qpKnx7EeMyLA==} + /@babel/core/7.16.12: + resolution: {integrity: sha512-dK5PtG1uiN2ikk++5OzSYsitZKny4wOCD0nrO4TqnW4BVBTQ2NGS3NgilvT/TEyxTST7LNyWV/T4tXDoD3fOgg==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.16.7 '@babel/generator': 7.16.8 - '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.16.10 + '@babel/helper-compilation-targets': 7.16.7_@babel+core@7.16.12 '@babel/helper-module-transforms': 7.16.7 '@babel/helpers': 7.16.7 - '@babel/parser': 7.16.10 + '@babel/parser': 7.16.12 '@babel/template': 7.16.7 '@babel/traverse': 7.16.10 '@babel/types': 7.16.8 @@ -375,14 +375,14 @@ packages: - supports-color dev: true - /@babel/eslint-parser/7.16.5_@babel+core@7.16.10+eslint@8.7.0: + /@babel/eslint-parser/7.16.5_@babel+core@7.16.12+eslint@8.7.0: resolution: {integrity: sha512-mUqYa46lgWqHKQ33Q6LNCGp/wPR3eqOYTUixHFsfrSQqRxH0+WOzca75iEjFr5RDGH1dDz622LaHhLOzOuQRUA==} engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} peerDependencies: '@babel/core': '>=7.11.0' eslint: ^7.5.0 || ^8.0.0 dependencies: - '@babel/core': 7.16.10 + '@babel/core': 7.16.12 eslint: 8.7.0 eslint-scope: 5.1.1 eslint-visitor-keys: 2.1.0 @@ -398,14 +398,14 @@ packages: source-map: 0.5.7 dev: true - /@babel/helper-compilation-targets/7.16.7_@babel+core@7.16.10: + /@babel/helper-compilation-targets/7.16.7_@babel+core@7.16.12: resolution: {integrity: sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/compat-data': 7.16.8 - '@babel/core': 7.16.10 + '@babel/core': 7.16.12 '@babel/helper-validator-option': 7.16.7 browserslist: 4.19.1 semver: 6.3.0 @@ -508,8 +508,8 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/parser/7.16.10: - resolution: {integrity: sha512-Sm/S9Or6nN8uiFsQU1yodyDW3MWXQhFeqzMPM+t8MJjM+pLsnFVxFZzkpXKvUXh+Gz9cbMoYYs484+Jw/NTEFQ==} + /@babel/parser/7.16.12: + resolution: {integrity: sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A==} engines: {node: '>=6.0.0'} hasBin: true dev: true @@ -548,7 +548,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.16.7 - '@babel/parser': 7.16.10 + '@babel/parser': 7.16.12 '@babel/types': 7.16.8 dev: true @@ -562,7 +562,7 @@ packages: '@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.16.10 + '@babel/parser': 7.16.12 '@babel/types': 7.16.8 debug: 4.3.3 globals: 11.12.0 @@ -1204,7 +1204,7 @@ packages: dependencies: '@octokit/auth-token': 2.5.0 '@octokit/graphql': 4.8.0 - '@octokit/request': 5.6.2 + '@octokit/request': 5.6.3 '@octokit/request-error': 2.1.0 '@octokit/types': 6.34.0 before-after-hook: 2.2.2 @@ -1224,7 +1224,7 @@ packages: /@octokit/graphql/4.8.0: resolution: {integrity: sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==} dependencies: - '@octokit/request': 5.6.2 + '@octokit/request': 5.6.3 '@octokit/types': 6.34.0 universal-user-agent: 6.0.0 transitivePeerDependencies: @@ -1320,8 +1320,8 @@ packages: once: 1.4.0 dev: true - /@octokit/request/5.6.2: - resolution: {integrity: sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==} + /@octokit/request/5.6.3: + resolution: {integrity: sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==} dependencies: '@octokit/endpoint': 6.0.12 '@octokit/request-error': 2.1.0 @@ -1340,7 +1340,7 @@ packages: '@octokit/plugin-paginate-rest': 1.1.2 '@octokit/plugin-request-log': 1.0.4 '@octokit/plugin-rest-endpoint-methods': 2.4.0 - '@octokit/request': 5.6.2 + '@octokit/request': 5.6.3 '@octokit/request-error': 1.2.1 atob-lite: 2.0.0 before-after-hook: 2.2.2 @@ -1395,6 +1395,23 @@ packages: prettier: 2.5.1 dev: true + /@samverschueren/stream-to-observable/0.3.1_rxjs@6.6.7: + resolution: {integrity: sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==} + engines: {node: '>=6'} + peerDependencies: + rxjs: '*' + zen-observable: '*' + peerDependenciesMeta: + rxjs: + optional: true + zen-observable: + optional: true + dependencies: + any-observable: 0.3.0 + rxjs: 6.6.7 + dev: true + optional: true + /@semantic-release/changelog/3.0.6_semantic-release@18.0.1: resolution: {integrity: sha512-9TqPL/VarLLj6WkUqbIqFiY3nwPmLuKFHy9fe/LamAW5s4MEW/ig9zW9vzYGOUVtWdErGJ1J62E3Edkamh3xaQ==} engines: {node: '>=8.16'} @@ -1660,6 +1677,12 @@ packages: engines: {node: '>=6'} dev: true + /@sindresorhus/is/2.1.1: + resolution: {integrity: sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==} + engines: {node: '>=10'} + dev: true + optional: true + /@sindresorhus/is/3.1.2: resolution: {integrity: sha512-JiX9vxoKMmu8Y3Zr2RVathBL1Cdu4Nt4MuNWemt1Nc06A0RAin9c5FArkhGsyMBWfCu4zj+9b+GxtjAnE4qqLQ==} engines: {node: '>=10'} @@ -1856,22 +1879,6 @@ packages: '@types/node': 17.0.10 dev: true - /@typescript-eslint/eslint-plugin-tslint/5.10.0_eslint@8.7.0+tslint@6.1.3: - resolution: {integrity: sha512-AnawlMlCWG+dzN9Bgvo0XFREEgAl6SdyggbJrhXfHl74P7ptPRqLgIUETvH1ND2PYYP79+BCdOVlQzTt/HZK4g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - tslint: ^5.0.0 || ^6.0.0 - typescript: '*' - dependencies: - '@typescript-eslint/utils': 5.10.0_eslint@8.7.0 - eslint: 8.7.0 - lodash: 4.17.21 - tslint: 6.1.3 - transitivePeerDependencies: - - supports-color - dev: true - /@typescript-eslint/eslint-plugin/5.10.0_dac6134a22fe1289d58145b0b92ae060: resolution: {integrity: sha512-XXVKnMsq2fuu9K2KsIxPUGqb6xAImz8MEChClbXmE3VbveFtBUU5bzM6IPVWqzyADIgdkS2Ws/6Xo7W2TeZWjQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2631,6 +2638,28 @@ packages: resolution: {integrity: sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=} dev: true + /any-observable/0.3.0: + resolution: {integrity: sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==} + engines: {node: '>=6'} + dev: true + optional: true + + /any-observable/0.5.1_rxjs@6.6.7: + resolution: {integrity: sha512-8zv01bgDOp9PTmRTNCAHTw64TFP2rvlX4LvtNJLachaXY+AjmIvLT47fABNPCiIe89hKiSCo2n5zmPqI9CElPA==} + engines: {node: '>=8'} + peerDependencies: + rxjs: '*' + zen-observable: '*' + peerDependenciesMeta: + rxjs: + optional: true + zen-observable: + optional: true + dependencies: + rxjs: 6.6.7 + dev: true + optional: true + /any-promise/1.3.0: resolution: {integrity: sha1-q8av7tzqUugJzcA3au0845Y10X8=} dev: true @@ -2873,6 +2902,12 @@ packages: dev: true optional: true + /async-exit-hook/2.0.1: + resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} + engines: {node: '>=0.12.0'} + dev: true + optional: true + /async/0.2.10: resolution: {integrity: sha1-trvgsGdLnXGXCMo43owjfLUmw9E=} dev: true @@ -3115,8 +3150,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001300 - electron-to-chromium: 1.4.49 + caniuse-lite: 1.0.30001301 + electron-to-chromium: 1.4.51 escalade: 3.1.1 node-releases: 2.0.1 picocolors: 1.0.0 @@ -3155,6 +3190,11 @@ packages: engines: {node: '>=6'} dev: true + /builtins/1.0.3: + resolution: {integrity: sha1-y5T662HIaWRR2zZTThQi+U8K7og=} + dev: true + optional: true + /bunyan/1.8.14: resolution: {integrity: sha512-LlahJUxXzZLuw/hetUQJmRgZ1LF6+cr5TPpRj6jf327AsiIq2jhYEH4oqUUkVKTor+9w2BT3oxVwhzE5lw9tcg==} engines: {'0': node >=0.10.0} @@ -3185,6 +3225,15 @@ packages: unset-value: 1.0.0 dev: true + /cacheable-lookup/2.0.1: + resolution: {integrity: sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==} + engines: {node: '>=10'} + dependencies: + '@types/keyv': 3.1.3 + keyv: 4.0.5 + dev: true + optional: true + /cacheable-lookup/5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} engines: {node: '>=10.6.0'} @@ -3281,8 +3330,8 @@ packages: engines: {node: '>=10'} dev: true - /caniuse-lite/1.0.30001300: - resolution: {integrity: sha512-cVjiJHWGcNlJi8TZVKNMnvMid3Z3TTdDHmLDzlOdIiZq138Exvo0G+G0wTdVYolxKb4AYwC+38pxodiInVtJSA==} + /caniuse-lite/1.0.30001301: + resolution: {integrity: sha512-csfD/GpHMqgEL3V3uIgosvh+SVIQvCh43SNu9HRbP1lnxkKm1kjDG4f32PP571JplkLjfS+mg2p1gxR7MYrrIA==} dev: true /cardinal/2.1.1: @@ -3549,6 +3598,15 @@ packages: colors: 1.4.0 dev: true + /cli-truncate/0.2.1: + resolution: {integrity: sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=} + engines: {node: '>=0.10.0'} + dependencies: + slice-ansi: 0.0.4 + string-width: 1.0.2 + dev: true + optional: true + /cli-truncate/2.1.0: resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} engines: {node: '>=8'} @@ -3971,6 +4029,11 @@ packages: requiresBuild: true dev: true + /core-js/3.20.3: + resolution: {integrity: sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag==} + requiresBuild: true + dev: true + /core-js/3.6.5: resolution: {integrity: sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==} requiresBuild: true @@ -4235,6 +4298,11 @@ packages: assert-plus: 1.0.0 dev: true + /date-fns/1.30.1: + resolution: {integrity: sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==} + dev: true + optional: true + /date.js/0.3.3: resolution: {integrity: sha512-HgigOS3h3k6HnW011nAb43c5xx5rBXk8P2v/WIT9Zv4koIaVXiH2BURguI78VVp+5Qc076T7OR378JViCnZtBw==} dependencies: @@ -4334,6 +4402,14 @@ packages: mimic-response: 2.1.0 dev: true + /decompress-response/5.0.0: + resolution: {integrity: sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==} + engines: {node: '>=10'} + dependencies: + mimic-response: 2.1.0 + dev: true + optional: true + /decompress-response/6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -4612,6 +4688,14 @@ packages: is-obj: 2.0.0 dev: true + /dot-prop/6.0.1: + resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==} + engines: {node: '>=10'} + dependencies: + is-obj: 2.0.0 + dev: true + optional: true + /dot-properties/1.0.1: resolution: {integrity: sha512-cjIHHKlf2dPINJ5Io3lPocWvWmthXn3ztqyHVzUfufRiCiPECb0oiEqEGbEGaunFZtcMvwgUcxP9CTpLG4KCsA==} dev: true @@ -4667,10 +4751,16 @@ packages: sigmund: 1.0.1 dev: true - /electron-to-chromium/1.4.49: - resolution: {integrity: sha512-k/0t1TRfonHIp8TJKfjBu2cKj8MqYTiEpOhci+q7CVEE5xnCQnx1pTa+V8b/sdhe4S3PR4p4iceEQWhGrKQORQ==} + /electron-to-chromium/1.4.51: + resolution: {integrity: sha512-JNEmcYl3mk1tGQmy0EvL5eik/CKSBuzAyGP0QFdG6LIgxQe3II0BL1m2zKc2MZMf3uGqHWE1TFddJML0RpjSHQ==} dev: true + /elegant-spinner/1.0.1: + resolution: {integrity: sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=} + engines: {node: '>=0.10.0'} + dev: true + optional: true + /emoji-regex/7.0.3: resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} dev: true @@ -4979,6 +5069,12 @@ packages: engines: {node: '>=8'} dev: true + /escape-goat/3.0.0: + resolution: {integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==} + engines: {node: '>=10'} + dev: true + optional: true + /escape-string-regexp/1.0.5: resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=} engines: {node: '>=0.8.0'} @@ -5011,22 +5107,23 @@ packages: eslint: 8.7.0 dev: true - /eslint-config-strict-mode/1.0.6: - resolution: {integrity: sha512-gNr6Kl2pFuuPZnSOXquVKExqpPyAyrQ82r2rC83nPylSTVvVtNiL751y/gPrhZGvIprVmy91uyFFNmm3uX+l9Q==} + /eslint-config-strict-mode/1.0.28: + resolution: {integrity: sha512-4f/cDnvfoyLf4zj/QhIvYzCeWR5cqf+qKzOvel6HKHp4ILjawGcwBc5h+J03TIKrJezLf7y74u6Udm8y5HWbHg==} engines: {node: '>=14.18.0'} dependencies: '@typescript-eslint/eslint-plugin': 5.10.0_dac6134a22fe1289d58145b0b92ae060 - '@typescript-eslint/eslint-plugin-tslint': 5.10.0_eslint@8.7.0+tslint@6.1.3 '@typescript-eslint/parser': 5.10.0_eslint@8.7.0 eslint: 8.7.0 eslint-config-prettier: 8.3.0_eslint@8.7.0 + eslint-formatter-git-log: 0.5.3_eslint@8.7.0 eslint-formatter-gitlab: 3.0.0_eslint@8.7.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@8.7.0 eslint-plugin-editorconfig: 3.2.0_@typescript-eslint+parser@5.10.0 eslint-plugin-eslint-comments: 3.2.0_eslint@8.7.0 - eslint-plugin-etc: 2.0.1_eslint@8.7.0 + eslint-plugin-etc: 2.0.2_eslint@8.7.0 eslint-plugin-ext: 0.1.0 eslint-plugin-filenames: 1.3.2_eslint@8.7.0 eslint-plugin-fp: 2.3.0_eslint@8.7.0 @@ -5037,12 +5134,11 @@ packages: eslint-plugin-jest-async: 1.0.3 eslint-plugin-jest-dom: 3.9.4_eslint@8.7.0 eslint-plugin-jest-formatting: 3.1.0_eslint@8.7.0 - eslint-plugin-jsdoc: 37.6.2_eslint@8.7.0 + eslint-plugin-jsdoc: 37.6.3_eslint@8.7.0 eslint-plugin-json-schema-validator: 1.2.49_eslint@8.7.0 eslint-plugin-jsonc: 1.7.0_eslint@8.7.0 eslint-plugin-no-constructor-bind: 2.0.4 eslint-plugin-no-explicit-type-exports: 0.12.1_dac6134a22fe1289d58145b0b92ae060 - eslint-plugin-no-loops: 0.3.0_eslint@8.7.0 eslint-plugin-no-secrets: 0.8.9_eslint@8.7.0 eslint-plugin-no-unsanitized: 3.2.0_eslint@8.7.0 eslint-plugin-no-use-extend-native: 0.5.0 @@ -5067,7 +5163,6 @@ packages: eslint-plugin-yml: 0.10.1_eslint@8.7.0 glob: 7.2.0 parse-gitignore: 1.0.1 - tslib: 2.3.1 tslint: 6.1.3 yaml: 1.10.2 transitivePeerDependencies: @@ -5076,6 +5171,7 @@ packages: - supports-color - tsutils - typescript + - zen-observable dev: true /eslint-etc/5.1.0_eslint@8.7.0: @@ -5092,6 +5188,15 @@ packages: - supports-color dev: true + /eslint-formatter-git-log/0.5.3_eslint@8.7.0: + resolution: {integrity: sha512-lwYPyg6fq6IQyNwLHkls1sjIXNWvY6RQx8S8hLTcgC+ldKYHd8Dc0G1qBpbQXoBFBrUmz73ZNyP1lMsAP8A33A==} + peerDependencies: + eslint: '>=5.0.0' + dependencies: + chalk: 2.4.2 + eslint: 8.7.0 + dev: true + /eslint-formatter-gitlab/3.0.0_eslint@8.7.0: resolution: {integrity: sha512-fqZ2G45rgbrHcFunqmwuG5Qo6QAOlxEsR+KdOP08T1Xegw5tJhHh9KFWMSct8q6x8xCMUyYGHypZd342bLUttA==} peerDependencies: @@ -5116,11 +5221,23 @@ packages: supports-hyperlinks: 2.2.0 dev: true + /eslint-formatter-summary/1.1.0: + resolution: {integrity: sha512-teOh471ZYeEShXhBFb16X87buUjNZa2TMNn3CSf//DIKLNneqbk7u5i9hDDiIaQVvZbBXJHkgYxj8urcNKWbXw==} + engines: {node: '>=12'} + dependencies: + chalk: 4.1.2 + core-js: 3.20.3 + optionalDependencies: + np: 7.6.0 + transitivePeerDependencies: + - zen-observable + dev: true + /eslint-import-resolver-node/0.3.6: resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} dependencies: debug: 3.2.7 - resolve: 1.21.0 + resolve: 1.22.0 dev: true /eslint-module-utils/2.7.2: @@ -5179,8 +5296,8 @@ packages: ignore: 5.2.0 dev: true - /eslint-plugin-etc/2.0.1_eslint@8.7.0: - resolution: {integrity: sha512-V9LWdEQj0zL21YQR+++Zi/zXaFZFEorOR/PP0DBkg1SrQYLOtS5Qh81rB91TcobkMYoO/WjVOszgglM5dT5ueA==} + /eslint-plugin-etc/2.0.2_eslint@8.7.0: + resolution: {integrity: sha512-g3b95LCdTCwZA8On9EICYL8m1NMWaiGfmNUd/ftZTeGZDXrwujKXUr+unYzqKjKFo1EbqJ31vt+Dqzrdm/sUcw==} peerDependencies: eslint: ^8.0.0 typescript: ^4.0.0 @@ -5270,7 +5387,7 @@ packages: is-glob: 4.0.3 minimatch: 3.0.4 object.values: 1.1.5 - resolve: 1.21.0 + resolve: 1.22.0 tsconfig-paths: 3.12.0 dev: true @@ -5323,8 +5440,8 @@ packages: - typescript dev: true - /eslint-plugin-jsdoc/37.6.2_eslint@8.7.0: - resolution: {integrity: sha512-yYvCkaq+7A+kWBi2fxJFgWErdGOth5W+h1XqVnJg4qWonWo99K8i7Hmow6HWaiixHShNobtvq66f9HG/dJZ+WA==} + /eslint-plugin-jsdoc/37.6.3_eslint@8.7.0: + resolution: {integrity: sha512-Ysd1ZK4kL7DjjRJtWzb6Z7YANu7ndalu5PQBhOn07SlpKQ/+8JXvdtQ6yyADOO8w9xW5ZEEzuGY3KWhtk4CRYA==} engines: {node: ^12 || ^14 || ^16 || ^17} peerDependencies: eslint: ^7.0.0 || ^8.0.0 @@ -5397,14 +5514,6 @@ packages: - typescript dev: true - /eslint-plugin-no-loops/0.3.0_eslint@8.7.0: - resolution: {integrity: sha1-6B/stOqvSUqSbZyrqafNhNH+3n0=} - peerDependencies: - eslint: '>=2.0.0' - dependencies: - eslint: 8.7.0 - dev: true - /eslint-plugin-no-secrets/0.8.9_eslint@8.7.0: resolution: {integrity: sha512-CqaBxXrImABCtxMWspAnm8d5UKkpNylC7zqVveb+fJHEvsSiNGJlSWzdSIvBUnW1XhJXkzifNIZQC08rEII5Ng==} engines: {node: '>=10.0.0', npm: '>=6.9.0'} @@ -5451,7 +5560,7 @@ packages: eslint-utils: 2.1.0 ignore: 5.2.0 minimatch: 3.0.4 - resolve: 1.21.0 + resolve: 1.22.0 semver: 6.3.0 dev: true @@ -5706,8 +5815,8 @@ packages: peerDependencies: eslint: '>=7.0.0' dependencies: - '@babel/core': 7.16.10 - '@babel/eslint-parser': 7.16.5_@babel+core@7.16.10+eslint@8.7.0 + '@babel/core': 7.16.12 + '@babel/eslint-parser': 7.16.5_@babel+core@7.16.12+eslint@8.7.0 eslint: 8.7.0 eslint-visitor-keys: 2.1.0 esquery: 1.4.0 @@ -5768,7 +5877,7 @@ packages: enquirer: 2.3.6 eslint-scope: 5.1.1 eslint-utils: 2.1.0 - eslint-visitor-keys: 2.1.0 + eslint-visitor-keys: 2.0.0 espree: 7.3.0 esquery: 1.4.0 esutils: 2.0.3 @@ -5789,7 +5898,7 @@ packages: optionator: 0.9.1 progress: 2.0.3 regexpp: 3.2.0 - semver: 7.3.5 + semver: 7.3.2 strip-ansi: 6.0.1 strip-json-comments: 3.1.1 table: 5.4.6 @@ -6121,6 +6230,15 @@ packages: pend: 1.2.0 dev: true + /figures/1.7.0: + resolution: {integrity: sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=} + engines: {node: '>=0.10.0'} + dependencies: + escape-string-regexp: 1.0.5 + object-assign: 4.1.1 + dev: true + optional: true + /figures/2.0.0: resolution: {integrity: sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=} engines: {node: '>=4'} @@ -6649,6 +6767,11 @@ packages: resolution: {integrity: sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=} dev: true + /github-url-from-git/1.5.0: + resolution: {integrity: sha1-+YX+3MCpqledyI16/waNVcxiUaA=} + dev: true + optional: true + /gitlog/4.0.4: resolution: {integrity: sha512-jeY2kO7CVyTa6cUM7ZD2ZxIyBkna1xvW2esV/3o8tbhiUneX1UBQCH4D9aMrHgGiohBjyXbuZogyjKXslnY5Yg==} engines: {node: '>= 10.x'} @@ -6739,6 +6862,14 @@ packages: ini: 1.3.7 dev: true + /global-dirs/3.0.0: + resolution: {integrity: sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==} + engines: {node: '>=10'} + dependencies: + ini: 2.0.0 + dev: true + optional: true + /global-modules/1.0.0: resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} engines: {node: '>=0.10.0'} @@ -6816,6 +6947,28 @@ packages: slash: 1.0.0 dev: true + /got/10.7.0: + resolution: {integrity: sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==} + engines: {node: '>=10'} + dependencies: + '@sindresorhus/is': 2.1.1 + '@szmarczak/http-timer': 4.0.6 + '@types/cacheable-request': 6.0.2 + cacheable-lookup: 2.0.1 + cacheable-request: 7.0.2 + decompress-response: 5.0.0 + duplexer3: 0.1.4 + get-stream: 5.2.0 + lowercase-keys: 2.0.0 + mimic-response: 2.1.0 + p-cancelable: 2.1.1 + p-event: 4.2.0 + responselike: 2.0.0 + to-readable-stream: 2.1.0 + type-fest: 0.10.0 + dev: true + optional: true + /got/11.5.2: resolution: {integrity: sha512-yUhpEDLeuGiGJjRSzEq3kvt4zJtAcjKmhIiwNp/eUs75tRlXfWcHo5tcBaMQtnjHWC7nQYT5HkY/l0QOQTkVww==} engines: {node: '>=10.19.0'} @@ -7109,6 +7262,14 @@ packages: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true + /hosted-git-info/3.0.8: + resolution: {integrity: sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw==} + engines: {node: '>=10'} + dependencies: + lru-cache: 6.0.0 + dev: true + optional: true + /hosted-git-info/4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} @@ -7242,6 +7403,13 @@ packages: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} dev: true + /ignore-walk/3.0.4: + resolution: {integrity: sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==} + dependencies: + minimatch: 3.0.4 + dev: true + optional: true + /ignore/3.3.10: resolution: {integrity: sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==} dev: true @@ -7288,6 +7456,16 @@ packages: engines: {node: '>=4'} dev: true + /import-local/3.1.0: + resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==} + engines: {node: '>=8'} + hasBin: true + dependencies: + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 + dev: true + optional: true + /imurmurhash/0.1.4: resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=} engines: {node: '>=0.8.19'} @@ -7332,6 +7510,21 @@ packages: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} dev: true + /ini/2.0.0: + resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} + engines: {node: '>=10'} + dev: true + optional: true + + /inquirer-autosubmit-prompt/0.2.0: + resolution: {integrity: sha512-mzNrusCk5L6kSzlN0Ioddn8yzrhYNLli+Sn2ZxMuLechMYAzakiFCIULxsxlQb5YKzthLGfrFACcWoAvM7p04Q==} + dependencies: + chalk: 2.4.2 + inquirer: 6.5.2 + rxjs: 6.6.7 + dev: true + optional: true + /inquirer/6.5.2: resolution: {integrity: sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==} engines: {node: '>=6.0.0'} @@ -7646,6 +7839,15 @@ packages: is-path-inside: 3.0.3 dev: true + /is-installed-globally/0.4.0: + resolution: {integrity: sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==} + engines: {node: '>=10'} + dependencies: + global-dirs: 3.0.0 + is-path-inside: 3.0.3 + dev: true + optional: true + /is-interactive/1.0.0: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} @@ -7714,6 +7916,14 @@ packages: engines: {node: '>=8'} dev: true + /is-observable/1.1.0: + resolution: {integrity: sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==} + engines: {node: '>=4'} + dependencies: + symbol-observable: 1.2.0 + dev: true + optional: true + /is-odd/0.1.2: resolution: {integrity: sha1-vFc7XONx7yqtbm9JeZtyvvE5eKc=} engines: {node: '>=0.10.0'} @@ -7748,6 +7958,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-promise/2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + dev: true + optional: true + /is-proto-prop/2.0.0: resolution: {integrity: sha512-jl3NbQ/fGLv5Jhan4uX+Ge9ohnemqyblWVVCpAvtTQzNFvV2xhJq+esnkIbYQ9F1nITXoLfDDQLp7LBw/zzncg==} dependencies: @@ -7768,6 +7983,14 @@ packages: engines: {node: '>=0.10.0'} dev: true + /is-scoped/2.1.0: + resolution: {integrity: sha512-Cv4OpPTHAK9kHYzkzCrof3VJh7H/PrG2MBUMvvJebaaUMbqhm0YAtXnvh0I3Hnj2tMZWwrRROWLSgfJrKqWmlQ==} + engines: {node: '>=8'} + dependencies: + scoped-regex: 2.1.0 + dev: true + optional: true + /is-self-closing/1.0.1: resolution: {integrity: sha512-E+60FomW7Blv5GXTlYee2KDrnG6srxF7Xt1SjrhWUGUEsTFIqY/nq2y3DaftCsgUMdh89V07IVfhY9KIJhLezg==} engines: {node: '>=0.12.0'} @@ -7825,6 +8048,12 @@ packages: engines: {node: '>=10'} dev: true + /is-url-superb/4.0.0: + resolution: {integrity: sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA==} + engines: {node: '>=10'} + dev: true + optional: true + /is-utf8/0.2.1: resolution: {integrity: sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=} dev: true @@ -7906,6 +8135,12 @@ packages: lodash.uniqby: 4.7.0 dev: true + /issue-regex/3.1.0: + resolution: {integrity: sha512-0RHjbtw9QXeSYnIEY5Yrp2QZrdtz21xBDV9C/GIlY2POmgoS6a7qjkYS5siRKXScnuAj5/SPv1C3YForNCHTJA==} + engines: {node: '>=10'} + dev: true + optional: true + /java-properties/1.0.2: resolution: {integrity: sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==} engines: {node: '>= 0.6.0'} @@ -8306,6 +8541,70 @@ packages: hasBin: true dev: true + /listr-input/0.2.1: + resolution: {integrity: sha512-oa8iVG870qJq+OuuMK3DjGqFcwsK1SDu+kULp9kEq09TY231aideIZenr3lFOQdASpAr6asuyJBbX62/a3IIhg==} + engines: {node: '>=6'} + dependencies: + inquirer: 7.3.3 + inquirer-autosubmit-prompt: 0.2.0 + rxjs: 6.6.7 + through: 2.3.8 + dev: true + optional: true + + /listr-silent-renderer/1.1.1: + resolution: {integrity: sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=} + engines: {node: '>=4'} + dev: true + optional: true + + /listr-update-renderer/0.5.0_listr@0.14.3: + resolution: {integrity: sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==} + engines: {node: '>=6'} + peerDependencies: + listr: ^0.14.2 + dependencies: + chalk: 1.1.3 + cli-truncate: 0.2.1 + elegant-spinner: 1.0.1 + figures: 1.7.0 + indent-string: 3.2.0 + listr: 0.14.3 + log-symbols: 1.0.2 + log-update: 2.3.0 + strip-ansi: 3.0.1 + dev: true + optional: true + + /listr-verbose-renderer/0.5.0: + resolution: {integrity: sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==} + engines: {node: '>=4'} + dependencies: + chalk: 2.4.2 + cli-cursor: 2.1.0 + date-fns: 1.30.1 + figures: 2.0.0 + dev: true + optional: true + + /listr/0.14.3: + resolution: {integrity: sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==} + engines: {node: '>=6'} + dependencies: + '@samverschueren/stream-to-observable': 0.3.1_rxjs@6.6.7 + is-observable: 1.1.0 + is-promise: 2.2.2 + is-stream: 1.1.0 + listr-silent-renderer: 1.1.1 + listr-update-renderer: 0.5.0_listr@0.14.3 + listr-verbose-renderer: 0.5.0 + p-map: 2.1.0 + rxjs: 6.6.7 + transitivePeerDependencies: + - zen-observable + dev: true + optional: true + /listr2/3.13.5_enquirer@2.3.6: resolution: {integrity: sha512-3n8heFQDSk+NcwBn3CgxEibZGaRzx+pC64n3YjpMD1qguV4nWus3Al+Oo3KooqFKTQEJ1v7MmnbnyyNspgx3NA==} engines: {node: '>=10.0.0'} @@ -8402,6 +8701,11 @@ packages: resolution: {integrity: sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=} dev: true + /lodash.isequal/4.5.0: + resolution: {integrity: sha1-QVxEePK8wwEgwizhDtMib30+GOA=} + dev: true + optional: true + /lodash.isinteger/4.0.4: resolution: {integrity: sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=} dev: true @@ -8503,6 +8807,14 @@ packages: success-symbol: 0.1.0 dev: true + /log-symbols/1.0.2: + resolution: {integrity: sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=} + engines: {node: '>=0.10.0'} + dependencies: + chalk: 1.1.3 + dev: true + optional: true + /log-symbols/4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} @@ -8511,6 +8823,16 @@ packages: is-unicode-supported: 0.1.0 dev: true + /log-update/2.3.0: + resolution: {integrity: sha1-iDKP19HOeTiykoN0bwsbwSayRwg=} + engines: {node: '>=4'} + dependencies: + ansi-escapes: 3.2.0 + cli-cursor: 2.1.0 + wrap-ansi: 3.0.1 + dev: true + optional: true + /log-update/4.0.0: resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==} engines: {node: '>=10'} @@ -8834,6 +9156,12 @@ packages: engines: {node: '>=6'} dev: true + /mimic-fn/3.1.0: + resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==} + engines: {node: '>=8'} + dev: true + optional: true + /mimic-response/1.0.1: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} @@ -9035,6 +9363,14 @@ packages: resolution: {integrity: sha512-SrQrok4CATudVzBS7coSz26QRSmlK9TzzoFbeKfcPBUFPjcQM9Rqvr/DlJkOrwI/0KcgvMub1n1g5Jt9EgRn4A==} dev: true + /new-github-release-url/1.0.0: + resolution: {integrity: sha512-dle7yf655IMjyFUqn6Nxkb18r4AOAkzRcgcZv6WZ0IqrOH4QCEZ8Sm6I7XX21zvHdBeeMeTkhR9qT2Z0EJDx6A==} + engines: {node: '>=10'} + dependencies: + type-fest: 0.4.1 + dev: true + optional: true + /next-tick/1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: true @@ -9138,6 +9474,72 @@ packages: engines: {node: '>=10'} dev: true + /np/7.6.0: + resolution: {integrity: sha512-WWGZtfNkE6MEkI7LE8NtG7poTqzTHj/tssBzcPnBAdMVPXkXDtX2wk0ptrj8YZ3u4TFmGSqioSohdud86aJxSg==} + engines: {git: '>=2.11.0', node: '>=10', npm: '>=6.8.0', yarn: '>=1.7.0'} + hasBin: true + requiresBuild: true + dependencies: + '@samverschueren/stream-to-observable': 0.3.1_rxjs@6.6.7 + any-observable: 0.5.1_rxjs@6.6.7 + async-exit-hook: 2.0.1 + chalk: 4.1.2 + cosmiconfig: 7.0.1 + del: 6.0.0 + escape-goat: 3.0.0 + escape-string-regexp: 4.0.0 + execa: 5.1.1 + github-url-from-git: 1.5.0 + has-yarn: 2.1.0 + hosted-git-info: 3.0.8 + ignore-walk: 3.0.4 + import-local: 3.1.0 + inquirer: 7.3.3 + is-installed-globally: 0.3.2 + is-interactive: 1.0.0 + is-scoped: 2.1.0 + issue-regex: 3.1.0 + listr: 0.14.3 + listr-input: 0.2.1 + log-symbols: 4.1.0 + meow: 8.1.2 + minimatch: 3.0.4 + new-github-release-url: 1.0.0 + npm-name: 6.0.1 + onetime: 5.1.2 + open: 7.4.2 + ow: 0.21.0 + p-memoize: 4.0.4 + p-timeout: 4.1.0 + pkg-dir: 5.0.0 + read-pkg-up: 7.0.1 + rxjs: 6.6.7 + semver: 7.3.5 + split: 1.0.1 + symbol-observable: 3.0.0 + terminal-link: 2.1.1 + update-notifier: 5.1.0 + transitivePeerDependencies: + - zen-observable + dev: true + optional: true + + /npm-name/6.0.1: + resolution: {integrity: sha512-fhKRvUAxaYzMEUZim4mXWyfFbVS+M1CbrCLdAo3txWzrctxKka/h+KaBW0O9Cz5uOM00Nldn2JLWhuwnyW3SUw==} + engines: {node: '>=10'} + dependencies: + got: 10.7.0 + is-scoped: 2.1.0 + is-url-superb: 4.0.0 + lodash.zip: 4.2.0 + org-regex: 1.0.0 + p-map: 3.0.0 + registry-auth-token: 4.2.1 + registry-url: 5.1.0 + validate-npm-package-name: 3.0.0 + dev: true + optional: true + /npm-run-path/2.0.2: resolution: {integrity: sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=} engines: {node: '>=4'} @@ -9523,6 +9925,15 @@ packages: is-wsl: 2.2.0 dev: true + /open/7.4.2: + resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} + engines: {node: '>=8'} + dependencies: + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: true + optional: true + /open/8.4.0: resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} engines: {node: '>=12'} @@ -9569,6 +9980,12 @@ packages: wcwidth: 1.0.1 dev: true + /org-regex/1.0.0: + resolution: {integrity: sha512-7bqkxkEJwzJQUAlyYniqEZ3Ilzjh0yoa62c7gL6Ijxj5bEpPL+8IE1Z0PFj0ywjjXQcdrwR51g9MIcLezR0hKQ==} + engines: {node: '>=8'} + dev: true + optional: true + /os-homedir/1.0.2: resolution: {integrity: sha1-/7xJiDNuDoM94MFox+8VISGqf7M=} engines: {node: '>=0.10.0'} @@ -9608,6 +10025,19 @@ packages: os-tmpdir: 1.0.2 dev: true + /ow/0.21.0: + resolution: {integrity: sha512-dlsoDe39g7mhdsdrC1R/YwjT7yjVqE3svWwOlMGvN690waBkgEZBmKBdkmKvSt5/wZ6E0Jn/nIesPqMZOpPKqw==} + engines: {node: '>=10'} + dependencies: + '@sindresorhus/is': 4.3.0 + callsites: 3.1.0 + dot-prop: 6.0.1 + lodash.isequal: 4.5.0 + type-fest: 0.20.2 + vali-date: 1.0.0 + dev: true + optional: true + /p-cancelable/1.1.0: resolution: {integrity: sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==} engines: {node: '>=6'} @@ -9628,6 +10058,14 @@ packages: engines: {node: '>=8'} dev: true + /p-event/4.2.0: + resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} + engines: {node: '>=8'} + dependencies: + p-timeout: 3.2.0 + dev: true + optional: true + /p-filter/2.1.0: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} @@ -9702,6 +10140,14 @@ packages: engines: {node: '>=6'} dev: true + /p-map/3.0.0: + resolution: {integrity: sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==} + engines: {node: '>=8'} + dependencies: + aggregate-error: 3.1.0 + dev: true + optional: true + /p-map/4.0.0: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} engines: {node: '>=10'} @@ -9709,11 +10155,27 @@ packages: aggregate-error: 3.1.0 dev: true + /p-memoize/4.0.4: + resolution: {integrity: sha512-ijdh0DP4Mk6J4FXlOM6vPPoCjPytcEseW8p/k5SDTSSfGV3E9bpt9Yzfifvzp6iohIieoLTkXRb32OWV0fB2Lw==} + engines: {node: '>=10'} + dependencies: + map-age-cleaner: 0.1.3 + mimic-fn: 3.1.0 + p-settle: 4.1.1 + dev: true + optional: true + /p-reduce/2.1.0: resolution: {integrity: sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==} engines: {node: '>=8'} dev: true + /p-reflect/2.1.0: + resolution: {integrity: sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg==} + engines: {node: '>=8'} + dev: true + optional: true + /p-retry/4.6.1: resolution: {integrity: sha512-e2xXGNhZOZ0lfgR9kL34iGlU8N/KO0xZnQxVEwdeOvpqNDQfdnxIYizvWtK8RglUa3bGqI8g0R/BdfzLMxRkiA==} engines: {node: '>=8'} @@ -9722,6 +10184,29 @@ packages: retry: 0.13.1 dev: true + /p-settle/4.1.1: + resolution: {integrity: sha512-6THGh13mt3gypcNMm0ADqVNCcYa3BK6DWsuJWFCuEKP1rpY+OKGp7gaZwVmLspmic01+fsg/fN57MfvDzZ/PuQ==} + engines: {node: '>=10'} + dependencies: + p-limit: 2.3.0 + p-reflect: 2.1.0 + dev: true + optional: true + + /p-timeout/3.2.0: + resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} + engines: {node: '>=8'} + dependencies: + p-finally: 1.0.0 + dev: true + optional: true + + /p-timeout/4.1.0: + resolution: {integrity: sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==} + engines: {node: '>=10'} + dev: true + optional: true + /p-try/1.0.0: resolution: {integrity: sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=} engines: {node: '>=4'} @@ -9965,6 +10450,22 @@ packages: load-json-file: 4.0.0 dev: true + /pkg-dir/4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + dependencies: + find-up: 4.1.0 + dev: true + optional: true + + /pkg-dir/5.0.0: + resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} + engines: {node: '>=10'} + dependencies: + find-up: 5.0.0 + dev: true + optional: true + /plur/4.0.0: resolution: {integrity: sha512-4UGewrYgqDFw9vV6zNV+ADmPAUAfJPKtGvb/VdpQAx25X5f3xXdGdyOEVFwkl8Hl/tl7+xbeHqSEM+D5/TirUg==} engines: {node: '>=10'} @@ -10595,6 +11096,14 @@ packages: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} dev: true + /resolve-cwd/3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + dependencies: + resolve-from: 5.0.0 + dev: true + optional: true + /resolve-dir/1.0.1: resolution: {integrity: sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=} engines: {node: '>=0.10.0'} @@ -10641,6 +11150,15 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true + /resolve/1.22.0: + resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==} + hasBin: true + dependencies: + is-core-module: 2.8.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + /resolve/1.7.1: resolution: {integrity: sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==} dependencies: @@ -10714,7 +11232,7 @@ packages: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} hasBin: true dependencies: - glob: 7.2.0 + glob: 7.1.6 dev: true /rimraf/3.0.2: @@ -10739,7 +11257,7 @@ packages: resolution: {integrity: sha512-omv1DIv5z1kV+zDAEjaDjWSkx8w5TbFp5NZoPwUipwzYVcor/4So9ZU3bUyQ1c8lxY5Q0Es/ztWW7PGjY7to0Q==} hasBin: true dependencies: - '@babel/parser': 7.16.10 + '@babel/parser': 7.16.12 '@babel/traverse': 7.16.10 '@babel/types': 7.16.8 bent: 7.3.12 @@ -10797,6 +11315,12 @@ packages: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} dev: true + /scoped-regex/2.1.0: + resolution: {integrity: sha512-g3WxHrqSWCZHGHlSrF51VXFdjImhwvH8ZO/pryFH56Qi0cDsZfylQa/t0jCzVQFNbNvM00HfHjkDPEuarKDSWQ==} + engines: {node: '>=8'} + dev: true + optional: true + /scslre/0.1.6: resolution: {integrity: sha512-JORxVRlQTfjvlOAaiQKebgFElyAm5/W8b50lgaZ0OkEnKnagJW2ufDh3xRfU75UD9z3FGIu1gL1IyR3Poa6Qmw==} dependencies: @@ -11072,6 +11596,12 @@ packages: string-break: 1.2.0 dev: true + /slice-ansi/0.0.4: + resolution: {integrity: sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=} + engines: {node: '>=0.10.0'} + dev: true + optional: true + /slice-ansi/2.1.0: resolution: {integrity: sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==} engines: {node: '>=6'} @@ -11562,6 +12092,18 @@ packages: engines: {node: '>= 0.4'} dev: true + /symbol-observable/1.2.0: + resolution: {integrity: sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==} + engines: {node: '>=0.10.0'} + dev: true + optional: true + + /symbol-observable/3.0.0: + resolution: {integrity: sha512-6tDOXSHiVjuCaasQSWTmHUWn4PuG7qa3+1WT031yTc/swT7+rLiw3GOrFxaH1E3lLP09dH3bVuVDf2gK5rxG3Q==} + engines: {node: '>=0.10'} + dev: true + optional: true + /table-layout/1.0.2: resolution: {integrity: sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==} engines: {node: '>=8.0.0'} @@ -11734,6 +12276,12 @@ packages: engines: {node: '>=6'} dev: true + /to-readable-stream/2.1.0: + resolution: {integrity: sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==} + engines: {node: '>=8'} + dev: true + optional: true + /to-regex-range/2.1.1: resolution: {integrity: sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=} engines: {node: '>=0.10.0'} @@ -11923,7 +12471,7 @@ packages: js-yaml: 3.14.1 minimatch: 3.0.4 mkdirp: 0.5.5 - resolve: 1.21.0 + resolve: 1.22.0 semver: 5.7.1 tslib: 1.14.1 tsutils: 2.29.0 @@ -12004,6 +12552,12 @@ packages: engines: {node: '>=4'} dev: true + /type-fest/0.10.0: + resolution: {integrity: sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==} + engines: {node: '>=8'} + dev: true + optional: true + /type-fest/0.16.0: resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} engines: {node: '>=10'} @@ -12029,6 +12583,12 @@ packages: engines: {node: '>=6'} dev: true + /type-fest/0.4.1: + resolution: {integrity: sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==} + engines: {node: '>=6'} + dev: true + optional: true + /type-fest/0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} @@ -12207,6 +12767,27 @@ packages: xdg-basedir: 4.0.0 dev: true + /update-notifier/5.1.0: + resolution: {integrity: sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==} + engines: {node: '>=10'} + dependencies: + boxen: 5.1.2 + chalk: 4.1.2 + configstore: 5.0.1 + has-yarn: 2.1.0 + import-lazy: 2.1.0 + is-ci: 2.0.0 + is-installed-globally: 0.4.0 + is-npm: 5.0.0 + is-yarn-global: 0.3.0 + latest-version: 5.1.0 + pupa: 2.1.1 + semver: 7.3.5 + semver-diff: 3.1.1 + xdg-basedir: 4.0.0 + dev: true + optional: true + /uri-js/4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} dependencies: @@ -12264,6 +12845,12 @@ packages: resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} dev: true + /vali-date/1.0.0: + resolution: {integrity: sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=} + engines: {node: '>=0.10.0'} + dev: true + optional: true + /validate-npm-package-license/3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} dependencies: @@ -12271,6 +12858,13 @@ packages: spdx-expression-parse: 3.0.1 dev: true + /validate-npm-package-name/3.0.0: + resolution: {integrity: sha1-X6kS2B630MdK/BQN5zF/DKffQ34=} + dependencies: + builtins: 1.0.3 + dev: true + optional: true + /verror/1.10.0: resolution: {integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=} engines: {'0': node >=0.6.0} @@ -12469,7 +13063,7 @@ packages: /wide-align/1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} dependencies: - string-width: 4.2.3 + string-width: 1.0.2 dev: true /widest-line/3.1.0: @@ -12507,6 +13101,15 @@ packages: typical: 5.2.0 dev: true + /wrap-ansi/3.0.1: + resolution: {integrity: sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=} + engines: {node: '>=4'} + dependencies: + string-width: 2.1.1 + strip-ansi: 4.0.0 + dev: true + optional: true + /wrap-ansi/6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} diff --git a/poetry.lock b/poetry.lock index a689f289..a27014b9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -24,7 +24,7 @@ setupext = "*" [[package]] name = "ansible-compat" -version = "0.5.0" +version = "1.0.0" description = "Ansible compatibility goodies" category = "dev" optional = false @@ -32,6 +32,7 @@ python-versions = ">=3.6" [package.dependencies] PyYAML = "*" +subprocess-tee = ">=0.3.5" [package.extras] test = ["coverage", "flaky", "pip-tools", "pytest", "pytest-markdown", "pytest-mock", "pytest-plus"] @@ -67,13 +68,14 @@ rich = "*" [[package]] name = "ansibler" -version = "0.1.169" +version = "0.2.3" description = "Generate JSON data that describes the dependencies of an Ansible playbook/role. Also, automatically generate OS compatibility charts using Molecule." category = "dev" optional = false python-versions = "*" [package.dependencies] +requests = "*" "ruamel.yaml" = "*" [[package]] @@ -164,11 +166,11 @@ test = ["tox", "pytest", "pytest-mock"] [[package]] name = "cachetools" -version = "4.2.4" +version = "5.0.0" description = "Extensible memoizing collections and decorators" category = "dev" optional = false -python-versions = "~=3.5" +python-versions = "~=3.7" [[package]] name = "cerberus" @@ -207,7 +209,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "charset-normalizer" -version = "2.0.9" +version = "2.0.10" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false @@ -324,7 +326,7 @@ tls = ["pyOpenSSL (>=17.5.0)", "cryptography (>=3.4.7)", "idna (>=2.0.0)"] [[package]] name = "enrich" -version = "1.2.6" +version = "1.2.7" description = "enrich" category = "dev" optional = false @@ -411,7 +413,7 @@ grpc = ["grpcio (>=1.0rc1)", "google-gax (>=0.12.3,<0.13dev)", "gax-google-pubsu [[package]] name = "google-api-core" -version = "2.3.2" +version = "2.4.0" description = "Google API client core library" category = "dev" optional = false @@ -430,7 +432,7 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2)"] [[package]] name = "google-api-python-client" -version = "2.33.0" +version = "2.36.0" description = "Google API Client Library for Python" category = "dev" optional = false @@ -441,18 +443,18 @@ google-api-core = ">=1.21.0,<3.0.0dev" google-auth = ">=1.16.0,<3.0.0dev" google-auth-httplib2 = ">=0.1.0" httplib2 = ">=0.15.0,<1dev" -uritemplate = ">=3.0.0,<5" +uritemplate = ">=3.0.1,<5" [[package]] name = "google-auth" -version = "2.3.3" +version = "2.4.0" description = "Google Authentication Library" category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*" [package.dependencies] -cachetools = ">=2.0.0,<5.0" +cachetools = ">=2.0.0,<6.0" pyasn1-modules = ">=0.2.1" rsa = {version = ">=3.1.4,<5", markers = "python_version >= \"3.6\""} six = ">=1.9.0" @@ -608,7 +610,7 @@ jinja2 = "*" name = "junit-xml" version = "1.9" description = "Creates JUnit XML test result documents that can be read by tools such as Jenkins" -category = "main" +category = "dev" optional = false python-versions = "*" @@ -780,7 +782,7 @@ pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" [[package]] name = "paramiko" -version = "2.8.1" +version = "2.9.2" description = "SSH2 protocol library" category = "dev" optional = false @@ -807,11 +809,11 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "platformdirs" -version = "2.4.0" +version = "2.4.1" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] @@ -847,7 +849,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pre-commit-hooks" -version = "4.0.1" +version = "4.1.0" description = "Some out-of-the-box hooks for pre-commit." category = "dev" optional = false @@ -926,7 +928,7 @@ python-versions = "*" [[package]] name = "pycryptodome" -version = "3.12.0" +version = "3.13.0" description = "Cryptographic library for Python" category = "dev" optional = false @@ -942,7 +944,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.10.0" +version = "2.11.2" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false @@ -950,15 +952,14 @@ python-versions = ">=3.5" [[package]] name = "pynacl" -version = "1.4.0" +version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] cffi = ">=1.4.1" -six = "*" [package.extras] docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] @@ -966,7 +967,7 @@ tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] [[package]] name = "pyparsing" -version = "3.0.6" +version = "3.0.7" description = "Python parsing module" category = "main" optional = false @@ -1044,7 +1045,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "requests" -version = "2.26.0" +version = "2.27.1" description = "Python HTTP for Humans." category = "main" optional = false @@ -1089,7 +1090,7 @@ test = ["commentjson", "packaging", "pytest"] [[package]] name = "rich" -version = "10.16.1" +version = "11.0.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" category = "dev" optional = false @@ -1116,16 +1117,27 @@ pyasn1 = ">=0.1.3" [[package]] name = "ruamel.yaml" -version = "0.17.17" +version = "0.17.20" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" category = "dev" optional = false python-versions = ">=3" +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} + [package.extras] docs = ["ryd"] jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] +[[package]] +name = "ruamel.yaml.clib" +version = "0.2.6" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +category = "dev" +optional = false +python-versions = ">=3.5" + [[package]] name = "selinux" version = "0.2.1" @@ -1206,7 +1218,7 @@ python-versions = ">=3.6" [[package]] name = "urllib3" -version = "1.26.7" +version = "1.26.8" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -1253,7 +1265,7 @@ pyyaml = "*" [metadata] lock-version = "1.1" python-versions = ">=3.10.0,<4.0.0" -content-hash = "9ddb036a6e5927f80fd9d41ea792dc468d2da099689e764a29f55ad1f6090ab6" +content-hash = "6bd6d033da6a2a230c67e9a4434c29847310a2bd2d417ff256d3ac5be9a8f4a5" [metadata.files] ansible = [ @@ -1263,8 +1275,8 @@ ansible-autodoc-fork = [ {file = "ansible-autodoc-fork-0.5.6.tar.gz", hash = "sha256:5718ede0383731ea366280004863f140c6a0e33dbcd0e7f3eb14da49e7b1320c"}, ] ansible-compat = [ - {file = "ansible-compat-0.5.0.tar.gz", hash = "sha256:0730fbbb32710d19f4244a4cabad9c6b33b4b92ddf72aee353484e17543405f5"}, - {file = "ansible_compat-0.5.0-py3-none-any.whl", hash = "sha256:3a696842689e108a827d3b60a1c32d53a546c94fe4fb244166259cbc25268c28"}, + {file = "ansible-compat-1.0.0.tar.gz", hash = "sha256:2ade2acbd1d7868eeb602c682afe92bc8c05480333205c328b4c2a1b09f970a4"}, + {file = "ansible_compat-1.0.0-py3-none-any.whl", hash = "sha256:8b9c9678e82c328df9574cd7eb3777e7f25cf1530bc48e612597213bc6e4bd0d"}, ] ansible-core = [ {file = "ansible-core-2.11.7.tar.gz", hash = "sha256:b87188beacfac1bb6dc5cf65663f3c52e66e0f9990742db00a3dca71ebae2eee"}, @@ -1274,7 +1286,7 @@ ansible-lint = [ {file = "ansible_lint-4.3.7-py2.py3-none-any.whl", hash = "sha256:300e841f690b556a08d44902d6414283dc101079b27909e3a892f1cf1d10d7ff"}, ] ansibler = [ - {file = "ansibler-0.1.169.tar.gz", hash = "sha256:d056b730b1475c13c3114c11594863327dd06413a03b4509287ae057c85e9f92"}, + {file = "ansibler-0.2.3.tar.gz", hash = "sha256:0481922ad8a2bb0131dd448e98831c5fc9e994bb4408d6f4df417bb5947e0b1d"}, ] apache-libcloud = [ {file = "apache-libcloud-3.4.1.tar.gz", hash = "sha256:88f18da0cf3fac0af723e743fb741d9d1be251881edab7a5a0d1629955b5011b"}, @@ -1306,8 +1318,8 @@ blocklint = [ {file = "blocklint-0.2.3.tar.gz", hash = "sha256:b6c154b126dd605f8b66d6af9aa9c138ebbf08d184a1ad76e3ea2cb57155c9c5"}, ] cachetools = [ - {file = "cachetools-4.2.4-py3-none-any.whl", hash = "sha256:92971d3cb7d2a97efff7c7bb1657f21a8f5fb309a37530537c71b1774189f2d1"}, - {file = "cachetools-4.2.4.tar.gz", hash = "sha256:89ea6f1b638d5a73a4f9226be57ac5e4f399d22770b92355f92dcb0f7f001693"}, + {file = "cachetools-5.0.0-py3-none-any.whl", hash = "sha256:8fecd4203a38af17928be7b90689d8083603073622229ca7077b72d8e5a976e4"}, + {file = "cachetools-5.0.0.tar.gz", hash = "sha256:486471dfa8799eb7ec503a8059e263db000cdda20075ce5e48903087f79d5fd6"}, ] cerberus = [ {file = "Cerberus-1.3.2.tar.gz", hash = "sha256:302e6694f206dd85cb63f13fd5025b31ab6d38c99c50c6d769f8fa0b0f299589"}, @@ -1373,8 +1385,8 @@ chardet = [ {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] charset-normalizer = [ - {file = "charset-normalizer-2.0.9.tar.gz", hash = "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"}, - {file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"}, + {file = "charset-normalizer-2.0.10.tar.gz", hash = "sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd"}, + {file = "charset_normalizer-2.0.10-py3-none-any.whl", hash = "sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455"}, ] click = [ {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, @@ -1427,8 +1439,8 @@ docker = [ {file = "docker-5.0.3.tar.gz", hash = "sha256:d916a26b62970e7c2f554110ed6af04c7ccff8e9f81ad17d0d40c75637e227fb"}, ] enrich = [ - {file = "enrich-1.2.6-py3-none-any.whl", hash = "sha256:ed0b3ac33495cc95f1ccafaf6c7ec0a0fcabb20f7f7a90121f37eb83a85bf82b"}, - {file = "enrich-1.2.6.tar.gz", hash = "sha256:0e99ff57d87f7b5def0ca79917e88fb9351aa0d52e228ee38bff7cd858315fe4"}, + {file = "enrich-1.2.7-py3-none-any.whl", hash = "sha256:f29b2c8c124b4dbd7c975ab5c3568f6c7a47938ea3b7d2106c8a3bd346545e4f"}, + {file = "enrich-1.2.7.tar.gz", hash = "sha256:0a2ab0d2931dff8947012602d1234d2a3ee002d9a355b5d70be6bf5466008893"}, ] flake8 = [ {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, @@ -1447,16 +1459,16 @@ gcloud = [ {file = "gcloud-0.18.3.tar.gz", hash = "sha256:0af2dec59fce20561752f86e42d981c6a255e306a6c5e5d1fa3d358a8857e4fb"}, ] google-api-core = [ - {file = "google-api-core-2.3.2.tar.gz", hash = "sha256:c8889f45cf58deca522888ae1d39b2a25e93e7d1b019ae8cee6456d5c726a40c"}, - {file = "google_api_core-2.3.2-py2.py3-none-any.whl", hash = "sha256:3c562d393aed7e3d2011fcd1f103b490c411dcf5644b6312ca11a166a6ea8faf"}, + {file = "google-api-core-2.4.0.tar.gz", hash = "sha256:ba8787b7c61632cd0340f095e1c036bef9426b2594f10afb290ba311ae8cb2cb"}, + {file = "google_api_core-2.4.0-py2.py3-none-any.whl", hash = "sha256:58e2c1171a3d51778bf4e428fbb4bf79cbd05007b4b44deaa80cf73c80eebc0f"}, ] google-api-python-client = [ - {file = "google-api-python-client-2.33.0.tar.gz", hash = "sha256:38e98611794632a12479fafbabe0b5027e8fcfc412e8375f1b23db0bc0209181"}, - {file = "google_api_python_client-2.33.0-py2.py3-none-any.whl", hash = "sha256:1322d026110bc62eb29a4a25a15895dac51486b30a29b5943bc456318677280b"}, + {file = "google-api-python-client-2.36.0.tar.gz", hash = "sha256:533c69e8ddce6630a2ce5b98348f6a58a23df49cb92040a0086315df979404af"}, + {file = "google_api_python_client-2.36.0-py2.py3-none-any.whl", hash = "sha256:9e8863dec46224d4375cf3834e27ffd4bce79858f0f00a6109a51e3e62b4f3c2"}, ] google-auth = [ - {file = "google-auth-2.3.3.tar.gz", hash = "sha256:d83570a664c10b97a1dc6f8df87e5fdfff012f48f62be131e449c20dfc32630e"}, - {file = "google_auth-2.3.3-py2.py3-none-any.whl", hash = "sha256:a348a50b027679cb7dae98043ac8dbcc1d7951f06d8387496071a1e05a2465c0"}, + {file = "google-auth-2.4.0.tar.gz", hash = "sha256:ef6f4827f6a3f9c5ff884616e2ba779acb5d690486fb70ca5e3091ed85ad932a"}, + {file = "google_auth-2.4.0-py2.py3-none-any.whl", hash = "sha256:d1fad279d9d97e7d6b4a09a53e851ab2ee6d36d5c19547354a3f47a8a6ae41b9"}, ] google-auth-httplib2 = [ {file = "google-auth-httplib2-0.1.0.tar.gz", hash = "sha256:a07c39fd632becacd3f07718dfd6021bf396978f03ad3ce4321d060015cc30ac"}, @@ -1655,16 +1667,16 @@ packaging = [ {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] paramiko = [ - {file = "paramiko-2.8.1-py2.py3-none-any.whl", hash = "sha256:7b5910f5815a00405af55da7abcc8a9e0d9657f57fcdd9a89894fdbba1c6b8a8"}, - {file = "paramiko-2.8.1.tar.gz", hash = "sha256:85b1245054e5d7592b9088cc6d08da22445417912d3a3e48138675c7a8616438"}, + {file = "paramiko-2.9.2-py2.py3-none-any.whl", hash = "sha256:04097dbd96871691cdb34c13db1883066b8a13a0df2afd4cb0a92221f51c2603"}, + {file = "paramiko-2.9.2.tar.gz", hash = "sha256:944a9e5dbdd413ab6c7951ea46b0ab40713235a9c4c5ca81cfe45c6f14fa677b"}, ] pathspec = [ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] platformdirs = [ - {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, - {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, + {file = "platformdirs-2.4.1-py3-none-any.whl", hash = "sha256:1d7385c7db91728b83efd0ca99a5afb296cab9d0ed8313a45ed8ba17967ecfca"}, + {file = "platformdirs-2.4.1.tar.gz", hash = "sha256:440633ddfebcc36264232365d7840a970e75e1018d15b4327d11f91909045fda"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, @@ -1678,8 +1690,8 @@ poyo = [ {file = "poyo-0.5.0.tar.gz", hash = "sha256:e26956aa780c45f011ca9886f044590e2d8fd8b61db7b1c1cf4e0869f48ed4dd"}, ] pre-commit-hooks = [ - {file = "pre_commit_hooks-4.0.1-py2.py3-none-any.whl", hash = "sha256:6efe92c7613c311abc7dd06817fc016f222d9289fe24b261e64412b0af96c662"}, - {file = "pre_commit_hooks-4.0.1.tar.gz", hash = "sha256:99f1b9fc00a82e6588990b6b92edcdf4bec9c3d65c6272b8867be389055ce05e"}, + {file = "pre_commit_hooks-4.1.0-py2.py3-none-any.whl", hash = "sha256:ba95316b79038e56ce998cdacb1ce922831ac0e41744c77bcc2b9677bf183206"}, + {file = "pre_commit_hooks-4.1.0.tar.gz", hash = "sha256:b6361865d1877c5da5ac3a944aab19ce6bd749a534d2ede28e683d07194a57e1"}, ] proselint = [ {file = "proselint-0.12.0-py3-none-any.whl", hash = "sha256:3c111aa1df0f4917c35d8aedab4211960653123726cdc045037663c14d1a1d83"}, @@ -1747,68 +1759,60 @@ pycrypto = [ {file = "pycrypto-2.6.1.tar.gz", hash = "sha256:f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c"}, ] pycryptodome = [ - {file = "pycryptodome-3.12.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:90ad3381ccdc6a24cc2841e295706a168f32abefe64c679695712acac71fd5da"}, - {file = "pycryptodome-3.12.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e80f7469b0b3ea0f694230477d8501dc5a30a717e94fddd4821e6721f3053eae"}, - {file = "pycryptodome-3.12.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:b91404611767a7485837a6f1fd20cf9a5ae0ad362040a022cd65827ecb1b0d00"}, - {file = "pycryptodome-3.12.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:db66ccda65d5d20c17b00768e462a86f6f540f9aea8419a7f76cc7d9effd82cd"}, - {file = "pycryptodome-3.12.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:dc88355c4b261ed259268e65705b28b44d99570337694d593f06e3b1698eaaf3"}, - {file = "pycryptodome-3.12.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:6f8f5b7b53516da7511951910ab458e799173722c91fea54e2ba2f56d102e4aa"}, - {file = "pycryptodome-3.12.0-cp27-cp27m-win32.whl", hash = "sha256:93acad54a72d81253242eb0a15064be559ec9d989e5173286dc21cad19f01765"}, - {file = "pycryptodome-3.12.0-cp27-cp27m-win_amd64.whl", hash = "sha256:5a8c24d39d4a237dbfe181ea6593792bf9b5582c7fcfa7b8e0e12fda5eec07af"}, - {file = "pycryptodome-3.12.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:32d15da81959faea6cbed95df2bb44f7f796211c110cf90b5ad3b2aeeb97fc8e"}, - {file = "pycryptodome-3.12.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:aed7eb4b64c600fbc5e6d4238991ad1b4179a558401f203d1fcbd24883748982"}, - {file = "pycryptodome-3.12.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:341c6bbf932c406b4f3ee2372e8589b67ac0cf4e99e7dc081440f43a3cde9f0f"}, - {file = "pycryptodome-3.12.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:de0b711d673904dd6c65307ead36cb76622365a393569bf880895cba21195b7a"}, - {file = "pycryptodome-3.12.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:3558616f45d8584aee3eba27559bc6fd0ba9be6c076610ed3cc62bd5229ffdc3"}, - {file = "pycryptodome-3.12.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:a78e4324e566b5fbc2b51e9240950d82fa9e1c7eb77acdf27f58712f65622c1d"}, - {file = "pycryptodome-3.12.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:3f2f3dd596c6128d91314e60a6bcf4344610ef0e97f4ae4dd1770f86dd0748d8"}, - {file = "pycryptodome-3.12.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:e05f994f30f1cda3cbe57441f41220d16731cf99d868bb02a8f6484c454c206b"}, - {file = "pycryptodome-3.12.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:4cded12e13785bbdf4ba1ff5fb9d261cd98162145f869e4fbc4a4b9083392f0b"}, - {file = "pycryptodome-3.12.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:1181c90d1a6aee68a84826825548d0db1b58d8541101f908d779d601d1690586"}, - {file = "pycryptodome-3.12.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:6bb0d340c93bcb674ea8899e2f6408ec64c6c21731a59481332b4b2a8143cc60"}, - {file = "pycryptodome-3.12.0-cp35-abi3-win32.whl", hash = "sha256:39da5807aa1ff820799c928f745f89432908bf6624b9e981d2d7f9e55d91b860"}, - {file = "pycryptodome-3.12.0-cp35-abi3-win_amd64.whl", hash = "sha256:212c7f7fe11cad9275fbcff50ca977f1c6643f13560d081e7b0f70596df447b8"}, - {file = "pycryptodome-3.12.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:b07a4238465eb8c65dd5df2ab8ba6df127e412293c0ed7656c003336f557a100"}, - {file = "pycryptodome-3.12.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:a6e1bcd9d5855f1a3c0f8d585f44c81b08f39a02754007f374fb8db9605ba29c"}, - {file = "pycryptodome-3.12.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:aceb1d217c3a025fb963849071446cf3aca1353282fe1c3cb7bd7339a4d47947"}, - {file = "pycryptodome-3.12.0-pp27-pypy_73-win32.whl", hash = "sha256:f699360ae285fcae9c8f53ca6acf33796025a82bb0ccd7c1c551b04c1726def3"}, - {file = "pycryptodome-3.12.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d845c587ceb82ac7cbac7d0bf8c62a1a0fe7190b028b322da5ca65f6e5a18b9e"}, - {file = "pycryptodome-3.12.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:d8083de50f6dec56c3c6f270fb193590999583a1b27c9c75bc0b5cac22d438cc"}, - {file = "pycryptodome-3.12.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:9ea2f6674c803602a7c0437fccdc2ea036707e60456974fe26ca263bd501ec45"}, - {file = "pycryptodome-3.12.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:5d4264039a2087977f50072aaff2346d1c1c101cb359f9444cf92e3d1f42b4cd"}, - {file = "pycryptodome-3.12.0.zip", hash = "sha256:12c7343aec5a3b3df5c47265281b12b611f26ec9367b6129199d67da54b768c1"}, + {file = "pycryptodome-3.13.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e468724173df02f9d83f3fea830bf0d04aa291b5add22b4a78e01c97aab04873"}, + {file = "pycryptodome-3.13.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:1fb7a6f222072412f320b9e48d3ce981920efbfce37b06d028ec9bd94093b37f"}, + {file = "pycryptodome-3.13.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4f1b594d0cf35bd12ec4244df1155a7f565bf6e6245976ac36174c1564688c90"}, + {file = "pycryptodome-3.13.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:9ea70f6c3f6566159e3798e4593a4a8016994a0080ac29a45200615b45091a1b"}, + {file = "pycryptodome-3.13.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:f7aad304575d075faf2806977b726b67da7ba294adc97d878f92a062e357a56a"}, + {file = "pycryptodome-3.13.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:702446a012fd9337b9327d168bb0c7dc714eb93ad361f6f61af9ca8305a301f1"}, + {file = "pycryptodome-3.13.0-cp27-cp27m-win32.whl", hash = "sha256:681ac47c538c64305d710eaed2bb49532f62b3f4c93aa7c423c520df981392e5"}, + {file = "pycryptodome-3.13.0-cp27-cp27m-win_amd64.whl", hash = "sha256:7b3478a187d897f003b2aa1793bcc59463e8d57a42e2aafbcbbe9cd47ec46863"}, + {file = "pycryptodome-3.13.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:eec02d9199af4b1ccfe1f9c587691a07a1fa39d949d2c1dc69d079ab9af8212f"}, + {file = "pycryptodome-3.13.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9c8e0e6c5e982699801b20fa74f43c19aa080d2b53a39f3c132d35958e153bd4"}, + {file = "pycryptodome-3.13.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f5457e44d3f26d9946091e92b28f3e970a56538b96c87b4b155a84e32a40b7b5"}, + {file = "pycryptodome-3.13.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:88d6d54e83cf9bbd665ce1e7b9079983ee2d97a05f42e0569ff00a70f1dd8b1e"}, + {file = "pycryptodome-3.13.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:72de8c4d71e6b11d54528bb924447fa4fdabcbb3d76cc0e7f61d3b6075def6b3"}, + {file = "pycryptodome-3.13.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:008ef2c631f112cd5a58736e0b29f4a28b4bb853e68878689f8b476fd56e0691"}, + {file = "pycryptodome-3.13.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:51ebe9624ad0a0b4da1aaaa2d43aabadf8537737fd494cee0ffa37cd6326de02"}, + {file = "pycryptodome-3.13.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:deede160bdf87ddb71f0a1314ad5a267b1a960be314ea7dc6b7ad86da6da89a3"}, + {file = "pycryptodome-3.13.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:857c16bffd938254e3a834cd6b2a755ed24e1a953b1a86e33da136d3e4c16a6f"}, + {file = "pycryptodome-3.13.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:ca6db61335d07220de0b665bfee7b8e9615b2dfc67a54016db4826dac34c2dd2"}, + {file = "pycryptodome-3.13.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:073dedf0f9c490ae22ca081b86357646ac9b76f3e2bd89119d137fc697a9e3b6"}, + {file = "pycryptodome-3.13.0-cp35-abi3-win32.whl", hash = "sha256:e3affa03c49cce7b0a9501cc7f608d4f8e61fb2522b276d599ac049b5955576d"}, + {file = "pycryptodome-3.13.0-cp35-abi3-win_amd64.whl", hash = "sha256:e5d72be02b17e6bd7919555811264403468d1d052fa67c946e402257c3c29a27"}, + {file = "pycryptodome-3.13.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:0896d5d15ffe584d46cb9b69a75cf14a2bc8f6daf635b7bf16c1b041342a44b1"}, + {file = "pycryptodome-3.13.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:e420cdfca73f80fe15f79bb34756959945231a052440813e5fce531e6e96331a"}, + {file = "pycryptodome-3.13.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:720fafdf3e5c5de93039d8308f765cc60b8e9e7e852ad7135aa65dd89238191f"}, + {file = "pycryptodome-3.13.0-pp27-pypy_73-win32.whl", hash = "sha256:7a8b0e526ff239b4f4c61dd6898e2474d609843ffc437267f3a27ddff626e6f6"}, + {file = "pycryptodome-3.13.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d92a5eddffb0ad39f582f07c1de26e9daf6880e3e782a94bb7ebaf939567f8bf"}, + {file = "pycryptodome-3.13.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:cb9453c981554984c6f5c5ce7682d7286e65e2173d7416114c3593a977a01bf5"}, + {file = "pycryptodome-3.13.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:765b8b16bc1fd699e183dde642c7f2653b8f3c9c1a50051139908e9683f97732"}, + {file = "pycryptodome-3.13.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:b3af53dddf848afb38b3ac2bae7159ddad1feb9bac14aa3acec6ef1797b82f8d"}, + {file = "pycryptodome-3.13.0.tar.gz", hash = "sha256:95bacf9ff7d1b90bba537d3f5f6c834efe6bfbb1a0195cb3573f29e6716ef08d"}, ] pyflakes = [ {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, ] pygments = [ - {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, - {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, + {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, + {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, ] pynacl = [ - {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"}, - {file = "PyNaCl-1.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514"}, - {file = "PyNaCl-1.4.0-cp27-cp27m-win32.whl", hash = "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574"}, - {file = "PyNaCl-1.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"}, - {file = "PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7"}, - {file = "PyNaCl-1.4.0-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122"}, - {file = "PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d"}, - {file = "PyNaCl-1.4.0-cp35-abi3-win32.whl", hash = "sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634"}, - {file = "PyNaCl-1.4.0-cp35-abi3-win_amd64.whl", hash = "sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6"}, - {file = "PyNaCl-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4"}, - {file = "PyNaCl-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25"}, - {file = "PyNaCl-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4"}, - {file = "PyNaCl-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6"}, - {file = "PyNaCl-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f"}, - {file = "PyNaCl-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f"}, - {file = "PyNaCl-1.4.0-cp38-cp38-win32.whl", hash = "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96"}, - {file = "PyNaCl-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420"}, - {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"}, + {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"}, + {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"}, + {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"}, + {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"}, + {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"}, ] pyparsing = [ - {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, - {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, + {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, + {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, ] python-dateutil = [ {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, @@ -1871,8 +1875,8 @@ pyyaml = [ {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] requests = [ - {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, - {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, ] requests-ntlm = [ {file = "requests_ntlm-1.1.0-py2.py3-none-any.whl", hash = "sha256:1eb43d1026b64d431a8e0f1e8a8c8119ac698e72e9b95102018214411a8463ea"}, @@ -1883,16 +1887,43 @@ resolvelib = [ {file = "resolvelib-0.5.5.tar.gz", hash = "sha256:123de56548c90df85137425a3f51eb93df89e2ba719aeb6a8023c032758be950"}, ] rich = [ - {file = "rich-10.16.1-py3-none-any.whl", hash = "sha256:bbe04dd6ac09e4b00d22cb1051aa127beaf6e16c3d8687b026e96d3fca6aad52"}, - {file = "rich-10.16.1.tar.gz", hash = "sha256:4949e73de321784ef6664ebbc854ac82b20ff60b2865097b93f3b9b41e30da27"}, + {file = "rich-11.0.0-py3-none-any.whl", hash = "sha256:d7a8086aa1fa7e817e3bba544eee4fd82047ef59036313147759c11475f0dafd"}, + {file = "rich-11.0.0.tar.gz", hash = "sha256:c32a8340b21c75931f157466fefe81ae10b92c36a5ea34524dff3767238774a4"}, ] rsa = [ {file = "rsa-4.8-py3-none-any.whl", hash = "sha256:95c5d300c4e879ee69708c428ba566c59478fd653cc3a22243eeb8ed846950bb"}, {file = "rsa-4.8.tar.gz", hash = "sha256:5c6bd9dc7a543b7fe4304a631f8a8a3b674e2bbfc49c2ae96200cdbe55df6b17"}, ] "ruamel.yaml" = [ - {file = "ruamel.yaml-0.17.17-py3-none-any.whl", hash = "sha256:9af3ec5d7f8065582f3aa841305465025d0afd26c5fb54e15b964e11838fc74f"}, - {file = "ruamel.yaml-0.17.17.tar.gz", hash = "sha256:9751de4cbb57d4bfbf8fc394e125ed4a2f170fbff3dc3d78abf50be85924f8be"}, + {file = "ruamel.yaml-0.17.20-py3-none-any.whl", hash = "sha256:810eef9c46523a3f77479c66267a4708255ebe806a2d540078408c2227f011af"}, + {file = "ruamel.yaml-0.17.20.tar.gz", hash = "sha256:4b8a33c1efb2b443a93fcaafcfa4d2e445f8e8c29c528d9f5cdafb7cc9e4004c"}, +] +"ruamel.yaml.clib" = [ + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"}, + {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"}, ] selinux = [ {file = "selinux-0.2.1-py2.py3-none-any.whl", hash = "sha256:820adcf1b4451c9cc7759848797703263ba0eb6a4cad76d73548a9e0d57b7926"}, @@ -1930,8 +1961,8 @@ uritemplate = [ {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, ] urllib3 = [ - {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, - {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, + {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"}, + {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"}, ] websocket-client = [ {file = "websocket-client-1.2.3.tar.gz", hash = "sha256:1315816c0acc508997eb3ae03b9d3ff619c9d12d544c9a9b553704b1cc4f6af5"}, diff --git a/pyproject.toml b/pyproject.toml index 593a53d8..04675070 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -58,7 +58,7 @@ pywinrm = ">=0.3.0" [tool.poetry.dev-dependencies] ansible-lint = "<5.0.0" -ansibler = "^0.1.14" +ansibler = "^0.2.2" apache-libcloud = "^3.4.1" black = "^21.10b0" blocklint = "^0.2.3" diff --git a/start.sh b/start.sh new file mode 100644 index 00000000..c380a48b --- /dev/null +++ b/start.sh @@ -0,0 +1,334 @@ +#!/usr/bin/env bash + +# @file .config/scripts/start.sh +# @brief Ensures Task is installed and up-to-date and then runs `task start` +# @description +# This script will ensure [Task](https://github.com/go-task/task) is up-to-date +# and then run the `start` task which is generally a good entrypoint for any repository +# that is using the Megabyte Labs templating/taskfile system. The `start` task will +# ensure that the latest upstream changes are retrieved, that the project is +# properly generated with them, and that all the development dependencies are installed. + +set -eo pipefail + +# @description Ensure .config/log is executable +if [ -f .config/log ]; then + chmod +x .config/log +fi + +# @description Installs package when user is root on Linux +# +# @example +# ensureRootPackageInstalled "sudo" +# +# @arg $1 string The name of the package that must be present +# +# @exitcode 0 The package was successfully installed +# @exitcode 1+ If there was an error, the package needs to be installed manually, or if the OS is unsupported +function ensureRootPackageInstalled() { + if ! type "$1" &> /dev/null; then + if [[ "$OSTYPE" == 'linux'* ]]; then + if [ -f "/etc/redhat-release" ]; then + yum update + yum install -y "$1" + elif [ -f "/etc/lsb-release" ]; then + apt update + apt install -y "$1" + elif [ -f "/etc/arch-release" ]; then + pacman update + pacman -S "$1" + elif [ -f "/etc/alpine-release" ]; then + apk update + apk add -y "$1" + fi + fi + fi +} + +# @description If the user is running this script as root, then create a new user named +# megabyte and restart the script with that user. This is required because Homebrew +# can only be invoked by non-root users. +if [ "$EUID" -eq 0 ] && [ -z "$INIT_CWD" ] && type useradd &> /dev/null; then + # shellcheck disable=SC2016 + echo 'INFO: Running as root - creating seperate user named `megabyte` to run script with' + echo "megabyte ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + useradd -m -s "$(which bash)" -c "Megabyte Labs Homebrew Account" megabyte + ensureRootPackageInstalled "sudo" + # shellcheck disable=SC2016 + echo 'INFO: Reloading the script with the `megabyte` user' + exec su megabyte "$0" -- "$@" +fi + +# @description Detect script paths +BASH_SRC="$(dirname "${BASH_SOURCE[0]}")" +SOURCE_PATH="$( + cd "$BASH_SRC" + pwd -P +)" +PROJECT_BASE_DIR="$SOURCE_PATH/../.." + +# @description Ensures ~/.local/bin is in the PATH variable on *nix machines and +# exits with an error on unsupported OS types +# +# @example +# ensureLocalPath +# +# @set PATH string The updated PATH with a reference to ~/.local/bin +# +# @noarg +# +# @exitcode 0 If the PATH was appropriately updated or did not need updating +# @exitcode 1+ If the OS is unsupported +function ensureLocalPath() { + if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux'* ]]; then + # shellcheck disable=SC2016 + PATH_STRING='PATH="$HOME/.local/bin:$PATH"' + mkdir -p "$HOME/.local/bin" + if grep -L "$PATH_STRING" "$HOME/.profile" > /dev/null; then + echo -e "${PATH_STRING}\n" >> "$HOME/.profile" + echo "INFO: Updated the PATH variable to include ~/.local/bin in $HOME/.profile" + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "FreeBSD support not added yet" && exit 1 + else + echo "System type not recognized" + fi +} + +# @description Ensures given package is installed on a system. +# +# @example +# ensurePackageInstalled "curl" +# +# @arg $1 string The name of the package that must be present +# +# @exitcode 0 The package(s) were successfully installed +# @exitcode 1+ If there was an error, the package needs to be installed manually, or if the OS is unsupported +function ensurePackageInstalled() { + if ! type "$1" &> /dev/null; then + if [[ "$OSTYPE" == 'darwin'* ]]; then + brew install "$1" + elif [[ "$OSTYPE" == 'linux'* ]]; then + if [ -f "/etc/redhat-release" ]; then + sudo yum update + sudo yum install -y "$1" + elif [ -f "/etc/lsb-release" ]; then + sudo apt update + sudo apt install -y "$1" + elif [ -f "/etc/arch-release" ]; then + sudo pacman update + sudo pacman -S "$1" + elif [ -f "/etc/alpine-release" ]; then + apk update + apk add -y "$1" + else + echo "ERROR: $1 is missing. Please install $1 to continue." && exit 1 + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "ERROR: System type not recognized" + fi + fi +} + +# @description Ensures the latest version of Task is installed to `/usr/local/bin` (or `~/.local/bin`, as +# a fallback. +# +# @example +# ensureTaskInstalled +# +# @noarg +# +# @exitcode 0 If the package is already present and up-to-date or if it was installed/updated +# @exitcode 1+ If the OS is unsupported or if there was an error either installing the package or setting the PATH +function ensureTaskInstalled() { + # @description Release API URL used to get the latest release's version + TASK_RELEASE_API="https://api.github.com/repos/go-task/task/releases/latest" + if ! type task &> /dev/null; then + if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl' ]]; then + installTask + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "ERROR: System type not recognized. You must install task manually." && exit 1 + fi + else + echo "INFO: Checking for latest version of Task" + CURRENT_VERSION="$(task --version | cut -d' ' -f3 | cut -c 2-)" + LATEST_VERSION="$(curl -s "$TASK_RELEASE_API" | grep tag_name | cut -c 17- | sed 's/\",//')" + if printf '%s\n%s\n' "$LATEST_VERSION" "$CURRENT_VERSION" | sort -V -c &> /dev/null; then + echo "INFO: Task is already up-to-date" + else + echo "INFO: A new version of Task is available (version $LATEST_VERSION)" + echo "INFO: The current version of Task installed is $CURRENT_VERSION" + # Replace with rm "$(which task)" &> /dev/null when ready + if ! type task &> /dev/null; then + installTask + else + echo "WARNING: Unable to remove previous version of Task" + fi + fi + fi +} + +# @description Helper function for ensureTaskInstalled that performs the installation of Task. +# +# @see ensureTaskInstalled +# +# @example +# installTask +# +# @noarg +# +# @exitcode 0 If Task installs/updates properly +# @exitcode 1+ If the installation fails +function installTask() { + # @description Release URL to use when downloading [Task](https://github.com/go-task/task) + TASK_RELEASE_URL="https://github.com/go-task/task/releases/latest" + CHECKSUM_DESTINATION=/tmp/megabytelabs/task_checksums.txt + CHECKSUMS_URL="$TASK_RELEASE_URL/download/task_checksums.txt" + DOWNLOAD_DESTINATION=/tmp/megabytelabs/task.tar.gz + TMP_DIR=/tmp/megabytelabs + if [[ "$OSTYPE" == 'darwin'* ]]; then + DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_darwin_amd64.tar.gz" + else + DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_linux_amd64.tar.gz" + fi + mkdir -p "$(dirname "$DOWNLOAD_DESTINATION")" + echo "INFO: Downloading latest version of Task" + curl -sSL "$DOWNLOAD_URL" -o "$DOWNLOAD_DESTINATION" + curl -sSL "$CHECKSUMS_URL" -o "$CHECKSUM_DESTINATION" + DOWNLOAD_BASENAME="$(basename "$DOWNLOAD_URL")" + DOWNLOAD_SHA256="$(grep "$DOWNLOAD_BASENAME" < "$CHECKSUM_DESTINATION" | cut -d ' ' -f 1)" + sha256 "$DOWNLOAD_DESTINATION" "$DOWNLOAD_SHA256" > /dev/null + echo "SUCCESS: Validated checksum" + mkdir -p "$TMP_DIR/task" + tar -xzvf "$DOWNLOAD_DESTINATION" -C "$TMP_DIR/task" > /dev/null + if type task &> /dev/null && [ -w "$(which task)" ]; then + TARGET_DEST="$(which task)" + else + if [ -w /usr/local/bin ]; then + TARGET_BIN_DIR='/usr/local/bin' + else + TARGET_BIN_DIR="$HOME/.local/bin" + fi + TARGET_DEST="$TARGET_BIN_DIR/task" + mkdir -p "$TARGET_BIN_DIR" + fi + mv "$TMP_DIR/task/task" "$TARGET_DEST" + echo "SUCCESS: Installed Task to $TARGET_DEST" + rm "$CHECKSUM_DESTINATION" + rm "$DOWNLOAD_DESTINATION" +} + +# @description Verifies the SHA256 checksum of a file +# +# @example +# sha256 myfile.tar.gz 5b30f9c042553141791ec753d2f96786c60a4968fd809f75bb0e8db6c6b4529b +# +# @arg $1 string Path to the file +# @arg $2 string The SHA256 signature +# +# @exitcode 0 The checksum is valid or the system is unrecognized +# @exitcode 1+ The OS is unsupported or if the checksum is invalid +function sha256() { + echo "$2" + echo "$1" + if [[ "$OSTYPE" == 'darwin'* ]]; then + if type brew &> /dev/null && ! type sha256sum &> /dev/null; then + brew install coreutils + else + echo "WARNING: Brew is not installed - this may cause issues" + fi + if type brew &> /dev/null; then + PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH" + fi + if type sha256sum &> /dev/null; then + echo "$2 $1" | sha256sum -c + else + echo "WARNING: Checksum validation is being skipped for $1 because the sha256sum program is not available" + fi + elif [[ "$OSTYPE" == 'linux-gnu'* ]]; then + if ! type shasum &> /dev/null; then + echo "WARNING: Checksum validation is being skipped for $1 because the shasum program is not installed" + else + echo "$2 $1" | shasum -s -a 256 -c + fi + elif [[ "$OSTYPE" == 'linux-musl' ]]; then + if ! type sha256sum &> /dev/null; then + echo "WARNING: Checksum validation is being skipped for $1 because the sha256sum program is not available" + else + echo "$2 $1" | sha256sum -c + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "WARNING: System type not recognized. Skipping checksum validation." + fi +} + +##### Main Logic ##### + +if [ ! -f "$HOME/.profile" ]; then + touch "$HOME/.profile" +fi + +# @description Ensures ~/.local/bin is in PATH +ensureLocalPath + +# @description Ensures base dependencies are installed +if [[ "$OSTYPE" == 'darwin'* ]]; then + if ! type curl &> /dev/null && type brew &> /dev/null; then + brew install curl + else + echo "ERROR: Neither curl nor brew are installed. Install one of them manually and try again." + fi + if ! type git &> /dev/null; then + # shellcheck disable=SC2016 + echo 'INFO: Git is not present. A password may be required to run `sudo xcode-select --install`' + sudo xcode-select --install + fi +elif [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then + if ! type curl &> /dev/null || ! type git &> /dev/null; then + ensurePackageInstalled "curl" + ensurePackageInstalled "git" + fi +fi + +# @description Ensures Homebrew and Poetry are installed +if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then + if ! type brew &> /dev/null && [ -z "$INIT_CWD" ]; then + echo "WARNING: Homebrew is not installed. The script will attempt to install Homebrew and you might be prompted for your password." + bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + fi +fi + +# @description Attempts to pull the latest changes if the folder is a git repository +cd "$PROJECT_BASE_DIR" || exit +if [ -d .git ] && type git &> /dev/null; then + HTTPS_VERSION="$(git remote get-url origin | sed 's/git@gitlab.com:/https:\/\/gitlab.com\//')" + git pull "$HTTPS_VERSION" master --ff-only + git submodule update --init --recursive +fi + +# @description Ensures Task is installed and properly configured +ensureTaskInstalled + +# @description Run the start logic, if appropriate +cd "$PROJECT_BASE_DIR" || exit +if [ -z "$GITLAB_CI" ] && [ -z "$INIT_CWD" ]; then + # shellcheck disable=SC1091 + . "$HOME/.profile" + task start + # shellcheck disable=SC2028 + echo 'INFO: There may have been changes to your PATH variable. You may have to reload your terminal or run:\n\n`. '"$HOME/.profile"'`' +fi diff --git a/update-init.sh b/update-init.sh new file mode 100644 index 00000000..c380a48b --- /dev/null +++ b/update-init.sh @@ -0,0 +1,334 @@ +#!/usr/bin/env bash + +# @file .config/scripts/start.sh +# @brief Ensures Task is installed and up-to-date and then runs `task start` +# @description +# This script will ensure [Task](https://github.com/go-task/task) is up-to-date +# and then run the `start` task which is generally a good entrypoint for any repository +# that is using the Megabyte Labs templating/taskfile system. The `start` task will +# ensure that the latest upstream changes are retrieved, that the project is +# properly generated with them, and that all the development dependencies are installed. + +set -eo pipefail + +# @description Ensure .config/log is executable +if [ -f .config/log ]; then + chmod +x .config/log +fi + +# @description Installs package when user is root on Linux +# +# @example +# ensureRootPackageInstalled "sudo" +# +# @arg $1 string The name of the package that must be present +# +# @exitcode 0 The package was successfully installed +# @exitcode 1+ If there was an error, the package needs to be installed manually, or if the OS is unsupported +function ensureRootPackageInstalled() { + if ! type "$1" &> /dev/null; then + if [[ "$OSTYPE" == 'linux'* ]]; then + if [ -f "/etc/redhat-release" ]; then + yum update + yum install -y "$1" + elif [ -f "/etc/lsb-release" ]; then + apt update + apt install -y "$1" + elif [ -f "/etc/arch-release" ]; then + pacman update + pacman -S "$1" + elif [ -f "/etc/alpine-release" ]; then + apk update + apk add -y "$1" + fi + fi + fi +} + +# @description If the user is running this script as root, then create a new user named +# megabyte and restart the script with that user. This is required because Homebrew +# can only be invoked by non-root users. +if [ "$EUID" -eq 0 ] && [ -z "$INIT_CWD" ] && type useradd &> /dev/null; then + # shellcheck disable=SC2016 + echo 'INFO: Running as root - creating seperate user named `megabyte` to run script with' + echo "megabyte ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers + useradd -m -s "$(which bash)" -c "Megabyte Labs Homebrew Account" megabyte + ensureRootPackageInstalled "sudo" + # shellcheck disable=SC2016 + echo 'INFO: Reloading the script with the `megabyte` user' + exec su megabyte "$0" -- "$@" +fi + +# @description Detect script paths +BASH_SRC="$(dirname "${BASH_SOURCE[0]}")" +SOURCE_PATH="$( + cd "$BASH_SRC" + pwd -P +)" +PROJECT_BASE_DIR="$SOURCE_PATH/../.." + +# @description Ensures ~/.local/bin is in the PATH variable on *nix machines and +# exits with an error on unsupported OS types +# +# @example +# ensureLocalPath +# +# @set PATH string The updated PATH with a reference to ~/.local/bin +# +# @noarg +# +# @exitcode 0 If the PATH was appropriately updated or did not need updating +# @exitcode 1+ If the OS is unsupported +function ensureLocalPath() { + if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux'* ]]; then + # shellcheck disable=SC2016 + PATH_STRING='PATH="$HOME/.local/bin:$PATH"' + mkdir -p "$HOME/.local/bin" + if grep -L "$PATH_STRING" "$HOME/.profile" > /dev/null; then + echo -e "${PATH_STRING}\n" >> "$HOME/.profile" + echo "INFO: Updated the PATH variable to include ~/.local/bin in $HOME/.profile" + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "FreeBSD support not added yet" && exit 1 + else + echo "System type not recognized" + fi +} + +# @description Ensures given package is installed on a system. +# +# @example +# ensurePackageInstalled "curl" +# +# @arg $1 string The name of the package that must be present +# +# @exitcode 0 The package(s) were successfully installed +# @exitcode 1+ If there was an error, the package needs to be installed manually, or if the OS is unsupported +function ensurePackageInstalled() { + if ! type "$1" &> /dev/null; then + if [[ "$OSTYPE" == 'darwin'* ]]; then + brew install "$1" + elif [[ "$OSTYPE" == 'linux'* ]]; then + if [ -f "/etc/redhat-release" ]; then + sudo yum update + sudo yum install -y "$1" + elif [ -f "/etc/lsb-release" ]; then + sudo apt update + sudo apt install -y "$1" + elif [ -f "/etc/arch-release" ]; then + sudo pacman update + sudo pacman -S "$1" + elif [ -f "/etc/alpine-release" ]; then + apk update + apk add -y "$1" + else + echo "ERROR: $1 is missing. Please install $1 to continue." && exit 1 + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "ERROR: System type not recognized" + fi + fi +} + +# @description Ensures the latest version of Task is installed to `/usr/local/bin` (or `~/.local/bin`, as +# a fallback. +# +# @example +# ensureTaskInstalled +# +# @noarg +# +# @exitcode 0 If the package is already present and up-to-date or if it was installed/updated +# @exitcode 1+ If the OS is unsupported or if there was an error either installing the package or setting the PATH +function ensureTaskInstalled() { + # @description Release API URL used to get the latest release's version + TASK_RELEASE_API="https://api.github.com/repos/go-task/task/releases/latest" + if ! type task &> /dev/null; then + if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl' ]]; then + installTask + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "ERROR: System type not recognized. You must install task manually." && exit 1 + fi + else + echo "INFO: Checking for latest version of Task" + CURRENT_VERSION="$(task --version | cut -d' ' -f3 | cut -c 2-)" + LATEST_VERSION="$(curl -s "$TASK_RELEASE_API" | grep tag_name | cut -c 17- | sed 's/\",//')" + if printf '%s\n%s\n' "$LATEST_VERSION" "$CURRENT_VERSION" | sort -V -c &> /dev/null; then + echo "INFO: Task is already up-to-date" + else + echo "INFO: A new version of Task is available (version $LATEST_VERSION)" + echo "INFO: The current version of Task installed is $CURRENT_VERSION" + # Replace with rm "$(which task)" &> /dev/null when ready + if ! type task &> /dev/null; then + installTask + else + echo "WARNING: Unable to remove previous version of Task" + fi + fi + fi +} + +# @description Helper function for ensureTaskInstalled that performs the installation of Task. +# +# @see ensureTaskInstalled +# +# @example +# installTask +# +# @noarg +# +# @exitcode 0 If Task installs/updates properly +# @exitcode 1+ If the installation fails +function installTask() { + # @description Release URL to use when downloading [Task](https://github.com/go-task/task) + TASK_RELEASE_URL="https://github.com/go-task/task/releases/latest" + CHECKSUM_DESTINATION=/tmp/megabytelabs/task_checksums.txt + CHECKSUMS_URL="$TASK_RELEASE_URL/download/task_checksums.txt" + DOWNLOAD_DESTINATION=/tmp/megabytelabs/task.tar.gz + TMP_DIR=/tmp/megabytelabs + if [[ "$OSTYPE" == 'darwin'* ]]; then + DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_darwin_amd64.tar.gz" + else + DOWNLOAD_URL="$TASK_RELEASE_URL/download/task_linux_amd64.tar.gz" + fi + mkdir -p "$(dirname "$DOWNLOAD_DESTINATION")" + echo "INFO: Downloading latest version of Task" + curl -sSL "$DOWNLOAD_URL" -o "$DOWNLOAD_DESTINATION" + curl -sSL "$CHECKSUMS_URL" -o "$CHECKSUM_DESTINATION" + DOWNLOAD_BASENAME="$(basename "$DOWNLOAD_URL")" + DOWNLOAD_SHA256="$(grep "$DOWNLOAD_BASENAME" < "$CHECKSUM_DESTINATION" | cut -d ' ' -f 1)" + sha256 "$DOWNLOAD_DESTINATION" "$DOWNLOAD_SHA256" > /dev/null + echo "SUCCESS: Validated checksum" + mkdir -p "$TMP_DIR/task" + tar -xzvf "$DOWNLOAD_DESTINATION" -C "$TMP_DIR/task" > /dev/null + if type task &> /dev/null && [ -w "$(which task)" ]; then + TARGET_DEST="$(which task)" + else + if [ -w /usr/local/bin ]; then + TARGET_BIN_DIR='/usr/local/bin' + else + TARGET_BIN_DIR="$HOME/.local/bin" + fi + TARGET_DEST="$TARGET_BIN_DIR/task" + mkdir -p "$TARGET_BIN_DIR" + fi + mv "$TMP_DIR/task/task" "$TARGET_DEST" + echo "SUCCESS: Installed Task to $TARGET_DEST" + rm "$CHECKSUM_DESTINATION" + rm "$DOWNLOAD_DESTINATION" +} + +# @description Verifies the SHA256 checksum of a file +# +# @example +# sha256 myfile.tar.gz 5b30f9c042553141791ec753d2f96786c60a4968fd809f75bb0e8db6c6b4529b +# +# @arg $1 string Path to the file +# @arg $2 string The SHA256 signature +# +# @exitcode 0 The checksum is valid or the system is unrecognized +# @exitcode 1+ The OS is unsupported or if the checksum is invalid +function sha256() { + echo "$2" + echo "$1" + if [[ "$OSTYPE" == 'darwin'* ]]; then + if type brew &> /dev/null && ! type sha256sum &> /dev/null; then + brew install coreutils + else + echo "WARNING: Brew is not installed - this may cause issues" + fi + if type brew &> /dev/null; then + PATH="$(brew --prefix)/opt/coreutils/libexec/gnubin:$PATH" + fi + if type sha256sum &> /dev/null; then + echo "$2 $1" | sha256sum -c + else + echo "WARNING: Checksum validation is being skipped for $1 because the sha256sum program is not available" + fi + elif [[ "$OSTYPE" == 'linux-gnu'* ]]; then + if ! type shasum &> /dev/null; then + echo "WARNING: Checksum validation is being skipped for $1 because the shasum program is not installed" + else + echo "$2 $1" | shasum -s -a 256 -c + fi + elif [[ "$OSTYPE" == 'linux-musl' ]]; then + if ! type sha256sum &> /dev/null; then + echo "WARNING: Checksum validation is being skipped for $1 because the sha256sum program is not available" + else + echo "$2 $1" | sha256sum -c + fi + elif [[ "$OSTYPE" == 'cygwin' ]] || [[ "$OSTYPE" == 'msys' ]] || [[ "$OSTYPE" == 'win32' ]]; then + echo "ERROR: Windows is not directly supported. Use WSL or Docker." && exit 1 + elif [[ "$OSTYPE" == 'freebsd'* ]]; then + echo "ERROR: FreeBSD support not added yet" && exit 1 + else + echo "WARNING: System type not recognized. Skipping checksum validation." + fi +} + +##### Main Logic ##### + +if [ ! -f "$HOME/.profile" ]; then + touch "$HOME/.profile" +fi + +# @description Ensures ~/.local/bin is in PATH +ensureLocalPath + +# @description Ensures base dependencies are installed +if [[ "$OSTYPE" == 'darwin'* ]]; then + if ! type curl &> /dev/null && type brew &> /dev/null; then + brew install curl + else + echo "ERROR: Neither curl nor brew are installed. Install one of them manually and try again." + fi + if ! type git &> /dev/null; then + # shellcheck disable=SC2016 + echo 'INFO: Git is not present. A password may be required to run `sudo xcode-select --install`' + sudo xcode-select --install + fi +elif [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then + if ! type curl &> /dev/null || ! type git &> /dev/null; then + ensurePackageInstalled "curl" + ensurePackageInstalled "git" + fi +fi + +# @description Ensures Homebrew and Poetry are installed +if [[ "$OSTYPE" == 'darwin'* ]] || [[ "$OSTYPE" == 'linux-gnu'* ]] || [[ "$OSTYPE" == 'linux-musl'* ]]; then + if ! type brew &> /dev/null && [ -z "$INIT_CWD" ]; then + echo "WARNING: Homebrew is not installed. The script will attempt to install Homebrew and you might be prompted for your password." + bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + fi +fi + +# @description Attempts to pull the latest changes if the folder is a git repository +cd "$PROJECT_BASE_DIR" || exit +if [ -d .git ] && type git &> /dev/null; then + HTTPS_VERSION="$(git remote get-url origin | sed 's/git@gitlab.com:/https:\/\/gitlab.com\//')" + git pull "$HTTPS_VERSION" master --ff-only + git submodule update --init --recursive +fi + +# @description Ensures Task is installed and properly configured +ensureTaskInstalled + +# @description Run the start logic, if appropriate +cd "$PROJECT_BASE_DIR" || exit +if [ -z "$GITLAB_CI" ] && [ -z "$INIT_CWD" ]; then + # shellcheck disable=SC1091 + . "$HOME/.profile" + task start + # shellcheck disable=SC2028 + echo 'INFO: There may have been changes to your PATH variable. You may have to reload your terminal or run:\n\n`. '"$HOME/.profile"'`' +fi