#!/usr/bin/env bash # # Builds a release package for a managed project. # # The product name, the version file and the exclude list are variables at the # top instead of hardcoded paths. # # It ships with the manage client package. Copy it into `scripts/` of the project # it builds, adjust the CONFIGURATION block, and run it from the project root: # # ./scripts/create-release-zip.sh v1.3.0 # # It writes the version into the version file, packs every git-tracked file # minus the exclusions, and prints the SHA-256 and size. Upload the resulting # ZIP in the manage server under "Releases". set -euo pipefail # --- CONFIGURATION ---------------------------------------------------------- # Package name prefix. Must match MANAGE_PACKAGE_PREFIX on the manage server. PRODUCT="example-orderform" # File holding the installed version, relative to the project root. VERSION_FILE="includes/version.php" # Name of the constant inside that file. Empty means a plain text file that # contains nothing but the version. VERSION_CONSTANT="APP_VERSION" # Output directory for built packages, relative to the project root. BUILD_DIR="build/releases" # Paths excluded from the package. Anything holding credentials or runtime data # of the target installation MUST be listed here. EXCLUDES=( ".gitignore" "config.php" "manage-client/config.php" "data/" "build/" "scripts/" ".codex/" ) # --- END CONFIGURATION ------------------------------------------------------ usage() { cat <&2 exit 1 fi for tool in git zip sed awk; do if ! command -v "$tool" >/dev/null 2>&1; then echo "Error: required tool not found: $tool" >&2 exit 1 fi done if ! git rev-parse --show-toplevel >/dev/null 2>&1; then echo "Error: not inside a git repository. Run this from the project root." >&2 exit 1 fi REPO_ROOT="$(git rev-parse --show-toplevel)" cd "$REPO_ROOT" if [[ ! -f "$VERSION_FILE" ]]; then echo "Error: version file not found: $VERSION_FILE" >&2 exit 1 fi # Uncommitted changes would silently stay out of the package, because the file # list comes from git. Warn rather than refuse: building from a dirty tree is # sometimes deliberate. if [[ -n "$(git status --porcelain --untracked-files=no)" ]]; then echo "Warning: the working tree has uncommitted changes." >&2 echo " Only committed content is packaged." >&2 fi # --- write the version ------------------------------------------------------ write_version() { if [[ -n "$VERSION_CONSTANT" ]]; then # PHP file with a define(). Replace only the version literal. sed -i.bak -E \ "s/(define\\(\\s*[\"']${VERSION_CONSTANT}[\"']\\s*,\\s*[\"'])[^\"']*([\"'])/\\1${VERSION}\\2/" \ "$VERSION_FILE" rm -f "${VERSION_FILE}.bak" else printf '%s\n' "$VERSION" > "$VERSION_FILE" fi } read_version() { if [[ -n "$VERSION_CONSTANT" ]]; then grep -oE "define\\(\\s*[\"']${VERSION_CONSTANT}[\"']\\s*,\\s*[\"']v[0-9]+\\.[0-9]+\\.[0-9]+[\"']" \ "$VERSION_FILE" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1 else tr -d '[:space:]' < "$VERSION_FILE" fi } write_version WRITTEN="$(read_version)" if [[ "$WRITTEN" != "$VERSION" ]]; then echo "Error: could not write the version into $VERSION_FILE (found: '${WRITTEN}')." >&2 echo " Check VERSION_CONSTANT and the file's format." >&2 exit 1 fi echo "Version written to ${VERSION_FILE}: ${VERSION}" # --- collect the files ------------------------------------------------------ is_excluded() { local path="$1" local pattern for pattern in "${EXCLUDES[@]}"; do if [[ "$pattern" == */ ]]; then [[ "$path" == "${pattern}"* ]] && return 0 else [[ "$path" == "$pattern" ]] && return 0 fi done return 1 } FILE_LIST="$(mktemp)" trap 'rm -f "$FILE_LIST"' EXIT COUNT=0 while IFS= read -r path; do if is_excluded "$path"; then continue fi [[ -f "$path" ]] || continue printf '%s\n' "$path" >> "$FILE_LIST" COUNT=$((COUNT + 1)) done < <(git ls-files) if [[ "$COUNT" -eq 0 ]]; then echo "Error: no files to package." >&2 exit 1 fi # --- build ------------------------------------------------------------------ mkdir -p "$BUILD_DIR" ARCHIVE="${BUILD_DIR}/${PRODUCT}-${VERSION}.zip" rm -f "$ARCHIVE" zip -q -X "$ARCHIVE" -@ < "$FILE_LIST" if command -v sha256sum >/dev/null 2>&1; then SHA="$(sha256sum "$ARCHIVE" | awk '{print $1}')" else SHA="$(shasum -a 256 "$ARCHIVE" | awk '{print $1}')" fi SIZE="$(wc -c < "$ARCHIVE" | tr -d '[:space:]')" cat <