create-release-zip.sh 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. )
  38. # --- END CONFIGURATION ------------------------------------------------------
  39. usage() {
  40. cat <<USAGE
  41. Usage: $(basename "$0") vX.Y.Z
  42. Builds ${BUILD_DIR}/${PRODUCT}-vX.Y.Z.zip from the git-tracked files of the
  43. current repository and writes the version into ${VERSION_FILE}.
  44. USAGE
  45. }
  46. VERSION="${1:-}"
  47. if [[ -z "$VERSION" || "$VERSION" == "-h" || "$VERSION" == "--help" ]]; then
  48. usage
  49. exit 1
  50. fi
  51. if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  52. echo "Error: version must look like v1.3.0" >&2
  53. exit 1
  54. fi
  55. for tool in git zip sed awk; do
  56. if ! command -v "$tool" >/dev/null 2>&1; then
  57. echo "Error: required tool not found: $tool" >&2
  58. exit 1
  59. fi
  60. done
  61. if ! git rev-parse --show-toplevel >/dev/null 2>&1; then
  62. echo "Error: not inside a git repository. Run this from the project root." >&2
  63. exit 1
  64. fi
  65. REPO_ROOT="$(git rev-parse --show-toplevel)"
  66. cd "$REPO_ROOT"
  67. if [[ ! -f "$VERSION_FILE" ]]; then
  68. echo "Error: version file not found: $VERSION_FILE" >&2
  69. exit 1
  70. fi
  71. # Uncommitted changes would silently stay out of the package, because the file
  72. # list comes from git. Warn rather than refuse: building from a dirty tree is
  73. # sometimes deliberate.
  74. if [[ -n "$(git status --porcelain --untracked-files=no)" ]]; then
  75. echo "Warning: the working tree has uncommitted changes." >&2
  76. echo " Only committed content is packaged." >&2
  77. fi
  78. # --- write the version ------------------------------------------------------
  79. write_version() {
  80. if [[ -n "$VERSION_CONSTANT" ]]; then
  81. # PHP file with a define(). Replace only the version literal.
  82. sed -i.bak -E \
  83. "s/(define\\(\\s*[\"']${VERSION_CONSTANT}[\"']\\s*,\\s*[\"'])[^\"']*([\"'])/\\1${VERSION}\\2/" \
  84. "$VERSION_FILE"
  85. rm -f "${VERSION_FILE}.bak"
  86. else
  87. printf '%s\n' "$VERSION" > "$VERSION_FILE"
  88. fi
  89. }
  90. read_version() {
  91. if [[ -n "$VERSION_CONSTANT" ]]; then
  92. grep -oE "define\\(\\s*[\"']${VERSION_CONSTANT}[\"']\\s*,\\s*[\"']v[0-9]+\\.[0-9]+\\.[0-9]+[\"']" \
  93. "$VERSION_FILE" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1
  94. else
  95. tr -d '[:space:]' < "$VERSION_FILE"
  96. fi
  97. }
  98. write_version
  99. WRITTEN="$(read_version)"
  100. if [[ "$WRITTEN" != "$VERSION" ]]; then
  101. echo "Error: could not write the version into $VERSION_FILE (found: '${WRITTEN}')." >&2
  102. echo " Check VERSION_CONSTANT and the file's format." >&2
  103. exit 1
  104. fi
  105. echo "Version written to ${VERSION_FILE}: ${VERSION}"
  106. # --- collect the files ------------------------------------------------------
  107. is_excluded() {
  108. local path="$1"
  109. local pattern
  110. for pattern in "${EXCLUDES[@]}"; do
  111. if [[ "$pattern" == */ ]]; then
  112. [[ "$path" == "${pattern}"* ]] && return 0
  113. else
  114. [[ "$path" == "$pattern" ]] && return 0
  115. fi
  116. done
  117. return 1
  118. }
  119. FILE_LIST="$(mktemp)"
  120. trap 'rm -f "$FILE_LIST"' EXIT
  121. COUNT=0
  122. while IFS= read -r path; do
  123. if is_excluded "$path"; then
  124. continue
  125. fi
  126. [[ -f "$path" ]] || continue
  127. printf '%s\n' "$path" >> "$FILE_LIST"
  128. COUNT=$((COUNT + 1))
  129. done < <(git ls-files)
  130. if [[ "$COUNT" -eq 0 ]]; then
  131. echo "Error: no files to package." >&2
  132. exit 1
  133. fi
  134. # --- build ------------------------------------------------------------------
  135. mkdir -p "$BUILD_DIR"
  136. ARCHIVE="${BUILD_DIR}/${PRODUCT}-${VERSION}.zip"
  137. rm -f "$ARCHIVE"
  138. zip -q -X "$ARCHIVE" -@ < "$FILE_LIST"
  139. if command -v sha256sum >/dev/null 2>&1; then
  140. SHA="$(sha256sum "$ARCHIVE" | awk '{print $1}')"
  141. else
  142. SHA="$(shasum -a 256 "$ARCHIVE" | awk '{print $1}')"
  143. fi
  144. SIZE="$(wc -c < "$ARCHIVE" | tr -d '[:space:]')"
  145. cat <<SUMMARY
  146. Package: ${ARCHIVE}
  147. Files: ${COUNT}
  148. Size: ${SIZE} bytes
  149. SHA-256: ${SHA}
  150. Next: upload it in the manage server under "Releases" with version ${VERSION}.
  151. Checksum and size are recomputed there; the values above are for checking.
  152. SUMMARY