diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b14e94e..78cf018 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -24,3 +24,11 @@ updates: interval: "weekly" assignees: - "xZero707" + + # Maintain Docker image dependencies for tests-util (build) + - package-ecosystem: "docker" + directory: "/build/tests-util" + schedule: + interval: "weekly" + assignees: + - "xZero707" \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..23a3dae --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,32 @@ +on: + push: + branches: + - "master" + +jobs: + tests: + runs-on: ubuntu-latest + + steps: + - + name: "Checkout" + uses: actions/checkout@v3 + + - + name: "Set up Docker Buildx" + uses: docker/setup-buildx-action@v2 + + - + name: "Build tests-util image" + uses: docker/build-push-action@v3 + id: build + with: + context: build/tests-util/ + cache-from: type=gha + cache-to: type=gha,mode=max + load: true + tags: localhost/tests-util:latest + + - + name: "Run tests" + run: bin/tests diff --git a/bin/tests b/bin/tests new file mode 100755 index 0000000..bcd17ef --- /dev/null +++ b/bin/tests @@ -0,0 +1,9 @@ +#!/usr/bin/env sh +set -ex + +#docker build -t localhost/tests-util build/tests-util +docker run --init \ + --rm \ + -it \ + -v "${PWD}:/var/www/html" \ + localhost/tests-util wp-patch-tests diff --git a/build/tests-util/Dockerfile b/build/tests-util/Dockerfile new file mode 100644 index 0000000..ed94011 --- /dev/null +++ b/build/tests-util/Dockerfile @@ -0,0 +1,45 @@ +ARG PHP_VERSION=7.4 +ARG WP_VERSION=6.0.2 + + +# Build composer dependencies +FROM composer AS builder + +WORKDIR "/var/www/composer/" +COPY ["./composer.lock", "./composer.json", "/var/www/composer/"] + +RUN set -eux \ + && composer install --dev --no-interaction --ignore-platform-reqs + + + +# Build/tag WPCLI +FROM --platform=${TARGETPLATFORM} wordpress:cli-php${PHP_VERSION} AS wp-cli + + + +# Build root filesystem +FROM scratch AS rootfs + +COPY --from=builder ["/usr/bin/composer", "/usr/local/bin/"] +COPY --from=builder ["/var/www/composer/vendor", "/var/www/composer/vendor"] + +# Install wp-cli +COPY --from=wp-cli ["/usr/local/bin/wp", "/usr/local/bin/wp"] + +# WP patch tests +COPY --chmod=0777 ["./wp-patch-tests.sh", "/usr/local/bin/wp-patch-tests"] + + +# Build final image +ARG PHP_VERSION +ARG WP_VERSION +FROM --platform=${TARGETPLATFORM} wordpress:${WP_VERSION}-php${PHP_VERSION}-fpm-alpine + + +RUN set -eux \ + && apk add --update --no-cache git patch less \ + && git config --global --add safe.directory /var/www/html \ + && ln -sf /var/www/composer/vendor/bin/parallel-lint /usr/local/bin/php-parallel-lint + +COPY --from=rootfs ["/", "/"] diff --git a/build/tests-util/composer.json b/build/tests-util/composer.json new file mode 100644 index 0000000..8a55b29 --- /dev/null +++ b/build/tests-util/composer.json @@ -0,0 +1,6 @@ +{ + "require-dev": { + "php-parallel-lint/php-parallel-lint": "^1.3", + "php-parallel-lint/php-console-highlighter": "^1.0" + } +} diff --git a/build/tests-util/wp-patch-tests.sh b/build/tests-util/wp-patch-tests.sh new file mode 100755 index 0000000..ffa3031 --- /dev/null +++ b/build/tests-util/wp-patch-tests.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +# Register exit handler +trap scriptExitHandler EXIT + +function scriptExitHandler() { + LAST_EXIT_CODE=$? + + if [ -n "${WP_DL_TEMP_DIR}" ] && [ -d "${WP_DL_TEMP_DIR}" ]; then + rm -rf "${WP_DL_TEMP_DIR:?}" + fi + + if [ -n "${PHP_TESTS_TEMP_DIR}" ] && [ -d "${PHP_TESTS_TEMP_DIR}" ]; then + rm -rf "${PHP_TESTS_TEMP_DIR:?}" + fi + + if [ "${LAST_EXIT_CODE}" = "0" ]; then + echo "> Script finished successfully" + exit "${LAST_EXIT_CODE}" + fi + + echo "> Script finished with an error" + exit "${LAST_EXIT_CODE}" +} + +set -e + +PHP_TESTS_TEMP_DIR="$(mktemp -d -t XXXXXXXXXXX)" + +function getFileCount() { + ALL_DIRECTORIES=("${1:?Target dir is required}"/*) + echo ${#ALL_DIRECTORIES[@]} # Sometimes, you just need a count +} + +function taskPrepareWpPatch() { + WP_DL_TEMP_DIR="$(mktemp -d -t XXXXXXXXXXX)" + PATCH_DIR=${1:?} + + WP_WORK_LONG_VERSION="$(basename "${PATCH_DIR}")" + WP_WORK_SHORT_VERSION="$(echo "${WP_WORK_LONG_VERSION:?}" | sed --expression='s/.0$//g')" + wp --allow-root core download --locale="en_GB" --version="${WP_WORK_SHORT_VERSION}" --path="${WP_DL_TEMP_DIR}" + + echo "> Applying patch" + patch "${WP_DL_TEMP_DIR}/wp-admin/update-core.php" <"${PATCH_DIR}/wp-admin-update-core.patch" + mv -v "${WP_DL_TEMP_DIR}/wp-admin/update-core.php" "${PHP_TESTS_TEMP_DIR}/update-core-${WP_WORK_LONG_VERSION}.php" + + rm "${WP_DL_TEMP_DIR?}" -rf +} + +# For each patch, download appropriate WP version, apply patch and check if file syntax is correct afterwards +for PATCH_DIR in patches/*/; do + echo "> Deploying task ${PATCH_DIR}" + + # Introduce ~50ms overhead before deploying another task + # Even shorter overhead helps. but better to be on safe side. + # This should prevent concurrency issues + sleep 0.05 + + # Run task concurrently + taskPrepareWpPatch "${PATCH_DIR}" & +done + +echo "Waiting for all tasks to finish..." +wait + +# Make sure that directory is not empty +if [ ! "$(ls -A "${PHP_TESTS_TEMP_DIR}")" ]; then + echo "Error: Target directory is empty" + exit 1 +fi + +NUMBER_OF_PATCHES="$(getFileCount patches)" +NUMBER_OF_TEST_FILES="$(getFileCount "${PHP_TESTS_TEMP_DIR}")" + +if [ "${NUMBER_OF_PATCHES}" != "${NUMBER_OF_TEST_FILES}" ]; then + echo "> Error - Unexpected number of files" + echo " Expected: ${NUMBER_OF_PATCHES}" + echo " Actual: ${NUMBER_OF_TEST_FILES}" + exit 1 +fi + +# Run php-lint on resulting patch files +php-parallel-lint "${PHP_TESTS_TEMP_DIR}" -s --blame --exclude vendor -p php +exit $? \ No newline at end of file