diff --git a/Dockerfile b/Dockerfile index 93b24f2a18c5..7221525e067f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -25,3 +25,6 @@ RUN npm install && npm install -g osu-wiki && pip3 install -r requirements.txt # Run the container with UID and GID of the host COPY meta/docker-entrypoint.sh / ENTRYPOINT ["/docker-entrypoint.sh"] + +# By default, run all checks when the container is started +CMD ["meta/check-all.sh"] diff --git a/meta/check-all.sh b/meta/check-all.sh new file mode 100755 index 000000000000..c25200fba008 --- /dev/null +++ b/meta/check-all.sh @@ -0,0 +1,68 @@ +#!/bin/sh + +set -eu + +test_output_pipe=/tmp/osu-wiki-test-output-pipe +rm -f "$test_output_pipe" +mkfifo "$test_output_pipe" + +run_test() { + test_name="$1" + shift + + printf '\033[1m## Running %s test...\033[m\n' "$test_name" >&2 + + # Run the test as a job, preserving its original stdin on fd 4, and directing + # all of its output to a pipe opened for reading on fd 3 + exec 4<&0 + "$@" <&4 >"$test_output_pipe" 2>&1 & + exec 3<"$test_output_pipe" + + # If the test has output, print the first line with a preceding newline + if IFS= read -r first_line <&3; then + printf '\n%s\n' "$first_line" >&2 + fi + + # Stream the rest of the test's output + cat <&3 >&2 + + # If the test had output, print another newline + if test -n "$first_line"; then + printf '\n' >&2 + fi + + # Print the final message based on the exit code of the test + if wait $!; then + printf '\033[1;32m## Passed %s test.\033[m\n' "$test_name" >&2 + else + printf '\033[1;31m## Failed %s test.\033[m\n' "$test_name" >&2 + fi + + # Close fd 3 and fd 4 + exec 3<&- 4<&- +} + +cd -- "$(dirname "$0")/.." + +changed_files="$( + { + git diff --diff-filter=d --name-only --no-renames master + git ls-files --exclude-standard --others + } | sort -u +)" + +if test -z "$changed_files"; then + printf 'No files changed since master.\n' >&2 + exit +fi + +printf '%s\n' "$changed_files" | run_test 'file size' xargs meta/check-file-sizes.sh +{ printf '%s\n' "$changed_files" | grep '\.md$' || true; } | run_test Remark xargs -r meta/remark.sh +run_test 'YAML style' osu-wiki-tools check-yaml +run_test link osu-wiki-tools check-links --all + +first_commit_hash="$(git log --pretty=%H master.. | tail -n 1)" + +if test -n "$first_commit_hash"; then + run_test 'outdated article' osu-wiki-tools check-outdated-articles --base-commit "$first_commit_hash" --workflow +fi diff --git a/meta/docker-run.sh b/meta/docker-run.sh new file mode 100755 index 000000000000..1437d43faae0 --- /dev/null +++ b/meta/docker-run.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +set -eu + +osu_wiki_path="$(cd -- "$(dirname "$0")/.." && pwd)" +remove_node_modules="$(test -e "$osu_wiki_path/node_modules" || printf 1)" + +# Map the host repo directory to /osu-wiki, but use node_modules from the image +docker run \ + --rm \ + --volume "$osu_wiki_path:/osu-wiki" \ + --volume /osu-wiki/node_modules/ \ + osu-wiki "$@" + +# FIXME: The above command leaves behind an empty node_modules directory if it +# didn't already exist, and I can't figure out how to prevent that... so +# here I delete the node_modules directory if it is new. —clayton +if test -n "$remove_node_modules"; then + rm -rf "$osu_wiki_path/node_modules" +fi diff --git a/run-checks.sh b/run-checks.sh index 4797ecdb7975..56eaf8eb7d08 100755 --- a/run-checks.sh +++ b/run-checks.sh @@ -1,117 +1,35 @@ -#!/bin/bash +#!/bin/sh -function print_error() { printf -- "\e[0;31m$1\e[m\n" 1>&2; } -function print_warning() { printf -- "\e[0;34m$1\e[m\n" 1>&2; } -function print_success() { printf -- "\e[0;32m$1\e[m\n" 1>&2; } -function print_ok() { printf -- "$1\n" 1>&2; } +set -eu -function _build_container() { - if ! ( which docker > /dev/null ); then - print_error "Missing Docker -- install it from https://docs.docker.com/engine and restart the shell." +build_docker_image() { + if ! which docker >/dev/null 2>&1; then + printf '\033[31mError:\033[m Docker not found. Install it from https://docs.docker.com/get-docker/ and restart the shell.\n' >&2 exit 1 fi - print_ok "Preparing the Docker image..." - if ! ( DOCKER_BUILDKIT=1 docker build -q -t osu-wiki . ); then - print_error "Failed to build the Docker image." + if ! DOCKER_BUILDKIT=1 docker build --tag osu-wiki . >/dev/null; then + printf '\033[31mError:\033[m Failed to build Docker image.\n' >&2 exit 1 fi } -# Do not shadow node_modules in the container: https://stackoverflow.com/q/29181032#comment97216954_37898591 -function _docker() { - osu_wiki_root=$( cd -- "$( dirname $0 )" && pwd ) - container_workdir="/osu-wiki" - docker run \ - --volume ${osu_wiki_root}:${container_workdir}/ \ - --volume ${container_workdir}/node_modules \ - --workdir ${container_workdir} osu-wiki bash -c "$*" -} - -function _test_wrapper() { - test_name="$1" - command_line="$2" - files=( $( echo "${@: 3}" | tr '\n' ' ' ) ) # bash v3.2 on macOS doesn't support readarray - - if test -z "$files"; then - print_success "* Skipped $test_name test." - else - print_ok "* Run $test_name test" - $command_line "${files[@]}" - return_code=$? - if test $return_code -eq 0; then - print_success "* Passed $test_name test." - else - print_error "* Failed $test_name test (exit code $return_code)." - fi - fi -} - -function _usage() { - echo "Usage: $( basename $0 ) [-- [COMMAND] [ARGS]]" - echo -e "Run osu! wiki-related commands in a Docker container.\n" - echo -e "Without arguments, run default test suite on all changes, both committed and not.\n" - echo " -- [COMMAND] [ARGS] run COMMAND in the container, passing ARGS as its arguments" -} - -function main() { - while [[ $# -gt 0 ]]; do - case $1 in - -h|--help) - _usage - exit 2 - ;; - --) - shift - _build_container - _docker "$@" - exit $? - ;; - *|-*|--*) - echo -e "Unrecognized option '$1'\nTry '$( basename $0 ) --help' for more information." - exit 1 - ;; - esac - done - - current_branch=$( git branch --show-current ) - if test ${current_branch} = 'master'; then - print_error "Please run this from a feature branch, i.e. not 'master'" - exit 1 - fi - - first_commit_hash=$( git log master..${current_branch} --pretty=format:%H | tail -1 ) +cd -- "$(dirname "$0")" - interesting_files=$( - sort -u < <( - # Changes that are not committed (staged + unstaged + untracked), but without deleted files - git status --short -v -v --no-renames --porcelain | awk '$1 != "D" { print $2 }' - # Changes committed so far (may overlap with the above) - if [[ -n ${first_commit_hash} ]]; then - git diff --no-renames --name-only --diff-filter=d ${first_commit_hash}^ - fi - ) - ) - - if test -z "${interesting_files}"; then - print_success "No changes detected -- nothing to check." - exit 0 +if test $# -gt 0; then + if test $# -gt 1 -a "$1" = --; then + shift + build_docker_image + exec meta/docker-run.sh "$@" fi - # ppy/osu-wiki#8867 -- disable style checks for non-article Markdown files, such as README.md - interesting_articles=$( echo "${interesting_files}" | grep -e ^wiki/ -e ^news/ | grep .md$ | grep -E -v '^[A-Z-]+\.md$' ) - - _build_container - - # TODO give no arguments to check-file-sizes - _test_wrapper "file size" "_docker meta/check-file-sizes.sh" "${interesting_files}" - _test_wrapper "article style" "_docker meta/remark.sh" "${interesting_articles}" - - _test_wrapper "YAML style" "_docker osu-wiki-tools check-yaml --target" . - - # TODO: _test_wrapper expects a third argument but luckily --all overrides --target. please rewrite to make this prettier - _test_wrapper "link" "_docker osu-wiki-tools check-links --all --target" "${interesting_articles}" - _test_wrapper "article freshness" "_docker osu-wiki-tools check-outdated-articles --workflow --base-commit" "${first_commit_hash}" -} + exec >&2 + printf 'Run the test suite on files changed since master:\n\n' + printf ' \033[4m%s\033[m\n\n' "$0" + printf 'Run a command in the osu-wiki Docker container:\n\n' + printf ' \033[4m%s\033[m -- []\n' "$0" + exit 1 +fi -main $@ +build_docker_image +exec meta/docker-run.sh