Skip to content

Commit 3317ea7

Browse files
committed
chore: replace broken-link-checker with linkinator
Broken-link-checker (BLC) is basically dead -- no releases for 8 years, no updates to master for 5. The trigger for this change is, however, that BLC was not honoring exclusions on redirects, which cause sporadic errors (e.g. 429) because we hit external servers in our link test. Replace with linkinator, which is in maintenance and does the job pretty well. I have forked linkinator and added some patches specific to our environment which I may push upstream later when we are confident of the setup. Unify the ci.yml and build.sh test steps (this will need to be tested in CI once this merges to master). Test-bot: skip
1 parent ece6d4f commit 3317ea7

4 files changed

Lines changed: 132 additions & 62 deletions

File tree

.github/workflows/ci.yml

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ jobs:
88
runs-on: ubuntu-latest
99
env:
1010
KEYMANHOSTS_TIER: TIER_TEST
11+
CONTAINER_DESC: keyman-com-app
12+
CONTAINER_PORT: 8053
1113

1214
steps:
1315
- name: Checkout
@@ -22,53 +24,56 @@ jobs:
2224
fail-fast: true
2325

2426
#
25-
# Finally, run the tests
27+
# Run tests -- these step definitions are identical across:
28+
# help.keyman.com, keyman.com, keymanweb.com, api.keyman.com
2629
#
30+
- name: Test setup
31+
shell: bash
32+
run: |
33+
source ./resources/commands.inc.sh
34+
set -e
35+
do_test_record_start_time
36+
echo "TEST_START_TIME=${TEST_START_TIME}" >> "$GITHUB_ENV"
37+
2738
- name: PHP test
39+
if: ${{ !cancelled() }}
2840
shell: bash
2941
run: |
30-
docker exec keyman-com-app sh -c "vendor/bin/phpunit --testdox"
42+
source ./resources/commands.inc.sh
43+
set -e
44+
do_test_unit_tests "$CONTAINER_DESC"
3145
3246
- name: Lint
47+
if: ${{ !cancelled() }}
3348
shell: bash
3449
run: |
35-
( set +e; set +o pipefail; find . -name '*.php' | grep -v '/vendor/' | xargs -n 1 -d '\n' php -l | grep -v "No syntax errors detected"; exit ${PIPESTATUS[2]} )
50+
source ./resources/commands.inc.sh
51+
set -e
52+
do_test_lint "$CONTAINER_DESC"
3653
3754
- name: Check broken links
55+
if: ${{ !cancelled() }}
3856
shell: bash
39-
continue-on-error: false
4057
run: |
41-
set +e
42-
set +o pipefail
43-
readarray -t ignoresArray <<< $(find ./_includes/locale/strings/keyboards/ -maxdepth 1 -name '*.php' ! -name "en.php" \
44-
-execdir basename {} .php ';')
45-
baseURL="http://localhost:8053"
46-
ignoreStr=(" --exclude ${baseURL}*downloads/releases/*")
47-
for locale in "${ignoresArray[@]}"; do
48-
ignoreStr+=" --exclude ${baseURL}/${locale}/*"
49-
done
50-
echo "ignoreStr: ${ignoreStr[@]}"
51-
npx broken-link-checker ${baseURL}/_test --recursive --ordered ---host-requests 50 -e --filter-level 3 ${ignoreStr} | tee blc.log
52-
echo "BLC_RESULT=${PIPESTATUS[0]}" >> "$GITHUB_ENV"
58+
source ./resources/commands.inc.sh
59+
set -e
60+
do_test_links "http://localhost:${CONTAINER_PORT}"
61+
62+
# We split the reporting of broken links into a separate step for ease of
63+
# viewing because the broken links are otherwise hidden in a sea of good
64+
# links in a very long report
5365

