diff --git a/.drevops/docs/.ahoy.yml b/.drevops/docs/.ahoy.yml index 2b9dd44fa..4b174fe9c 100644 --- a/.drevops/docs/.ahoy.yml +++ b/.drevops/docs/.ahoy.yml @@ -39,6 +39,16 @@ commands: cmd: ./.utils/lint.sh test: + usage: Test + cmd: | + ahoy test-scripts + ahoy test-site + + test-scripts: + usage: Test scripts + cmd: ./.utils/test.sh + + test-site: usage: Test site cmd: docker compose run linkchecker http://mkdocs:8080 "$@" diff --git a/.drevops/docs/.utils/.aspell.en.pws b/.drevops/docs/.utils/.aspell.en.pws index 6b0c92c05..2a1e777b8 100644 --- a/.drevops/docs/.utils/.aspell.en.pws +++ b/.drevops/docs/.utils/.aspell.en.pws @@ -73,7 +73,6 @@ Ssl Suboptimal TBD Terminalizer -Twig CS Fixer UAT UI UUID diff --git a/.drevops/docs/.utils/.gitignore b/.drevops/docs/.utils/.gitignore index 22d0d82f8..862e5ebf6 100644 --- a/.drevops/docs/.utils/.gitignore +++ b/.drevops/docs/.utils/.gitignore @@ -1 +1,3 @@ vendor +node_modules +!package-lock.json diff --git a/.drevops/docs/.utils/publish.sh b/.drevops/docs/.utils/publish.sh index df2612a74..606508042 100755 --- a/.drevops/docs/.utils/publish.sh +++ b/.drevops/docs/.utils/publish.sh @@ -136,7 +136,7 @@ build() { echo "Copy combined dir into the container." docker compose cp "${combined_repo_dir}/." mkdocs:"/tmp/build" docker compose exec mkdocs git config --global --add safe.directory "/tmp/build" - # The credentials below are only used to produce some entermediate commits + # The credentials below are only used to produce some intermediate commits # while building the docs. They are not used to push the code. docker compose exec mkdocs git config --global user.name "Deployment robot" docker compose exec mkdocs git config --global user.email "robot@example.com" diff --git a/.drevops/docs/.utils/test.sh b/.drevops/docs/.utils/test.sh new file mode 100755 index 000000000..c4a866ea4 --- /dev/null +++ b/.drevops/docs/.utils/test.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +## +# Run DrevOps docs tests in CI. +# +# LCOV_EXCL_START + +set -eu +[ "${DREVOPS_DEBUG-}" = "1" ] && set -x + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" + +SCRIPTS_DIR="${ROOT_DIR}/docs/.utils" + +TEST_DIR="${ROOT_DIR}/docs/.utils/tests" + +# ------------------------------------------------------------------------------ + +# Configure git username and email if it is not set. +[ "$(git config --global user.name)" = "" ] && echo "==> Configuring global git user name" && git config --global user.name "Test user" +[ "$(git config --global user.email)" = "" ] && echo "==> Configuring global git user email" && git config --global user.email "someone@example.com" + +# Create stub of local framework. +docker network create amazeeio-network 2>/dev/null || true + +echo "==> Run docs tests." + +[ ! -d "${TEST_DIR}/node_modules" ] && echo " > Installing test Node dependencies into ${TEST_DIR}." && npm --prefix="${TEST_DIR}" ci + +bats() { + pushd "${ROOT_DIR}" >/dev/null || exit 1 + if [ -n "${DREVOPS_DEV_TEST_COVERAGE_DIR:-}" ]; then + mkdir -p "${DREVOPS_DEV_TEST_COVERAGE_DIR}" + kcov --include-pattern=.sh,.bash --bash-parse-files-in-dir="${SCRIPTS_DIR}","${TEST_DIR}" --exclude-pattern=vendor,node_modules "${DREVOPS_DEV_TEST_COVERAGE_DIR}" "${TEST_DIR}/node_modules/.bin/bats" "$@" + else + "${TEST_DIR}/node_modules/.bin/bats" "$@" + fi + popd >/dev/null || exit 1 +} + +bats "${TEST_DIR}/bats/docs.bats" diff --git a/.drevops/docs/.utils/tests/bats/_helper.bash b/.drevops/docs/.utils/tests/bats/_helper.bash new file mode 100644 index 000000000..8756c0b1b --- /dev/null +++ b/.drevops/docs/.utils/tests/bats/_helper.bash @@ -0,0 +1,164 @@ +#!/usr/bin/env bash +# +# Helpers related to DrevOps common testing functionality. +# +# In some cases, shell may report platform incorrectly. Run with forced platform: +# DOCKER_DEFAULT_PLATFORM=linux/amd64 bats --tap tests/bats/test.bats +# +# shellcheck disable=SC2155,SC2119,SC2120,SC2044,SC2294 +# + +################################################################################ +# BATS HOOK IMPLEMENTATIONS # +################################################################################ + +setup() { + # The root directory of the project. + export ROOT_DIR="$(dirname "$(cd "$(dirname "${BATS_TEST_DIRNAME}")/../../.." && pwd)")" + + [ ! -d "${ROOT_DIR}/.drevops" ] && echo 'ERROR: The test should be run from the ".drevops" directory.' && exit 1 + + # Register a path to libraries. + export BATS_LIB_PATH="${BATS_TEST_DIRNAME}/../node_modules" + bats_load_library bats-helpers + + ## + ## Application test directories structure setup. + ## + + # To run installation tests, several fixture directories are required. + # They are defined and created in setup() test method. + # + # Root build directory where the rest of fixture directories located. + # The "build" in this context is a place to store assets produced by the + # installer script during the test. + export BUILD_DIR="${BUILD_DIR:-"${BATS_TEST_TMPDIR//\/\//\/}/drevops-$(date +%s)"}" + + # Directory where the installer script is executed. + # May have existing project files (e.g. from previous installations) or be + # empty (to facilitate brand-new install). + export CURRENT_PROJECT_DIR="${BUILD_DIR}/star_wars" + + # Directory where the installer script will be sourcing the instance of DrevOps. + # As a part of test setup, the local copy of DrevOps at the last commit is + # copied to this location. This means that during development of tests local + # changes need to be committed. + export LOCAL_REPO_DIR="${BUILD_DIR}/local_repo" + + fixture_prepare_dir "${BUILD_DIR}" + fixture_prepare_dir "${CURRENT_PROJECT_DIR}" + fixture_prepare_dir "${LOCAL_REPO_DIR}" + + # Allow to override debug variables from environment when developing tests. + export DREVOPS_DEBUG="${TEST_DREVOPS_DEBUG:-}" + + ## + ## SUT files setup. + ## + + # Copy DrevOps at the last commit. + fixture_export_codebase "${LOCAL_REPO_DIR}" "${ROOT_DIR}" >/dev/null + + # Prepare global git config and ignore files required for testing cleanup scenarios. + prepare_global_gitconfig + prepare_global_gitignore + + ## + ## Setting debug mode. + ## + + # Print debug if "--verbose-run" is passed or TEST_DREVOPS_DEBUG is set to "1". + if [ "${BATS_VERBOSE_RUN:-}" = "1" ] || [ "${TEST_DREVOPS_DEBUG:-}" = "1" ]; then + echo "Verbose run enabled." >&3 + echo "BUILD_DIR: ${BUILD_DIR}" >&3 + export RUN_STEPS_DEBUG=1 + fi + + # Change directory to the current project directory for each test. Tests + # requiring to operate outside of CURRENT_PROJECT_DIR (like deployment tests) + # should change directory explicitly within their tests. + pushd "${CURRENT_PROJECT_DIR}" >/dev/null || exit 1 +} + +teardown() { + restore_global_gitignore + popd >/dev/null || cd "${ROOT_DIR}" || exit 1 +} + +# Print step. +step() { + # Using prefix different from command prefix in SUT for easy debug. + echo "**> STEP: ${1}" >&3 +} + +# Print sub-step. +substep() { + echo " > ${1}" >&3 +} + +git_init() { + local allow_receive_update="${1:-0}" + local dir="${2:-$(pwd)}" + + assert_not_git_repo "${dir}" + git --work-tree="${dir}" --git-dir="${dir}/.git" init >/dev/null + + if [ "${allow_receive_update:-}" -eq 1 ]; then + git --work-tree="${dir}" --git-dir="${dir}/.git" config receive.denyCurrentBranch updateInstead >/dev/null + fi +} + +git_add_all_commit() { + local message="${1}" + local dir="${2:-$(pwd)}" + + assert_git_repo "${dir}" + + git --work-tree="${dir}" --git-dir="${dir}/.git" add -A + git --work-tree="${dir}" --git-dir="${dir}/.git" commit -m "${message}" >/dev/null + commit=$(git --work-tree="${dir}" --git-dir="${dir}/.git" rev-parse HEAD) + echo "${commit}" +} + + +prepare_global_gitconfig() { + git config --global init.defaultBranch >/dev/null || git config --global init.defaultBranch "main" +} + +prepare_global_gitignore() { + filename="${HOME}/.gitignore" + filename_backup="${filename}".bak + + if git config --global --list | grep -q core.excludesfile; then + git config --global core.excludesfile >/tmp/git_config_global_exclude + fi + + [ -f "${filename}" ] && cp "${filename}" "${filename_backup}" + + cat <"${filename}" +## +## Temporary files generated by various OSs and IDEs +## +Thumbs.db +._* +.DS_Store +.idea +.idea/* +*.sublime* +.project +.netbeans +.vscode +.vscode/* +nbproject +nbproject/* +EOT + + git config --global core.excludesfile "${filename}" +} + +restore_global_gitignore() { + filename=${HOME}/.gitignore + filename_backup="${filename}".bak + [ -f "${filename_backup}" ] && cp "${filename_backup}" "${filename}" + [ -f "/tmp/git_config_global_exclude" ] && git config --global core.excludesfile "$(cat /tmp/git_config_global_exclude)" +} diff --git a/.drevops/tests/bats/docs.bats b/.drevops/docs/.utils/tests/bats/docs.bats similarity index 86% rename from .drevops/tests/bats/docs.bats rename to .drevops/docs/.utils/tests/bats/docs.bats index a009d9155..110389e15 100644 --- a/.drevops/tests/bats/docs.bats +++ b/.drevops/docs/.utils/tests/bats/docs.bats @@ -2,10 +2,12 @@ # # Test for docs publishing functionality. # -# shellcheck disable=SC2030,SC2031,SC2129 +# shellcheck disable=SC2030,SC2031,SC2129,SC2002 load _helper.bash +export BATS_FIXTURE_EXPORT_CODEBASE_ENABLED=1 + @test "Docs release" { update_local_repo @@ -23,7 +25,7 @@ load _helper.bash # The very first version is set as latest. assert_version "feature-test-branch-first" 1 - substep "Test 2: Followup publish from \"feature-test-branch-second\" branch." + substep 'Test 2: Followup publish from "feature-test-branch-second" branch.' export DOCS_PUBLISH_SRC_BRANCH="feature/test-branch-second" run ./.utils/publish.sh assert_success @@ -32,7 +34,7 @@ load _helper.bash assert_version "feature-test-branch-first" 1 assert_version "feature-test-branch-second" - substep "Test 3: Followup publish from \"1.0.0\" tag." + substep 'Test 3: Followup publish from "1.0.0" tag.' export DOCS_PUBLISH_SRC_BRANCH="main" export DOCS_PUBLISH_SRC_TAG="1.0.0" run ./.utils/publish.sh @@ -41,7 +43,7 @@ load _helper.bash assert_version "feature-test-branch-second" assert_version "1.0.0" 1 - substep "Test 4: Followup publish from \"1.1.0\" tag." + substep 'Test 4: Followup publish from "1.1.0" tag.' export DOCS_PUBLISH_SRC_BRANCH="main" export DOCS_PUBLISH_SRC_TAG="1.1.0" run ./.utils/publish.sh @@ -51,7 +53,7 @@ load _helper.bash assert_version "1.0.0" assert_version "1.1.0" 1 - substep "Test 5: Followup publish from \"main\" branch." + substep 'Test 5: Followup publish from "main" branch.' export DOCS_PUBLISH_SRC_BRANCH="main" export DOCS_PUBLISH_CANARY_BRANCH="main" export DOCS_PUBLISH_SRC_TAG="" @@ -75,10 +77,10 @@ assert_version() { assert_file_exists "${REMOTE_REPO_DIR}/CNAME" assert_file_exists "${REMOTE_REPO_DIR}/versions.json" - actual_has_version="$(cat "${REMOTE_REPO_DIR}/versions.json" | jq 'any(.[]; .version == "'${expected_version}'")')" + actual_has_version="$(cat "${REMOTE_REPO_DIR}/versions.json" | jq 'any(.[]; .version == "'"${expected_version}"'")')" assert_equal "true" "${actual_has_version}" - actual_has_alias="$(cat "${REMOTE_REPO_DIR}/versions.json" | jq 'any(.[]; .version == "'${expected_version}'" and any(.aliases[]?; . == "latest"))')" + actual_has_alias="$(cat "${REMOTE_REPO_DIR}/versions.json" | jq 'any(.[]; .version == "'"${expected_version}"'" and any(.aliases[]?; . == "latest"))')" if [ "${expected_has_alias}" -eq 1 ]; then assert_equal "true" "${actual_has_alias}" else diff --git a/.drevops/docs/.utils/tests/package-lock.json b/.drevops/docs/.utils/tests/package-lock.json new file mode 100644 index 000000000..d5a1f2d77 --- /dev/null +++ b/.drevops/docs/.utils/tests/package-lock.json @@ -0,0 +1,52 @@ +{ + "name": "drevops-tests", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "drevops-tests", + "version": "1.0.0", + "license": "GPL-2.0-or-later", + "devDependencies": { + "bats-helpers": "npm:@drevops/bats-helpers@^1.3.1" + } + }, + "node_modules/bats": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/bats/-/bats-1.10.0.tgz", + "integrity": "sha512-yOQrC7npuCrN+Ic3TyjTjJlzHa0qlK3oEO6VAYPWwFeutx/GmpljIyB6uNSl/UTASyc2w4FgVuA/QMMf9OdsCw==", + "dev": true, + "bin": { + "bats": "bin/bats" + } + }, + "node_modules/bats-helpers": { + "name": "@drevops/bats-helpers", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@drevops/bats-helpers/-/bats-helpers-1.3.1.tgz", + "integrity": "sha512-CeKDILw3+o1W+L3EzMEo5E1is5UMc+tCbjMYJT+NE42cjNhH5l2+HTWksX5nc8unhmqysTOTn7zkp79LJLoX8w==", + "dev": true, + "dependencies": { + "bats": "^1" + } + } + }, + "dependencies": { + "bats": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/bats/-/bats-1.10.0.tgz", + "integrity": "sha512-yOQrC7npuCrN+Ic3TyjTjJlzHa0qlK3oEO6VAYPWwFeutx/GmpljIyB6uNSl/UTASyc2w4FgVuA/QMMf9OdsCw==", + "dev": true + }, + "bats-helpers": { + "version": "npm:@drevops/bats-helpers@1.3.1", + "resolved": "https://registry.npmjs.org/@drevops/bats-helpers/-/bats-helpers-1.3.1.tgz", + "integrity": "sha512-CeKDILw3+o1W+L3EzMEo5E1is5UMc+tCbjMYJT+NE42cjNhH5l2+HTWksX5nc8unhmqysTOTn7zkp79LJLoX8w==", + "dev": true, + "requires": { + "bats": "^1" + } + } + } +} diff --git a/.drevops/docs/.utils/tests/package.json b/.drevops/docs/.utils/tests/package.json new file mode 100644 index 000000000..3892a005d --- /dev/null +++ b/.drevops/docs/.utils/tests/package.json @@ -0,0 +1,9 @@ +{ + "name": "drevops-tests", + "version": "1.0.0", + "description": "Packages used for testing of DrevOps", + "license": "GPL-2.0-or-later", + "devDependencies": { + "bats-helpers": "npm:@drevops/bats-helpers@^1.3.1" + } +} diff --git a/.drevops/tests/lint.spelling.sh b/.drevops/tests/lint.spelling.sh index 6903bc0ae..4520e2674 100755 --- a/.drevops/tests/lint.spelling.sh +++ b/.drevops/tests/lint.spelling.sh @@ -20,7 +20,6 @@ done < <( find \ "${ROOT_DIR}"/.circleci \ "${ROOT_DIR}"/.docker \ - "${ROOT_DIR}"/docs \ "${ROOT_DIR}"/hooks/library \ "${ROOT_DIR}"/scripts \ "${ROOT_DIR}"/patches \ @@ -53,7 +52,7 @@ for file in "${targets[@]}"; do # Remove HTML. sed -E 's/<([^<]+)>//g' | # Remove code blocks. - sed -n '/\`\`\`/,/\`\`\`/ !p' | + sed '/^[[:space:]]*```/,/^[[:space:]]*```/d' | # Remove inline code. sed -n '/\`/,/\`/ !p' | # Remove anchors. diff --git a/.drevops/tests/test.common.sh b/.drevops/tests/test.common.sh index 7ed51df69..7a48b4500 100755 --- a/.drevops/tests/test.common.sh +++ b/.drevops/tests/test.common.sh @@ -48,4 +48,3 @@ bats "${TEST_DIR}/bats/install.integrations.bats" bats "${TEST_DIR}/bats/install.demo.bats" bats "${TEST_DIR}/bats/reset.bats" bats "${TEST_DIR}/bats/update-drevops.bats" -bats "${TEST_DIR}/bats/docs.bats" diff --git a/.github/workflows/drevops-test-docs.yml b/.github/workflows/drevops-test-docs.yml index 8c39e194b..2be30584c 100644 --- a/.github/workflows/drevops-test-docs.yml +++ b/.github/workflows/drevops-test-docs.yml @@ -12,18 +12,27 @@ jobs: drevops-test-docs: runs-on: ubuntu-latest + env: + DREVOPS_DEV_TEST_COVERAGE_DIR: /tmp/.drevops-coverage-html + steps: - name: Checkout code uses: actions/checkout@v4 with: persist-credentials: false + - name: Adjust git config to allow running git-related tests + run: git config --global safe.directory '*' + - name: Install Ahoy run: os=$(uname -s | tr [:upper:] [:lower:]) && architecture=$(case $(uname -m) in x86_64 | amd64) echo "amd64" ;; aarch64 | arm64 | armv8) echo "arm64" ;; *) echo "amd64" ;; esac) && sudo wget -q https://github.com/ahoy-cli/ahoy/releases/latest/download/ahoy-bin-$os-$architecture -O /usr/local/bin/ahoy && sudo chown $USER /usr/local/bin/ahoy && chmod +x /usr/local/bin/ahoy - name: Install Aspell run: sudo apt-get update -y && sudo apt-get install -y aspell + - name: Install Kcov + run: wget https://github.com/SimonKagstrom/kcov/releases/download/v42/kcov-amd64.tar.gz && tar -xf kcov-amd64.tar.gz && sudo mv ./usr/local/bin/kcov /usr/local/bin/kcov && kcov --version + - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -37,4 +46,21 @@ jobs: - name: Run tests working-directory: .drevops/docs - run: ahoy test + run: ahoy test-site + + - name: Run tests + run: ./docs/.utils/test.sh + working-directory: .drevops + + - name: Upload coverage report as an artifact + uses: actions/upload-artifact@v4 + with: + name: ${{github.job}}-code-coverage-report + path: /tmp/.drevops-coverage-html + + - name: Upload coverage report to Codecov + uses: codecov/codecov-action@v4 + with: + directory: /tmp/.drevops-coverage-html + fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }}