diff --git a/.cicd-tools/bin/manifest.sh b/.cicd-tools/bin/manifest.sh new file mode 100755 index 00000000..140069de --- /dev/null +++ b/.cicd-tools/bin/manifest.sh @@ -0,0 +1,109 @@ +#!/bin/bash + +# Manifest file reader. +# Requires the jq binary: https://stedolan.github.io/jq/download/ + +# CICD-Tools script. + +set -eo pipefail + +# shellcheck source=./.cicd-tools/boxes/bootstrap/libraries/logging.sh +source "$(dirname -- "${BASH_SOURCE[0]}")/../boxes/bootstrap/libraries/logging.sh" + +manifest() { + local MANIFEST_FILE + _manifest_args "$@" +} + +_manifest_args() { + while getopts "m:" OPTION; do + case "$OPTION" in + m) + MANIFEST_FILE="${OPTARG}" + ;; + \?) + _manifest_usage + ;; + :) + _manifest_usage + ;; + *) + _manifest_usage + ;; + esac + done + shift $((OPTIND - 1)) + if [[ -z "${MANIFEST_FILE}" ]]; then + _manifest_usage + fi + _manifest_commands "$@" +} + +_manifest_commands() { + case "$1" in + security) + [[ -n "${2}" ]] && _manifest_usage + log "DEBUG" "MANIFEST > Reading security status from manifest." + _manifest_security + ;; + toolbox_url) + [[ -z "${2}" ]] && _manifest_usage + log "DEBUG" "MANIFEST > Reading toolbox url for '${2}' from manifest." + _manifest_toolbox_url "${2}" + ;; + toolbox_sha) + [[ -z "${2}" ]] && _manifest_usage + log "DEBUG" "MANIFEST > Reading toolbox checksum for '${2}' from manifest." + _manifest_toolbox_sha "${2}" + ;; + *) + _manifest_usage + ;; + esac +} + +_manifest_usage() { + log "ERROR" "manifest.sh -- interact with the CICD-Tools manifest file." + log "ERROR" "USAGE: manifest.sh -p [PATH TO MANIFEST] [COMMAND]" + log "ERROR" " COMMANDS:" + log "ERROR" " toolbox_url [VERSION] - Retrieves the URL of the given toolbox version." + log "ERROR" " toolbox_sha [FILENAME] - Retrieves the checksum of the given file." + log "ERROR" " security - Indicates if hash validation is enabled or disabled." + exit 127 +} + +_manifest_security() { + jq -rM ".disable_security" "${MANIFEST_FILE}" +} + +_manifest_toolbox_prefix() { + local REMOTE_SHA + local REMOTE_SOURCE + local REMOTE_PATH + REMOTE_SHA="$(jq -erM '.version' "${MANIFEST_FILE}")" + REMOTE_SOURCE="$(jq -erM '.source' "${MANIFEST_FILE}")" + REMOTE_PATH="$(jq -erM '.toolbox_path' "${MANIFEST_FILE}")" + echo "${REMOTE_SOURCE}/${REMOTE_SHA}/${REMOTE_PATH}" +} + +_manifest_toolbox_is_present() { + jq --arg version "${1}.tar.gz" -erM '.manifest[$version]' "${MANIFEST_FILE}" +} + +_manifest_toolbox_url() { + if ! _manifest_toolbox_is_present "${1}" > /dev/null; then + log "ERROR" "MANIFEST > Toolbox version '${1}' is not in the manifest." + exit 127 + fi + echo "$(_manifest_toolbox_prefix)/${1}.tar.gz" +} + +_manifest_toolbox_sha() { + if ! _manifest_toolbox_is_present "${1}" > /dev/null; then + log "ERROR" "MANIFEST > Toolbox version '${1}' is not in the manifest." + exit 127 + fi + jq --arg version "${1}.tar.gz" -erM '.manifest[$version]' "${MANIFEST_FILE}" +} + +manifest "$@" diff --git a/.cicd-tools/bin/toolbox.sh b/.cicd-tools/bin/toolbox.sh new file mode 100755 index 00000000..99744df9 --- /dev/null +++ b/.cicd-tools/bin/toolbox.sh @@ -0,0 +1,163 @@ +#!/bin/bash + +# Remote toolbox downloader. +# Requires gpg binary: https://gnupg.org/ + +# CICD-Tools script. + +set -eo pipefail + +TOOLBOX_PATH="$(pwd)/.cicd-tools" +TOOLBOX_REMOTES_FOLDER="boxes" +TOOLBOX_MANIFEST_FILE="${TOOLBOX_PATH}/manifest.json" + +# shellcheck source=./.cicd-tools/boxes/bootstrap/libraries/logging.sh +source "$(dirname -- "${BASH_SOURCE[0]}")/../boxes/bootstrap/libraries/logging.sh" + +# shellcheck source=./.cicd-tools/boxes/bootstrap/libraries/environment.sh +source "$(dirname -- "${BASH_SOURCE[0]}")/../boxes/bootstrap/libraries/environment.sh" \ + -o "DOWNLOAD_RETRIES DOWNLOAD_MAX_TIME" \ + -d "3 30" + +main() { + OPTIND=1 + + local MANIFEST_ASC + local MANIFEST_DISABLE_SECURITY="false" + local TARGET_TOOLBOX_VERSION + local TARGET_TOOLBOX_URL + local TEMP_DIRECTORY + + TEMP_DIRECTORY="$(mktemp -d)" + + _toolbox_args "$@" + _toolbox_manifest_download + _toolbox_manifest_load + _toolbox_box_download + _toolbox_box_checksum + _toolbox_box_install +} + +_toolbox_args() { + while getopts "b:m:r:t:" OPTION; do + case "$OPTION" in + b) + TARGET_TOOLBOX_VERSION="${OPTARG}" + TARGET_TOOLBOX_FILENAME="${TARGET_TOOLBOX_VERSION}.tar.gz" + ;; + m) + MANIFEST_ASC="${OPTARG}" + ;; + r) + DOWNLOAD_RETRIES="${OPTARG}" + ;; + t) + DOWNLOAD_MAX_TIME="${OPTARG}" + ;; + \?) + _toolbox_usage + ;; + :) + _toolbox_usage + ;; + *) + _toolbox_usage + ;; + esac + done + shift $((OPTIND - 1)) + + if [[ -z "${TARGET_TOOLBOX_VERSION}" ]] || + [[ -z "${MANIFEST_ASC}" ]]; then + _toolbox_usage + fi +} + +_toolbox_box_checksum() { + pushd "${TEMP_DIRECTORY}" >> /dev/null + if [[ "${MANIFEST_DISABLE_SECURITY}" == "false" ]]; then + if ! echo "${TARGET_TOOLBOX_SHA} ${TARGET_TOOLBOX_FILENAME}" | sha256sum -c; then + log "ERROR" "CHECKSUM > Hash of remote file does not match!" + log "ERROR" "CHECKSUM > Cannot proceed." + exit 127 + else + log "INFO" "CHECKSUM > Hash verification has passed." + fi + else + log "WARNING" "CHECKSUM > The manifest has DISABLED all checksum validation." + fi + cp "${TARGET_TOOLBOX_FILENAME}" "${TOOLBOX_PATH}/${TOOLBOX_REMOTES_FOLDER}" + popd >> /dev/null +} + +_toolbox_box_download() { + if [[ -f "${TOOLBOX_PATH}/${TOOLBOX_REMOTES_FOLDER}/${TARGET_TOOLBOX_FILENAME}" ]]; then + mv "${TOOLBOX_PATH}/${TOOLBOX_REMOTES_FOLDER}/${TARGET_TOOLBOX_FILENAME}" "${TEMP_DIRECTORY}" + log "INFO" "BOX > Toolbox Version ${TARGET_TOOLBOX_VERSION} has already been downloaded." + else + _toolbox_box_fetch + fi +} + +_toolbox_box_fetch() { + log "DEBUG" "BOX > Target Toolbox Version: ${TARGET_TOOLBOX_VERSION}" + log "DEBUG" "BOX > Target Toolbox SHA: ${TARGET_TOOLBOX_SHA}" + log "DEBUG" "BOX > Target Toolbox URL: ${TARGET_TOOLBOX_URL}" + + mkdir -p "${TOOLBOX_PATH}/${TOOLBOX_REMOTES_FOLDER}" + + pushd "${TEMP_DIRECTORY}" >> /dev/null + _toolbox_fetch "${TARGET_TOOLBOX_URL}" > "${TARGET_TOOLBOX_FILENAME}" + popd >> /dev/null + + log "INFO" "BOX > Remote toolbox retrieved." +} + +_toolbox_box_install() { + pushd "${TOOLBOX_PATH}/${TOOLBOX_REMOTES_FOLDER}" >> /dev/null + tar xvzf "${TARGET_TOOLBOX_FILENAME}" + log "DEBUG" "BOX > Toolbox Version ${TARGET_TOOLBOX_VERSION} has been installed to ${TOOLBOX_PATH}/${TOOLBOX_REMOTES_FOLDER}." + ln -sf "${TARGET_TOOLBOX_VERSION}" active + log "INFO" "BOX > Toolbox Version ${TARGET_TOOLBOX_VERSION} has been activated." + popd >> /dev/null +} + +_toolbox_fetch() { + # 1: url + log "DEBUG" "FETCH > URL: ${1}" + log "DEBUG" "FETCH > Retries: ${DOWNLOAD_RETRIES}" + log "DEBUG" "FETCH > Max Time: ${DOWNLOAD_MAX_TIME}" + + set -x + curl --fail \ + --location \ + --silent \ + --show-error \ + --retry "${DOWNLOAD_RETRIES}" \ + --retry-max-time "${DOWNLOAD_MAX_TIME}" \ + "${1}" + { set +x; } 2> /dev/null + + log "DEBUG" "FETCH > Fetch complete." +} + +_toolbox_manifest_download() { + gpg --yes --output "${TOOLBOX_MANIFEST_FILE}" --verify <(_toolbox_fetch "${MANIFEST_ASC}") + log "INFO" "MANIFEST > Remote manifest retrieved." +} + +_toolbox_manifest_load() { + TARGET_TOOLBOX_SHA="$(./.cicd-tools/bin/manifest.sh -m "${TOOLBOX_MANIFEST_FILE}" toolbox_sha "${TARGET_TOOLBOX_VERSION}")" + MANIFEST_DISABLE_SECURITY="$(./.cicd-tools/bin/manifest.sh -m "${TOOLBOX_MANIFEST_FILE}" security)" + TARGET_TOOLBOX_URL="$(./.cicd-tools/bin/manifest.sh -m "${TOOLBOX_MANIFEST_FILE}" toolbox_url "${TARGET_TOOLBOX_VERSION}")" + log "INFO" "MANIFEST > Remote manifest loaded." +} + +_toolbox_usage() { + log "ERROR" "toolbox.sh -- download a remote toolbox from the CICD-Tools manifest." + log "ERROR" "USAGE: toolbox.sh -b [TOOLBOX VERSION] -m [REMOTE MANIFEST URL]" + log "ERROR" " Optional: -r [OPTIONAL RETRY COUNT] -m [OPTIONAL MAX RETRY TIME]" + exit 127 +} + +main "$@" diff --git a/.cicd-tools/bin/verify.sh b/.cicd-tools/bin/verify.sh new file mode 100755 index 00000000..7441fff6 --- /dev/null +++ b/.cicd-tools/bin/verify.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Remote gpg key verification. +# Requires gpg binary: https://gnupg.org/ + +# CICD-Tools script. + +set -eo pipefail + +# shellcheck source=./.cicd-tools/boxes/bootstrap/libraries/logging.sh +source "$(dirname -- "${BASH_SOURCE[0]}")/../boxes/bootstrap/libraries/logging.sh" + +main() { + local CICD_TOOLS_GPG_KEY + + _verify_args "$@" + _verify_check_key + _verify_trust_key +} + +_verify_args() { + while getopts "k:" OPTION; do + case "$OPTION" in + k) + CICD_TOOLS_GPG_KEY="${OPTARG}" + ;; + \?) + _toolbox_usage + ;; + :) + _toolbox_usage + ;; + *) + _toolbox_usage + ;; + esac + done + shift $((OPTIND - 1)) + + if [[ -z "${CICD_TOOLS_GPG_KEY}" ]]; then + _verify_usage + fi +} + +_verify_check_key() { + gpg \ + --verify "$(dirname -- "${BASH_SOURCE[0]}")/../pgp/verification.sign" \ + "$(dirname -- "${BASH_SOURCE[0]}")/../pgp/verification.txt" +} + +_verify_trust_key() { + echo "${CICD_TOOLS_GPG_KEY}:6:" | gpg --import-ownertrust +} + +_verify_usage() { + log "ERROR" "verify.sh -- verify the CICD-Tools gpg key." + log "ERROR" "USAGE: verify.sh -k [GPG KEY ID]" + exit 127 +} + +main "$@" diff --git a/.cicd-tools/boxes/bootstrap/changlog/config.json b/.cicd-tools/boxes/bootstrap/changlog/config.json new file mode 100644 index 00000000..60304346 --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/changlog/config.json @@ -0,0 +1,21 @@ +{ + "options": { + "preset": { + "name": "conventionalcommits", + "types": [ + { "type": "feat", "section": "Features" }, + { "type": "feature", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance Improvements" }, + { "type": "revert", "section": "Reverts" }, + { "type": "docs", "section": "Documentation"}, + { "type": "style", "section": "Styles"}, + { "type": "chore", "section": "Miscellaneous Chores"}, + { "type": "refactor", "section": "Code Refactoring"}, + { "type": "test", "section": "Tests"}, + { "type": "build", "section": "Build System"}, + { "type": "ci", "section": "Continuous Integration"} + ] + } + } +} diff --git a/.cicd-tools/boxes/bootstrap/commitizen/pre_bump.sh b/.cicd-tools/boxes/bootstrap/commitizen/pre_bump.sh new file mode 100755 index 00000000..e521bf52 --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/commitizen/pre_bump.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# Commitizen 'pre_bump_hook' script to make TOML quotes compatible with tomll. + +# Commitizen pre_bump_hook script only. + +set -eo pipefail + +main() { + # sed compatible with Linux and BSD + sed -i.bak "s,\"${CZ_PRE_NEW_VERSION}\",'${CZ_PRE_NEW_VERSION}',g" pyproject.toml + rm pyproject.toml.bak +} + +main diff --git a/.cicd-tools/boxes/bootstrap/libraries/colours.sh b/.cicd-tools/boxes/bootstrap/libraries/colours.sh new file mode 100644 index 00000000..984d4d2c --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/libraries/colours.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Library for reading the CICD-Tools manifest. + +# CICD_TOOLS_COLOUR_DISABLE: Optionally disable coloured messages. + +set -eo pipefail + +# shellcheck disable=SC2034 +colour() { + local PREFIX + local COMMAND + + local BLACK=0 + local RED=1 + local GREEN=2 + local YELLOW=3 + local BLUE=4 + local PURPLE=5 + local CYAN=6 + local WHITE=7 + + if [[ -z "${CICD_TOOLS_COLOUR_DISABLE}" ]]; then + PREFIX="_colour" + COMMAND="${PREFIX}_${1}" + if [[ $(type -t "${COMMAND}") == function ]]; then + shift + "${COMMAND}" "$@" + else + "${PREFIX}_usage" + fi + fi +} + +_colour_bg() { + tput setab "${!1}" 2> /dev/null +} + +_colour_bold() { + tput setab bold 2> /dev/null +} + +_colour_fg() { + tput setaf "${!1}" 2> /dev/null +} + +_colour_clear() { + tput sgr0 2> /dev/null +} + +_colour_usage() { + { + echo "colour.sh -- set the desired terminal colour." + echo "USAGE: colour.sh [COMMAND]" + echo " COMMANDS:" + echo " bg [BLACK|RED|GREEN|YELLOW|BLUE|PURPLE|CYAN|WHITE] -- set background colour" + echo " bold -- set bold text" + echo " fg [BLACK|RED|GREEN|YELLOW|BLUE|PURPLE|CYAN|WHITE] -- set foreground colour" + echo " clear -- set default terminal colours" + } >> /dev/stderr +} diff --git a/.cicd-tools/boxes/bootstrap/libraries/environment.sh b/.cicd-tools/boxes/bootstrap/libraries/environment.sh new file mode 100644 index 00000000..9fe520d1 --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/libraries/environment.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# Library for enforcing optional and mandatory environment variables. + +set -eo pipefail + +# shellcheck source=/dev/null +source "$(dirname -- "${BASH_SOURCE[0]}")/logging.sh" + +environment() { + local MANDATORY=() + local OPTIONAL=() + local DEFAULTS=() + + log "DEBUG" "${BASH_SOURCE[0]} '$*'" + + _environment_args "$@" + _environment_defaults +} + +_environment_args() { + while getopts "m:o:d:" OPTION; do + case "$OPTION" in + m) + _environment_parse_mandatory "${OPTARG}" + ;; + o) + _environment_parse_optional "${OPTARG}" + ;; + d) + _environment_parse_defaults "${OPTARG}" + ;; + \?) + _environment_usage + ;; + :) + _environment_usage + ;; + esac + done + + if [[ "${#OPTIONAL[@]}" -ne "${#DEFAULTS[@]}" ]]; then + log "ERROR" "ENVIRONMENT > You must specify the same number of DEFAULT values and OPTIONAL environment variables!" + exit 127 + fi +} + +_environment_defaults() { + log "DEBUG" "ENVIRONMENT > Setting DEFAULT environment variable values." + local INDEX=-1 + for VARIABLE in "${DEFAULTS[@]}"; do + ((INDEX++)) || true + if [[ -z "${!OPTIONAL[${INDEX}]}" ]]; then + export "${OPTIONAL[${INDEX}]}" + eval "${OPTIONAL[${INDEX}]}"="${DEFAULTS[${INDEX}]}" + log "INFO" "ENVIRONMENT > Default: '${DEFAULTS[${INDEX}]}' is being used for: '${OPTIONAL[${INDEX}]}'." + fi + done +} + +_environment_parse_mandatory() { + log "DEBUG" "ENVIRONMENT > Parsing MANDATORY environment variables." + # shellcheck disable=SC2034 + IFS=' ' read -r -a MANDATORY <<< "${1}" + for VARIABLE in "${MANDATORY[@]}"; do + if [[ -z ${!VARIABLE} ]]; then + log "ERROR" "ENVIRONMENT > The environment variable '${VARIABLE}' is required!" + exit 127 + fi + done +} + +_environment_parse_optional() { + log "DEBUG" "ENVIRONMENT > Parsing OPTIONAL environment variables." + # shellcheck disable=SC2034 + IFS=' ' read -r -a OPTIONAL <<< "${1}" +} + +_environment_parse_defaults() { + log "DEBUG" "ENVIRONMENT > Parsing DEFAULT environment variable values." + # shellcheck disable=SC2034 + IFS=' ' read -r -a DEFAULTS <<< "${1}" +} + +_environment_usage() { + log "ERROR" "environment.sh -- enforce environment variables." + log "ERROR" "USAGE: source environment.sh -m [MANDATORY] -o [OPTIONAL] -d [DEFAULTS]" + log "ERROR" " Multiple items should be specified as space separated quoted strings." + exit 127 +} + +environment "$@" diff --git a/.cicd-tools/boxes/bootstrap/libraries/logging.sh b/.cicd-tools/boxes/bootstrap/libraries/logging.sh new file mode 100644 index 00000000..7011f776 --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/libraries/logging.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Library for logging functions and commands. +# The LOGGING_LEVEL environment variable controls verbosity. + +set -eo pipefail + +# shellcheck source=/dev/null +source "$(dirname -- "${BASH_SOURCE[0]}")/colours.sh" + +LOGGING_LEVEL=${LOGGING_LEVEL-"DEBUG"} + +function log() { + # USAGE: + # colour bg [CRITICAL|ERROR|WARNING|INFO|DEBUG] [MESSAGE CONTENTS] + + local LOGGING_SEVERITY_LEVELS=("DEBUG" "INFO" "WARNING" "ERROR" "CRITICAL") + + # shellcheck disable=SC2034 + local CRITICAL="RED" + # shellcheck disable=SC2034 + local ERROR="RED" + # shellcheck disable=SC2034 + local WARNING="YELLOW" + # shellcheck disable=SC2034 + local INFO="GREEN" + # shellcheck disable=SC2034 + local DEBUG="CYAN" + # shellcheck disable=SC2034 + + local LOGGING_MESSAGE_LEVEL="${1}" + local MESSAGE_CONTENT="${2}" + + if [[ -z "${!LOGGING_LEVEL}" ]] || + [[ -z "${MESSAGE_CONTENT}" ]]; then + log "ERROR" "Invalid logging statement!" + return 127 + fi + + if [[ "$(_log_get_severity_level "${LOGGING_MESSAGE_LEVEL}")" -ge "$(_log_get_severity_level "${LOGGING_LEVEL}")" ]]; then + echo "[$(date -u)] [$(colour fg "${!LOGGING_MESSAGE_LEVEL}")${LOGGING_MESSAGE_LEVEL}$(colour clear)] ${MESSAGE_CONTENT}" >> /dev/stderr + fi + +} + +function _log_get_severity_level() { + #1: The severity type as a string. + local LOGGIN_SEVERITY_LEVEL + for LOGGIN_SEVERITY_LEVEL in "${!LOGGING_SEVERITY_LEVELS[@]}"; do + if [[ "${LOGGING_SEVERITY_LEVELS["${LOGGIN_SEVERITY_LEVEL}"]}" = "${1}" ]]; then + echo "${LOGGIN_SEVERITY_LEVEL}" + fi + done +} diff --git a/.cicd-tools/boxes/bootstrap/libraries/tools.sh b/.cicd-tools/boxes/bootstrap/libraries/tools.sh new file mode 100644 index 00000000..9b2065fa --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/libraries/tools.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Library for working with the CICD-Tools projects. + +set -eo pipefail + +# shellcheck source=/dev/null +source "$(dirname -- "${BASH_SOURCE[0]}")/logging.sh" + +cicd_tools() { + local PREFIX + local COMMAND + + PREFIX="_cicd_tools" + COMMAND="${PREFIX}_${1}" + if [[ $(type -t "${COMMAND}") == function ]]; then + shift + "${COMMAND}" "$@" + else + "${PREFIX}_usage" + fi +} + +_cicd_tools_is_template() { + [[ -f "cookiecutter.json" ]] +} + +_cicd_tools_config_value() { + # 1: The config file to parse. + # 2: The key of the value to extract. + + local CICD_TOOLS_CONFIG_FILE="${1}" + local CICD_TOOLS_KEY="${2}" + + log "DEBUG" "CONFIGURATION > extracting key: '${CICD_TOOLS_KEY}' from: '${CICD_TOOLS_CONFIG_FILE}'." + + REGEX="\"${2}\": \"([^\"]+)\"" + if [[ "$(cat "${CICD_TOOLS_CONFIG_FILE}")" =~ ${REGEX} ]]; then + log "DEBUG" "CONFIGURATION > found value: '${BASH_REMATCH[1]}'." + echo "${BASH_REMATCH[1]}" + else + log "ERROR" "CONFIGURATION > key: '${CICD_TOOLS_KEY}' not found in '${CICD_TOOLS_CONFIG_FILE}'." + return 127 + fi +} + +_cicd_tools_poetry() { + # @: A command and arguments to run in either directly, or through Poetry. + + if [[ "${POETRY_ACTIVE}" == "1" ]]; then + "$@" + else + poetry run "$@" + fi +} + +_cicd_tools_usage() { + log "ERROR" "tools.sh -- CICD-Tools project helpers." + log "ERROR" "USAGE: tools.sh [COMMAND]" + log "ERROR" " COMMANDS:" + log "ERROR" " is_template -- Evaluates whether the current context is a cookiecutter project." + log "ERROR" " config_value [JSON FILE PATH] [KEY] -- Reads the given JSON file, and returns the value of the given key." +} diff --git a/.cicd-tools/boxes/bootstrap/pre-commit/lint-ansible.sh b/.cicd-tools/boxes/bootstrap/pre-commit/lint-ansible.sh new file mode 100755 index 00000000..4bc651a6 --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/pre-commit/lint-ansible.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# Runs ansible-galaxy to install/update the dependencies if needed, and then runs ansible-lint on each active target folder's changes. + +# @: An array of folders to run ansible-lin on. + +# pre-commit script. + +set -eo pipefail + +# shellcheck source=/dev/null +source "$(dirname -- "${BASH_SOURCE[0]}")/../libraries/tools.sh" + +main() { + local TARGET_FOLDERS=${*-"."} + for TARGET in ${TARGET_FOLDERS}; do + log "INFO" "PRE-COMMIT > Moving to target folder: '${TARGET}' ..." + pushd "${TARGET}" >> /dev/null + log "DEBUG" "PRE-COMMIT > Executing 'ansible-lint' ..." + cicd_tools "poetry" ansible-lint + popd >> /dev/null + done +} + +main "$@" diff --git a/.cicd-tools/boxes/bootstrap/pre-commit/lint-github-workflow-header.sh b/.cicd-tools/boxes/bootstrap/pre-commit/lint-github-workflow-header.sh new file mode 100755 index 00000000..a7d9980c --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/pre-commit/lint-github-workflow-header.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Verifies the correct headers are present on GitHub workflow files. + +# @: An array of GitHub workflow files to lint. + +# pre-commit script. + +set -eo pipefail + +# shellcheck source=/dev/null +source "$(dirname -- "${BASH_SOURCE[0]}")/../libraries/logging.sh" + +main() { + for WORKFLOW_FILE_PATH in "$@"; do + + log "INFO" "Checking header for: '${WORKFLOW_FILE_PATH}' ... " + + WORKFLOW_BASENAME="$(basename "${WORKFLOW_FILE_PATH}")" + + log "DEBUG" "Basename: '${WORKFLOW_BASENAME}' ... " + + if [[ "${WORKFLOW_BASENAME}" == job-* ]]; then + log "DEBUG" "Checking Job Header ..." + HEADER_NAME="$(echo "${WORKFLOW_BASENAME}" | cut -d. -f1)" + else + log "DEBUG" "Checking Workflow Header ..." + HEADER_NAME=".+-github-$(echo "${WORKFLOW_BASENAME}" | cut -d. -f1)" + fi + + if ! grep -E "^name: ${HEADER_NAME}$" "${WORKFLOW_FILE_PATH}" >> /dev/null; then + log "ERROR" "Incorrect Header on '${WORKFLOW_FILE_PATH}'" + log "ERROR" "EXPECTED PATTERN: ^${HEADER_NAME}$" + exit 127 + fi + + done +} + +main "$@" diff --git a/.cicd-tools/boxes/bootstrap/pre-commit/spelling-commit-message.sh b/.cicd-tools/boxes/bootstrap/pre-commit/spelling-commit-message.sh new file mode 100755 index 00000000..4d5ede7d --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/pre-commit/spelling-commit-message.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Runs vale on the specified commit message file, with the Git content filtered out. + +# 1: The Docker image and tag to use. +# 2: The commit message file to lint. + +# pre-commit script. + +set -eo pipefail + +# shellcheck source=/dev/null +source "$(dirname -- "${BASH_SOURCE[0]}")/../libraries/logging.sh" + +main() { + local PRECOMMIT_GIT_COMMIT_MESSAGE_FILE + local PRECOMMIT_GIT_CONTENT_REGEX + local PRECOMMIT_VALE_DOCKER_IMAGE + + PRECOMMIT_GIT_COMMIT_MESSAGE_FILE="${2}" + PRECOMMIT_GIT_CONTENT_REGEX='/^#[[:blank:]]*.*$/d' + PRECOMMIT_VALE_DOCKER_IMAGE="${1}" + + log "DEBUG" "PRE_COMMIT > Docker Image: '${PRECOMMIT_VALE_DOCKER_IMAGE}'" + log "DEBUG" "PRE_COMMIT > Commit Message: '${PRECOMMIT_GIT_COMMIT_MESSAGE_FILE}'" + sed "${PRECOMMIT_GIT_CONTENT_REGEX}" "${PRECOMMIT_GIT_COMMIT_MESSAGE_FILE}" + log "DEBUG" "PRE_COMMIT > Running vale ..." + sed "${PRECOMMIT_GIT_CONTENT_REGEX}" "${PRECOMMIT_GIT_COMMIT_MESSAGE_FILE}" | + docker run -i --rm -v "$(pwd)":/mnt --workdir /mnt "${PRECOMMIT_VALE_DOCKER_IMAGE}" vale + log "INFO" "PRE-COMMIT > Commit message spelling has passed!" +} + +main "$@" diff --git a/.cicd-tools/boxes/bootstrap/schemas/cookiecutter.json b/.cicd-tools/boxes/bootstrap/schemas/cookiecutter.json new file mode 100644 index 00000000..663f2e6d --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/schemas/cookiecutter.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "0.1.0", + "description": "CICD-Tools Cookiecutter Required Fields Schema", + "additionalProperties": true, + "minProperties": 10, + "required": [ + "github_handle", + "project_slug", + "project_name", + "_BRANCH_NAME_BASE", + "_BRANCH_NAME_DEVELOPMENT", + "_DOCKER_DEFAULT_CONTAINER", + "_GITHUB_CI_DEFAULT_CONCURRENCY", + "_GITHUB_CI_DEFAULT_PYTHON_VERSIONS", + "_GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS" + ], + "type": "object", + "uniqueItems": true, + "properties": { + "github_handle": { + "description": "The author's GitHub handle, used to create repository paths.", + "type": "string" + }, + "project_name": { + "description": "The plaintext name of the new project that will be templated.", + "type": "string" + }, + "project_slug": { + "description": "The slugified name of the new project that will be templated, used for the repository name.", + "type": "string" + }, + "_BRANCH_NAME_BASE": { + "description": "The name of the base branch that will be used in the templated repository.", + "type": "string" + }, + "_BRANCH_NAME_DEVELOPMENT": { + "description": "The name of the development branch that will be used in the templated repository.", + "type": "string" + }, + "_DOCKER_DEFAULT_CONTAINER": { + "description": "The container that will be used for the shellcheck, shfmt and other core binaries.", + "type": "string" + }, + "_GITHUB_CI_DEFAULT_CONCURRENCY": { + "description": "The default concurrency value that will be used for GitHub workflows.", + "type": "number" + }, + "_GITHUB_CI_DEFAULT_PYTHON_VERSIONS": { + "description": "The list of Python versions that will be used in GitHub workflows.", + "type": "array", + "contains": { + "pattern": "^3\\.[0-9]$", + "type": "string" + }, + "minContains": 1 + }, + "_GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS": { + "description": "The default verbosity of GitHub Action notifications.", + "type": "boolean" + } + } +} diff --git a/.cicd-tools/boxes/bootstrap/schemas/manifest.json b/.cicd-tools/boxes/bootstrap/schemas/manifest.json new file mode 100644 index 00000000..27cd7f98 --- /dev/null +++ b/.cicd-tools/boxes/bootstrap/schemas/manifest.json @@ -0,0 +1,49 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "0.1.0", + "description": "CICD-Tools Manifest Schema", + "additionalProperties": false, + "minProperties": 5, + "type": "object", + "uniqueItems": true, + "properties": { + "disable_security": { + "description": "Use hash validation to ensure downloaded content is trusted (highly recommended).", + "type": "boolean" + }, + "manifest": { + "description": "Trusted bundles and files available on this SHA of CICD-Tools.", + "type": "object", + "uniqueItems": true, + "properties": {}, + "patternProperties": { + "^[A-Za-z0-9\\.]$": { + "$ref": "#/definitions/ENTRY" + } + } + }, + "source": { + "description": "The download URL for CICD-Tools repository.", + "$ref": "#/definitions/URL" + }, + "toolbox_path": { + "description": "The path in the CICD-Tools repository to bundles.", + "type": "string" + }, + "version": { + "description": "The SHA identifier (Branch, Tag or Commit ID) of the CICD-Tools repository to use.", + "pattern": "^[A-Za-z]+$", + "type": "string" + } + }, + "definitions": { + "URL": { + "format": "uri", + "pattern": "^https?:\/\/" + }, + "ENTRY": { + "description": "A downloadable file in the manifest, with it's SHA256 sum.", + "format": "string" + } + } +} diff --git a/.cicd-tools/configuration/actionlint.yaml b/.cicd-tools/configuration/actionlint.yaml new file mode 100644 index 00000000..1627bf13 --- /dev/null +++ b/.cicd-tools/configuration/actionlint.yaml @@ -0,0 +1,4 @@ +--- +self-hosted-runner: + # Labels of self-hosted runner in array of string + labels: [] diff --git a/.cicd-tools/configuration/changelog.json b/.cicd-tools/configuration/changelog.json new file mode 100644 index 00000000..1a62ab6e --- /dev/null +++ b/.cicd-tools/configuration/changelog.json @@ -0,0 +1,21 @@ +{ + "options": { + "preset": { + "name": "conventionalcommits", + "types": [ + { "type": "feat", "section": "Features" }, + { "type": "feature", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance Improvements" }, + { "type": "revert", "section": "Reverts" }, + { "type": "docs", "section": "Documentation"}, + { "type": "style", "section": "Styles"}, + { "type": "chore", "section": "Miscellaneous Chores"}, + { "type": "refactor", "section": "Code Refactoring"}, + { "type": "test", "section": "Tests"}, + { "type": "build", "section": "Build System"}, + { "type": "ci", "section": "Continuous Integration"} + ] + } + } +} diff --git a/.cicd-tools/pgp/verification.sign b/.cicd-tools/pgp/verification.sign new file mode 100644 index 00000000..44bc5381 --- /dev/null +++ b/.cicd-tools/pgp/verification.sign @@ -0,0 +1,7 @@ +-----BEGIN PGP SIGNATURE----- + +iHUEABYKAB0WIQTwenlkfpHlYaeGttDZAg9/7iDb8gUCZCkYCQAKCRDZAg9/7iDb +8nvIAQDTZImTu5eKtipUhlDA5TYXroyOhX0CZnwbEsS9ySNsmgEAqcXQZBeXW2FZ +VyKPtGiGe4bUvD+BJJPa6yjw35gS5wY= +=w1bO +-----END PGP SIGNATURE----- diff --git a/.cicd-tools/pgp/verification.txt b/.cicd-tools/pgp/verification.txt new file mode 100644 index 00000000..844c10f0 --- /dev/null +++ b/.cicd-tools/pgp/verification.txt @@ -0,0 +1 @@ +CICD-Tools Verified diff --git a/.github/actions/action-00-toolbox/action.yml b/.github/actions/action-00-toolbox/action.yml new file mode 100644 index 00000000..f5ed6465 --- /dev/null +++ b/.github/actions/action-00-toolbox/action.yml @@ -0,0 +1,18 @@ +--- +name: action-00-toolbox +description: "Fetches the CICD-Tools toolbox." +author: niall@niallbyrne.ca + +inputs: + PROJECT_ROOT_PATH: + default: "." + description: "Optional, allows you to specify a path to the project's root." + required: false + +runs: + using: "composite" + steps: + - name: Toolbox - Install CICD-Tools Toolbox + uses: cicd-tools-org/cicd-tools/.github/actions/action-00-toolbox@master + with: + PROJECT_ROOT_PATH: ${{ inputs.PROJECT_ROOT_PATH }} diff --git a/.github/scenarios/0.toml_linting-0.workflow_linting.json b/.github/scenarios/0.toml_linting-0.workflow_linting.json new file mode 100644 index 00000000..8bcc690c --- /dev/null +++ b/.github/scenarios/0.toml_linting-0.workflow_linting.json @@ -0,0 +1,29 @@ +{ + "project_name": "Flower Generator", + "project_slug": "{{ cookiecutter.project_name|slugify }}", + "optional_toml_linting": ["false", "true"], + "optional_workflow_linting": ["false", "true"], + "github_handle": "niall-byrne", + "galaxy_role_slug": "{{ cookiecutter.project_slug.replace('-', '_').replace('.', '_') }}", + "galaxy_namespace_slug": "{{ cookiecutter.github_handle|slugify|replace('-', '_')|replace('.', '_') }}", + "description": "Not the baking kind.", + "author": "Niall Byrne", + "company": "Shared Vision Solutions", + "email": "niall@niallbyrne.ca", + "_BRANCH_NAME_BASE": "master", + "_BRANCH_NAME_DEVELOPMENT": "dev", + "_DOCKER_DEFAULT_CONTAINER": "ghcr.io/cicd-tools-org/cicd-tools:master", + "_GITHUB_CI_ACTIONLINT_SCRIPT_URL": "https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash", + "_GITHUB_CI_DEFAULT_CONCURRENCY": 4, + "_GITHUB_CI_DEFAULT_PYTHON_VERSIONS": ["3.9"], + "_GITHUB_CI_DEFAULT_MOLECULE_TEST_PLATFORMS": ["ubuntu-latest"], + "_GITHUB_CI_DEFAULT_MOLECULE_EXCLUDED_SCENARIOS_REGEX": "^default$|^noci-.*$", + "_GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS": false, + "_*DO_NOT_MODIFY_THIS_FILE*": "This file is created to assist with upgrading to future versions of this template.", + "_copy_without_render": [ + ".cicd-tools/boxes/bootstrap", + ".github/actions", + ".github/workflows/job*", + "scripts" + ] +} diff --git a/.github/scenarios/0.toml_linting-1.workflow_linting.json b/.github/scenarios/0.toml_linting-1.workflow_linting.json new file mode 100644 index 00000000..174c0d63 --- /dev/null +++ b/.github/scenarios/0.toml_linting-1.workflow_linting.json @@ -0,0 +1,30 @@ +{ + "project_name": "Flower Generator", + "project_slug": "{{ cookiecutter.project_name|slugify }}", + "optional_toml_linting": ["false", "true"], + "optional_workflow_linting": ["true", "false"], + "github_handle": "niall-byrne", + "galaxy_role_slug": "{{ cookiecutter.project_slug.replace('-', '_').replace('.', '_') }}", + "galaxy_namespace_slug": "{{ cookiecutter.github_handle|slugify|replace('-', '_')|replace('.', '_') }}", + "description": "Not the baking kind.", + "author": "Niall Byrne", + "company": "Shared Vision Solutions", + "email": "niall@niallbyrne.ca", + "_BRANCH_NAME_BASE": "master", + "_BRANCH_NAME_DEVELOPMENT": "dev", + "_DOCKER_DEFAULT_CONTAINER": "ghcr.io/cicd-tools-org/cicd-tools:master", + "_GITHUB_CI_ACTIONLINT_SCRIPT_URL": "https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash", + "_GITHUB_CI_DEFAULT_CONCURRENCY": 4, + "_GITHUB_CI_DEFAULT_PYTHON_VERSIONS": ["3.9"], + "_GITHUB_CI_DEFAULT_MOLECULE_TEST_PLATFORMS": ["ubuntu-latest"], + "_GITHUB_CI_DEFAULT_MOLECULE_EXCLUDED_SCENARIOS_REGEX": "^default$|^noci-.*$", + "_GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS": false, + "_*DO_NOT_MODIFY_THIS_FILE*": "This file is created to assist with upgrading to future versions of this template.", + "_copy_without_render": [ + ".cicd-tools/boxes/bootstrap", + ".github/actions", + ".github/workflows/job*", + "scripts" + ] +} + diff --git a/.github/scenarios/1.toml_linting-0.workflow_linting.json b/.github/scenarios/1.toml_linting-0.workflow_linting.json new file mode 100644 index 00000000..dfd1988e --- /dev/null +++ b/.github/scenarios/1.toml_linting-0.workflow_linting.json @@ -0,0 +1,29 @@ +{ + "project_name": "Flower Generator", + "project_slug": "{{ cookiecutter.project_name|slugify }}", + "optional_toml_linting": ["true", "false"], + "optional_workflow_linting": ["false", "true"], + "github_handle": "niall-byrne", + "galaxy_role_slug": "{{ cookiecutter.project_slug.replace('-', '_').replace('.', '_') }}", + "galaxy_namespace_slug": "{{ cookiecutter.github_handle|slugify|replace('-', '_')|replace('.', '_') }}", + "description": "Not the baking kind.", + "author": "Niall Byrne", + "company": "Shared Vision Solutions", + "email": "niall@niallbyrne.ca", + "_BRANCH_NAME_BASE": "master", + "_BRANCH_NAME_DEVELOPMENT": "dev", + "_DOCKER_DEFAULT_CONTAINER": "ghcr.io/cicd-tools-org/cicd-tools:master", + "_GITHUB_CI_ACTIONLINT_SCRIPT_URL": "https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash", + "_GITHUB_CI_DEFAULT_CONCURRENCY": 4, + "_GITHUB_CI_DEFAULT_PYTHON_VERSIONS": ["3.9"], + "_GITHUB_CI_DEFAULT_MOLECULE_TEST_PLATFORMS": ["ubuntu-latest"], + "_GITHUB_CI_DEFAULT_MOLECULE_EXCLUDED_SCENARIOS_REGEX": "^default$|^noci-.*$", + "_GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS": false, + "_*DO_NOT_MODIFY_THIS_FILE*": "This file is created to assist with upgrading to future versions of this template.", + "_copy_without_render": [ + ".cicd-tools/boxes/bootstrap", + ".github/actions", + ".github/workflows/job*", + "scripts" + ] +} diff --git a/.github/scenarios/1.toml_linting-1.workflow_linting.json b/.github/scenarios/1.toml_linting-1.workflow_linting.json new file mode 100644 index 00000000..ca5d9224 --- /dev/null +++ b/.github/scenarios/1.toml_linting-1.workflow_linting.json @@ -0,0 +1,29 @@ +{ + "project_name": "Flower Generator", + "project_slug": "{{ cookiecutter.project_name|slugify }}", + "optional_toml_linting": ["true", "false"], + "optional_workflow_linting": ["true", "false"], + "github_handle": "niall-byrne", + "galaxy_role_slug": "{{ cookiecutter.project_slug.replace('-', '_').replace('.', '_') }}", + "galaxy_namespace_slug": "{{ cookiecutter.github_handle|slugify|replace('-', '_')|replace('.', '_') }}", + "description": "Not the baking kind.", + "author": "Niall Byrne", + "company": "Shared Vision Solutions", + "email": "niall@niallbyrne.ca", + "_BRANCH_NAME_BASE": "master", + "_BRANCH_NAME_DEVELOPMENT": "dev", + "_DOCKER_DEFAULT_CONTAINER": "ghcr.io/cicd-tools-org/cicd-tools:master", + "_GITHUB_CI_ACTIONLINT_SCRIPT_URL": "https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash", + "_GITHUB_CI_DEFAULT_CONCURRENCY": 4, + "_GITHUB_CI_DEFAULT_PYTHON_VERSIONS": ["3.9"], + "_GITHUB_CI_DEFAULT_MOLECULE_TEST_PLATFORMS": ["ubuntu-latest"], + "_GITHUB_CI_DEFAULT_MOLECULE_EXCLUDED_SCENARIOS_REGEX": "^default$|^noci-.*$", + "_GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS": false, + "_*DO_NOT_MODIFY_THIS_FILE*": "This file is created to assist with upgrading to future versions of this template.", + "_copy_without_render": [ + ".cicd-tools/boxes/bootstrap", + ".github/actions", + ".github/workflows/job*", + "scripts" + ] +} diff --git a/.github/scripts/job-50-test-precommit.sh b/.github/scripts/job-50-test-precommit.sh old mode 100644 new mode 100755 index 2a374dde..c9dcf9a0 --- a/.github/scripts/job-50-test-precommit.sh +++ b/.github/scripts/job-50-test-precommit.sh @@ -1,89 +1,140 @@ #!/bin/bash -# .github/scripts/job-50-test-precommit.sh # Performs tests on the pre-commit hooks. -# 1: The name of a pre-commit test scenario. (See 'main' below.) -# TEMPLATED_NAME: The name of the rendered test project. +# Implementation: +# Templates implementing this script will likely also have to customize their .job-50-precommit.yml workflow. +# The API demonstrated here is more for example purposes. + +# 1: The name of a pre-commit test scenario. (See 'main' below.) +# TEST_PROJECT_NAME: The name of the rendered test project. # CI only script. set -eo pipefail -test_commit_lint() { - util_git_reset - touch empty_file.txt - git stage empty_file.txt - git commit -m 'test - pre-commit: improperly formatted commit' || exit 0 - util_fail_test +main() { + pushd "${TEST_PROJECT_NAME}" >> /dev/null + scenario "${1}" + popd >> /dev/null } -test_molecule_lint() { - util_git_reset - echo "" >> tasks/main.yml - git stage tasks/main.yml - git commit -m 'test(PRE-COMMIT): fail due to ansible-lint' > error.log 2>&1 || grep "empty-lines" error.log > /dev/null && exit 0 - util_fail_test -} +scenario() { -test_toml_lint_1() { - util_git_reset - sed -i.bak 's/authors =/ authors = /g' pyproject.toml - git stage pyproject.toml - git commit -m 'test(PRE-COMMIT): fail due to tomll' || exit 0 - util_fail_test -} + local TEMP_FILE -test_toml_lint_2() { - util_git_reset - sed -i.bak 's/python = ">=.*,<4.0/python = ">=3.9.0,<5.0/g' pyproject.toml - git stage pyproject.toml - git commit -m 'test(PRE-COMMIT): upgrade python without issue' -} + test_ansible_lint_fails() { + util "git_reset" + echo "" >> tasks/main.yml + git stage tasks/main.yml + git commit -m 'test(PRE-COMMIT): fail due to ansible-lint' > error.log 2>&1 || grep "empty-lines" error.log > /dev/null && exit 0 + util "fail" + } -test_workflow_lint() { - util_git_reset - find .github -type f -name '*.yml' -exec sed -i.bak 's/ubuntu-latest/non-existent-os/g' {} \; - git stage .github - git commit -m 'test(PRE-COMMIT): fail due to actionlint' || exit 0 -} + test_commit_lint_fails() { + util "git_reset" + TEMP_FILE=$(util "create_tmp") + touch "${TEMP_FILE}" + git stage "${TEMP_FILE}" + git commit -m 'test - pre-commit: improperly formatted commit' || exit 0 + util "fail" + } -util_fail_test() { - echo "This commit should have failed." - exit 127 -} + test_commit_spelling_fails() { + util "git_reset" + TEMP_FILE=$(util "create_tmp") + touch "${TEMP_FILE}" + git stage "${TEMP_FILE}" + git commit -m 'test(PRE-COMMIT): ssspelling error' || exit 0 + util "fail" + } + + test_toml_lint_fails() { + util "git_reset" + sed -i.bak 's/authors =/ authors = /g' pyproject.toml + git stage pyproject.toml + git commit -m 'test(PRE-COMMIT): fail due to tomll' || exit 0 + util "fail" + } + + test_toml_lint_passes() { + util "git_reset" + sed -i.bak "s/description = '.*'/description = 'updated description'/g" pyproject.toml + git stage pyproject.toml + git commit -m 'test(PRE-COMMIT): upgrade python without issue' + } + + test_shell_lint_fails() { + util "git_reset" + TEMP_FILE=$(util "create_tmp") + echo -e "#!/bin/bash\nls *.bash" > "${TEMP_FILE}.sh" + git stage "${TEMP_FILE}.sh" + git commit -m 'test(PRE-COMMIT): fail due to shellcheck' || exit 0 + util "fail" + } + + test_shell_format_fails() { + util "git_reset" + TEMP_FILE=$(util "create_tmp") + echo -e "#!/bin/bash\nls bash_scripts;ls shell_scripts" > "${TEMP_FILE}.sh" + git stage "${TEMP_FILE}.sh" + git commit -m 'test(PRE-COMMIT): fail due to shfmt' || exit 0 + util "fail" + } + + test_workflow_lint_fails() { + util "git_reset" + find .github/workflows -type f -name '*.yml' -exec sed -i.bak 's/uses:/illegal-yaml-key:/g' {} \; + git stage .github + git commit -m 'test(PRE-COMMIT): fail due to actionlint' || exit 0 + util "fail" + } + + test_workflow_header_lint_fails() { + util "git_reset" + sed -i.bak 's,-github-workflow,-github-wrong-name,g' .github/workflows/workflow-*.yml + git stage .github + git commit -m 'test(PRE-COMMIT): fail due to workflow header lint' || exit 0 + util fail + } + + "$@" -util_git_reset() { - git reset HEAD - git clean -fd - git checkout . } -main() { +util() { + + local COMMAND + local PREFIX + + _util_create_tmp() { + mktemp tmp.XXXXXXX + } + + _util_fail() { + echo "This commit should have failed." + exit 127 + } + + _util_git_reset() { + git reset HEAD + git clean -fd + git checkout . + } + + _util_unknown_command() { + echo "Unknown utility command: '${COMMAND}'" + exit 127 + } - pushd "${TEMPLATED_NAME}" - case $1 in - commit-lint) - test_commit_lint - ;; - molecule-lint) - test_molecule_lint - ;; - toml-lint-1) - test_toml_lint_1 - ;; - toml-lint-2) - test_toml_lint_2 - ;; - workflow-lint) - test_workflow_lint - ;; - *) - echo "Invalid test scenario." - exit 127 - ;; - esac - popd + PREFIX="_util" + COMMAND="${PREFIX}_${1}" + if [[ $(type -t "${COMMAND}") == function ]]; then + shift + "${COMMAND}" "$@" + else + "${PREFIX}_unknown_command" + fi } diff --git a/.github/scripts/job-60-prune-test-releases.sh b/.github/scripts/job-60-prune-test-releases.sh deleted file mode 100644 index d72f568d..00000000 --- a/.github/scripts/job-60-prune-test-releases.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# .github/scripts/job-60-prune-test-releases.sh -# Remove existing releases on the test repository. - -# GITHUB_TOKEN: The token used to authorize the call. -# REMOTE_ORIGIN: The git remote repository name (organization/repo). -# TEST_PUSH_TAG: The tag name to clean up. - -# CI only script. - -set -eo pipefail - -check_releases() { - gh release view -R "${REMOTE_ORIGIN}" "${TEST_PUSH_TAG}" -} - -main() { - - while check_releases; do - sleep 0.5 - gh release delete -R "${REMOTE_ORIGIN}" -y "${TEST_PUSH_TAG}" - done - -} - -main "$@" diff --git a/.github/scripts/job-60-test-push.sh b/.github/scripts/job-60-test-push.sh deleted file mode 100644 index b1e797f2..00000000 --- a/.github/scripts/job-60-test-push.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -# .github/scripts/job-60-test-push.sh -# Performs pushes against the test repository to trigger rendered workflows. - -# 1: The name of the branch you wish to push. -# 2: Optionally set a tag you'd like to push. -# REMOTE_TOKEN: The auth token that will be used to push. -# REMOTE_ORIGIN: The remote repository we're pushing to. (format: "owner/repository") -# TEMPLATED_NAME: The name of the rendered test project. - -# CI only script. - -set -eo pipefail - -add_test_commit() { - echo "test commit" > test_file.txt - git add test_file.txt - git commit -m 'feat(TEST_FILE): add test file' -} - -push() { - # 1: The branch you are pushing. - git push "$(util_get_remote)" HEAD:"${1}" --force -} - -push_tags() { - # 1: The tag you'd like to push - - # Don't push this tag again. - git tag --delete 0.0.0 - - # Ensure remote tag doesn't exist - set +e - git push --delete "$(util_get_remote)" "${1}" - set -e - - git tag "${1}" - git push "$(util_get_remote)" --tags -} - -util_get_remote() { - echo "https://${REMOTE_TOKEN}@github.com/${REMOTE_ORIGIN}.git" -} - -main() { - - pushd "${TEMPLATED_NAME}" - git checkout "${1}" - if [[ -z "${2}" ]]; then - add_test_commit - push "${1}" - else - push_tags "${2}" - fi - popd - -} - -main "$@" diff --git a/.github/scripts/step-render-template.sh b/.github/scripts/step-render-template.sh new file mode 100755 index 00000000..6f1c153d --- /dev/null +++ b/.github/scripts/step-render-template.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Perform automated templating. + +# Implementation: +# Templates implementing this script should be designed to work from inside a cloned copy of the repository. +# To test various template "scenarios", make a copy of the 'cookiecutter.json' file and copy to the .github/scenarios folder. + +# 1: Scenario filename. (Provide basename only, the path is implied.) +# 2: Git Username +# 3: Git Email + +# CI only script. + +set -eo pipefail + +SCENARIO="${1}" +NAME="${2:-"Pro Buddy Dev"}" +EMAIL="${3:-"somedude@coolstartup.com"}" + +main() { + + git config --global user.name "${NAME}" + git config --global user.email "${EMAIL}" + + if _render_is_scenario_present; then + _render_setup_scenario + fi + + cookiecutter --no-input -o .. . + +} + +_render_is_scenario_present() { + test "${SCENARIO}" != "" +} + +_render_setup_scenario() { + cp "./.github/scenarios/${SCENARIO}.json" ./cookiecutter.json + git diff cookiecutter.json +} + +main "$@" diff --git a/.github/scripts/step-requirements-template.sh b/.github/scripts/step-requirements-template.sh new file mode 100755 index 00000000..75d36f25 --- /dev/null +++ b/.github/scripts/step-requirements-template.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +# Installs all Python packages required to render the cookiecutter template. + +# Implementation: +# Templates implementing this script must install versions of cookiecutter and poetry, +# plus any other required Python packages. + +# CI only script. + +set -eo pipefail + +main() { + + python -m pip install cookiecutter poetry --verbose + +} + +main "$@" diff --git a/.github/scripts/step-setup-environment.sh b/.github/scripts/step-setup-environment.sh new file mode 100755 index 00000000..cfbe4579 --- /dev/null +++ b/.github/scripts/step-setup-environment.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Configures environment variables for GitHub Actions. + +# Implementation: +# Templates implementing this script must set the required environment variables in the GitHub runner's environment context. +# +# BRANCH_OR_TAG: The current branch or tag being tested. +# CACHE_TTL: A unique CACHE value that determines the cache's TTL. (Default strategy: day of the month.) +# NOTIFICATION_LINK: Consumed by the notification script to provide a clickable link to the workflow run in GitHub. +# PROJECT_NAME: The slugified name of the template project. Should match the GitHub repository name. +# PROJECT_OWNER: The GitHub owner of the project. +# TEMPLATE_BRANCH_NAME_BASE: The name of the templated repository's default branch name. (Defaults to 'master'.) +# TEMPLATE_BRANCH_NAME_DEVELOPMENT: The name of the templated repository's development branch name. (Defaults to 'dev'.) +# TEST_PROJECT_NAME: The slugified name of the template when populated during testing. Should match any test GitHub repository in use. + +# CI only script. + +set -eo pipefail + +WORKFLOW_NAME="${WORKFLOW_NAME:-""}" + +main() { + + PROJECT_NAME="ansible-workbench" + PROJECT_OWNER="niall-byrne" + TEST_PROJECT_NAME="flower-generator" + + TEMPLATE_BRANCH_NAME_BASE="master" + TEMPLATE_BRANCH_NAME_DEVELOPMENT="dev" + + BRANCH_OR_TAG="$(echo "${GITHUB_REF}" | sed -E 's,refs/heads/|refs/tags/,,g')" + WORKFLOW_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + + [[ "${WORKFLOW_NAME}" != "" ]] && WORKFLOW_NAME="-${WORKFLOW_NAME}" + + { + echo "BRANCH_OR_TAG=${BRANCH_OR_TAG}" + echo "CACHE_TTL=$(date +%d)" + echo "NOTIFICATION_LINK=${PROJECT_NAME}${WORKFLOW_NAME} [<${WORKFLOW_URL}|${BRANCH_OR_TAG}>]" + echo "PROJECT_NAME=${PROJECT_NAME}" + echo "PROJECT_OWNER=${PROJECT_OWNER}" + echo "TEMPLATE_BRANCH_NAME_BASE=${TEMPLATE_BRANCH_NAME_BASE-master}" + echo "TEMPLATE_BRANCH_NAME_DEVELOPMENT=${TEMPLATE_BRANCH_NAME_DEVELOPMENT-dev}" + echo "TEST_PROJECT_NAME=${TEST_PROJECT_NAME}" + } >> "${GITHUB_ENV}" + +} + +main "$@" diff --git a/.github/scripts/task-render-template.sh b/.github/scripts/task-render-template.sh deleted file mode 100644 index 4fa90a2e..00000000 --- a/.github/scripts/task-render-template.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash - -# .github/scripts/task-render-template.sh -# Perform automated templating. - -# 1: Optional TOML Formatting -# 2: Optional workflow Formatting -# 3: Git Username -# 4: Git Email - -# CI only script. - -set -eo pipefail - -OPTIONAL_TOML_LINTING=${1:-"1"} -OPTIONAL_WORKFLOW_LINTING=${2:-"1"} -NAME=${3:-"Pro Buddy Dev"} -EMAIL=${4:-"somedude@coolstartup.com"} - -main() { - - git config --global user.name "${NAME}" - git config --global user.email "${EMAIL}" - - echo -e "\n\n${OPTIONAL_TOML_LINTING}\n${OPTIONAL_WORKFLOW_LINTING}\n\n\n\n\n\n\n\n" | cookiecutter template/ - -} - -main "$@" diff --git a/.github/scripts/task-template-requirements.sh b/.github/scripts/task-template-requirements.sh deleted file mode 100644 index a1467dbc..00000000 --- a/.github/scripts/task-template-requirements.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -# .github/scripts/task-template-requirements.sh -# Centralized management of template requirements installs. - -# CI only script - -set -eo pipefail - -main () { - - python -m pip install cookiecutter poetry --verbose - -} - -main "$@" diff --git a/.github/scripts/workflow-setup-environment.sh b/.github/scripts/workflow-setup-environment.sh deleted file mode 100644 index 21ad70c4..00000000 --- a/.github/scripts/workflow-setup-environment.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# .github/scripts/workflow-setup-environment.sh -# Configures environment variables for GitHub Workflows. - -# CI only script. - -set -eo pipefail - -main() { - - ANSIBLE_WORKBENCH_BRANCH_NAME_BASE="master" - ANSIBLE_WORKBENCH_BRANCH_NAME_DEVELOPMENT="dev" - PROJECT_NAME="ansible-workbench" - USER_NAME="niall-byrne" - TEMPLATED_NAME="flower-generator" - - BRANCH_OR_TAG="$(echo "${GITHUB_REF}" | sed 's/refs\/heads\///g' | sed 's/refs\/tags\///g')" - WORKFLOW_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" - - { - echo "ANSIBLE_WORKBENCH_BRANCH_NAME_BASE=${ANSIBLE_WORKBENCH_BRANCH_NAME_BASE}" - echo "ANSIBLE_WORKBENCH_BRANCH_NAME_DEVELOPMENT=${ANSIBLE_WORKBENCH_BRANCH_NAME_DEVELOPMENT}" - echo "BRANCH_OR_TAG=${BRANCH_OR_TAG}" - echo "CACHE_TTL=$(date +%d)" - echo "NOTIFICATION=${PROJECT_NAME} [<${WORKFLOW_URL}|${BRANCH_OR_TAG}>]" - echo "PROJECT_NAME=${PROJECT_NAME}" - echo "TEMPLATED_NAME=${TEMPLATED_NAME}" - echo "USER_NAME=${USER_NAME}" - echo "WEBHOOK_URL=${WEBHOOK_URL}" - } >> "${GITHUB_ENV}" - -} - -main "$@" diff --git a/.github/workflows/.job-10-security.yml b/.github/workflows/.job-10-security.yml deleted file mode 100644 index 4c41ab78..00000000 --- a/.github/workflows/.job-10-security.yml +++ /dev/null @@ -1,55 +0,0 @@ ---- -name: ansible-workbench-job-security - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -env: - ANSIBLE_WORKBENCH_SKIP_POETRY: 1 - ANSIBLE_WORKBENCH_SKIP_PRECOMMIT: 1 - -jobs: - - run_trufflehog: - - runs-on: ubuntu-latest - - steps: - - name: Security Test Repo -- Checkout Repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Security Test Repo -- Setup Environment - run: | - source "./.github/scripts/workflow-setup-environment.sh" - source "./{{cookiecutter.project_slug}}/.github/scripts/workflow-determine-pushed-commits.sh" - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Security Test -- Run Trufflehog - uses: trufflesecurity/trufflehog@v3.27.1 - with: - path: . - base: ${{ env.PUSHED_COMMIT_START }} - head: ${{ env.BRANCH_OR_TAG }} - - - name: Security Test Repo -- Report Job Status on Success - if: fromJSON(inputs.CONFIGURATION)._GITHUB_CI_VERBOSE_NOTIFICATIONS == true - run: | - "./{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: security checks succeeded!" - - - name: Security Test Repo -- Report Job Status on Failure - if: failure() - run: | - "./{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: security checks failed!" diff --git a/.github/workflows/.job-30-documentation.yml b/.github/workflows/.job-30-documentation.yml deleted file mode 100644 index 75d6cfd0..00000000 --- a/.github/workflows/.job-30-documentation.yml +++ /dev/null @@ -1,82 +0,0 @@ ---- -name: ansible-workbench-job-documentation - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -env: - ANSIBLE_WORKBENCH_SKIP_POETRY: 1 - ANSIBLE_WORKBENCH_SKIP_PRECOMMIT: 1 - -jobs: - - check_markdown_links: - - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - python-version: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS }} - max-parallel: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} - - steps: - - name: Documentation Test -- Checkout Repository - uses: actions/checkout@v3 - with: - path: 'template' - - - name: Documentation Test -- Setup Environment - run: | - source "./template/.github/scripts/workflow-setup-environment.sh" - env: - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Documentation Test -- Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Documentation Test -- Install Template Requirements - run: | - source "./template/.github/scripts/task-template-requirements.sh" - - - name: Documentation Test -- Render Template - run: | - source "./template/.github/scripts/task-render-template.sh" - - - name: Documentation Test -- Check Markdown Links For Template - uses: gaurav-nelson/github-action-markdown-link-check@v1 - with: - config-file: 'template/{{cookiecutter.project_slug}}/.github/config/actions/gaurav-nelson-github-action-markdown-link-check.json' - use-quiet-mode: 'no' - use-verbose-mode: 'yes' - folder-path: 'template, template/.github/workflows' - max-depth: 1 - - - name: Documentation Test -- Check Markdown Links For Rendered Template - uses: gaurav-nelson/github-action-markdown-link-check@v1 - with: - config-file: 'template/{{cookiecutter.project_slug}}/.github/config/actions/gaurav-nelson-github-action-markdown-link-check.json' - use-quiet-mode: 'no' - use-verbose-mode: 'yes' - folder-path: ${{ env.TEMPLATED_NAME }} - max-depth: -1 - - - name: Documentation Test -- Report Job Status on Success - if: fromJSON(inputs.CONFIGURATION)._GITHUB_CI_VERBOSE_NOTIFICATIONS == true - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: documentation checks succeeded!" - - - name: Documentation Test -- Report Job Status on Failure - if: failure() - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: documentation checks failed!" diff --git a/.github/workflows/.job-40-molecule.yml b/.github/workflows/.job-40-molecule.yml deleted file mode 100644 index a023e08a..00000000 --- a/.github/workflows/.job-40-molecule.yml +++ /dev/null @@ -1,88 +0,0 @@ ---- -name: ansible-workbench-job-molecule - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -env: - ANSIBLE_WORKBENCH_SKIP_POETRY: 0 - ANSIBLE_WORKBENCH_SKIP_PRECOMMIT: 0 - -jobs: - - run_molecule_lint: - - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - python-version: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS }} - max-parallel: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} - - steps: - - name: Molecule Lint Test -- Checkout Repository - uses: actions/checkout@v3 - with: - path: 'template' - - - name: Molecule Lint Test -- Setup Environment - run: | - source "./template/.github/scripts/workflow-setup-environment.sh" - env: - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Molecule Lint Test -- Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Molecule Lint Test -- Install Template Requirements - run: | - source "./template/.github/scripts/task-template-requirements.sh" - - - name: Molecule Lint Test -- Initialize Cache Locations - run: | - mkdir -p ~/.cache/pypoetry/virtualenvs - source "./template/{{cookiecutter.project_slug}}/.github/scripts/task-setup-ansible-cache.sh" \ - "$(pwd)/ansible_cache" \ - ~/.cache - - - name: Molecule Lint Test -- Mount Ansible Cache - uses: actions/cache@v3 - with: - key: ansible-${{ hashFiles('./template/{{cookiecutter.project_slug}}/requirements.yml') }}-${{ env.CACHE_TTL }} - path: ansible_cache - - - name: Molecule Lint Test -- Mount Poetry Cache - uses: actions/cache@v3 - with: - key: poetry-${{ hashFiles('./template/{{cookiecutter.project_slug}}/pyproject.toml') }}-${{ runner.os }}-${{ env.CACHE_TTL }} - path: ~/.cache/pypoetry/virtualenvs - - - name: Molecule Lint Test -- Render Template - run: | - source "./template/.github/scripts/task-render-template.sh" - - - name: Molecule Lint Test -- Lint Rendered Template With Default Scenario - run: | - cd "${TEMPLATED_NAME}" - poetry run molecule lint - - - name: Molecule Lint Test -- Report Job Status on Success - if: fromJSON(inputs.CONFIGURATION)._GITHUB_CI_VERBOSE_NOTIFICATIONS == true - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: molecule linting checks succeeded!" - - - name: Molecule Lint Test -- Report Job Status on Failure - if: failure() - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: molecule linting checks failed!" diff --git a/.github/workflows/.job-50-precommit.yml b/.github/workflows/.job-50-precommit.yml deleted file mode 100644 index fc4b4926..00000000 --- a/.github/workflows/.job-50-precommit.yml +++ /dev/null @@ -1,117 +0,0 @@ ---- -name: ansible-workbench-job-precommit - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -env: - ANSIBLE_WORKBENCH_SKIP_POETRY: 0 - ANSIBLE_WORKBENCH_SKIP_PRECOMMIT: 0 - -jobs: - - precommit_hook_tests: - - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - cookiecutter-toml-selection: [1, 2] - cookiecutter-workflow-selection: [1, 2] - python-version: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS }} - max-parallel: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} - - steps: - - name: Pre-Commit Test -- Checkout Repository - uses: actions/checkout@v3 - with: - path: 'template' - - - name: Pre-Commit Test -- Setup Environment - run: | - source "./template/.github/scripts/workflow-setup-environment.sh" - env: - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Pre-Commit Test -- Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Pre-Commit Test -- Install Template Requirements - run: | - source "./template/.github/scripts/task-template-requirements.sh" - - - name: Pre-Commit Test -- Install Binaries - run: | - sudo apt-get install -y golang-github-pelletier-go-toml - bash <(curl "${{ fromJson(inputs.CONFIGURATION)._GITHUB_CI_ACTIONLINT_SCRIPT_URL }}") - sudo mv actionlint /usr/local/bin - - - name: Pre-Commit -- Initialize Cache Locations - run: | - mkdir -p ~/.cache/pypoetry/virtualenvs - source "./template/{{cookiecutter.project_slug}}/.github/scripts/task-setup-ansible-cache.sh" \ - "$(pwd)/ansible_cache" \ - ~/.cache - - - name: Pre-Commit -- Mount Ansible Cache - uses: actions/cache@v3 - with: - key: ansible-${{ hashFiles('./template/{{cookiecutter.project_slug}}/requirements.yml') }}-${{ env.CACHE_TTL }} - path: ansible_cache - - - name: Pre-Commit Test -- Mount Poetry Cache - uses: actions/cache@v3 - with: - key: poetry-${{ hashFiles('./template/{{cookiecutter.project_slug}}/pyproject.toml') }}-${{ runner.os }}-${{ env.CACHE_TTL }} - path: ~/.cache/pypoetry/virtualenvs - - - name: Pre-Commit Test -- Render Template - run: | - source "./template/.github/scripts/task-render-template.sh" "${SELECTION_TOML}" "${SELECTION_WORKFLOW}" "GitHub Action" "action@github.com" - env: - SELECTION_TOML: ${{ matrix.cookiecutter-toml-selection }} - SELECTION_WORKFLOW: ${{ matrix.cookiecutter-workflow-selection }} - - - name: Pre-Commit Test -- Test Commit Lint -- Expect Failure - run: | - source "./template/.github/scripts/job-50-test-precommit.sh" "commit-lint" - - - name: Pre-Commit Test -- Test Molecule Lint -- Expect Failure - run: | - source "./template/.github/scripts/job-50-test-precommit.sh" "molecule-lint" - - - name: Pre-Commit Test -- Test TOML Lint -- Expect Failure - if: matrix.cookiecutter-toml-selection == 1 - run: | - source "./template/.github/scripts/job-50-test-precommit.sh" "toml-lint-1" - - - name: Pre-Commit Test -- Test TOML Lint -- Expect Success - if: matrix.cookiecutter-toml-selection == 1 - run: | - source "./template/.github/scripts/job-50-test-precommit.sh" "toml-lint-2" - - - name: Pre-Commit Test -- Test Workflow Lint -- Expect Failure - if: matrix.cookiecutter-workflow-selection == 1 - run: | - source "./template/.github/scripts/job-50-test-precommit.sh" "workflow-lint" - - - name: Pre-Commit Test -- Report Job Status on Success - if: fromJSON(inputs.CONFIGURATION)._GITHUB_CI_VERBOSE_NOTIFICATIONS == true - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: pre-commit (toml-${{ matrix.cookiecutter-toml-selection }}, workflow-${{ matrix.cookiecutter-workflow-selection }}) hook test has passed!" - - - name: Pre-Commit Test -- Report Job Status on Failure - if: failure() - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: pre-commit (toml-${{ matrix.cookiecutter-toml-selection }}, workflow-${{ matrix.cookiecutter-workflow-selection }}) hook test has failed!" diff --git a/.github/workflows/.job-60-remote-push.yml b/.github/workflows/.job-60-remote-push.yml deleted file mode 100644 index 69bde464..00000000 --- a/.github/workflows/.job-60-remote-push.yml +++ /dev/null @@ -1,115 +0,0 @@ ---- -name: ansible-workbench-job-remote-push - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - REMOTE_TOKEN: - description: "GitHub token with access to the test repository." - required: true - REMOTE_ORIGIN: - description: "Identifies the test repository as: owner/repository" - required: true - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -env: - ANSIBLE_WORKBENCH_SKIP_POETRY: 1 - ANSIBLE_WORKBENCH_SKIP_PRECOMMIT: 1 - TEST_PUSH_TAG: "0.1.0" - -jobs: - - push_to_test_repository: - - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - cookiecutter-optionals-selection: [1, 2] - python-version: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS }} - max-parallel: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} - - steps: - - name: Push Test -- Checkout Repository - uses: actions/checkout@v3 - with: - path: 'template' - persist-credentials: false - - - name: Push Test -- Setup Environment - run: | - source "./template/.github/scripts/workflow-setup-environment.sh" - env: - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Push Test -- Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Push Test -- Install Template Requirements - run: | - source "./template/.github/scripts/task-template-requirements.sh" - - - name: Push Test -- Render Template - run: | - source "./template/.github/scripts/task-render-template.sh" "${SELECTIONS}" "${SELECTIONS}" "GitHub Action" "action@github.com" - env: - SELECTIONS: ${{ matrix.cookiecutter-optionals-selection }} - - - name: Push Test -- Clean Up Test Releases - if: matrix.cookiecutter-optionals-selection == 1 - run: | - source "./template/.github/scripts/job-60-prune-test-releases.sh" - env: - GITHUB_TOKEN: ${{ secrets.REMOTE_TOKEN }} - REMOTE_ORIGIN: ${{ secrets.REMOTE_ORIGIN }} - - - name: Push Test -- Push To Test Repository (${{ env.ANSIBLE_WORKBENCH_BRANCH_NAME_BASE }}) - if: matrix.cookiecutter-optionals-selection == 1 - run: | - source "./template/.github/scripts/job-60-test-push.sh" "${ANSIBLE_WORKBENCH_BRANCH_NAME_BASE}" - env: - REMOTE_TOKEN: ${{ secrets.REMOTE_TOKEN }} - REMOTE_ORIGIN: ${{ secrets.REMOTE_ORIGIN }} - - - name: Push Test -- Push To Test Repository (${{ env.TEST_PUSH_TAG }}) - if: matrix.cookiecutter-optionals-selection == 1 - run: | - source "./template/.github/scripts/job-60-test-push.sh" "${ANSIBLE_WORKBENCH_BRANCH_NAME_BASE}" "${TEST_PUSH_TAG}" - env: - REMOTE_TOKEN: ${{ secrets.REMOTE_TOKEN }} - REMOTE_ORIGIN: ${{ secrets.REMOTE_ORIGIN }} - - - name: Push Test -- Trigger Release Workflow Test - if: matrix.cookiecutter-optionals-selection == 1 - run: | - gh workflow run -r "${TEST_PUSH_TAG}" -R "${REMOTE_ORIGIN}" workflow-publish-to-galaxy.yml -f "TAG=${TEST_PUSH_TAG}" - env: - GITHUB_TOKEN: ${{ secrets.REMOTE_TOKEN }} - REMOTE_ORIGIN: ${{ secrets.REMOTE_ORIGIN }} - - - name: Push Test -- Push To Test Repository (${{ env.ANSIBLE_WORKBENCH_BRANCH_NAME_DEVELOPMENT }}) - if: matrix.cookiecutter-optionals-selection == 2 - run: | - source "./template/.github/scripts/job-60-test-push.sh" "${ANSIBLE_WORKBENCH_BRANCH_NAME_DEVELOPMENT}" - env: - REMOTE_TOKEN: ${{ secrets.REMOTE_TOKEN }} - REMOTE_ORIGIN: ${{ secrets.REMOTE_ORIGIN }} - - - name: Push Test -- Report Job Status on Success - if: fromJSON(inputs.CONFIGURATION)._GITHUB_CI_VERBOSE_NOTIFICATIONS == true - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: push has been triggered!" - - - name: Push Test -- Report Job Status on Failure - if: failure() - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: push failed to trigger!" diff --git a/.github/workflows/.job-80-commit-lint.yml b/.github/workflows/.job-80-commit-lint.yml deleted file mode 100644 index 8a3696b3..00000000 --- a/.github/workflows/.job-80-commit-lint.yml +++ /dev/null @@ -1,81 +0,0 @@ ---- -name: ansible-workbench-job-commit-lint - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -env: - ANSIBLE_WORKBENCH_SKIP_POETRY: 0 - ANSIBLE_WORKBENCH_SKIP_PRECOMMIT: 1 - ANSIBLE_WORKBENCH_PUSH_FALLBACK_REV_RANGE: "8f9a7c315416747257b3ef2adfc425c63d85adf8..HEAD" - -jobs: - - run_commitizen: - - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - python-version: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS }} - max-parallel: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} - - steps: - - name: Commit Lint Test -- Checkout Repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Commit Lint Test -- Setup Environment - run: | - source "./.github/scripts/workflow-setup-environment.sh" - source "./{{cookiecutter.project_slug}}/.github/scripts/workflow-determine-pushed-commits.sh" - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Commit Lint Test -- Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Commit Lint Test -- Install Poetry - run: | - source "./{{cookiecutter.project_slug}}/.github/scripts/task-use-poetry.sh" "install-poetry" - - - name: Commit Lint Test -- Initialize Cache Locations - run: | - mkdir -p ~/.cache/pypoetry/virtualenvs - - - name: Commit Lint Test -- Mount Poetry Cache - uses: actions/cache@v3 - with: - key: poetry-${{ hashFiles('./pyproject.toml') }}-${{ runner.os }}-${{ env.CACHE_TTL }} - path: ~/.cache/pypoetry/virtualenvs - - - name: Commit Lint Test -- Install Requirements - run: | - source "./{{cookiecutter.project_slug}}/.github/scripts/task-use-poetry.sh" "install-project" - - - name: Commit Lint Test -- Lint Pushed Commits - run: | - poetry run cz check --rev-range "${PUSHED_COMMIT_REV_RANGE}" - - - name: Commit Lint Test -- Report Job Status on Success - if: fromJSON(inputs.CONFIGURATION)._GITHUB_CI_VERBOSE_NOTIFICATIONS == true - run: | - "./{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: commit lint checks succeeded!" - - - name: Commit Lint Test -- Report Job Status on Failure - if: failure() - run: | - "./{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: commit lint checks failed!" diff --git a/.github/workflows/.job-80-shell-lint.yml b/.github/workflows/.job-80-shell-lint.yml deleted file mode 100644 index 45f3bb8b..00000000 --- a/.github/workflows/.job-80-shell-lint.yml +++ /dev/null @@ -1,76 +0,0 @@ ---- -name: ansible-workbench-job-shell-lint - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -env: - ANSIBLE_WORKBENCH_SKIP_POETRY: 1 - ANSIBLE_WORKBENCH_SKIP_PRECOMMIT: 1 - -jobs: - - run_shellcheck: - - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - python-version: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS }} - max-parallel: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} - - steps: - - name: Shell Lint Test -- Checkout Repository - uses: actions/checkout@v3 - with: - path: 'template' - - - name: Shell Lint Test -- Setup Environment - run: | - source "./template/.github/scripts/workflow-setup-environment.sh" - env: - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Shell Lint Test -- Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Shell Lint Test -- Install Template Requirements - run: | - source "./template/.github/scripts/task-template-requirements.sh" - - - name: Shell Lint Test -- Render Template - run: | - source "./template/.github/scripts/task-render-template.sh" - - - name: Shell Lint Test -- Shellcheck Scripts - run: | - shellcheck ./template/hooks/*.sh - shellcheck ./template/scripts/*.sh - shellcheck ./template/.github/scripts/*.sh - - - name: Shell Lint Test -- Shellcheck Rendered Scripts - run: | - cd "${TEMPLATED_NAME}" - shellcheck ./.github/scripts/*.sh - shellcheck -x ./.pre-commit/*.sh - - - name: Shell Lint Test -- Report Job Status on Success - if: fromJSON(inputs.CONFIGURATION)._GITHUB_CI_VERBOSE_NOTIFICATIONS == true - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: shellcheck checks succeeded!" - - - name: Shell Lint Test -- Report Job Status on Failure - if: failure() - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: shellcheck checks failed!" diff --git a/.github/workflows/.job-80-toml-lint.yml b/.github/workflows/.job-80-toml-lint.yml deleted file mode 100644 index 0fa6626d..00000000 --- a/.github/workflows/.job-80-toml-lint.yml +++ /dev/null @@ -1,80 +0,0 @@ ---- -name: ansible-workbench-job-toml-lint - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -env: - ANSIBLE_WORKBENCH_SKIP_POETRY: 1 - ANSIBLE_WORKBENCH_SKIP_PRECOMMIT: 1 - -jobs: - - run_tomll: - - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - python-version: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS }} - max-parallel: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} - - steps: - - name: Toml Lint Test -- Checkout Repository - uses: actions/checkout@v3 - with: - path: 'template' - - - name: Toml Lint Test -- Setup Environment - run: | - source "./template/.github/scripts/workflow-setup-environment.sh" - env: - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Toml Lint Test -- Install Linter - run: | - sudo apt-get install -y golang-github-pelletier-go-toml - - - name: Toml Lint Test -- Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Toml Lint Test -- Install Template Requirements - run: | - source "./template/.github/scripts/task-template-requirements.sh" - - - name: Toml Lint Test -- Render Template - run: | - source "./template/.github/scripts/task-render-template.sh" - - - name: Toml Lint Test -- Run Linter (Template) - run: | - cd template - find . -type f -name "*.toml" -exec tomll "{}" \; - git diff --exit-code - - - name: Toml Lint -- Run Linter (Rendered Tempalte) - run: | - cd "${TEMPLATED_NAME}" - find . -type f -name "*.toml" -exec tomll "{}" \; - git diff --exit-code - - - name: Toml Lint Test -- Report Job Status (Success) - if: fromJSON(inputs.CONFIGURATION)._GITHUB_CI_VERBOSE_NOTIFICATIONS == true - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: toml linting was successful!" - - - name: Toml Lint Test -- Report Job Status (Failure) - if: failure() - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: toml linting has failed!" diff --git a/.github/workflows/.job-80-workflow-lint.yml b/.github/workflows/.job-80-workflow-lint.yml deleted file mode 100644 index dd2e8757..00000000 --- a/.github/workflows/.job-80-workflow-lint.yml +++ /dev/null @@ -1,73 +0,0 @@ ---- -name: profile-generator-job-workflow-lint - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -env: - PROFILE_GENERATOR_SKIP_POETRY: 1 - PROFILE_GENERATOR_SKIP_PRECOMMIT: 1 - -jobs: - - run_actionlint: - - runs-on: ubuntu-latest - - steps: - - name: Workflow Lint -- Checkout Repository - uses: actions/checkout@v3 - with: - path: 'template' - - - name: Workflow Lint -- Setup Environment - run: | - source "./template/.github/scripts/workflow-setup-environment.sh" - env: - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Workflow Lint -- Install Binary - run: | - bash <(curl "${{ fromJson(inputs.CONFIGURATION)._GITHUB_CI_ACTIONLINT_SCRIPT_URL }}") - - - name: Workflow Lint -- Set up Python ${{ fromJson(inputs.CONFIGURATION)._MAC_MAKER_PYTHON_VERSION }} - uses: actions/setup-python@v4 - with: - python-version: ${{ fromJson(inputs.CONFIGURATION)._MAC_MAKER_PYTHON_VERSION }} - - - name: Workflow Lint -- Install Template Requirements - run: | - source "./template/.github/scripts/task-template-requirements.sh" - - - name: Workflow Lint -- Render Template - run: | - source "./template/.github/scripts/task-render-template.sh" - - - name: Workflow Lint -- Run Linter (Template) - run: | - cd template - ../actionlint -color - - - name: Workflow Lint -- Run Linter (Rendered Template) - run: | - cd "${TEMPLATED_NAME}" - ../actionlint -color - - - name: Workflow Lint -- Report Job Status (Success) - if: fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS == true - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: workflow linting was successful!" - - - name: Workflow Lint -- Report Job Status (Failure) - if: failure() - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: workflow linting has failed!" diff --git a/.github/workflows/.job-80-yaml-lint.yml b/.github/workflows/.job-80-yaml-lint.yml deleted file mode 100644 index 1f2a29a4..00000000 --- a/.github/workflows/.job-80-yaml-lint.yml +++ /dev/null @@ -1,83 +0,0 @@ ---- -name: ansible-workbench-job-yaml-lint - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -env: - ANSIBLE_WORKBENCH_SKIP_POETRY: 1 - ANSIBLE_WORKBENCH_SKIP_PRECOMMIT: 1 - -jobs: - - run_yamllint: - - runs-on: ubuntu-latest - strategy: - fail-fast: true - matrix: - cookiecutter-toml-selection: [1, 2] - cookiecutter-workflow-selection: [1, 2] - python-version: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS }} - max-parallel: ${{ fromJSON(inputs.CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} - - steps: - - name: Yaml Lint Test -- Checkout Repository - uses: actions/checkout@v3 - with: - path: 'template' - - - name: Yaml Lint Test -- Setup Environment - run: | - source "./template/.github/scripts/workflow-setup-environment.sh" - env: - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Yaml Lint Test -- Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Yaml Lint Test-- Install Template Requirements - run: | - source "./template/.github/scripts/task-template-requirements.sh" - - - name: Yaml Lint Test -- Render Template - run: | - source "./template/.github/scripts/task-render-template.sh" "${SELECTION_TOML}" "${SELECTION_WORKFLOW}" - env: - SELECTION_TOML: ${{ matrix.cookiecutter-toml-selection }} - SELECTION_WORKFLOW: ${{ matrix.cookiecutter-workflow-selection }} - - - name: Yaml Lint Test -- Lint Template GitHub Workflows - uses: ibiqlik/action-yamllint@v3 - with: - format: standard - file_or_dir: ./template/.github/workflows - config_file: ./${{ env.TEMPLATED_NAME }}/.yamllint.yml - - - name: Yaml Lint Test -- Lint Rendered GitHub Workflows - uses: ibiqlik/action-yamllint@v3 - with: - format: standard - file_or_dir: ./${{ env.TEMPLATED_NAME }}/.github/workflows/*.yml -c ./${{ env.TEMPLATED_NAME }}/.yamllint.yml - config_file: ./${{ env.TEMPLATED_NAME }}/.yamllint.yml - - - name: Yaml Lint Test -- Report Job Status on Success - if: fromJSON(inputs.CONFIGURATION)._GITHUB_CI_VERBOSE_NOTIFICATIONS == true - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: YAML linting (toml-${{ matrix.cookiecutter-toml-selection }}, workflow-${{ matrix.cookiecutter-workflow-selection }}) checks succeeded!" - - - name: Yaml Lint Test -- Report Job Status on Failure - if: failure() - run: | - "./template/{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: YAML linting (toml-${{ matrix.cookiecutter-toml-selection }}, workflow-${{ matrix.cookiecutter-workflow-selection }}) checks failed!" diff --git a/.github/workflows/.job-99-create-release.yml b/.github/workflows/.job-99-create-release.yml deleted file mode 100644 index 924b4d09..00000000 --- a/.github/workflows/.job-99-create-release.yml +++ /dev/null @@ -1,83 +0,0 @@ ---- -name: ansible-workbench-job-release - -on: - workflow_call: - inputs: - CONFIGURATION: - description: "The 'cookiecutter.json' file as a configuration object." - required: true - type: string - secrets: - SLACK_WEBHOOK: - description: "Optional, enables Slack notifications." - required: false - -jobs: - - generate_github_release: - - runs-on: ubuntu-latest - - steps: - - name: Create Release -- Checkout Repository - uses: actions/checkout@v3 - - - name: Create Release -- Filter Release Candidates - id: filter - run: | - source "./{{cookiecutter.project_slug}}/.github/scripts/job-99-release-candidate.sh" "${{ github.event.ref }}" - - - name: Create Release -- Checkout Repository (All Commits) - if: steps.filter.outputs.release_candidate == 'TRUE' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Create Release -- Setup Environment - if: steps.filter.outputs.release_candidate == 'TRUE' - run: | - source "./template/.github/scripts/workflow-setup-environment.sh" - env: - WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }} - - - name: Create Release -- Install Poetry - if: steps.filter.outputs.release_candidate == 'TRUE' - run: | - source "./{{cookiecutter.project_slug}}/.github/scripts/task-use-poetry.sh" "install-poetry" - - - name: Create Release -- Check 'pyproject.toml' Matches Tag - if: steps.filter.outputs.release_candidate == 'TRUE' - run: | - source "./{{cookiecutter.project_slug}}/.github/scripts/task-99-check-version.sh" - - - name: Create Release -- Generate Changelog - if: steps.filter.outputs.release_candidate == 'TRUE' - run: - source "./{{cookiecutter.project_slug}}/.github/scripts/job-99-create-changelog.sh" - - - name: Create Release -- Generate GitHub Release Draft - if: steps.filter.outputs.release_candidate == 'TRUE' - id: create_release - uses: actions/github-script@v6 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const script = require('./{{cookiecutter.project_slug}}/.github/scripts/job-99-create-release.js'); - const body = process.env.CHANGE_LOG_CONTENT + "\n" + process.env.CHECK_LIST_CONTENT; - const tag = process.env.BRANCH_OR_TAG; - await script({ body, context, core, github, tag }) - env: - CHECK_LIST_CONTENT: | - ## Deployment Checklist - - [] Ensure master points to new tag - - - name: Create Release -- Report Job Status on Success - if: steps.filter.outputs.release_candidate == 'TRUE' - run: | - "./{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":white_check_mark: automated release has been created!\nhttps://github.com/${USER_NAME}/${PROJECT_NAME}/releases" - - - name: Create Release -- Report Job Status on Failure - if: failure() - run: | - "./{{cookiecutter.project_slug}}/.github/scripts/task-slack-notification.sh" "${NOTIFICATION}" ":x: automated release creation has failed!" diff --git a/.github/workflows/workflow-template-test.yml b/.github/workflows/workflow-template-test.yml index 1f804c56..57dcc080 100644 --- a/.github/workflows/workflow-template-test.yml +++ b/.github/workflows/workflow-template-test.yml @@ -1,5 +1,5 @@ --- -name: ansible-workbench-workflow-template-test +name: ansible-workbench-github-workflow-template-test on: push: @@ -9,7 +9,7 @@ on: # secrets: # REMOTE_TOKEN: -# description: "GitHub token with access to the test repository." +# description: "GitHub token with access to the test repository." # required: true # REMOTE_ORIGIN: # description: "Identifies the test repository as: owner/repository" @@ -21,108 +21,225 @@ on: jobs: configuration: - - runs-on: ubuntu-latest - outputs: - configuration: ${{ steps.cookiecutter_configuration.outputs.value }} - - steps: - - name: Create Configuration -- Checkout Repository - uses: actions/checkout@v3 - - - name: Create Configuration -- Set Cookiecutter Configuration as Output - id: cookiecutter_configuration - run: | - source "./{{cookiecutter.project_slug}}/.github/scripts/workflow-set-value.sh" cat "cookiecutter.json" + uses: cicd-tools-org/cicd-tools/.github/workflows/job-00-cookiecutter-read_configuration.yml@master start: - secrets: inherit - uses: niall-byrne/cicd-tools/.github/workflows/.job-00-notification.yml@master + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-00-generic-notification.yml@master with: - NOTIFICATION_EMOJI: ":white_check_mark:" + NOTIFICATION_EMOJI: ":vertical_traffic_light:" NOTIFICATION_MESSAGE: "workflow has started!" - success: - needs: [start] - secrets: inherit - uses: niall-byrne/cicd-tools/.github/workflows/.job-00-notification.yml@master + security: + needs: [configuration] + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-10-generic-security_scan_credentials.yml@master with: - NOTIFICATION_EMOJI: ":checkered_flag:" - NOTIFICATION_MESSAGE: "workflow has completed successfully!" + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} - security_test: + markdown_links: needs: [configuration] - secrets: inherit - uses: niall-byrne/cicd-tools/.github/workflows/.job-10-security.yml@master + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-30-cookiecutter-markdown_links.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + CONFIG_FILE: "{{cookiecutter.project_slug}}/.github/config/actions/gaurav-nelson-github-action-markdown-link-check.json" + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} - documentation_test: + pre-commit_hooks: needs: [configuration] - uses: ./.github/workflows/.job-30-documentation.yml + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-50-cookiecutter-test_precommit_hooks.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} - secrets: inherit + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + ENABLED_PRECOMMIT_CHECKS: '["test_ansible_lint_fails", "test_commit_lint_fails", "test_commit_spelling_fails", "test_toml_lint_fails", "test_toml_lint_passes", "test_shell_lint_fails", "test_shell_format_fails", "test_workflow_lint_fails", "test_workflow_header_lint_fails"]' + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + TEMPLATE_SCENARIOS: ${{ needs.configuration.outputs.COOKIECUTTER_ALL_SCENARIOS }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} - molecule_lint_test: + ansible_lint: needs: [configuration] - uses: ./.github/workflows/.job-40-molecule.yml + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-40-cookiecutter-ansible_lint.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} - secrets: inherit + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} - precommit_test: + commit_lint: + needs: [configuration] + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-80-poetry-rev_range_command.yml@master + with: + COMMAND: | + poetry run cz check --rev-range "${PUSHED_COMMIT_REV_RANGE}" + COMMAND_NAME: "Commit Message Lint" + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + REV_RANGE: "2f0ced23209eb629e07299e3aadeb04e58720932..HEAD" + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} + + commit_spell_check: needs: [configuration] - uses: ./.github/workflows/.job-50-precommit.yml + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-80-poetry-rev_range_command.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} - secrets: inherit + COMMAND: | + CICD_COMMIT_MESSAGES_FILE="$(mktemp XXXXXXXX.git_history_file)" + git log --pretty=format:%s "${PUSHED_COMMIT_REV_RANGE}" > "${CICD_COMMIT_MESSAGES_FILE}" + poetry run pre-commit run --hook-stage commit-msg spelling-commit-message --commit-msg-filename "${CICD_COMMIT_MESSAGES_FILE}" + COMMAND_NAME: "Commit Message Spelling" + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + REV_RANGE: "f86a1787cf52122495717813ee87b0da6a46eb52..HEAD" + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} + + json_schema_lint: + needs: [configuration] + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-80-poetry-precommit_commit_stage_hook.yml@master + with: + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PRECOMMIT_HOOK_ID: "check-jsonschema" + PRECOMMIT_HOOK_NAME: "Cookiecutter JSON Schema Linter" + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} - push_repository_test: - needs: [configuration, commit_lint_test, documentation_test, molecule_lint_test, precommit_test, security_test, shellcheck_test, start, toml_lint_test, yaml_lint_test] - uses: ./.github/workflows/.job-60-remote-push.yml + markdown_lint: + needs: [configuration] + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-80-cookiecutter-precommit_commit_stage_hook.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} - secrets: inherit + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PRECOMMIT_HOOK_ID: "lint-markdown" + PRECOMMIT_HOOK_NAME: "Markdown Linting" + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} - commit_lint_test: + markdown_spelling: needs: [configuration] - uses: ./.github/workflows/.job-80-commit-lint.yml + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-80-cookiecutter-precommit_commit_stage_hook.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} - secrets: inherit + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PRECOMMIT_HOOK_ID: "spelling-markdown" + PRECOMMIT_HOOK_NAME: "Markdown Spelling" + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} - shellcheck_test: + shell_lint: needs: [configuration] - uses: ./.github/workflows/.job-80-shell-lint.yml + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + strategy: + fail-fast: true + matrix: + hook: + - id: "format-shell" + name: "Shell Formatting" + - id: "lint-shell" + name: "Shell Linting" + max-parallel: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-80-cookiecutter-precommit_commit_stage_hook.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} - secrets: inherit + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PRECOMMIT_HOOK_ID: ${{ matrix.hook.id }} + PRECOMMIT_HOOK_NAME: ${{ matrix.hook.name }} + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} - toml_lint_test: + toml_lint: needs: [configuration] - uses: ./.github/workflows/.job-80-toml-lint.yml + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-80-cookiecutter-precommit_commit_stage_hook.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} - secrets: inherit + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PRECOMMIT_HOOK_ID: "format-toml" + PRECOMMIT_HOOK_NAME: "TOML Formatting" + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} - workflow_lint_test: + workflow_lint: needs: [configuration] - uses: ./.github/workflows/.job-80-workflow-lint.yml + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + strategy: + fail-fast: true + matrix: + hook: + - id: "lint-github-workflow" + name: "Workflow Linting" + - id: "lint-github-workflow-header" + name: "Workflow Header Linting" + max-parallel: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-80-cookiecutter-precommit_commit_stage_hook.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} - secrets: inherit + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PRECOMMIT_HOOK_ID: ${{ matrix.hook.id }} + PRECOMMIT_HOOK_NAME: ${{ matrix.hook.name }} + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} - yaml_lint_test: + yaml_lint: needs: [configuration] - uses: ./.github/workflows/.job-80-yaml-lint.yml + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-80-cookiecutter-precommit_commit_stage_hook.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} - secrets: inherit + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PRECOMMIT_HOOK_ID: "yamllint" + PRECOMMIT_HOOK_NAME: "YAML Linting" + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + TEMPLATE_SCENARIOS: ${{ needs.configuration.outputs.COOKIECUTTER_ALL_SCENARIOS }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} + + remote_push: + needs: [ansible_lint, commit_lint, commit_spell_check, configuration, json_schema_lint, markdown_links, markdown_lint, markdown_spelling, pre-commit_hooks, security, shell_lint, start, toml_lint, workflow_lint, yaml_lint] + secrets: + REMOTE_TOKEN: ${{ secrets.REMOTE_TOKEN }} + REMOTE_ORIGIN: ${{ secrets.REMOTE_ORIGIN }} + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-60-cookiecutter-remote_push.yml@master + with: + CONCURRENCY: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_CONCURRENCY }} + PYTHON_VERSIONS: ${{ toJSON(fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_PYTHON_VERSIONS) }} + REMOTE_TEST_TAG: "0.1.0" + REMOTE_RELEASE_WORKFLOW: "" + SCENARIO_TRIGGER_BASE_BRANCH_PUSH: "0.toml_linting-0.workflow_linting" + SCENARIO_TRIGGER_DEV_BRANCH_PUSH: "1.toml_linting-1.workflow_linting" + SCENARIO_TRIGGER_TAG_PUSH: "0.toml_linting-0.workflow_linting" + TEMPLATE_SCENARIOS: ${{ needs.configuration.outputs.COOKIECUTTER_ALL_SCENARIOS }} + VERBOSE_NOTIFICATIONS: ${{ fromJSON(needs.configuration.outputs.COOKIECUTTER_CONFIGURATION)._GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS }} create_release: - needs: [configuration, push_repository_test] - uses: ./.github/workflows/.job-99-create-release.yml + permissions: + contents: write + needs: [configuration, remote_push] + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-99-poetry-create_release.yml@master with: - CONFIGURATION: ${{ needs.configuration.outputs.configuration }} - secrets: inherit + APPENDED_CONTENT: | + ## Release Checklist + - [] Ensure master is synchronized with the release tag. + + success: + needs: [create_release] + secrets: + SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }} + uses: cicd-tools-org/cicd-tools/.github/workflows/job-00-generic-notification.yml@master + with: + NOTIFICATION_EMOJI: ":checkered_flag:" + NOTIFICATION_MESSAGE: "workflow has completed successfully!" diff --git a/.gitignore b/.gitignore index 2fe16fa5..5d4bc707 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ *.retry .ansible .cache +.cicd-tools/boxes/* +!.cicd-tools/boxes/bootstrap .idea .tool-versions poetry.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9a157292..2dafac6c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,47 +1,60 @@ --- +default_install_hook_types: + - pre-commit + - commit-msg repos: - - repo: local + - repo: https://github.com/commitizen-tools/commitizen + rev: v2.42.1 hooks: - - id: commit-lint - name: commit-lint - description: "Check whether the commit message follows committing rules." - entry: ./{{cookiecutter.project_slug}}/.pre-commit/commit-lint.sh - language: system + - id: commitizen stages: [commit-msg] - - id: shell-lint - name: shell-lint - description: "Lint the project's shell scripts." - entry: shellcheck - files: "^.+\\.sh$" - exclude: "^{{cookiecutter.project_slug}}/.+$" - language: system - pass_filenames: true - stages: [commit] - - id: toml-lint - name: toml-lint - description: "Lint the project's TOML files." - entry: ./{{cookiecutter.project_slug}}/.pre-commit/toml-lint.sh - files: "^.+\\.toml$" - language: system - stages: [commit] - pass_filenames: true - - id: yaml-lint - name: yaml-lint - description: "Lint the project's YAML files." - entry: ./{{cookiecutter.project_slug}}/.pre-commit/yaml-lint.sh - args: ["-c", "./{{cookiecutter.project_slug}}/.yamllint.yml"] - files: "^.+\\.(yaml|yml)$" - exclude: "^{{cookiecutter.project_slug}}/.+$" - language: system - pass_filenames: true + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.22.0 + hooks: + - id: check-jsonschema + name: check-cookiecutter-schema + files: "^(cookiecutter\\.json|\\.github/scenarios/.*\\.json)$" + args: + - "--schemafile" + - ".cicd-tools/boxes/bootstrap/schemas/cookiecutter.json" stages: [commit] - - id: workflow-lint - name: workflow-lint - description: "Lint the project's GitHub workflow files." - entry: actionlint - files: "^.github/.+\\.(yaml|yml)$" - exclude: "^{{cookiecutter.project_slug}}/.+$" - language: system - pass_filenames: false - require_serial: true + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.29.0 + hooks: + - id: yamllint + args: + - "-c" + - "./{{cookiecutter.project_slug}}/.yamllint.yml" + exclude: "^{{cookiecutter\\.project_slug}}/.+$" stages: [commit] + - repo: https://github.com/cicd-tools-org/pre-commit.git + rev: 0.1.0 + hooks: + - id: format-shell + args: + - "-w" + - "--indent=2" + - "-ci" + - "-sr" + exclude: "^{{cookiecutter\\.project_slug}}/.+$|^\\.cicd-tools/boxes/.+$" + files: "^.+\\.(bash|sh)$" + - id: format-toml + - id: lint-github-workflow + exclude: "^{{cookiecutter\\.project_slug}}/.+$" + - id: lint-github-workflow-header + exclude: "^{{cookiecutter\\.project_slug}}/.+$" + - id: lint-markdown + args: + - "-c" + - "{{cookiecutter.project_slug}}/.markdownlint.yml" + exclude: "^{{cookiecutter\\.project_slug}}/.+$" + - id: lint-shell + args: + - "--color=always" + - "--source-path=SCRIPTDIR" + - "--exclude=SC2317" + - "-x" + exclude: "^{{cookiecutter\\.project_slug}}/.+$|^\\.cicd-tools/boxes/.+$" + - id: spelling-commit-message + - id: spelling-markdown + exclude: "^{{cookiecutter\\.project_slug}}/.+$" diff --git a/.vale.ini b/.vale.ini new file mode 100644 index 00000000..9696b6f5 --- /dev/null +++ b/.vale.ini @@ -0,0 +1,10 @@ +StylesPath = ".vale" +Vocab = "ansible-workbench" + +[*] +BasedOnStyles = Vale +Vale.Terms = NO + +[*.md] +BasedOnStyles = Vale +Vale.Terms = YES diff --git a/.vale/Vocab/ansible-workbench/accept.txt b/.vale/Vocab/ansible-workbench/accept.txt new file mode 100644 index 00000000..c0f80f18 --- /dev/null +++ b/.vale/Vocab/ansible-workbench/accept.txt @@ -0,0 +1,23 @@ +(A|a)nsible +(D|d)ev +(P|p)osix +Exe +actionlint +agentless +commitizen +config +cookiecutter +dependabot +gitleaks +golang +json|JSON +rebase +rebasing +shellcheck +shfmt +templated +tomll +trufflehog +virtualenv +yamllint +yaml|YAML diff --git a/.vale/Vocab/ansible-workbench/reject.txt b/.vale/Vocab/ansible-workbench/reject.txt new file mode 100644 index 00000000..e69de29b diff --git a/README.md b/README.md index b6fedef5..e8315880 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Modify the templated [requirements.yml]({{cookiecutter.project_slug}}/requiremen The [pyproject.toml]({{cookiecutter.project_slug}}/pyproject.toml) file is there to store your project's Python dependencies in accordance with [PEP 518](https://www.python.org/dev/peps/pep-0518/). -[Poetry](https://python-poetry.org/docs/pyproject/#dependencies-and-dev-dependencies) is leveraged to manage the Python dependencies: +[Poetry](https://python-poetry.org/docs/managing-dependencies/) is leveraged to manage the Python dependencies: - [Adding Python Packages with Poetry](https://python-poetry.org/docs/cli/#add) - [Removing Python Packages With Poetry](https://python-poetry.org/docs/cli/#remove) @@ -93,7 +93,7 @@ A fundamental pillar of Ansible Workbench is the use of [Conventional Commits](h #### 1. Why Conventional Commits? - Following this standard has numerous advantages, but among the largest is its tight integration with [Semantic Versioning](https://semver.org/). -- For the Ansible Workbench CI/CD in particular, [changelog generation]({{cookiecutter.project_slug}}/.github/scripts/job-99-create-changelog.sh) and [release automation]({{cookiecutter.project_slug}}/.github/workflows/workflow-publish-to-galaxy.yml) is made possible through adherence to this format. +- For the Ansible Workbench CI/CD in particular, [changelog generation]({{cookiecutter.project_slug}}/.github/scripts/job-99-create-changelog.sh) and [release automation]({{cookiecutter.project_slug}}/.github/deactivated/workflow-publish-to-galaxy.yml) is made possible through adherence to this format. - Being able to read commits from different people that conform to common standard also makes [interactive rebasing](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) relatively painless. #### 2. Making A Conventional Commit With Commitizen @@ -120,13 +120,13 @@ A fundamental pillar of Ansible Workbench is the use of [Conventional Commits](h - To ensure the CI passes, you could [trigger a manual run](https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow) of the workflow. ## Writing Molecule Tests with Ansible Workbench -There are 3 example [Molecule scenarios](https://molecule.readthedocs.io/en/stable/configuration.html#scenario) created during templating: +There are 3 example [Molecule scenarios](https://molecule.readthedocs.io/getting-started/#molecule-scenarios) created during templating: | Scenario Name | Description | | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | | [default]({{cookiecutter.project_slug}}/molecule/default/molecule.yml) | Intended as a configuration reference and for use in linting and caching dependencies. | | [docker1]({{cookiecutter.project_slug}}/molecule/docker1/molecule.yml) | An example using Molecule's [docker](https://github.com/ansible-community/molecule-plugins) driver. | -| [hostmachine1]({{cookiecutter.project_slug}}/molecule/hostmachine1/molecule.yml) | An example using Molecule's [delegated](https://molecule.readthedocs.io/en/stable/configuration.html#delegated) driver. | +| [hostmachine1]({{cookiecutter.project_slug}}/molecule/hostmachine1/molecule.yml) | An example using Molecule's [delegated](https://molecule.readthedocs.io/configuration/#delegated) driver. | ### 1. The Recommended Molecule Scenario Workflow @@ -141,7 +141,7 @@ To add tests to your role, create new scenarios with Molecule: #### iii. Add Your New Scenarios to CI/CD -If you are using the rendered [GitHub CI/CD]({{cookiecutter.project_slug}}/.github/workflows/workflow-push.yml), make sure to add your new scenario to the list that are tested in the `molecule_test` step. +If you are using the rendered [GitHub CI/CD]({{cookiecutter.project_slug}}/.github/deactivated/workflow-push.yml), make sure to add your new scenario to the list that are tested in the `molecule_test` step. ### 2. The Recommended Ansible Galaxy Settings @@ -152,7 +152,7 @@ Connections to the [Ansible Galaxy](https://galaxy.ansible.com/) API can sometim In your Molecule Scenario file set the dependency option `force: false` as shown [here]({{cookiecutter.project_slug}}/molecule/default/molecule.yml). In concert with this setting, it's also strongly recommended to avoid modifying the [default]({{cookiecutter.project_slug}}/molecule/default/molecule.yml) Molecule scenario: -- You'll only download dependencies the first time they are needed, or [when they change]({{cookiecutter.project_slug}}/.pre-commit/molecule-lint.sh). +- You'll only download dependencies the first time they are needed, or [when they change]({{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/ansible-lint.sh). - This will also help you get the most speed out of the default CI/CD configuration. #### ii. Set a Generous Timeout Value @@ -163,11 +163,11 @@ In your Molecule Scenario file set the dependency option `timeout: 120` as shown The python library [pre-commit](https://pre-commit.com/) is installed during templating with a few useful initial hooks. ### Included Pre-Commit Hooks: -| Hook Name | Description | -| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [commit-lint]({{cookiecutter.project_slug}}/.pre-commit/commit-lint.sh) | Runs [commitizen](https://commitizen-tools.github.io/commitizen/) on your commit message to validate it. | -| [molecule-lint]({{cookiecutter.project_slug}}/.pre-commit/molecule-lint.sh) | Runs [ansible-lint](https://ansible-lint.readthedocs.io/) and [yamllint](https://yamllint.readthedocs.io/en/stable/) to checks your role for best Ansible practices and behaviour. | -| [toml-lint]({{cookiecutter.project_slug}}/.pre-commit/toml-lint.sh) | Optionally runs [tomll](https://github.com/pelletier/go-toml) on your TOML configuration file. | +| Hook Name | Description | +| ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [commit-lint](https://github.com/commitizen-tools/commitizen) | Runs [commitizen](https://commitizen-tools.github.io/commitizen/) on your commit message to validate it. | +| [molecule-lint]({{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/ansible-lint.sh) | Runs [ansible-lint](https://ansible-lint.readthedocs.io/) and [yamllint](https://yamllint.readthedocs.io/en/stable/) to checks your role for best Ansible practices and behaviour. | +| [toml-lint]({{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit//toml-lint.sh) | Optionally runs [tomll](https://github.com/pelletier/go-toml) on your TOML configuration file. | ## Using GitHub with Ansible Workbench @@ -201,7 +201,7 @@ You'll need to create some [secrets](https://docs.github.com/en/actions/security To make the most out of your [templated CI/CD]({{cookiecutter.project_slug}}/.github/workflows), create the following secrets: - `SLACK_WEBHOOK`: This secret value can optionally be set to a [Slack Webhook](https://api.slack.com/messaging/webhooks) you can configure to get status updates on how your commit is proceeding through the CI/CD. - - The verbosity of this integration can be controlled by setting the `VERBOSE_NOTIFICATIONS` environment variable to 1 in [this]({{cookiecutter.project_slug}}/.github/workflows/workflow-push.yml) workflow. + - The verbosity of this integration can be controlled by setting the `VERBOSE_NOTIFICATIONS` environment variable to 1 in [this]({{cookiecutter.project_slug}}/.github/deactivated/workflow-push.yml) workflow. - See this documentation on how to create a [Slack Webhook](https://api.slack.com/messaging/webhooks) for your team. - `GALAXY_API_KEY`: This secret API key can be found on your [Ansible Galaxy](https://galaxy.ansible.com/) account page, and enables automated publishing to Galaxy. - If you do not wish to publish your role, simply leave this secret unset. @@ -223,7 +223,7 @@ To make the most out of your [templated CI/CD]({{cookiecutter.project_slug}}/.gi | step | description | | ---------------------- | ------------------------------------------------------------------------------------- | | **molecule_lint_test** | Runs [molecule lint]({{cookiecutter.project_slug}}/molecule/default/molecule.yml) on the default scenario to ensure Ansible best practices. | -| **molecule_test** | Runs [molecule test](https://molecule.readthedocs.io/en/latest/usage/#valid-actions) on each molecule [scenario](https://molecule.readthedocs.io/en/latest/configuration/#scenario) found [here]({{cookiecutter.project_slug}}/molecule).| +| **molecule_test** | Runs [molecule test](https://molecule.readthedocs.io/usage/#valid-actions) on each molecule [scenario](https://molecule.readthedocs.io/configuration/#scenario) found [here]({{cookiecutter.project_slug}}/molecule).| ##### Third Stage: @@ -269,7 +269,7 @@ Tag your release with [Semantic Versioning](https://semver.org/). (Avoid prefix #### iv. Publishing Your Release to Ansible Galaxy - If you have configured a [secret](#2-Setting-Up-Your-CICD) for Ansible Galaxy more automation will now begin **after** you've published your GitHub release. -- The [release workflow]({{cookiecutter.project_slug}}/.github/workflows/workflow-publish-to-galaxy.yml) will be triggered, and will publish your release automatically to [Ansible Galaxy](https://galaxy.ansible.com/). +- The [release workflow]({{cookiecutter.project_slug}}/.github/deactivated/workflow-publish-to-galaxy.yml) will be triggered, and will publish your release automatically to [Ansible Galaxy](https://galaxy.ansible.com/). ## License diff --git a/cookiecutter.json b/cookiecutter.json index 19b893e6..ca5d9224 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -12,18 +12,18 @@ "email": "niall@niallbyrne.ca", "_BRANCH_NAME_BASE": "master", "_BRANCH_NAME_DEVELOPMENT": "dev", - "_GITHUB_ACTION_CACHE": "actions/cache@v3", - "_GITHUB_ACTION_CHECKOUT": "actions/checkout@v3", - "_GITHUB_ACTION_MARKDOWN_LINK_CHECK": "gaurav-nelson/github-action-markdown-link-check@v1", - "_GITHUB_ACTION_PYTHON": "actions/setup-python@v4", - "_GITHUB_ACTION_SCRIPT": "actions/github-script@v6", - "_GITHUB_ACTION_TRUFFLEHOG": "trufflesecurity/trufflehog@v3.28.0", - "_GITHUB_ACTION_YAMLLINT": "ibiqlik/action-yamllint@v3", + "_DOCKER_DEFAULT_CONTAINER": "ghcr.io/cicd-tools-org/cicd-tools:master", "_GITHUB_CI_ACTIONLINT_SCRIPT_URL": "https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash", "_GITHUB_CI_DEFAULT_CONCURRENCY": 4, "_GITHUB_CI_DEFAULT_PYTHON_VERSIONS": ["3.9"], "_GITHUB_CI_DEFAULT_MOLECULE_TEST_PLATFORMS": ["ubuntu-latest"], "_GITHUB_CI_DEFAULT_MOLECULE_EXCLUDED_SCENARIOS_REGEX": "^default$|^noci-.*$", "_GITHUB_CI_DEFAULT_VERBOSE_NOTIFICATIONS": false, - "*DO_NOT_MODIFY_THIS_FILE*": "This file is created to assist with upgrading to future versions of this template." + "_*DO_NOT_MODIFY_THIS_FILE*": "This file is created to assist with upgrading to future versions of this template.", + "_copy_without_render": [ + ".cicd-tools/boxes/bootstrap", + ".github/actions", + ".github/workflows/job*", + "scripts" + ] } diff --git a/hooks/post_gen_project.sh b/hooks/post_gen_project.sh index bf7f9cb0..cc094879 100644 --- a/hooks/post_gen_project.sh +++ b/hooks/post_gen_project.sh @@ -1,40 +1,39 @@ #!/bin/bash -# hooks/post_gen_project.sh # Configures the templated profile for use. -# ANSIBLE_WORKBENCH_BRANCH_NAME_BASE: Optional alternate base branch name. -# ANSIBLE_WORKBENCH_BRANCH_NAME_DEVELOPMENT: Optional alternate development branch name. -# ANSIBLE_WORKBENCH_SKIP_GIT_INIT: Optionally set to 1 to skip creating branches and initial commit. -# ANSIBLE_WORKBENCH_SKIP_POETRY: Optionally set to 1 to skip installing dependencies. -# ANSIBLE_WORKBENCH_SKIP_PRECOMMIT: Optionally set to 1 to skip installing pre-commit hooks. +# TEMPLATE_BRANCH_NAME_BASE: Optional alternate base branch name. +# TEMPLATE_BRANCH_NAME_DEVELOPMENT: Optional alternate development branch name. +# TEMPLATE_SKIP_GIT_INIT: Optionally set to 1 to skip creating branches and initial commit. +# TEMPLATE_SKIP_POETRY: Optionally set to 1 to skip installing dependencies. +# TEMPLATE_SKIP_PRECOMMIT: Optionally set to 1 to skip installing pre-commit hooks. -# cookiecutter only script. +# Cookiecutter only script. set -eo pipefail -ANSIBLE_WORKBENCH_BRANCH_NAME_BASE="${ANSIBLE_WORKBENCH_BRANCH_NAME_BASE-"{{ cookiecutter._BRANCH_NAME_BASE }}"}" -ANSIBLE_WORKBENCH_BRANCH_NAME_DEVELOPMENT="${ANSIBLE_WORKBENCH_BRANCH_NAME_DEVELOPMENT-"{{ cookiecutter._BRANCH_NAME_DEVELOPMENT }}"}" -ANSIBLE_WORKBENCH_TEMPLATE_URL="https://github.com/niall-byrne/ansible-workbench.git" +TEMPLATE_BRANCH_NAME_BASE="${TEMPLATE_BRANCH_NAME_BASE-"{{ cookiecutter._BRANCH_NAME_BASE }}"}" +TEMPLATE_BRANCH_NAME_DEVELOPMENT="${TEMPLATE_BRANCH_NAME_DEVELOPMENT-"{{ cookiecutter._BRANCH_NAME_DEVELOPMENT }}"}" +TEMPLATE_URL="https://github.com/niall-byrne/ansible-workbench.git" initialize_git() { - if [[ "${ANSIBLE_WORKBENCH_SKIP_GIT_INIT}" != "1" ]]; then + if [[ "${TEMPLATE_SKIP_GIT_INIT}" != "1" ]]; then git init - git checkout -b "${ANSIBLE_WORKBENCH_BRANCH_NAME_BASE}" + git checkout -b "${TEMPLATE_BRANCH_NAME_BASE}" git stage . git commit -m "build(COOKIECUTTER): initial generation" - git symbolic-ref HEAD "refs/heads/${ANSIBLE_WORKBENCH_BRANCH_NAME_BASE}" - git tag 0.0.0 - git checkout -b "${ANSIBLE_WORKBENCH_BRANCH_NAME_DEVELOPMENT}" - mkdir -p files templates + git symbolic-ref HEAD "refs/heads/${TEMPLATE_BRANCH_NAME_BASE}" + git checkout -b "${TEMPLATE_BRANCH_NAME_DEVELOPMENT}" + git checkout "${TEMPLATE_BRANCH_NAME_BASE}" + mkdir -p templates fi } initialize_poetry() { - if [[ "${ANSIBLE_WORKBENCH_SKIP_POETRY}" != "1" ]]; then + if [[ "${TEMPLATE_SKIP_POETRY}" != "1" ]]; then poetry install --verbose fi @@ -42,18 +41,18 @@ initialize_poetry() { initialize_precommit() { - if [[ "${ANSIBLE_WORKBENCH_SKIP_PRECOMMIT}" != "1" ]]; then - poetry run pre-commit install -t pre-commit -t commit-msg + if [[ "${TEMPLATE_SKIP_PRECOMMIT}" != "1" ]]; then + poetry run pre-commit install poetry run molecule dependency fi } -update_template_values() { +rewrite_source() { - if ! grep "${ANSIBLE_WORKBENCH_TEMPLATE_URL}" .cookiecutter/cookiecutter.json; then - # sed compatible with Linux and BSD - sed -i.bak 's,"_template": ".*","_template": "'"${ANSIBLE_WORKBENCH_TEMPLATE_URL}"'",g' .cookiecutter/cookiecutter.json + if ! grep "${TEMPLATE_URL}" .cookiecutter/cookiecutter.json; then + # sed compatible with Linux and BSD + sed -i.bak 's,"_template": ".*","_template": "'"${TEMPLATE_URL}"'",g' .cookiecutter/cookiecutter.json rm .cookiecutter/cookiecutter.json.bak fi @@ -61,7 +60,7 @@ update_template_values() { main() { - update_template_values + rewrite_source initialize_git initialize_poetry initialize_precommit diff --git a/pyproject.toml b/pyproject.toml index ab26eb9e..b743624a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,25 +1,24 @@ - [build-system] - build-backend = "poetry.core.masonry.api" - requires = ["poetry-core"] +build-backend = 'poetry.core.masonry.api' +requires = ['poetry-core'] [tool] +[tool.commitizen] +bump_message = 'bump(RELEASE): $current_version → $new_version' +pre_bump_hooks = ['.cicd-tools/boxes/bootstrap/commitizen/pre_bump.sh'] +version = '0.1.6' +version_files = ['pyproject.toml:version'] +version_provider = 'poetry' - [tool.commitizen] - bump_message = "bump(RELEASE): $current_version → $new_version" - version = "1.1.6" - version_files = ["pyproject.toml:version"] - - [tool.poetry] - authors = ["Niall Byrne "] - description = "An Ansible role development environment, powered by Molecule." - license = "MPL-2" - name = "ansible-workbench" - readme = "README.md" - version = "1.0.0" +[tool.poetry] +authors = ['Niall Byrne '] +description = 'An Ansible role development environment, powered by Molecule.' +license = 'MPL-2' +name = 'ansible-workbench' +readme = 'README.md' +version = '0.1.6' - [tool.poetry.dependencies] - commitizen = "^2.42.1" - pre-commit = "^3.1.0" - python = ">=3.9.0,<4.0" - yamllint = "^1.29.0" +[tool.poetry.dependencies] +commitizen = '^3.0.0' +pre-commit = '^3.1.0' +python = '^3.9' diff --git a/scripts/test.sh b/scripts/test.sh index b9150e10..63e51c8f 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,6 +1,5 @@ #!/bin/bash -# scripts/test.sh # Templates and builds the example role with default values for testing. # Development only script. @@ -15,10 +14,10 @@ main() { rm -rf ../flower-generator pushd .. - echo -e "\n\n${OPTIONAL_TOML_LINTING}\n${OPTIONAL_WORKFLOW_LINTING}\n\n\n\n\n\n\n\n" | cookiecutter ansible-workbench - cd flower-generator - echo -e "\nExit from this shell when finished testing ..." - bash + echo -e "\n\n${OPTIONAL_TOML_LINTING}\n${OPTIONAL_WORKFLOW_LINTING}\n\n\n\n\n\n\n\n" | cookiecutter ansible-workbench + cd flower-generator + echo -e "\nExit from this shell when finished testing ..." + poetry shell popd } diff --git a/scripts/update.sh b/scripts/update.sh index 685018cf..0def826d 100755 --- a/scripts/update.sh +++ b/scripts/update.sh @@ -35,47 +35,47 @@ error() { main() { pushd "$1" || error - if [[ "$3" == "--force" ]]; then - if git branch | grep "${ANSIBLE_WORKBENCH_UPDATE_BRANCH}"; then - set +eo pipefail - git checkout master - git reset origin/master - git clean -fd . - git branch -D "${ANSIBLE_WORKBENCH_UPDATE_BRANCH}" - rm -rf .git/cookiecutter - set -eo pipefail - fi + if [[ "$3" == "--force" ]]; then + if git branch | grep "${ANSIBLE_WORKBENCH_UPDATE_BRANCH}"; then + set +eo pipefail + git checkout master + git reset origin/master + git clean -fd . + git branch -D "${ANSIBLE_WORKBENCH_UPDATE_BRANCH}" + rm -rf .git/cookiecutter + set -eo pipefail fi - - ANSIBLE_WORKBENCH_SKIP_GIT_INIT=1 \ - ANSIBLE_WORKBENCH_SKIP_POETRY=1 \ - ANSIBLE_WORKBENCH_SKIP_PRECOMMIT=1 \ - cookiecutter_project_upgrader \ - -c .cookiecutter/cookiecutter.json \ - -b "${ANSIBLE_WORKBENCH_UPDATE_BRANCH}" \ - -u "$2" \ - -f "${ANSIBLE_WORKBENCH_TEMPLATE_SOURCE}" \ - -e "defaults" \ - -e "handlers" \ - -e "meta" \ - -e "molecule" \ - -e "tasks" \ - -e "tests" \ - -e "vars" \ - -e ".gitignore" \ - -e "pyproject.toml" \ - -e "requirements.yml" \ - -e "LICENSE" \ - -e "README.md" - - git checkout update-template - - echo -e "\n===========" - echo -e "\nThe following files differ from the template's newest version:" - git diff HEAD~1 --summary - echo -e "\nPlease review these changes carefully, and exit from this shell when finished. Nothing has been pushed or merged yet." - - bash + fi + + TEMPLATE_SKIP_GIT_INIT=1 \ + TEMPLATE_SKIP_POETRY=1 \ + TEMPLATE_SKIP_PRECOMMIT=1 \ + cookiecutter_project_upgrader \ + -c .cookiecutter/cookiecutter.json \ + -b "${ANSIBLE_WORKBENCH_UPDATE_BRANCH}" \ + -u "$2" \ + -f "${ANSIBLE_WORKBENCH_TEMPLATE_SOURCE}" \ + -e "defaults" \ + -e "handlers" \ + -e "meta" \ + -e "molecule" \ + -e "tasks" \ + -e "tests" \ + -e "vars" \ + -e ".gitignore" \ + -e "pyproject.toml" \ + -e "requirements.yml" \ + -e "LICENSE" \ + -e "README.md" + + git checkout update-template + + echo -e "\n===========" + echo -e "\nThe following files differ from the template's newest version:" + git diff HEAD~1 --summary + echo -e "\nPlease review these changes carefully, and exit from this shell when finished. Nothing has been pushed or merged yet." + + bash popd || true diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/bin/manifest.sh b/{{cookiecutter.project_slug}}/.cicd-tools/bin/manifest.sh new file mode 120000 index 00000000..a91b61bd --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/bin/manifest.sh @@ -0,0 +1 @@ +../../../.cicd-tools/bin/manifest.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/bin/toolbox.sh b/{{cookiecutter.project_slug}}/.cicd-tools/bin/toolbox.sh new file mode 120000 index 00000000..a90ea423 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/bin/toolbox.sh @@ -0,0 +1 @@ +../../../.cicd-tools/bin/toolbox.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/bin/verify.sh b/{{cookiecutter.project_slug}}/.cicd-tools/bin/verify.sh new file mode 120000 index 00000000..0dee1b26 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/bin/verify.sh @@ -0,0 +1 @@ +../../../.cicd-tools/bin/verify.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/commitizen/pre_bump.sh b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/commitizen/pre_bump.sh new file mode 120000 index 00000000..8fe43ee3 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/commitizen/pre_bump.sh @@ -0,0 +1 @@ +../../../../../.cicd-tools/boxes/bootstrap/commitizen/pre_bump.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/colours.sh b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/colours.sh new file mode 120000 index 00000000..afad31e3 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/colours.sh @@ -0,0 +1 @@ +../../../../../.cicd-tools/boxes/bootstrap/libraries/colours.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/environment.sh b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/environment.sh new file mode 120000 index 00000000..14741037 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/environment.sh @@ -0,0 +1 @@ +../../../../../.cicd-tools/boxes/bootstrap/libraries/environment.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/logging.sh b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/logging.sh new file mode 120000 index 00000000..282069c3 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/logging.sh @@ -0,0 +1 @@ +../../../../../.cicd-tools/boxes/bootstrap/libraries/logging.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/tools.sh b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/tools.sh new file mode 120000 index 00000000..257f30b5 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/libraries/tools.sh @@ -0,0 +1 @@ +../../../../../.cicd-tools/boxes/bootstrap/libraries/tools.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/lint-ansible.sh b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/lint-ansible.sh new file mode 120000 index 00000000..86c13111 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/lint-ansible.sh @@ -0,0 +1 @@ +../../../../../.cicd-tools/boxes/bootstrap/pre-commit/lint-ansible.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/lint-github-workflow-header.sh b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/lint-github-workflow-header.sh new file mode 120000 index 00000000..36fa9918 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/lint-github-workflow-header.sh @@ -0,0 +1 @@ +../../../../../.cicd-tools/boxes/bootstrap/pre-commit/lint-github-workflow-header.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/spelling-commit-message.sh b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/spelling-commit-message.sh new file mode 120000 index 00000000..b88e0a44 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/pre-commit/spelling-commit-message.sh @@ -0,0 +1 @@ +../../../../../.cicd-tools/boxes/bootstrap/pre-commit/spelling-commit-message.sh \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/schemas/cookiecutter.json b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/schemas/cookiecutter.json new file mode 120000 index 00000000..39fdef8f --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/schemas/cookiecutter.json @@ -0,0 +1 @@ +../../../../../.cicd-tools/boxes/bootstrap/schemas/cookiecutter.json \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/schemas/manifest.json b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/schemas/manifest.json new file mode 120000 index 00000000..72ef3f1e --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/boxes/bootstrap/schemas/manifest.json @@ -0,0 +1 @@ +../../../../../.cicd-tools/boxes/bootstrap/schemas/manifest.json \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/configuration.json b/{{cookiecutter.project_slug}}/.cicd-tools/configuration.json new file mode 100644 index 00000000..d3103cc6 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/configuration.json @@ -0,0 +1,5 @@ +{ + "SHFMT_OPTIONS": "{{ cookiecutter._CONFIG_DEFAULT_SHFMT_OPTIONS }}", + "SHELLCHECK_OPTIONS": "{{ cookiecutter._CONFIG_DEFAULT_SHELLCHECK_OPTIONS }}", + "CONTAINER": "{{ cookiecutter._DOCKER_DEFAULT_CONTAINER }}" +} \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/configuration/actionlint.yaml b/{{cookiecutter.project_slug}}/.cicd-tools/configuration/actionlint.yaml new file mode 120000 index 00000000..9ca19d44 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/configuration/actionlint.yaml @@ -0,0 +1 @@ +../../../.cicd-tools/configuration/actionlint.yaml \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/configuration/changelog.json b/{{cookiecutter.project_slug}}/.cicd-tools/configuration/changelog.json new file mode 120000 index 00000000..95b13b3f --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/configuration/changelog.json @@ -0,0 +1 @@ +../../../.cicd-tools/configuration/changelog.json \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/pgp/verification.sign b/{{cookiecutter.project_slug}}/.cicd-tools/pgp/verification.sign new file mode 120000 index 00000000..ddfcf0a5 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/pgp/verification.sign @@ -0,0 +1 @@ +../../../.cicd-tools/pgp/verification.sign \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.cicd-tools/pgp/verification.txt b/{{cookiecutter.project_slug}}/.cicd-tools/pgp/verification.txt new file mode 120000 index 00000000..9673ce44 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.cicd-tools/pgp/verification.txt @@ -0,0 +1 @@ +../../../.cicd-tools/pgp/verification.txt \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.github/actions/action-00-toolbox/action.yml b/{{cookiecutter.project_slug}}/.github/actions/action-00-toolbox/action.yml new file mode 120000 index 00000000..b48befea --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/actions/action-00-toolbox/action.yml @@ -0,0 +1 @@ +../../../../.github/actions/action-00-toolbox/action.yml \ No newline at end of file diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-00-start.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-00-start.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-00-start.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-00-start.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-00-success.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-00-success.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-00-success.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-00-success.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-10-security.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-10-security.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-10-security.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-10-security.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-30-documentation.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-30-documentation.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-30-documentation.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-30-documentation.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-40-molecule-lint.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-40-molecule-lint.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-40-molecule-lint.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-40-molecule-lint.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-40-molecule-test.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-40-molecule-test.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-40-molecule-test.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-40-molecule-test.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-80-commit-lint.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-80-commit-lint.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-80-commit-lint.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-80-commit-lint.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-80-toml-lint.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-80-toml-lint.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-80-toml-lint.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-80-toml-lint.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-80-workflow-lint.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-80-workflow-lint.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-80-workflow-lint.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-80-workflow-lint.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-80-yaml-lint.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-80-yaml-lint.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-80-yaml-lint.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-80-yaml-lint.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-99-create-release.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-99-create-release.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-99-create-release.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-99-create-release.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/.job-99-import-role.yml b/{{cookiecutter.project_slug}}/.github/deactivated/.job-99-import-role.yml similarity index 100% rename from {{cookiecutter.project_slug}}/.github/workflows/.job-99-import-role.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/.job-99-import-role.yml diff --git a/{{cookiecutter.project_slug}}/.github/workflows/workflow-publish-to-galaxy.yml b/{{cookiecutter.project_slug}}/.github/deactivated/workflow-publish-to-galaxy.yml similarity index 97% rename from {{cookiecutter.project_slug}}/.github/workflows/workflow-publish-to-galaxy.yml rename to {{cookiecutter.project_slug}}/.github/deactivated/workflow-publish-to-galaxy.yml index 9a2a7b7f..cfbe9492 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/workflow-publish-to-galaxy.yml +++ b/{{cookiecutter.project_slug}}/.github/deactivated/workflow-publish-to-galaxy.yml @@ -1,5 +1,5 @@ --- -name: {{ cookiecutter.project_slug }}-workflow-publish-to-galaxy +name: {{ cookiecutter.project_slug }}-github-workflow-publish-to-galaxy # This workflow is configured by values found in the .github/config/workflows/workflow-publish-to-galaxy.json file. # It's encouraged to explore the configuration before customizing this file. diff --git a/{{cookiecutter.project_slug}}/.github/deactivated/workflow-push.yml b/{{cookiecutter.project_slug}}/.github/deactivated/workflow-push.yml new file mode 100644 index 00000000..e64c5710 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/deactivated/workflow-push.yml @@ -0,0 +1,137 @@ +--- +name: {{ cookiecutter.project_slug }}-github-workflow-push + +# This workflow is configured by values found in the .github/config/workflows/workflow-push.json file. +# It's encouraged to explore the configuration before customizing this file. +# This will allow you to upgrade to future versions of this template without complications. + +# For further details please consult the documentation here: +# https://github.com/niall-byrne/ansible-workbench + +on: + push: + schedule: + - cron: "0 6 * * 1" + workflow_dispatch: + +# secrets: +# SLACK_WEBHOOK: +# description: "Optional, enables Slack notifications." +# required: false +# GALAXY_API_KEY: +# description: "Optional, enables importing the role into Ansible Galaxy." +# required: false + +jobs: + + configuration: + + runs-on: ubuntu-latest + outputs: + configuration: {% raw %}${{ steps.configuration.outputs.value }}{% endraw %} + molecule-scenarios: {% raw %}${{ steps.molecule-scenarios.outputs.value }}{% endraw %} + + steps: + - name: Create Configuration -- Checkout Repository + uses: {{ cookiecutter._GITHUB_ACTION_CHECKOUT }} + + - name: Create Configuration -- Validate the 'workflow-push.json' File + run: | + python -m json.tool "./.github/config/workflows/workflow-push.json" >> /dev/null + + - name: Create Configuration -- Set the Active Molecule Test Scenarios as Output + id: molecule-scenarios + run: | + source "./.github/scripts/workflow-identify-molecule-scenarios.sh" + + - name: Create Configuration -- Set the 'workflow-push.json' Configuration File as Output + id: configuration + run: | + source "./.github/scripts/workflow-set-value.sh" cat "./.github/config/workflows/workflow-push.json" + + start: + uses: ./.github/workflows/.job-00-start.yml + secrets: inherit + + success: + needs: [create_release] + uses: ./.github/workflows/.job-00-success.yml + secrets: inherit + + security_test: + needs: [configuration] + uses: ./.github/workflows/.job-10-security.yml + with: + TRUFFLEHOG_ARGS: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_trufflehog_extra_scan_args }}{% endraw %} + VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} + secrets: inherit + + documentation_test: + needs: [configuration] + uses: ./.github/workflows/.job-30-documentation.yml + with: + VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} + secrets: inherit + + molecule_lint_test: + needs: [configuration] + uses: ./.github/workflows/.job-40-molecule-lint.yml + with: + PYTHON_VERSIONS: {% raw %}${{ toJSON(fromJSON(needs.configuration.outputs.configuration).ci_python_versions) }}{% endraw %} + VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} + secrets: inherit + + molecule_test: + needs: [configuration] + uses: ./.github/workflows/.job-40-molecule-test.yml + with: + MOLECULE_PLATFORMS: {% raw %}${{ toJSON(fromJSON(needs.configuration.outputs.configuration).ci_molecule_test_platforms) }}{% endraw %} + MOLECULE_SCENARIOS: {% raw %}${{ needs.configuration.outputs.molecule-scenarios }}{% endraw %} + PYTHON_VERSIONS: {% raw %}${{ toJSON(fromJSON(needs.configuration.outputs.configuration).ci_python_versions) }}{% endraw %} + VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} + secrets: inherit + + commit_lint_test: + needs: [configuration] + uses: ./.github/workflows/.job-80-commit-lint.yml + with: + COMMITIZEN_REV_RANGE: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_commitzen_rev_range }}{% endraw %} + PYTHON_VERSIONS: {% raw %}${{ toJSON(fromJSON(needs.configuration.outputs.configuration).ci_python_versions) }}{% endraw %} + VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} + secrets: inherit + + {%- if cookiecutter.optional_toml_linting == 'true' %} + + toml_lint_test: + needs: [configuration] + uses: ./.github/workflows/.job-80-toml-lint.yml + with: + VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} + secrets: inherit + + {%- endif %} + {%- if cookiecutter.optional_workflow_linting == 'true' %} + + workflow_lint_test: + needs: [configuration] + uses: ./.github/workflows/.job-80-workflow-lint.yml + with: + VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} + secrets: inherit + + {%- endif %} + + yaml_lint_test: + needs: [configuration] + uses: ./.github/workflows/.job-80-yaml-lint.yml + with: + VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} + secrets: inherit + + create_release: + needs: [commit_lint_test, configuration, documentation_test, molecule_lint_test, molecule_test, security_test, start,{% if cookiecutter.optional_toml_linting == 'true' %} toml_lint_test,{% endif %} yaml_lint_test] + uses: ./.github/workflows/.job-99-create-release.yml + with: + EXTRA_RELEASE_CONTENT: {% raw %}${{ toJSON(fromJSON(needs.configuration.outputs.configuration).ci_extra_release_content) }}{% endraw %} + VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} + secrets: inherit diff --git a/{{cookiecutter.project_slug}}/.github/scripts/job-99-create-changelog.sh b/{{cookiecutter.project_slug}}/.github/scripts/job-99-create-changelog.sh index 9581a00c..0e69ea64 100644 --- a/{{cookiecutter.project_slug}}/.github/scripts/job-99-create-changelog.sh +++ b/{{cookiecutter.project_slug}}/.github/scripts/job-99-create-changelog.sh @@ -15,7 +15,7 @@ main() { { echo "CHANGE_LOG_CONTENT<> "${GITHUB_ENV}" diff --git a/{{cookiecutter.project_slug}}/.github/scripts/job-99-release-candidate.sh b/{{cookiecutter.project_slug}}/.github/scripts/job-99-release-candidate.sh index 4d6e99bf..7456fd86 100644 --- a/{{cookiecutter.project_slug}}/.github/scripts/job-99-release-candidate.sh +++ b/{{cookiecutter.project_slug}}/.github/scripts/job-99-release-candidate.sh @@ -14,7 +14,7 @@ MATCH="FALSE" main() { if [[ "${1}" =~ ^refs/tags/[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$ ]] && - [[ "${1}" != "refs/tags/0.0.0" ]]; then + [[ "${1}" != "refs/tags/0.0.0" ]]; then MATCH="TRUE" fi diff --git a/{{cookiecutter.project_slug}}/.github/scripts/step-setup-environment.sh b/{{cookiecutter.project_slug}}/.github/scripts/step-setup-environment.sh new file mode 100755 index 00000000..f8f64364 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/scripts/step-setup-environment.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Configures environment variables for GitHub Actions. + +# Implementation: +# Templates implementing this script must set the required environment variables in the GitHub runner's environment context. +# +# BRANCH_OR_TAG: The current branch or tag being tested. +# CACHE_TTL: A unique CACHE value that determines the cache's TTL. (Default strategy: day of the month.) +# NOTIFICATION_LINK: Consumed by the notification script to provide a clickable link to the workflow run in GitHub. +# PROJECT_NAME: The slugified name of the template project. Should match the GitHub repository name. +# PROJECT_OWNER: The GitHub owner of the project. +# RELEASE_EXTRA_CONTENT: Extra string contents to append to the generated releases. + +# CI only script. + +set -eo pipefail + +WORKFLOW_NAME="${WORKFLOW_NAME:-""}" + +main() { + + PROJECT_NAME="{{ cookiecutter.project_slug }}" + PROJECT_OWNER="{{ cookiecutter.github_handle }}" + + BRANCH_OR_TAG="$(echo "${GITHUB_REF}" | sed -E 's,refs/heads/|refs/tags/,,g')" + WORKFLOW_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" + + [[ "${WORKFLOW_NAME}" != "" ]] && WORKFLOW_NAME="-${WORKFLOW_NAME}" + + { + echo "BRANCH_OR_TAG=${BRANCH_OR_TAG}" + echo "CACHE_TTL=$(date +%d)" + echo "NOTIFICATION_LINK=${PROJECT_NAME}${WORKFLOW_NAME} [<${WORKFLOW_URL}|${BRANCH_OR_TAG}>]" + echo "PROJECT_NAME=${PROJECT_NAME}" + echo "PROJECT_OWNER=${PROJECT_OWNER}" + } >> "${GITHUB_ENV}" + +} + +main "$@" diff --git a/{{cookiecutter.project_slug}}/.github/scripts/task-setup-ansible-cache.sh b/{{cookiecutter.project_slug}}/.github/scripts/task-setup-ansible-cache.sh index 062ed37b..ae80892e 100644 --- a/{{cookiecutter.project_slug}}/.github/scripts/task-setup-ansible-cache.sh +++ b/{{cookiecutter.project_slug}}/.github/scripts/task-setup-ansible-cache.sh @@ -11,7 +11,7 @@ set -eo pipefail -main () { +main() { MOUNT_FOLDER="${1}" USAGE_FOLDER="${2}" diff --git a/{{cookiecutter.project_slug}}/.github/scripts/task-use-poetry.sh b/{{cookiecutter.project_slug}}/.github/scripts/task-use-poetry.sh index ecf8ff07..f7280763 100644 --- a/{{cookiecutter.project_slug}}/.github/scripts/task-use-poetry.sh +++ b/{{cookiecutter.project_slug}}/.github/scripts/task-use-poetry.sh @@ -19,7 +19,7 @@ install_project() { } -main () { +main() { case $1 in install-poetry) diff --git a/{{cookiecutter.project_slug}}/.github/scripts/workflow-identify-molecule-scenarios.sh b/{{cookiecutter.project_slug}}/.github/scripts/workflow-identify-molecule-scenarios.sh index 29083d78..89e363e7 100644 --- a/{{cookiecutter.project_slug}}/.github/scripts/workflow-identify-molecule-scenarios.sh +++ b/{{cookiecutter.project_slug}}/.github/scripts/workflow-identify-molecule-scenarios.sh @@ -10,12 +10,11 @@ set -eo pipefail main() { EXCLUDED="$(jq -rM ".ci_molecule_excluded_scenarios_regex" ".github/config/workflows/workflow-push.json")" - SCENARIOS=(); while IFS='' read -r SCENARIO; do SCENARIOS+=("${SCENARIO}"); done < <(find molecule/* -maxdepth 0 -type d -not -name "default" -not -name "${EXCLUDED}" -exec basename {} \;) + SCENARIOS=() + while IFS='' read -r SCENARIO; do SCENARIOS+=("${SCENARIO}"); done < <(find molecule/* -maxdepth 0 -type d -not -name "default" -not -name "${EXCLUDED}" -exec basename {} \;) source ".github/scripts/workflow-set-value.sh" jq -M --null-input "\$ARGS.positional" --args "${SCENARIOS[@]}" } main "$@" - - diff --git a/{{cookiecutter.project_slug}}/.github/scripts/workflow-set-value.sh b/{{cookiecutter.project_slug}}/.github/scripts/workflow-set-value.sh index 03dcc5ef..d8d73c3f 100644 --- a/{{cookiecutter.project_slug}}/.github/scripts/workflow-set-value.sh +++ b/{{cookiecutter.project_slug}}/.github/scripts/workflow-set-value.sh @@ -13,12 +13,10 @@ main() { { echo "value<> "${GITHUB_OUTPUT}" } main "$@" - - diff --git a/{{cookiecutter.project_slug}}/.github/workflows/workflow-push.yml b/{{cookiecutter.project_slug}}/.github/workflows/workflow-push.yml index 473b1906..f9b42033 100644 --- a/{{cookiecutter.project_slug}}/.github/workflows/workflow-push.yml +++ b/{{cookiecutter.project_slug}}/.github/workflows/workflow-push.yml @@ -1,5 +1,5 @@ --- -name: {{ cookiecutter.project_slug }}-workflow-push +name: {{ cookiecutter.project_slug }}-github-workflow-push # This workflow is configured by values found in the .github/config/workflows/workflow-push.json file. # It's encouraged to explore the configuration before customizing this file. @@ -48,90 +48,3 @@ jobs: id: configuration run: | source "./.github/scripts/workflow-set-value.sh" cat "./.github/config/workflows/workflow-push.json" - - start: - uses: ./.github/workflows/.job-00-start.yml - secrets: inherit - - success: - needs: [create_release] - uses: ./.github/workflows/.job-00-success.yml - secrets: inherit - - security_test: - needs: [configuration] - uses: ./.github/workflows/.job-10-security.yml - with: - TRUFFLEHOG_ARGS: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_trufflehog_extra_scan_args }}{% endraw %} - VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} - secrets: inherit - - documentation_test: - needs: [configuration] - uses: ./.github/workflows/.job-30-documentation.yml - with: - VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} - secrets: inherit - - molecule_lint_test: - needs: [configuration] - uses: ./.github/workflows/.job-40-molecule-lint.yml - with: - PYTHON_VERSIONS: {% raw %}${{ toJSON(fromJSON(needs.configuration.outputs.configuration).ci_python_versions) }}{% endraw %} - VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} - secrets: inherit - - molecule_test: - needs: [configuration] - uses: ./.github/workflows/.job-40-molecule-test.yml - with: - MOLECULE_PLATFORMS: {% raw %}${{ toJSON(fromJSON(needs.configuration.outputs.configuration).ci_molecule_test_platforms) }}{% endraw %} - MOLECULE_SCENARIOS: {% raw %}${{ needs.configuration.outputs.molecule-scenarios }}{% endraw %} - PYTHON_VERSIONS: {% raw %}${{ toJSON(fromJSON(needs.configuration.outputs.configuration).ci_python_versions) }}{% endraw %} - VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} - secrets: inherit - - commit_lint_test: - needs: [configuration] - uses: ./.github/workflows/.job-80-commit-lint.yml - with: - COMMITIZEN_REV_RANGE: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_commitzen_rev_range }}{% endraw %} - PYTHON_VERSIONS: {% raw %}${{ toJSON(fromJSON(needs.configuration.outputs.configuration).ci_python_versions) }}{% endraw %} - VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} - secrets: inherit - - {%- if cookiecutter.optional_toml_linting == 'true' %} - - toml_lint_test: - needs: [configuration] - uses: ./.github/workflows/.job-80-toml-lint.yml - with: - VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} - secrets: inherit - - {%- endif %} - {%- if cookiecutter.optional_workflow_linting == 'true' %} - - workflow_lint_test: - needs: [configuration] - uses: ./.github/workflows/.job-80-workflow-lint.yml - with: - VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} - secrets: inherit - - {%- endif %} - - yaml_lint_test: - needs: [configuration] - uses: ./.github/workflows/.job-80-yaml-lint.yml - with: - VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} - secrets: inherit - - create_release: - needs: [commit_lint_test, configuration, documentation_test, molecule_lint_test, molecule_test, security_test, start,{% if cookiecutter.optional_toml_linting == 'true' %} toml_lint_test,{% endif %} yaml_lint_test] - uses: ./.github/workflows/.job-99-create-release.yml - with: - EXTRA_RELEASE_CONTENT: {% raw %}${{ toJSON(fromJSON(needs.configuration.outputs.configuration).ci_extra_release_content) }}{% endraw %} - VERBOSITY: {% raw %}${{ fromJSON(needs.configuration.outputs.configuration).ci_verbose_notifications }}{% endraw %} - secrets: inherit diff --git a/{{cookiecutter.project_slug}}/.gitignore b/{{cookiecutter.project_slug}}/.gitignore index 2fe16fa5..b34e5f40 100644 --- a/{{cookiecutter.project_slug}}/.gitignore +++ b/{{cookiecutter.project_slug}}/.gitignore @@ -4,3 +4,5 @@ .idea .tool-versions poetry.lock +.cicd-tools/boxes/* +!.cicd-tools/boxes/bootstrap diff --git a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml index 088ab67d..ab7cd074 100644 --- a/{{cookiecutter.project_slug}}/.pre-commit-config.yaml +++ b/{{cookiecutter.project_slug}}/.pre-commit-config.yaml @@ -1,27 +1,52 @@ --- +default_install_hook_types: + - pre-commit + - commit-msg repos: - - repo: local + - repo: https://github.com/commitizen-tools/commitizen + rev: v2.42.1 hooks: - - id: commit-lint - name: commit-lint - description: "Check whether the commit message follows committing rules." - entry: ./.pre-commit/commit-lint.sh - language: system + - id: commitizen stages: [commit-msg] - - id: molecule-lint - name: molecule-lint + - repo: https://github.com/adrienverge/yamllint.git + rev: v1.29.0 + hooks: + - id: yamllint + args: ["-c", "./.yamllint.yml"] + stages: [commit] + - repo: local + hooks: + - id: ansible-lint + name: ansible-lint description: "Check the profile for Ansible best practices." - entry: ./.pre-commit/molecule-lint.sh - files: "^.+\\.(yaml|yml)$|^.ansible-lint$" - exclude: "^.github/.+$" + entry: ./.cicd-tools/boxes/bootstrap/pre-commit/ansible-lint.sh + files: "^.+\\.(yaml|yml)$" language: system pass_filenames: false stages: [commit] + - id: shell-fmt + name: shell-fmt + description: "Format the project's shell scripts." + entry: ./.cicd-tools/boxes/bootstrap/pre-commit/shell-fmt.sh + exclude: "^\\.cicd-tools/ci/.+$" + files: "^.+\\.(bash|sh)$" + language: system + pass_filenames: true + stages: [commit] + - id: shell-lint + name: shell-lint + description: "Lint the project's shell scripts." + entry: ./.cicd-tools/boxes/bootstrap/pre-commit/shell-lint.sh + exclude: "^\\.cicd-tools/ci/.+$" + files: "^.+\\.(bash|sh)$" + language: system + pass_filenames: true + stages: [commit] {%- if cookiecutter.optional_toml_linting == 'true' %} - id: toml-lint name: toml-lint description: "Lint the project's TOML files." - entry: ./.pre-commit/toml-lint.sh + entry: ./.cicd-tools/boxes/bootstrap/pre-commit/toml-lint.sh files: "^.+\\.toml$" language: system stages: [commit] @@ -31,10 +56,17 @@ repos: - id: workflow-lint name: workflow-lint description: "Lint the project's GitHub workflow files." - entry: actionlint - files: "^.github/.+\\.(yaml|yml)$" + entry: ./.cicd-tools/boxes/bootstrap/pre-commit/workflow-lint.sh + files: "^.github/workflows/.+\\.(yaml|yml)$" language: system pass_filenames: false - require_serial: true + stages: [commit] + - id: workflow-header-lint + name: workflow-header-lint + description: "Lint the headers of the project's GitHub workflow files." + entry: ./.cicd-tools/boxes/bootstrap/pre-commit/workflow-header-lint.sh + files: "^.github/workflows/.+\\.(yaml|yml)$" + language: system + pass_filenames: true stages: [commit] {%- endif %} diff --git a/{{cookiecutter.project_slug}}/.pre-commit/.poetry-compatible.sh b/{{cookiecutter.project_slug}}/.pre-commit/.poetry-compatible.sh deleted file mode 100644 index 9bd337b1..00000000 --- a/{{cookiecutter.project_slug}}/.pre-commit/.poetry-compatible.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# scripts/pre-commit/.poetry-compatible.sh -# Enables running commands in or outside the Poetry virtual environment seamlessly. - -# @: An array of commands to run. - -# pre-commit script. - -set -eo pipefail - -run_command() { - - if [[ "${POETRY_ACTIVE}" == "1" ]]; then - "$@" - else - poetry run "$@" - fi - -} diff --git a/{{cookiecutter.project_slug}}/.pre-commit/commit-lint.sh b/{{cookiecutter.project_slug}}/.pre-commit/commit-lint.sh deleted file mode 100755 index eb70da87..00000000 --- a/{{cookiecutter.project_slug}}/.pre-commit/commit-lint.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# scripts/pre-commit/commit-lint.sh -# Runs commitizen on the passed commit message file. - -# 1: The path to the commit message file. - -# pre-commit script. - -set -eo pipefail - - -main () { - - # shellcheck source=./.pre-commit/.poetry-compatible.sh - source "$(dirname -- "${BASH_SOURCE[0]}")/.poetry-compatible.sh" - - run_command cz check --allow-abort --commit-msg-file "$1" - -} - -main "$@" diff --git a/{{cookiecutter.project_slug}}/.pre-commit/molecule-lint.sh b/{{cookiecutter.project_slug}}/.pre-commit/molecule-lint.sh deleted file mode 100755 index abde8ec5..00000000 --- a/{{cookiecutter.project_slug}}/.pre-commit/molecule-lint.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# scripts/pre-commit/ansible-lint.sh -# Runs molecule to install/update the dependencies if needed, and then lints changes. - -# pre-commit script. - -set -eo pipefail - -main() { - - # shellcheck source=./.pre-commit/.poetry-compatible.sh - source "$(dirname -- "${BASH_SOURCE[0]}")/.poetry-compatible.sh" - - if ! git diff --exit-code HEAD -- requirements.yml; then - run_command molecule dependency - fi - - run_command molecule lint - -} - -main "$@" diff --git a/{{cookiecutter.project_slug}}/.pre-commit/toml-lint.sh b/{{cookiecutter.project_slug}}/.pre-commit/toml-lint.sh deleted file mode 100755 index 3eb28b4d..00000000 --- a/{{cookiecutter.project_slug}}/.pre-commit/toml-lint.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -# scripts/pre-commit/toml-lint.sh -# Runs tomll on the specified files and then runs diff to detect changes. - -# @: An array of toml files to lint. - -# pre-commit script. - -set -eo pipefail - -main () { - - for TOML_FILE in "$@"; do - - # shellcheck disable=SC2002 - diff "${TOML_FILE}" <(cat "${TOML_FILE}" | tomll) - - done - -} - -main "$@" diff --git a/{{cookiecutter.project_slug}}/.pre-commit/yaml-lint.sh b/{{cookiecutter.project_slug}}/.pre-commit/yaml-lint.sh deleted file mode 100755 index 21d68047..00000000 --- a/{{cookiecutter.project_slug}}/.pre-commit/yaml-lint.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# scripts/pre-commit/yaml-lint.sh -# Runs yamllint on the specified files. - -# @: An array of yaml files to lint. - -# pre-commit script. - -set -eo pipefail - -main () { - - # shellcheck source=./.pre-commit/.poetry-compatible.sh - source "$(dirname -- "${BASH_SOURCE[0]}")/.poetry-compatible.sh" - - run_command yamllint -f standard "$@" - -} - -main "$@" diff --git a/{{cookiecutter.project_slug}}/pyproject.toml b/{{cookiecutter.project_slug}}/pyproject.toml index a0f65bed..91c5a6e8 100644 --- a/{{cookiecutter.project_slug}}/pyproject.toml +++ b/{{cookiecutter.project_slug}}/pyproject.toml @@ -1,34 +1,34 @@ - [build-system] - build-backend = "poetry.core.masonry.api" - requires = ["poetry-core>=1.0.0"] +build-backend = 'poetry.core.masonry.api' +requires = ['poetry-core>=1.0.0'] [tool] - - [tool.commitizen] - bump_message = "bump(RELEASE): $current_version → $new_version" - version = "0.1.0" - version_files = ["pyproject.toml:version"] - - [tool.poetry] - authors = ["{{cookiecutter.author}} <{{cookiecutter.email}}>"] - description = "{{cookiecutter.description}}" - name = "{{cookiecutter.project_slug}}" - version = "0.1.0" - - [tool.poetry.dependencies] - python = ">={{ cookiecutter._GITHUB_CI_DEFAULT_PYTHON_VERSIONS | first }}.0,<4.0" - - [tool.poetry.dev-dependencies] - ansible = "^7.2.0" - commitizen = "^2.42.0" - pre-commit = "^3.1.0" - yamllint = "^1.29.0" - - [tool.poetry.dev-dependencies.ansible-lint] - markers = "platform_system != 'Windows'" - version = "^6.12.2" - - [tool.poetry.dev-dependencies.molecule] - extras = ["docker"] - version = "^4.0.0" +[tool.commitizen] +bump_message = 'bump(RELEASE): $current_version → $new_version' +pre_bump_hooks = ['.cicd-tools/boxes/bootstrap/commitizen/pre_bump.sh'] +version = '0.1.0' +version_files = ['pyproject.toml:version'] +version_provider = 'poetry' + +[tool.poetry] +authors = ['{{cookiecutter.author}} <{{cookiecutter.email}}>'] +description = '{{cookiecutter.description}}' +name = '{{cookiecutter.project_slug}}' +version = '0.1.0' + +[tool.poetry.dependencies] +python = '^{{ cookiecutter._GITHUB_CI_DEFAULT_PYTHON_VERSIONS | first }}.0,<4.0' + +[tool.poetry.dev-dependencies] +ansible = '^7.5.0' +commitizen = '^3.0.0' +pre-commit = '^3.1.1' +yamllint = '^1.29.0' + +[tool.poetry.dev-dependencies.ansible-lint] +markers = "platform_system != 'Windows'" +version = '^6.12.2' + +[tool.poetry.dev-dependencies.molecule] +extras = ['docker'] +version = '^4.0.4'