Skip to content

Commit 5dd53bf

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 5dd53bf

4 files changed

Lines changed: 129 additions & 61 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: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -52,44 +52,32 @@ 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 LINK_RESULT
5859
echo "TIER_TEST" > tier.txt
59-
set +e;
60-
set +o pipefail;
60+
61+
# from commands.inc.sh
62+
63+
do_test_record_start_time
6164

6265
builder_echo blue "---- PHP unit tests"
63-
docker exec $KEYMAN_CONTAINER_DESC sh -c "vendor/bin/phpunit --testdox"
66+
do_test_unit_tests "${KEYMAN_CONTAINER_DESC}"
6467

65-
# Lint .php files for obvious errors
6668
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"
69+
do_test_lint "${KEYMAN_CONTAINER_DESC}"
6870

69-
# NOTE: link checker runs on host rather than in docker image
7071
builder_echo blue "---- Testing links"
72+
LINK_RESULT=0
73+
do_test_links "http://localhost:${PORT_KEYMAN_COM}" || LINK_RESULT=$?
74+
builder_echo blue "Done checking links; linkinator exit code: ${LINK_RESULT}"
75+
do_test_print_link_report
76+
77+
do_test_print_container_error_logs "${KEYMAN_CONTAINER_DESC}"
7178

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"
9179
rm tier.txt
92-
return "${BLC_RESULT}"
80+
return "$LINK_RESULT"
9381
}
9482

9583
builder_run_action configure do_configure

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)