diff --git a/.ci/grep.sh b/.ci/grep.sh
new file mode 100755
index 0000000000000000000000000000000000000000..94e95f1773a91517bd2d0edc7acd3454ab2b7870
--- /dev/null
+++ b/.ci/grep.sh
@@ -0,0 +1,16 @@
+#!/bin/sh -e
+# Description: check various bad patterns with grep
+# https://postmarktos.org/pmb-ci
+
+if [ "$(id -u)" = 0 ]; then
+	set -x
+	apk -q add grep
+	exec su "${TESTUSER:-build}" -c "sh -e $0"
+fi
+
+# Find CHANGEMEs in APKBUILDs
+if grep -qr '(CHANGEME!)' *; then
+	echo "ERROR: Please replace '(CHANGEME!)' in the following files:"
+	grep --color=always -r '(CHANGEME!)' *
+	exit 1
+fi
diff --git a/.ci/shellcheck.sh b/.ci/shellcheck.sh
index 42592ee0b0b75efad3e909ece772a01d44f493f0..874772cb3aaaae6cc3c80f96e53329aeeb93eb9f 100755
--- a/.ci/shellcheck.sh
+++ b/.ci/shellcheck.sh
@@ -1,16 +1,15 @@
 #!/bin/sh -e
-# Copyright 2021 Oliver Smith
+# Copyright 2022 Oliver Smith
 # SPDX-License-Identifier: GPL-3.0-or-later
+# Description: lint all shell scripts
+# https://postmarktos.org/pmb-ci
 
-set -e
-DIR="$(cd "$(dirname "$0")" && pwd -P)"
-cd "$DIR/.."
+DIR="$(cd "$(dirname "$0")/.." && pwd -P)"
 
-# Find CHANGEMEs in APKBUILDs
-if grep -qr '(CHANGEME!)' *; then
-	echo "ERROR: Please replace '(CHANGEME!)' in the following files:"
-	grep --color=always -r '(CHANGEME!)' *
-	exit 1
+if [ "$(id -u)" = 0 ]; then
+	set -x
+	apk -q add shellcheck
+	exec su "${TESTUSER:-build}" -c "sh -e $0"
 fi
 
 # Shell: shellcheck
@@ -44,8 +43,9 @@ sh_files="
 
 	$(find . -path '.ci/*.sh')
 "
+
 for file in $sh_files; do
 	echo "Test with shellcheck: $file"
-	cd "$DIR/../$(dirname "$file")"
+	cd "$DIR/$(dirname "$file")"
 	shellcheck -e SC1008 -x "$(basename "$file")"
 done
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f65233ac3acbc98442ecae66558784cb2b88def5..f4b566cc3452f68ece540cb4757bf39778b0e973 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -51,15 +51,15 @@ flake8:
     - .ci/lib/gitlab_prepare_ci.sh
     - .ci/flake8.sh
 
-# shellcheck
-shellcheck:
+# shellcheck and various grep checks
+shellcheck-grep:
   stage: lint
   <<: *only-default
   image: alpine:edge
-  before_script:
-    - apk -q add shellcheck
   script:
+    - .ci/lib/gitlab_prepare_ci.sh
     - .ci/shellcheck.sh
+    - .ci/grep.sh
 
 editor-config:
   stage: lint