5466
- name: Report on broken links
67+
if: ${{ !cancelled() }}
5568
run: |
56-
set +e
57-
set +o pipefail
58-
cat blc.log | \
59-
grep -E "BROKEN|Getting links from" | \
60-
grep -B 1 "BROKEN";
61-
exit "${BLC_RESULT}"
69+
source ./resources/commands.inc.sh
70+
set -e
71+
do_test_print_link_report
6272
6373
- name: Check PHP errors
74+
if: ${{ !cancelled() }}
6475
shell: bash
6576
run: |
66-
CONTAINER=`docker container ls -l -q`
67-
if docker container logs $CONTAINER 2>&1 | grep -q 'php7'; then
68-
echo 'PHP reported errors or warnings:'
69-
docker container logs $CONTAINER 2>&1 | grep 'php7'
70-
exit 1
71-
else
72-
echo 'No PHP errors found'
73-
exit 0
74-
fi
77+
source ./resources/commands.inc.sh
78+
set -e
79+
do_test_print_container_error_logs "$CONTAINER_DESC"

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cdn/deploy/
55
tier.txt
66

77
# unit test artifacts
8-
blc.log
8+
linkinator-results.json
99
.phpunit.result.cache
1010

1111
vendor*
@@ -21,4 +21,4 @@ _common/
2121
_control/debug
2222

2323
# .htaccess is generated from .htaccess.in during build.sh build
24-
.htaccess
24+
.htaccess

build.sh

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -52,44 +52,34 @@ function do_start() {
5252
start_docker_container $KEYMAN_IMAGE_NAME $KEYMAN_CONTAINER_NAME $KEYMAN_CONTAINER_DESC $HOST_KEYMAN_COM $PORT_KEYMAN_COM $BUILDER_CONFIGURATION
5353
}
5454

