This is an automated email from the ASF dual-hosted git repository.

djwang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudberry.git


The following commit(s) were added to refs/heads/main by this push:
     new 98240f92dbf CI: add ASF-release support under macOS
98240f92dbf is described below

commit 98240f92dbf31427d0ae992bc16f3df2c8316226
Author: Dianjin Wang <[email protected]>
AuthorDate: Thu Feb 5 12:55:55 2026 +0800

    CI: add ASF-release support under macOS
    
    This commit is used to make an ASF-release (tarball) file for committers
    under macOS machine and enhance the developer experience for running the
    script.
    
    Main changes:
    * Enforce GNU tar and sha512sum usage on macOS for generating artfacts
    * Clean macOS extended attributes to prevent compilation errors on Linux
    * Add platform and tool validation with installation guidance
    * Support RC tag validation with base version matching in source
    * Add interactive confirmations and enhanced verification steps
    * Exclude release dir from .gitignore
---
 .gitignore                           |   1 -
 devops/release/cloudberry-release.sh | 269 ++++++++++++++++++++++++++++++-----
 2 files changed, 230 insertions(+), 40 deletions(-)

diff --git a/.gitignore b/.gitignore
index 44a59792420..5c21989c4ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,7 +45,6 @@ config.status.lineno
 autom4te.cache
 configure.lineno
 Debug
-Release
 pgsql.sln
 cscope.*
 build.sh
diff --git a/devops/release/cloudberry-release.sh 
b/devops/release/cloudberry-release.sh
index 5fd579b481e..641d435f26a 100755
--- a/devops/release/cloudberry-release.sh
+++ b/devops/release/cloudberry-release.sh
@@ -32,11 +32,13 @@
 #   - Verifies Git identity (user.name and user.email) prior to tagging
 #   - Creates a BUILD_NUMBER file (currently hardcoded as 1) in the release 
tarball
 #   - Recursively archives all submodules into the source tarball
-#   - Generates SHA-512 checksum (.sha512) for the source tarball
+#   - Generates SHA-512 checksum (.sha512) using sha512sum for cross-platform 
consistency
 #   - Generates GPG signature (.asc) for the source tarball, unless 
--skip-signing is used
 #   - Moves signed artifacts into a dedicated artifacts/ directory
 #   - Verifies integrity and authenticity of artifacts via SHA-512 checksum 
and GPG signature
 #   - Allows skipping of upstream remote URL validation (e.g., for forks) via 
--skip-remote-check
+#   - Excludes macOS extended attribute files (._*, .DS_Store, __MACOSX) for 
cross-platform compatibility
+#   - Validates availability of required tools (sha512sum, gtar, gpg) with 
platform-specific guidance
 #
 # Usage:
 #   ./cloudberry-release.sh --stage --tag 2.0.0-incubating-rc1 --gpg-user 
[email protected]
@@ -56,6 +58,8 @@
 #     or the path must be explicitly provided using --repo
 #   - Git user.name and user.email must be configured
 #   - Repository remote must be: [email protected]:apache/cloudberry.git
+#   - Required tools: sha512sum, tar (gtar on macOS), gpg, xmllint
+#   - On macOS: brew install coreutils gnu-tar gnupg
 #
 # Examples:
 #   ./cloudberry-release.sh -s -t 2.0.0-incubating-rc1 --gpg-user 
[email protected]
@@ -70,6 +74,101 @@
 
 set -euo pipefail
 
