create-release-zip.sh 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. #!/usr/bin/env bash
  2. #
  3. # Builds a release package for a managed project.
  4. #
  5. # The product name, the version file and the exclude list are variables at the
  6. # top instead of hardcoded paths.
  7. #
  8. # It ships with the manage client package. Copy it into `scripts/` of the project
  9. # it builds, adjust the CONFIGURATION block, and run it from the project root:
  10. #
  11. # ./scripts/create-release-zip.sh v1.3.0
  12. #
  13. # It writes the version into the version file, packs every git-tracked file
  14. # minus the exclusions, and prints the SHA-256 and size. Upload the resulting
  15. # ZIP in the manage server under "Releases".
  16. set -euo pipefail
  17. # --- CONFIGURATION ----------------------------------------------------------
  18. # Package name prefix. Must match MANAGE_PACKAGE_PREFIX on the manage server.
  19. PRODUCT="psa-orderform"
  20. # File holding the installed version, relative to the project root.
  21. VERSION_FILE="includes/version.php"
  22. # Name of the constant inside that file. Empty means a plain text file that
  23. # contains nothing but the version.
  24. VERSION_CONSTANT="APP_VERSION"
  25. # Output directory for built packages, relative to the project root.
  26. BUILD_DIR="build/releases"
  27. # Paths excluded from the package. Anything holding credentials or runtime data
  28. # of the target installation MUST be listed here.
  29. EXCLUDES=(
  30. ".gitignore"
  31. "config.php"
  32. "manage-client/config.php"
  33. "data/"
  34. "build/"
  35. "scripts/"
  36. ".codex/"
  37. "client-package/"
  38. )
  39. # --- END CONFIGURATION ------------------------------------------------------
  40. usage() {
  41. cat <<USAGE
  42. Usage: $(basename "$0") vX.Y.Z
  43. Builds ${BUILD_DIR}/${PRODUCT}-vX.Y.Z.zip from the git-tracked files of the
  44. current repository and writes the version into ${VERSION_FILE}.
  45. USAGE
  46. }
  47. VERSION="${1:-}"
  48. if [[ -z "$VERSION" || "$VERSION" == "-h" || "$VERSION" == "--help" ]]; then
  49. usage
  50. exit 1
  51. fi
  52. if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  53. echo "Error: version must look like v1.3.0" >&2
  54. exit 1
  55. fi
  56. for tool in git zip sed awk; do
  57. if ! command -v "$tool" >/dev/null 2>&1; then
  58. echo "Error: required tool not found: $tool" >&2
  59. exit 1
  60. fi
  61. done
  62. if ! git rev-parse --show-toplevel >/dev/null 2>&1; then
  63. echo "Error: not inside a git repository. Run this from the project root." >&2
  64. exit 1
  65. fi
  66. REPO_ROOT="$(git rev-parse --show-toplevel)"
  67. cd "$REPO_ROOT"
  68. if [[ ! -f "$VERSION_FILE" ]]; then
  69. echo "Error: version file not found: $VERSION_FILE" >&2
  70. exit 1
  71. fi
  72. # Uncommitted changes would silently stay out of the package, because the file
  73. # list comes from git. Warn rather than refuse: building from a dirty tree is
  74. # sometimes deliberate.
  75. if [[ -n "$(git status --porcelain --untracked-files=no)" ]]; then
  76. echo "Warning: the working tree has uncommitted changes." >&2
  77. echo " Only committed content is packaged." >&2
  78. fi
  79. # --- write the version ------------------------------------------------------
  80. write_version() {
  81. if [[ -n "$VERSION_CONSTANT" ]]; then
  82. # PHP file with a define(). Replace only the version literal.
  83. sed -i.bak -E \
  84. "s/(define\\(\\s*[\"']${VERSION_CONSTANT}[\"']\\s*,\\s*[\"'])[^\"']*([\"'])/\\1${VERSION}\\2/" \
  85. "$VERSION_FILE"
  86. rm -f "${VERSION_FILE}.bak"
  87. else
  88. printf '%s\n' "$VERSION" > "$VERSION_FILE"
  89. fi
  90. }
  91. read_version() {
  92. if [[ -n "$VERSION_CONSTANT" ]]; then
  93. grep -oE "define\\(\\s*[\"']${VERSION_CONSTANT}[\"']\\s*,\\s*[\"']v[0-9]+\\.[0-9]+\\.[0-9]+[\"']" \
  94. "$VERSION_FILE" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1
  95. else
  96. tr -d '[:space:]' < "$VERSION_FILE"
  97. fi
  98. }
  99. write_version
  100. WRITTEN="$(read_version)"
  101. if [[ "$WRITTEN" != "$VERSION" ]]; then
  102. echo "Error: could not write the version into $VERSION_FILE (found: '${WRITTEN}')." >&2
  103. echo " Check VERSION_CONSTANT and the file's format." >&2
  104. exit 1
  105. fi
  106. echo "Version written to ${VERSION_FILE}: ${VERSION}"
  107. # --- collect the files ------------------------------------------------------
  108. is_excluded() {
  109. local path="$1"
  110. local pattern
  111. for pattern in "${EXCLUDES[@]}"; do
  112. if [[ "$pattern" == */ ]]; then
  113. [[ "$path" == "${pattern}"* ]] && return 0
  114. else
  115. [[ "$path" == "$pattern" ]] && return 0
  116. fi
  117. done
  118. return 1
  119. }
  120. FILE_LIST="$(mktemp)"
  121. trap 'rm -f "$FILE_LIST"' EXIT
  122. COUNT=0
  123. while IFS= read -r path; do
  124. if is_excluded "$path"; then
  125. continue
  126. fi
  127. [[ -f "$path" ]] || continue
  128. printf '%s\n' "$path" >> "$FILE_LIST"
  129. COUNT=$((COUNT + 1))
  130. done < <(git ls-files)
  131. if [[ "$COUNT" -eq 0 ]]; then
  132. echo "Error: no files to package." >&2
  133. exit 1
  134. fi
  135. # --- build ------------------------------------------------------------------
  136. mkdir -p "$BUILD_DIR"
  137. ARCHIVE="${BUILD_DIR}/${PRODUCT}-${VERSION}.zip"
  138. rm -f "$ARCHIVE"
  139. zip -q -X "$ARCHIVE" -@ < "$FILE_LIST"
  140. if command -v sha256sum >/dev/null 2>&1; then
  141. SHA="$(sha256sum "$ARCHIVE" | awk '{print $1}')"
  142. else
  143. SHA="$(shasum -a 256 "$ARCHIVE" | awk '{print $1}')"
  144. fi
  145. SIZE="$(wc -c < "$ARCHIVE" | tr -d '[:space:]')"
  146. cat <<SUMMARY
  147. Package: ${ARCHIVE}
  148. Files: ${COUNT}
  149. Size: ${SIZE} bytes
  150. SHA-256: ${SHA}
  151. Next: upload it in the manage server under "Releases" with version ${VERSION}.
  152. Checksum and size are recomputed there; the values above are for checking.
  153. SUMMARY