Files
wp-plugin-starter-template-…/scripts/build-release.sh
T
marcus 2f91c3e6af WP Plugin Starter 1.0.0: a fresh start
The parts every wpallstars plugin needs: settings screen with search and an empty General tab, Read Me tab, features as classes, replaced plugins, the shared GitHub updater, release and check scripts, CI and the shared rules. scripts/rename-plugin.sh starts a new plugin; scripts/sync-core.sh keeps plugins' core files in step with this starter. Built with aidevops (https://aidevops.sh).
2026-10-03 03:30:42 +01:00

196 lines
5.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# Build the release zips of the plugin from a Git ref.
#
# Two builds of the same version, both with one {slug}/ folder inside (the
# slug is the main file's name, scripts/lib/plugin.sh):
# {slug}-X.Y.Z.zip
# GitHub release asset: the files in Git, less .distignore.
# wordpress-org-{slug}-X.Y.Z.zip
# WordPress.org build: the same, less the files in .distignore-wporg and
# the GitHub updater header lines. Its name does not start with the
# slug, so no updater picks it even if it is attached to a GitHub
# release by mistake (it takes the first asset whose name starts with
# the plugin slug). Never attach it to a GitHub release.
# SHA256SUMS
#
# Files come from the Git ref (git archive), never from the working tree, so
# uncommitted changes and untracked files are never included.
#
# Usage: scripts/build-release.sh [--ref REF] [--out DIR] [--quiet]
# --ref REF Commit, branch or tag to build (default: HEAD).
# --out DIR Output folder (default: dist/ in the repository, gitignored).
# --quiet Print only the paths of the zips.
#
# Needs git, rsync, zip, unzip and shasum or sha256sum.
set -euo pipefail
umask 022
export TZ=UTC
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_DIR
# shellcheck source=scripts/lib/plugin.sh disable=SC1091 # followed only with -x
. "$SCRIPT_DIR/lib/plugin.sh"
readonly WPORG_IGNORE=".distignore-wporg"
# Header lines read only by GitHub updaters (ours and Git Updater); left out of the WordPress.org build.
readonly WPORG_STRIP_HEADERS='GitHub Plugin URI|Primary Branch|Release Asset'
TMP_DIR=""
SLUG=""
MAIN_FILE=""
die() {
local message="$1"
printf 'build-release: %s\n' "$message" >&2
exit 1
}
usage() {
sed -n '2,24p' "$0" | sed 's/^# \{0,1\}//'
return 0
}
cleanup() {
if [ -n "$TMP_DIR" ] && [ -d "$TMP_DIR" ]; then
rm -rf "$TMP_DIR"
fi
return 0
}
need() {
local tool="$1"
command -v "$tool" >/dev/null 2>&1 || die "needs $tool"
return 0
}
# Version: header of the main file at a commit.
version_at() {
local sha="$1"
git show "$sha:$MAIN_FILE" | sed -n 's/^[[:space:]*]*Version:[[:space:]]*\([^[:space:]]*\).*/\1/p' | head -n 1
return 0
}
sha256_of() {
local file="$1"
if command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$file" | cut -d' ' -f1
else
sha256sum "$file" | cut -d' ' -f1
fi
return 0
}
# Zip <folder>/<SLUG> as <zip>, with the slug folder at the top. Fixed times,
# modes (umask) and entry order make the same ref give the same zip anywhere.
make_zip() {
local folder="$1"
local zip_path="$2"
local partial="$zip_path.partial"
rm -f "$zip_path" "$partial"
(cd "$folder" && find "$SLUG" -exec touch -h -t 202001010000 {} + && find "$SLUG" | LC_ALL=C sort | zip -qX "$partial" -@)
mv "$partial" "$zip_path"
return 0
}
# Remove the GitHub updater header lines from the main file of a build.
strip_updater_headers() {
local main="$1"
local stripped="$main.tmp"
grep -Ev "^[[:space:]*]*($WPORG_STRIP_HEADERS):" "$main" >"$stripped"
mv "$stripped" "$main"
return 0
}
main() {
local ref="HEAD"
local out=""
local quiet=0
local arg
while [ $# -gt 0 ]; do
arg="$1"
case "$arg" in
--ref)
[ $# -ge 2 ] || die "--ref needs a value"
ref="$2"
shift
;;
--out)
[ $# -ge 2 ] || die "--out needs a value"
out="$2"
shift
;;
--quiet) quiet=1 ;;
-h | --help)
usage
return 0
;;
*) die "unknown argument: $arg" ;;
esac
shift
done
local tool
for tool in git rsync zip unzip; do
need "$tool"
done
local root sha version
root="$(git rev-parse --show-toplevel)" || die "run this inside a checkout of the plugin"
cd "$root"
sha="$(git rev-parse --verify --quiet "$ref^{commit}")" || die "not a commit: $ref"
plugin_identity "$sha" || die "cannot tell which plugin this is at $ref"
SLUG="$PLUGIN_SLUG"
MAIN_FILE="$PLUGIN_MAIN_FILE"
version="$(version_at "$sha")"
[ -n "$version" ] || die "no Version: header in $MAIN_FILE at $ref"
if [ "$ref" = "HEAD" ] && [ -n "$(git status --porcelain --untracked-files=no)" ] && [ "$quiet" -eq 0 ]; then
printf 'Note: uncommitted changes are not in the build (it is made from HEAD).\n'
fi
[ -n "$out" ] || out="$root/dist"
mkdir -p "$out"
out="$(cd "$out" && pwd)"
trap cleanup EXIT
TMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/$SLUG-build.XXXXXX")"
mkdir -p "$TMP_DIR/src" "$TMP_DIR/github/$SLUG" "$TMP_DIR/wporg/$SLUG"
git archive --format=tar "$sha" | tar -x -C "$TMP_DIR/src"
# .distignore lists itself and is export-ignored, so read it from Git.
git show "$sha:.distignore" >"$TMP_DIR/distignore" || die ".distignore missing at $ref"
rsync -a --exclude-from="$TMP_DIR/distignore" "$TMP_DIR/src/" "$TMP_DIR/github/$SLUG/"
[ -f "$TMP_DIR/github/$SLUG/$MAIN_FILE" ] || die "$MAIN_FILE is not in the build; check .distignore"
if git cat-file -e "$sha:$WPORG_IGNORE" 2>/dev/null; then
git show "$sha:$WPORG_IGNORE" >"$TMP_DIR/wporg-ignore"
else
: >"$TMP_DIR/wporg-ignore"
fi
rsync -a --exclude-from="$TMP_DIR/wporg-ignore" "$TMP_DIR/github/$SLUG/" "$TMP_DIR/wporg/$SLUG/"
strip_updater_headers "$TMP_DIR/wporg/$SLUG/$MAIN_FILE"
local github_zip="$out/$SLUG-$version.zip"
local wporg_zip="$out/wordpress-org-$SLUG-$version.zip"
make_zip "$TMP_DIR/github" "$github_zip"
make_zip "$TMP_DIR/wporg" "$wporg_zip"
{
printf '%s %s\n' "$(sha256_of "$github_zip")" "$(basename "$github_zip")"
printf '%s %s\n' "$(sha256_of "$wporg_zip")" "$(basename "$wporg_zip")"
} >"$out/SHA256SUMS"
if [ "$quiet" -eq 1 ]; then
printf '%s\n%s\n' "$github_zip" "$wporg_zip"
return 0
fi
printf '%s %s from %s (%s)\n' "$PLUGIN_NAME" "$version" "$ref" "${sha:0:12}"
printf ' GitHub release: %s (%s files)\n' "$github_zip" "$(unzip -Z1 "$github_zip" | grep -cv '/$')"
printf ' WordPress.org: %s (%s files)\n' "$wporg_zip" "$(unzip -Z1 "$wporg_zip" | grep -cv '/$')"
printf ' Checksums: %s\n' "$out/SHA256SUMS"
printf 'Check them with: scripts/preflight-release.sh --ref %s\n' "$ref"
return 0
}
main "$@"