+# Global variables for detected platform and tools
+DETECTED_PLATFORM=""
+DETECTED_SHA_TOOL=""
+DETECTED_TAR_TOOL=""
+
+# Platform detection and tool check
+check_platform_and_tools() {
+  local has_errors=false
+  
+  # Detect platform
+  case "$(uname -s)" in
+    Linux*)   DETECTED_PLATFORM="Linux" ;;
+    Darwin*)  DETECTED_PLATFORM="macOS" ;;
+    CYGWIN*|MINGW*|MSYS*) DETECTED_PLATFORM="Windows" ;;
+    *)        DETECTED_PLATFORM="Unknown" ;;
+  esac
+  
+  echo "Platform detected: $DETECTED_PLATFORM"
+  echo
+  
+  # Check sha512sum
+  if command -v sha512sum >/dev/null 2>&1; then
+    DETECTED_SHA_TOOL="sha512sum"
+    echo "[OK] SHA-512 tool: $DETECTED_SHA_TOOL"
+  else
+    echo "[ERROR] SHA-512 tool: sha512sum not found"
+    has_errors=true
+  fi
+  
+  # Check tar tool
+  if [[ "$DETECTED_PLATFORM" == "macOS" ]]; then
+    if command -v gtar >/dev/null 2>&1; then
+      DETECTED_TAR_TOOL="gtar"
+      echo "[OK] Tar tool: $DETECTED_TAR_TOOL (GNU tar)"
+    else
+      echo "[ERROR] Tar tool: gtar not found (GNU tar required on macOS)"
+      has_errors=true
+    fi
+  else
+    if command -v tar >/dev/null 2>&1; then
+      DETECTED_TAR_TOOL="tar"
+      echo "[OK] Tar tool: $DETECTED_TAR_TOOL"
+    else
+      echo "[ERROR] Tar tool: tar not found"
+      has_errors=true
+    fi
+  fi
+  
+  # Check GPG tool (only when signing is required)
+  if [[ "$SKIP_SIGNING" == true ]]; then
+    echo "- GPG tool: skipped (--skip-signing enabled)"
+  else
+    if command -v gpg >/dev/null 2>&1; then
+      local gpg_version=$(gpg --version | head -n1 | sed 's/gpg (GnuPG) //')
+      echo "[OK] GPG tool: gpg $gpg_version"
+    else
+      echo "[ERROR] GPG tool: gpg not found"
+      has_errors=true
+    fi
+  fi
+  
+  # Check xmllint tool
+  if command -v xmllint >/dev/null 2>&1; then
+    echo "[OK] XML tool: xmllint"
+  else
+    echo "[ERROR] XML tool: xmllint not found"
+    has_errors=true
+  fi
+  
+  # Show installation guidance if there are errors
+  if [[ "$has_errors" == true ]]; then
+    echo
+    echo "Missing required tools. Installation guidance:"
+    case "$DETECTED_PLATFORM" in
+      Linux)
+        echo "  Please install required packages: coreutils tar gnupg 
libxml2-utils"
+        ;;
+      macOS)
+        echo "  brew install coreutils gnu-tar gnupg"
+        ;;
+      Windows)
+        echo "  Please use Git Bash or install GNU tools"
+        ;;
+      *)
+        echo "  Please install GNU coreutils, tar, GnuPG, and libxml2"
+        ;;
+    esac
+    echo
+    echo "These tools ensure consistent cross-platform behavior and secure 
signing."
+    return 1
+  fi
+  
+  return 0
+}
+
 confirm() {
   read -r -p "$1 [y/N] " response
   case "$response" in
@@ -78,6 +177,25 @@ confirm() {
   esac
 }
 
+# Interactive step confirmation
+confirm_next_step() {
+  echo
+  read -r -p "Press Enter or type y/yes to continue, or 'n' to exit: " response
+  case "$response" in
+    ""|[yY][eE][sS]|[yY]) 
+      return 0 
+      ;;
+    [nN]|[nN][oO]) 
+      echo "Process stopped by user."
+      exit 0 
+      ;;
+    *) 
+      echo "Invalid input. Please press Enter or type y/yes to continue, or 
'n' to exit."
+      confirm_next_step
+      ;;
+  esac
+}
+
 section() {
   echo
   echo "================================================================="
@@ -199,22 +317,16 @@ else
 fi
 
 if [[ -n "$REPO_ARG" ]]; then
-  if [[ -n "$REPO_ARG" ]]; then
-    if [[ ! -d "$REPO_ARG" || ! -f "$REPO_ARG/configure.ac" ]]; then
-      echo "ERROR: '$REPO_ARG' does not appear to be a valid Cloudberry source 
directory."
-      echo "Expected to find a 'configure.ac' file but it is missing."
-      echo
-      echo "Hint: Make sure you passed the correct --repo path to a valid Git 
clone."
-      exit 1
-    fi
-    cd "$REPO_ARG"
-  elif [[ ! -f configure.ac ]]; then
-    echo "ERROR: No Cloudberry source directory specified and no 
'configure.ac' found in the current directory."
+  # Validate the specified repository path
+  if [[ ! -d "$REPO_ARG" || ! -f "$REPO_ARG/configure.ac" ]]; then
+    echo "ERROR: '$REPO_ARG' does not appear to be a valid Cloudberry source 
directory."
+    echo "Expected to find a 'configure.ac' file but it is missing."
     echo
-    echo "Hint: Either run this script from the root of a Cloudberry Git 
clone,"
-    echo "or use the --repo <path> option to specify the source directory."
+    echo "Hint: Make sure you passed the correct --repo path to a valid Git 
clone."
     exit 1
   fi
+  
+  # Change to the specified repository directory
   cd "$REPO_ARG"
 
   if [[ ! -d ".git" ]]; then
@@ -258,6 +370,15 @@ if $STAGE && [[ -z "$TAG" ]]; then
   show_help
 fi
 
+# Check platform and required tools early
+if $STAGE; then
+  section "Platform and Tool Detection"
+  if ! check_platform_and_tools; then
+    exit 1
+  fi
+  confirm_next_step
+fi
+
 section "Validating Version Consistency"
 
 # Extract version from configure.ac
@@ -272,6 +393,16 @@ if ! [[ "$TAG" =~ $SEMVER_REGEX ]]; then
   exit 1
 fi
 
+# Extract base version from tag (remove -rc suffix if present)
+BASE_VERSION="$TAG"
+if [[ "$TAG" =~ ^(.+)-rc[0-9]+$ ]]; then
+  BASE_VERSION="${BASH_REMATCH[1]}"
+fi
+
+echo "Version validation strategy:"
+echo "  Tag: $TAG"
+echo "  Base version (for source files): $BASE_VERSION"
+
 # Check gpversion.py consistency
 PY_LINE=$(grep "^MAIN_VERSION" gpMgmt/bin/gppylib/gpversion.py | sed -E 
's/#.*//' | tr -d '[:space:]')
 
@@ -281,28 +412,24 @@ if [[ "$PY_LINE" != "MAIN_VERSION=$EXPECTED" ]]; then
   exit 1
 fi
 
-# For final releases (non-RC), ensure configure.ac version matches tag exactly
-if [[ "$TAG" != *-rc* && "$CONFIGURE_AC_VERSION" != "$TAG" ]]; then
-  echo "ERROR: configure.ac version ($CONFIGURE_AC_VERSION) does not match 
final release tag ($TAG)"
-  echo "Please update configure.ac to match the tag before proceeding."
+# Ensure configure.ac version matches base version (without -rc suffix)
+if [[ "$CONFIGURE_AC_VERSION" != "$BASE_VERSION" ]]; then
+  echo "ERROR: configure.ac version ($CONFIGURE_AC_VERSION) does not match 
base version ($BASE_VERSION)"
+  echo "For RC tags like '$TAG', configure.ac should contain the base version 
'$BASE_VERSION'"
   exit 1
 fi
 
-# Ensure the generated 'configure' script is up to date
+# Ensure the generated 'configure' script matches base version
 CONFIGURE_VERSION_LINE=$(grep "^PACKAGE_VERSION=" configure || true)
 CONFIGURE_VERSION=$(echo "$CONFIGURE_VERSION_LINE" | sed -E 
"s/^PACKAGE_VERSION='([^']+)'.*/\1/")
 
-if [[ "$CONFIGURE_VERSION" != "$TAG" ]]; then
-  echo "ERROR: Version in generated 'configure' script ($CONFIGURE_VERSION) 
does not match release tag ($TAG)."
-  echo "This likely means autoconf was not run after updating configure.ac."
+if [[ "$CONFIGURE_VERSION" != "$BASE_VERSION" ]]; then
+  echo "ERROR: Version in generated 'configure' script ($CONFIGURE_VERSION) 
does not match base version ($BASE_VERSION)."
+  echo "This likely means autoconf was not run after updating configure.ac to 
the base version."
   exit 1
 fi
 
-# Ensure xmllint is available
-if ! command -v xmllint >/dev/null 2>&1; then
-  echo "ERROR: xmllint is required but not installed."
-  exit 1
-fi
+
 
 # Extract version from pom.xml using xmllint with namespace stripping
 POM_VERSION=$(xmllint --xpath 
'//*[local-name()="project"]/*[local-name()="version"]/text()' pom.xml 
2>/dev/null || true)
@@ -312,9 +439,9 @@ if [[ -z "$POM_VERSION" ]]; then
   exit 1
 fi
 
-if [[ "$POM_VERSION" != "$TAG" ]]; then
-  echo "ERROR: Version in pom.xml ($POM_VERSION) does not match release tag 
($TAG)."
-  echo "Please update pom.xml before tagging."
+if [[ "$POM_VERSION" != "$BASE_VERSION" ]]; then
+  echo "ERROR: Version in pom.xml ($POM_VERSION) does not match base version 
($BASE_VERSION)."
+  echo "For RC tags like '$TAG', pom.xml should contain the base version 
'$BASE_VERSION'"
   exit 1
 fi
 
@@ -324,12 +451,14 @@ if ! git diff-index --quiet HEAD --; then
   exit 1
 fi
 
-echo "MAIN_VERSION verified"
+echo "Version consistency verified"
 printf "    %-14s: %s\n" "Release Tag"   "$TAG"
+printf "    %-14s: %s\n" "Base Version"  "$BASE_VERSION"
 printf "    %-14s: %s\n" "configure.ac"  "$CONFIGURE_AC_VERSION"
 printf "    %-14s: %s\n" "configure"     "$CONFIGURE_VERSION"
 printf "    %-14s: %s\n" "pom.xml"       "$POM_VERSION"
 printf "    %-14s: %s\n" "gpversion.py"  "${EXPECTED//[\[\]]}"
+confirm_next_step
 
 section "Checking the state of the Tag"
 
@@ -357,6 +486,8 @@ else
   echo "INFO: Tag '$TAG' does not yet exist. It will be created during 
staging."
 fi
 
+confirm_next_step
+
 # Check and display submodule initialization status
 if [ -s .gitmodules ]; then
   section "Checking Git Submodules"
@@ -417,8 +548,9 @@ section "Staging release: $TAG"
     echo "INFO: Reusing existing tag '$TAG'; skipping tag creation."
   fi
 
-  echo "Creating BUILD_NUMBER file with value of 1"
-  echo "1" > BUILD_NUMBER
+  echo "Creating BUILD_NUMBER file with commit hash"
+  build_num=$(git rev-parse --short HEAD)
+  echo "$build_num" > BUILD_NUMBER
 
   echo -e "\nTag Summary"
   TAG_OBJECT=$(git rev-parse "$TAG")
@@ -426,6 +558,7 @@ section "Staging release: $TAG"
   echo "$TAG (tag object): $TAG_OBJECT"
   echo "    Points to commit: $TAG_COMMIT"
   git log -1 --format="%C(auto)%h %d" "$TAG"
+  confirm_next_step
 
   section "Creating Source Tarball"
 
@@ -433,6 +566,10 @@ section "Staging release: $TAG"
   TMP_DIR=$(mktemp -d)
   trap 'rm -rf "$TMP_DIR"' EXIT
 
+  # Set environment variables to prevent macOS extended attributes
+  export COPYFILE_DISABLE=1
+  export COPY_EXTENDED_ATTRIBUTES_DISABLE=1
+
   git archive --format=tar --prefix="apache-cloudberry-${TAG}/" "$TAG" | tar 
-x -C "$TMP_DIR"
   cp BUILD_NUMBER "$TMP_DIR/apache-cloudberry-${TAG}/"
 
@@ -447,16 +584,66 @@ section "Staging release: $TAG"
     "
   fi
 
-  tar -czf "$TAR_NAME" -C "$TMP_DIR" "apache-cloudberry-${TAG}"
+  # Clean up macOS extended attributes if on macOS
+  if [[ "$DETECTED_PLATFORM" == "macOS" ]]; then
+    echo "Cleaning macOS extended attributes from extracted files..."
+    # Remove all extended attributes recursively
+    if command -v xattr >/dev/null 2>&1; then
+      find "$TMP_DIR/apache-cloudberry-${TAG}" -type f -exec xattr -c {} \; 
2>/dev/null || true
+      echo "[OK] Extended attributes cleaned using xattr"
+    fi
+    
+    # Remove any ._* files that might have been created
+    find "$TMP_DIR/apache-cloudberry-${TAG}" -name '._*' -delete 2>/dev/null 
|| true
+    find "$TMP_DIR/apache-cloudberry-${TAG}" -name '.DS_Store' -delete 
2>/dev/null || true
+    find "$TMP_DIR/apache-cloudberry-${TAG}" -name '__MACOSX' -type d -exec rm 
-rf {} \; 2>/dev/null || true
+    echo "[OK] macOS-specific files removed"
+  fi
+
+  # Create tarball using the detected tar tool
+  if [[ "$DETECTED_PLATFORM" == "macOS" ]]; then
+    echo "Using GNU tar for cross-platform compatibility..."
+    $DETECTED_TAR_TOOL --exclude='._*' --exclude='.DS_Store' 
--exclude='__MACOSX' -czf "$TAR_NAME" -C "$TMP_DIR" "apache-cloudberry-${TAG}"
+    echo "INFO: macOS detected - applied extended attribute cleanup and GNU 
tar"
+  else
+    # On other platforms, use standard tar
+    $DETECTED_TAR_TOOL -czf "$TAR_NAME" -C "$TMP_DIR" 
"apache-cloudberry-${TAG}"
+  fi
+  
   rm -rf "$TMP_DIR"
   echo -e "Archive saved to: $TAR_NAME"
+  
+  # Verify that no macOS extended attribute files are included
+  if [[ "$DETECTED_PLATFORM" == "macOS" ]]; then
+    echo "Verifying tarball does not contain macOS-specific files..."
+    MACOS_FILES=$($DETECTED_TAR_TOOL -tzf "$TAR_NAME" | grep -E 
'\._|\.DS_Store|__MACOSX' || true)
+    if [[ -n "$MACOS_FILES" ]]; then
+      echo "WARNING: Found macOS-specific files in tarball:"
+      echo "$MACOS_FILES"
+      echo "This may cause compilation issues on Linux systems."
+    else
+      echo "[OK] Tarball verified clean of macOS-specific files"
+    fi
+    
+    # Additional check for extended attributes in tar headers
+    echo "Checking for extended attribute headers in tarball..."
+    if $DETECTED_TAR_TOOL -tvf "$TAR_NAME" 2>&1 | grep -q "LIBARCHIVE.xattr" 
2>/dev/null; then
+      echo "WARNING: Tarball may still contain extended attribute headers"
+      echo "This could cause 'Ignoring unknown extended header keyword' 
warnings on Linux"
+    else
+      echo "[OK] No extended attribute headers detected in tarball (GNU tar 
used)"
+    fi
+  fi
+  
+  confirm_next_step
 
   # Generate SHA-512 checksum
   section "Generating SHA-512 Checksum"
 
   echo -e "\nGenerating SHA-512 checksum"
