build-msi.sh 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  4. REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
  5. DIST_DIR="${DIST_DIR:-${REPO_ROOT}/dist}"
  6. ARCH="${ARCH:-amd64}"
  7. ZIP_NAME="OliveTin-windows-${ARCH}.zip"
  8. ZIP_PATH="${DIST_DIR}/${ZIP_NAME}"
  9. MSI_NAME="OliveTin-windows-${ARCH}.msi"
  10. MSI_PATH="${DIST_DIR}/${MSI_NAME}"
  11. if [[ ! -f "${ZIP_PATH}" ]]; then
  12. echo "Windows archive not found: ${ZIP_PATH}" >&2
  13. exit 1
  14. fi
  15. if ! command -v wixl >/dev/null || ! command -v wixl-heat >/dev/null; then
  16. echo "wixl and wixl-heat are required (install the wixl/msitools package)" >&2
  17. exit 1
  18. fi
  19. normalize_msi_version() {
  20. local raw="${1#v}"
  21. raw="${raw%%-*}"
  22. if [[ ! "${raw}" =~ ^[0-9]+(\.[0-9]+){0,3}$ ]]; then
  23. echo "Invalid MSI version (expected major[.minor[.patch[.build]]]): ${1}" >&2
  24. return 1
  25. fi
  26. local -a parts=()
  27. IFS='.' read -r -a parts <<<"${raw}"
  28. local major="${parts[0]:-0}"
  29. local minor="${parts[1]:-0}"
  30. local patch="${parts[2]:-0}"
  31. printf '%s.%s.%s' "${major}" "${minor}" "${patch}"
  32. }
  33. VERSION="${VERSION:-}"
  34. if [[ -z "${VERSION}" ]]; then
  35. VERSION="$(git -C "${REPO_ROOT}" describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || true)"
  36. fi
  37. if [[ -z "${VERSION}" ]]; then
  38. echo "Could not determine release version; set VERSION explicitly" >&2
  39. exit 1
  40. fi
  41. MSI_VERSION="$(normalize_msi_version "${VERSION}")" || exit 1
  42. STAGING="$(mktemp -d)"
  43. APP_STAGING="$(mktemp -d)"
  44. HEAT_WXS="$(mktemp)"
  45. trap 'rm -rf "${STAGING}" "${APP_STAGING}" "${HEAT_WXS}"' EXIT
  46. unzip -q "${ZIP_PATH}" -d "${STAGING}"
  47. SOURCE_ROOT="${STAGING}/OliveTin-windows-${ARCH}"
  48. if [[ ! -f "${SOURCE_ROOT}/OliveTin.exe" ]]; then
  49. echo "OliveTin.exe not found in ${SOURCE_ROOT}" >&2
  50. exit 1
  51. fi
  52. if [[ ! -f "${SOURCE_ROOT}/config.yaml" ]]; then
  53. echo "config.yaml not found in ${SOURCE_ROOT}" >&2
  54. exit 1
  55. fi
  56. mkdir -p "${APP_STAGING}/webui"
  57. cp "${SOURCE_ROOT}/OliveTin.exe" "${APP_STAGING}/"
  58. cp -a "${SOURCE_ROOT}/webui/." "${APP_STAGING}/webui/"
  59. (
  60. cd "${APP_STAGING}"
  61. find . -type f | sed 's|^\./||'
  62. ) | wixl-heat \
  63. -p "" \
  64. --component-group CG.AppFiles \
  65. --var var.SourceDir \
  66. --directory-ref INSTALLDIR \
  67. --win64 \
  68. > "${HEAT_WXS}"
  69. wixl \
  70. -v \
  71. -a x64 \
  72. -D "Version=${MSI_VERSION}" \
  73. -D "Win64=yes" \
  74. -D "SourceDir=${APP_STAGING}" \
  75. -D "ConfigSource=${SOURCE_ROOT}/config.yaml" \
  76. -o "${MSI_PATH}" \
  77. "${SCRIPT_DIR}/OliveTin.wxs" \
  78. "${HEAT_WXS}"
  79. echo "Built ${MSI_PATH}"