Kristian read the readme and found it still telling people to configure the plugin under WooCommerce, for a page that moved to the Nolimit menu three releases ago. Sweeping for it found seven stale places, and three of them are notices THE PLUGIN SHOWS THE USER — the missing-WooCommerce notice, the activation error and the incomplete-install warning all still called it WooCommerce Email Discount Manager. That is the mimir.guide failure exactly: the code moved, the text stood still, and nothing failed. It was found by somebody reading, which is the only way it could be found, and that is the argument for the check rather than for being more careful next time. ci/static.sh now asserts two things. The name in readme.txt's title matches the Plugin Name header, because the header is what WordPress shows and everything else has to agree with it. And no shipped PHP file contains OLD_PLUGIN_NAME, which is now a value in ci/plugin.conf rather than a habit. The changelog is deliberately exempt: an entry describes what was true for that release, and rewriting it would be a different lie. Only text describing the CURRENT state is checked. Both halves proven red — a readme title that disagrees, and the old name put back into one notice. Not changed: the page stays under Nolimit rather than gaining a second entry under WooCommerce. One menu is the family's shape, and a duplicate entry is clutter rather than a fix for a readme that was simply wrong. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
127 lines
4.8 KiB
Bash
127 lines
4.8 KiB
Bash
# Shared helpers for the ci/ scripts. Sourced, never executed.
|
|
#
|
|
# Nothing in here is specific to one plugin — ci/plugin.conf holds that.
|
|
|
|
# shellcheck shell=bash
|
|
|
|
ci_repo_root() {
|
|
cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd
|
|
}
|
|
|
|
# Load ci/plugin.conf and fail loudly on anything it forgot to set. A missing
|
|
# value would otherwise turn into an empty path and a test that passes by
|
|
# testing nothing.
|
|
ci_load_conf() {
|
|
local root
|
|
root="$(ci_repo_root)"
|
|
|
|
# shellcheck source=/dev/null
|
|
. "$root/ci/plugin.conf"
|
|
|
|
# MIN_* included on purpose: a port that copies this file but forgets them
|
|
# leaves the floors empty, every comparison becomes a no-op, and the hollow
|
|
# checks these were added to fix come back silently — the same shape of bug.
|
|
local missing=
|
|
for var in PLUGIN_SLUG PLUGIN_MAIN TEXT_DOMAIN NEEDS_WOOCOMMERCE MIN_SHIPPED_PHP_FILES MIN_TRANSLATION_CALLS RELEASE_REPO WPMGR_MENU_SHA OLD_PLUGIN_NAME; do
|
|
[ -n "${!var:-}" ] || missing="$missing $var"
|
|
done
|
|
if [ -n "$missing" ]; then
|
|
echo "ci/plugin.conf is missing:$missing" >&2
|
|
return 1
|
|
fi
|
|
|
|
if [ ! -f "$root/$PLUGIN_MAIN" ]; then
|
|
echo "ci/plugin.conf points PLUGIN_MAIN at '$PLUGIN_MAIN', which does not exist." >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# The version WordPress will compare against the update feed. Read from the
|
|
# plugin header, which is the only copy WordPress ever looks at.
|
|
ci_plugin_version() {
|
|
sed -n -E 's/^[[:space:]]*\*?[[:space:]]*Version:[[:space:]]*([^[:space:]]+).*/\1/p' \
|
|
"$(ci_repo_root)/$PLUGIN_MAIN" | head -1
|
|
}
|
|
|
|
# Read any other plugin header field, e.g. ci_plugin_header 'Requires at least'.
|
|
ci_plugin_header() {
|
|
sed -n -E "s/^[[:space:]]*\*?[[:space:]]*$1:[[:space:]]*(.*)/\1/p" \
|
|
"$(ci_repo_root)/$PLUGIN_MAIN" | head -1 | sed -E 's/[[:space:]]+$//'
|
|
}
|
|
|
|
# Run a shell script against a checkout of this repository, inside a container.
|
|
#
|
|
# The repository goes in over a tarball and `docker cp`, never a bind mount: a
|
|
# Gitea Actions job runs inside a container talking to the HOST's docker
|
|
# daemon, so a host path here lands on an empty directory nobody created and
|
|
# the check passes by checking nothing. The symptom reads like a typo in a path.
|
|
#
|
|
# Two details are load-bearing. The script is copied in as a FILE rather than
|
|
# interpolated into `sh -c`, so it can contain $, quotes and newlines without
|
|
# being mangled — which matters the moment it is more than one command. And
|
|
# --entrypoint sh is not decoration: some images (composer:2, for one) have an
|
|
# entrypoint that swallows its arguments.
|
|
#
|
|
# Taken from the subscriptions port, which needed it for composer.
|
|
ci_in_container() { # image script-text
|
|
local image="$1" script="$2"
|
|
local dir cid code=0
|
|
|
|
dir=$(mktemp -d -t wpci-XXXXXX)
|
|
( cd "$(ci_repo_root)" && git ls-files -z | tar --null -T - -cf "$dir/src.tar" )
|
|
printf '%s\n' "$script" > "$dir/run.sh"
|
|
|
|
cid=$(docker create --entrypoint sh -w /src "$image" \
|
|
-c 'mkdir -p /src && tar -xf /tmp/src.tar -C /src && exec sh /tmp/run.sh')
|
|
docker cp "$dir/src.tar" "$cid:/tmp/src.tar" >/dev/null
|
|
docker cp "$dir/run.sh" "$cid:/tmp/run.sh" >/dev/null
|
|
docker start -a "$cid" || code=$?
|
|
docker rm -f "$cid" >/dev/null
|
|
rm -rf "$dir"
|
|
return "$code"
|
|
}
|
|
|
|
# Run one of this repository's PHP scripts.
|
|
#
|
|
# Uses the local php when there is one — a laptop, GitHub's hosted runners —
|
|
# and a container otherwise. The Gitea runner image has no php, and losing a
|
|
# build to "php: command not found" after everything else passed is a silly way
|
|
# to spend an afternoon. Measured: it happened on the very first Gitea run.
|
|
ci_php() {
|
|
if command -v php >/dev/null 2>&1; then
|
|
php "$@"
|
|
return
|
|
fi
|
|
|
|
ci_in_container "php:8.4-cli" "exec php $(printf '%q ' "$@")"
|
|
}
|
|
|
|
# Run one of this repository's python scripts, with PyYAML available.
|
|
#
|
|
# Same shape and the same reason as ci_php above, and it is not hypothetical:
|
|
# the Gitea runner image has NO PyYAML (measured — `import yaml` raises
|
|
# ModuleNotFoundError there), while every laptop and GitHub's hosted runners
|
|
# have it. So a check that merely skips when the import fails is a check that
|
|
# is switched off on exactly one forge — and Gitea is the forge where a
|
|
# workflow that does not parse produces no run and no error to click on.
|
|
#
|
|
# alpine + py3-yaml rather than a python image + pip: no network install, one
|
|
# apk call, the same pinned answer every time.
|
|
ci_python_yaml() {
|
|
if python3 -c 'import yaml' 2>/dev/null; then
|
|
python3 "$@"
|
|
return
|
|
fi
|
|
|
|
ci_in_container "alpine:3.20" \
|
|
"apk add --no-cache python3 py3-yaml >/dev/null && exec python3 $(printf '%q ' "$@")"
|
|
}
|
|
|
|
ci_step() { printf '\n\033[1m→ %s\033[0m\n' "$*"; }
|
|
ci_fail() { printf '\033[31mFAIL\033[0m %s\n' "$*" >&2; }
|
|
|
|
# GitHub and Gitea both render these as annotations on the file and line.
|
|
ci_annotate() { # level file line message
|
|
printf '::%s file=%s,line=%s::%s\n' "$1" "$2" "$3" "$4"
|
|
}
|