-  shasum -a 512 "$TAR_NAME" > "${TAR_NAME}.sha512"
+  sha512sum "$TAR_NAME" > "${TAR_NAME}.sha512"
   echo "Checksum saved to: ${TAR_NAME}.sha512"
+  confirm_next_step
 
   section "Signing with GPG key: $GPG_USER"
   # Conditionally generate GPG signature
@@ -469,8 +656,10 @@ section "Staging release: $TAG"
   fi
 
   # Move artifacts to top-level artifacts directory
-
-  ARTIFACTS_DIR="$(cd "$(dirname "$REPO_ARG")" && cd .. && pwd)/artifacts"
+  # At this point, we're always in the cloudberry repository directory
+  # (either we started there, or we cd'd there via --repo)
+  ARTIFACTS_DIR="$(cd .. && pwd)/artifacts"
+  
   mkdir -p "$ARTIFACTS_DIR"
 
   section "Moving Artifacts to $ARTIFACTS_DIR"
@@ -479,10 +668,11 @@ section "Staging release: $TAG"
   mv -vf "$TAR_NAME" "$ARTIFACTS_DIR/"
   mv -vf "${TAR_NAME}.sha512" "$ARTIFACTS_DIR/"
   [[ -f "${TAR_NAME}.asc" ]] && mv -vf "${TAR_NAME}.asc" "$ARTIFACTS_DIR/"
+  confirm_next_step
 
   section "Verifying sha512 ($ARTIFACTS_DIR/${TAR_NAME}.sha512) Release 
Artifact"
-  cd "$ARTIFACTS_DIR"
-  sha512sum -c "$ARTIFACTS_DIR/${TAR_NAME}.sha512"
+  (cd "$ARTIFACTS_DIR" && sha512sum -c "${TAR_NAME}.sha512")
+  confirm_next_step
 
   section "Verifying GPG Signature ($ARTIFACTS_DIR/${TAR_NAME}.asc) Release 
Artifact"
 
@@ -491,6 +681,7 @@ section "Staging release: $TAG"
   else
     echo "INFO: Signature verification skipped (--skip-signing). Signature is 
only available when generated via this script."
   fi
+  confirm_next_step
 
   section "Release candidate for $TAG staged successfully"
 fi


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to