55-
function test_docker_container() {
56-
# Note: ci.yml replicates these
55+
source resources/commands.inc.sh
5756

57+
function test_docker_container() {
58+
local CONTAINER_DESC="$1"
59+
local CONTAINER_PORT="$2"
60+
local LINK_RESULT
5861
echo "TIER_TEST" > tier.txt
59-
set +e;
60-
set +o pipefail;
62+
63+
# from commands.inc.sh
64+
65+
do_test_record_start_time
6166

6267
builder_echo blue "---- PHP unit tests"
63-
docker exec $KEYMAN_CONTAINER_DESC sh -c "vendor/bin/phpunit --testdox"
68+
do_test_unit_tests "${CONTAINER_DESC}"
6469

65-
# Lint .php files for obvious errors
6670
builder_echo blue "---- Lint PHP files"
67-
docker exec $KEYMAN_CONTAINER_DESC sh -c "find . -name '*.php' | grep -v '/vendor/' | xargs -n 1 -d '\\n' php -l"
71+
do_test_lint "${CONTAINER_DESC}"
6872

69-
# NOTE: link checker runs on host rather than in docker image
7073
builder_echo blue "---- Testing links"
74+
LINK_RESULT=0
75+
do_test_links "http://localhost:${CONTAINER_PORT}" || LINK_RESULT=$?
76+
builder_echo blue "Done checking links; linkinator exit code: ${LINK_RESULT}"
77+
do_test_print_link_report
78+
79+
do_test_print_container_error_logs "${CONTAINER_DESC}"
7180

72-
# determine non-en locales to ignore along with /downloads/releases
73-
readarray -t ignoresArray <<< $(find ./_includes/locale/strings/keyboards/ -maxdepth 1 -name '*.php' ! -name "en.php" \
74-
-execdir basename {} .php ';')
75-
local baseURL="http://localhost:8053"
76-
local ignoreStr=(" --exclude ${baseURL}*/downloads/releases/*")
77-
for locale in "${ignoresArray[@]}"; do
78-
ignoreStr+=" --exclude ${baseURL}/${locale}/*"
79-
done
80-
echo "ignoreStr: ${ignoreStr[@]}"
81-
npx broken-link-checker ${baseURL}/_test --recursive --ordered ---host-requests 50 -e --filter-level 3 ${ignoreStr} | tee blc.log
82-
local BLC_RESULT=${PIPESTATUS[0]}
83-
echo ----------------------------------------------------------------------
84-
echo Link check summary
85-
echo ----------------------------------------------------------------------
86-
cat blc.log | \
87-
grep -E "BROKEN|Getting links from" | \
88-
grep -B 1 "BROKEN";
89-
90-
builder_echo blue "Done checking links"
9181
rm tier.txt
92-
return "${BLC_RESULT}"
82+
return "$LINK_RESULT"
9383
}
9484

9585
builder_run_action configure do_configure
@@ -98,5 +88,5 @@ builder_run_action stop stop_docker_container $KEYMAN_IMAGE_NAME $KEYMAN_
9888
builder_run_action build build_docker_container $KEYMAN_IMAGE_NAME $KEYMAN_CONTAINER_NAME $BUILDER_CONFIGURATION
9989
builder_run_action start do_start
10090

101-
builder_run_action test test_docker_container
91+
builder_run_action test test_docker_container $KEYMAN_CONTAINER_DESC $PORT_KEYMAN_COM
10292
builder_run_action htaccess preprocess_htaccess

resources/commands.inc.sh

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# shellcheck shell=bash
2+
3+
# Record the start time for unit tests for later log review
4+
function do_test_record_start_time() {
5+
TEST_START_TIME=$(date -Is -u)
6+
}
7+
8+
# Run unit tests through phpunit
9+
function do_test_unit_tests() {
10+
local CONTAINER="$1"
11+
docker exec "${CONTAINER}" sh -c "vendor/bin/phpunit --testdox"
12+
}
13+
14+
# Lint .php files for obvious errors
15+
function do_test_lint() {
16+
local CONTAINER="$1"
17+
docker exec "${CONTAINER}" sh -c "find . -name '*.php' | grep -v '/vendor/' | xargs -n 1 -d '\\n' php -l"
18+
}
19+
20+
# Check links on live local server using linkinator
21+
function do_test_links() {
22+
local baseURL="$1"
23+
24+
# NOTE: link checker runs on host rather than in docker image
25+
local ignoreLocales baseURL
26+
27+
# determine non-en locales to ignore
28+
ignoreLocales="$(jq -r 'keys | map(select(. != "en")) | join("|")' ./_includes/locale/locales.json)"
29+
30+
set +e
31+
npx https://github.com/keymanapp/linkinator \
32+
"${baseURL}/_test" \
33+
--clean-urls \
34+
--concurrency 50 \
35+
--format json \
36+
--output-filename linkinator-results.json \
37+
--skip "^(?!${baseURL})" \
38+
--skip "^${baseURL}/(${ignoreLocales})" \
39+
--skip "^${baseURL}/en/downloads/releases/" \
40+
--recurse \
41+
--redirects verify \
42+
--retry-errors \
43+
--root-path "${baseURL}"
44+
local RESULT=$?
45+
set -e
46+
47+
return "${RESULT}"
48+
}
49+
50+
# Print summary of results from linkinator
51+
function do_test_print_link_report() {
52+
echo ----------------------------------------------------------------------
53+
echo Link check summary
54+
echo ----------------------------------------------------------------------
55+
# Emit full JSON detail for broken links (may not be necessary)
56+
jq '.links[] | select(.state != "OK")' < linkinator-results.json
57+
echo
58+
echo
59+
# Emit a summary report
60+
jq -r '.links[] | select(.state != "OK") | "\(.state)[\(.status)]: \(.parent) --> \(.url)"' < linkinator-results.json
61+
}
62+
63+
# Scan logs recorded on container since start of tests to find any reported PHP
64+
# errors (note, depends on 'php7' string)
65+
function do_test_print_container_error_logs() {
66+
local CONTAINER="$1"
67+
if docker container logs "${CONTAINER}" --since "${TEST_START_TIME}" 2>&1 | grep -q 'php7'; then
68+
echo 'PHP reported errors or warnings:'
69+
docker container logs "${CONTAINER}" --since "${TEST_START_TIME}" 2>&1 | grep 'php7'
70+
return 1
71+
else
72+
echo 'No PHP errors found'
73+
return 0
74+
fi
75+
}

0 commit comments

Comments
 (0)