| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- #!/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 and is configured here for the
- # photography portfolio. Run it from the project root:
- #
- # ./scripts/create-release-zip.sh v1.3.0
- #
- # Then upload build/releases/foto-portfolio-v1.3.0.zip in the manage server
- # under "Releases". See docs/SETUP.md.
- #
- # 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="foto-portfolio"
- # File holding the installed version, relative to the project root.
- VERSION_FILE="app/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.
- #
- # The list is short because the file list comes from `git ls-files`: the target
- # installation's config, credentials and content are gitignored and cannot end
- # up in the package in the first place. The entries below name the files that
- # ARE tracked but must not travel — plus the three config files as belt to that
- # braces, in case one is ever force-added.
- #
- # NOT excluded, on purpose:
- # manage-client/ this is how the update client updates itself
- # migrations/ release migrations must travel with the release
- # config/*.sample.php, docs/
- # a new install needs them; both blocked by .htaccess
- # data/.htaccess, media/.htaccess
- # the only tracked files under those two directories, and
- # the deny-all fallback a fresh install must not be
- # missing. An update skips them anyway — data/ and media/
- # are in MANAGE_UPDATE_PROTECTED_PATHS.
- EXCLUDES=(
- "config/config.php"
- "config/credentials.php"
- "manage-client/config.php"
- "data/galleries/"
- "build/"
- "scripts/"
- ".gitignore"
- )
- # --- END CONFIGURATION ------------------------------------------------------
- usage() {
- cat <<USAGE
- Usage: $(basename "$0") vX.Y.Z
- Builds ${BUILD_DIR}/${PRODUCT}-vX.Y.Z.zip from the git-tracked files of the
- current repository and writes the version into ${VERSION_FILE}.
- USAGE
- }
- VERSION="${1:-}"
- if [[ -z "$VERSION" || "$VERSION" == "-h" || "$VERSION" == "--help" ]]; then
- usage
- exit 1
- fi
- if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
- echo "Error: version must look like v1.3.0" >&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 <<SUMMARY
- Package: ${ARCHIVE}
- Files: ${COUNT}
- Size: ${SIZE} bytes
- SHA-256: ${SHA}
- Next: upload it in the manage server under "Releases" with version ${VERSION}.
- Checksum and size are recomputed there; the values above are for checking.
- SUMMARY
|