Skip to content

Commit 1daae25

Browse files
authored
Packaging: Enable RPM package relocation via --prefix
Rework the RPM packaging so Cloudberry can be installed to a custom location and so multiple major versions can coexist, aligning the behavior with Greenplum's packaging model. Relocation (rpm --prefix): - Split the hardcoded install path into base_dir + name components and set Prefix to the base directory (/usr/local) so RPM's relocate engine can substitute it correctly. - Move the convenience symlink out of %files and create it in %post using RPM_INSTALL_PREFIX, so it follows the actual --prefix (with a fallback to the default base_dir). %postun removes the symlink only when it still points to the version being erased. - Disable build-id links so they are not emitted outside the relocatable prefix. Major-version coexistence: - Embed the major version in the package Name (apache-cloudberry-db-incubating-<major>), derived from %{version}, so different major versions register as distinct packages and can be installed side by side (dnf) or under separate prefixes (rpm -i). - Obsolete the previous unversioned package name on upgrade; this does not match the versioned names, so majors still coexist. - In %post, move the generic symlink only when the existing target is the same major version, leaving other majors untouched. The version is parsed from the target directory name rather than by sourcing environment files or executing installed binaries. - Keep the published file name in the historical format (without the "-<major>" segment) by renaming artifacts in build-rpm.sh; the file name does not affect the Name/Version stored in the RPM header. Packaging correctness and hardening: - Stop changing installed files to gpadmin ownership in %post; the package tree stays root-owned. - Do not expose bundled private shared libraries (libpq.so.5, etc.) as Provides, and do not require them from the system; they ship in the package and are resolved via RPATH. - Validate that version/release macros are supplied, mark cloudberry-env.sh as %config(noreplace), copy the tree with cp -a to preserve hidden files and attributes, add coreutils scriptlet dependencies, add python3 to the el9 runtime requires, and update the license tag to the SPDX identifier Apache-2.0. CI workflows: - Query the installed package by glob (rpm -qa 'apache-...*') instead of the fixed name, since the registered Name now carries the major. - Keep locating the published artifact by its historical file name, which build-rpm.sh restores after the build. Assisted-by: Claude Code Assisted-by: DeepSeek
1 parent 83022b5 commit 1daae25

6 files changed

Lines changed: 169 additions & 37 deletions

File tree

.github/workflows/build-cloudberry-rocky10.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,9 @@ jobs:
661661
"${SRC_DIR}"/devops/build/packaging/rpm/build-rpm.sh --version "${CBDB_VERSION}" --release "${BUILD_NUMBER}" "${DEBUG_RPMBUILD_OPT}"
662662
663663
# Get OS version and move RPM
664+
# build-rpm.sh normalizes the published file name to the historical
665+
# format (without the "-<major>" segment); the package Name metadata
666+
# still embeds the major version.
664667
os_version=$(grep -oP '(?<=^VERSION_ID=")[0-9]+' /etc/os-release)
665668
RPM_FILE="${HOME}"/rpmbuild/RPMS/x86_64/apache-cloudberry-db-incubating-"${CBDB_VERSION}"-"${BUILD_NUMBER}""${DEBUG_IDENTIFIER}".el"${os_version}".x86_64.rpm
666669
cp "${RPM_FILE}" "${SRC_DIR}"
@@ -920,9 +923,9 @@ jobs:
920923
fi
921924
922925
echo "Installation completed successfully"
923-
rpm -qi apache-cloudberry-db-incubating
926+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
924927
echo "Installed files:"
925-
rpm -ql apache-cloudberry-db-incubating
928+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -ql
926929
} 2>&1 | tee -a install-logs/details/rpm-installation.log
927930
928931
- name: Upload install logs
@@ -942,7 +945,7 @@ jobs:
942945
echo "# Installed Package Summary"
943946
echo "\`\`\`"
944947
945-
rpm -qi apache-cloudberry-db-incubating
948+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
946949
echo "\`\`\`"
947950
} >> "$GITHUB_STEP_SUMMARY" || true
948951
@@ -1330,7 +1333,7 @@ jobs:
13301333
fi
13311334
13321335
echo "Installation completed successfully"
1333-
rpm -qi apache-cloudberry-db-incubating
1336+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
13341337
} 2>&1 | tee -a build-logs/details/rpm-installation.log
13351338
13361339
# Clean up downloaded RPM artifacts to free disk space
@@ -1423,6 +1426,10 @@ jobs:
14231426
run: |
14241427
set -o pipefail
14251428
1429+
# Grant gpadmin write access to the install directory
1430+
# -H follows the command-line symlink to the real directory.
1431+
chown -RH gpadmin:gpadmin "${BUILD_DESTINATION}/"
1432+
14261433
# Initialize test status
14271434
overall_status=0
14281435

.github/workflows/build-cloudberry-rocky8.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,9 @@ jobs:
660660
"${SRC_DIR}"/devops/build/packaging/rpm/build-rpm.sh --version "${CBDB_VERSION}" --release "${BUILD_NUMBER}" "${DEBUG_RPMBUILD_OPT}"
661661
662662
# Get OS version and move RPM
663+
# build-rpm.sh normalizes the published file name to the historical
664+
# format (without the "-<major>" segment); the package Name metadata
665+
# still embeds the major version.
663666
os_version=$(grep -oP '(?<=^VERSION_ID=")[0-9]+' /etc/os-release)
664667
RPM_FILE="${HOME}"/rpmbuild/RPMS/x86_64/apache-cloudberry-db-incubating-"${CBDB_VERSION}"-"${BUILD_NUMBER}""${DEBUG_IDENTIFIER}".el"${os_version}".x86_64.rpm
665668
cp "${RPM_FILE}" "${SRC_DIR}"
@@ -919,9 +922,9 @@ jobs:
919922
fi
920923
921924
echo "Installation completed successfully"
922-
rpm -qi apache-cloudberry-db-incubating
925+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
923926
echo "Installed files:"
924-
rpm -ql apache-cloudberry-db-incubating
927+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -ql
925928
} 2>&1 | tee -a install-logs/details/rpm-installation.log
926929
927930
- name: Upload install logs
@@ -941,7 +944,7 @@ jobs:
941944
echo "# Installed Package Summary"
942945
echo "\`\`\`"
943946
944-
rpm -qi apache-cloudberry-db-incubating
947+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
945948
echo "\`\`\`"
946949
} >> "$GITHUB_STEP_SUMMARY" || true
947950
@@ -1329,7 +1332,7 @@ jobs:
13291332
fi
13301333
13311334
echo "Installation completed successfully"
1332-
rpm -qi apache-cloudberry-db-incubating
1335+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
13331336
} 2>&1 | tee -a build-logs/details/rpm-installation.log
13341337
13351338
# Clean up downloaded RPM artifacts to free disk space
@@ -1414,6 +1417,10 @@ jobs:
14141417
run: |
14151418
set -o pipefail
14161419
1420+
# Grant gpadmin write access to the install directory
1421+
# -H follows the command-line symlink to the real directory.
1422+
chown -RH gpadmin:gpadmin "${BUILD_DESTINATION}/"
1423+
14171424
# Initialize test status
14181425
overall_status=0
14191426

.github/workflows/build-cloudberry.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,9 @@ jobs:
659659
"${SRC_DIR}"/devops/build/packaging/rpm/build-rpm.sh --version "${CBDB_VERSION}" --release "${BUILD_NUMBER}" "${DEBUG_RPMBUILD_OPT}"
660660
661661
# Get OS version and move RPM
662+
# build-rpm.sh normalizes the published file name to the historical
663+
# format (without the "-<major>" segment); the package Name metadata
664+
# still embeds the major version.
662665
os_version=$(grep -oP '(?<=^VERSION_ID=")[0-9]+' /etc/os-release)
663666
RPM_FILE="${HOME}"/rpmbuild/RPMS/x86_64/apache-cloudberry-db-incubating-"${CBDB_VERSION}"-"${BUILD_NUMBER}""${DEBUG_IDENTIFIER}".el"${os_version}".x86_64.rpm
664667
cp "${RPM_FILE}" "${SRC_DIR}"
@@ -918,9 +921,9 @@ jobs:
918921
fi
919922
920923
echo "Installation completed successfully"
921-
rpm -qi apache-cloudberry-db-incubating
924+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
922925
echo "Installed files:"
923-
rpm -ql apache-cloudberry-db-incubating
926+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -ql
924927
} 2>&1 | tee -a install-logs/details/rpm-installation.log
925928
926929
- name: Upload install logs
@@ -940,7 +943,7 @@ jobs:
940943
echo "# Installed Package Summary"
941944
echo "\`\`\`"
942945
943-
rpm -qi apache-cloudberry-db-incubating
946+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
944947
echo "\`\`\`"
945948
} >> "$GITHUB_STEP_SUMMARY" || true
946949
@@ -1328,7 +1331,7 @@ jobs:
13281331
fi
13291332
13301333
echo "Installation completed successfully"
1331-
rpm -qi apache-cloudberry-db-incubating
1334+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
13321335
} 2>&1 | tee -a build-logs/details/rpm-installation.log
13331336
13341337
# Clean up downloaded RPM artifacts to free disk space
@@ -1421,6 +1424,10 @@ jobs:
14211424
run: |
14221425
set -o pipefail
14231426
1427+
# Grant gpadmin write access to the install directory
1428+
# -H follows the command-line symlink to the real directory.
1429+
chown -RH gpadmin:gpadmin "${BUILD_DESTINATION}/"
1430+
14241431
# Initialize test status
14251432
overall_status=0
14261433

.github/workflows/build-dbg-cloudberry.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,9 @@ jobs:
503503
"${SRC_DIR}"/devops/build/packaging/rpm/build-rpm.sh --version "${CBDB_VERSION}" --release "${BUILD_NUMBER}" "${DEBUG_RPMBUILD_OPT}"
504504
505505
# Get OS version and move RPM
506+
# build-rpm.sh normalizes the published file name to the historical
507+
# format (without the "-<major>" segment); the package Name metadata
508+
# still embeds the major version.
506509
os_version=$(grep -oP '(?<=^VERSION_ID=")[0-9]+' /etc/os-release)
507510
RPM_FILE="${HOME}"/rpmbuild/RPMS/x86_64/apache-cloudberry-db-incubating-"${CBDB_VERSION}"-"${BUILD_NUMBER}""${DEBUG_IDENTIFIER}".el"${os_version}".x86_64.rpm
508511
cp "${RPM_FILE}" "${SRC_DIR}"
@@ -723,9 +726,9 @@ jobs:
723726
fi
724727
725728
echo "Installation completed successfully"
726-
rpm -qi apache-cloudberry-db-incubating
729+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
727730
echo "Installed files:"
728-
rpm -ql apache-cloudberry-db-incubating
731+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -ql
729732
} 2>&1 | tee -a install-logs/details/rpm-installation.log
730733
731734
- name: Upload install logs
@@ -745,7 +748,7 @@ jobs:
745748
echo "# Installed Package Summary"
746749
echo "\`\`\`"
747750
748-
rpm -qi apache-cloudberry-db-incubating
751+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
749752
echo "\`\`\`"
750753
} >> "$GITHUB_STEP_SUMMARY" || true
751754
@@ -1095,7 +1098,7 @@ jobs:
10951098
fi
10961099
10971100
echo "Installation completed successfully"
1098-
rpm -qi apache-cloudberry-db-incubating
1101+
rpm -qa 'apache-cloudberry-db-incubating*' | xargs -r rpm -qi
10991102
} 2>&1 | tee -a build-logs/details/rpm-installation.log
11001103
11011104
- name: Extract source tarball

devops/build/packaging/rpm/apache-cloudberry-db-incubating.spec

Lines changed: 100 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,32 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717

18-
%define cloudberry_install_dir /usr/local/cloudberry-db
18+
# Validate required macros early so rpmbuild fails with a clear message
19+
# before reaching the header.
20+
%{!?version:%{error:The macro 'version' must be supplied as --define 'version ...'}}
21+
%{!?release:%{error:The macro 'release' must be supplied as --define 'release ...'}}
22+
23+
%define cloudberry_base_dir /usr/local
24+
%define cloudberry_name cloudberry-db
25+
%define cloudberry_install_dir %{cloudberry_base_dir}/%{cloudberry_name}
26+
27+
# Major version, used to build a versioned package Name so that different
28+
# major versions can be installed side by side (like greenplum-db-6 vs
29+
# greenplum-db-7). Derived from the version macro by default (single source
30+
# of truth), but can be overridden via --define 'cloudberry_major_version N'.
31+
%{!?cloudberry_major_version:%define cloudberry_major_version %(echo %{version} | cut -d. -f1)}
32+
33+
# Suppress build-id links so they are not created outside the relocatable prefix.
34+
%define _build_id_links none
35+
36+
# Do not expose bundled/private shared libraries as RPM Provides.
37+
# (e.g., libpq.so.5 would conflict with system postgresql-libs.)
38+
%global __provides_exclude_from ^%{cloudberry_install_dir}-%{version}/.*\.so
39+
40+
# Do not require these bundled libraries from the system;
41+
# they are shipped inside the package and located via RPATH.
42+
%global __requires_exclude ^(libpax\.so|libpaxformat\.so|libpostgres\.so|libpq\.so\.5|libxerces-c-3\.3\.so)
1943

20-
# Add at the top of the spec file
2144
# Default to non-debug build
2245
%bcond_with debug
2346

@@ -27,7 +50,11 @@
2750
%define __strip /bin/true
2851
%endif
2952

30-
Name: apache-cloudberry-db-incubating
53+
Name: apache-cloudberry-db-incubating-%{cloudberry_major_version}
54+
# Replace the previous unversioned package on upgrade. This targets only the
55+
# old fixed name; it does NOT match apache-cloudberry-db-incubating-<major>,
56+
# so different major versions still coexist.
57+
Obsoletes: apache-cloudberry-db-incubating < %{version}-%{release}
3158
Version: %{version}
3259
# In the release definition section
3360
%if %{with debug}
@@ -37,11 +64,11 @@ Release: %{release}%{?dist}
3764
%endif
3865
Summary: High-performance, open-source data warehouse based on PostgreSQL/Greenplum
3966

40-
License: ASL 2.0
67+
License: Apache-2.0
4168
URL: https://cloudberry.apache.org
4269
Vendor: Apache Cloudberry (Incubating)
4370
Group: Applications/Databases
44-
Prefix: %{cloudberry_install_dir}
71+
Prefix: %{cloudberry_base_dir}
4572

4673
# Disabled as we are shipping GO programs (e.g. gpbackup)
4774
%define _missing_build_ids_terminate_build 0
@@ -62,6 +89,10 @@ Requires: openssh-server
6289
Requires: rsync
6390
Requires: which
6491

92+
# Scriptlet dependencies (ln, readlink, rm are from coreutils).
93+
Requires(post): coreutils
94+
Requires(postun): coreutils
95+
6596
%if 0%{?rhel} == 8
6697
Requires: apr
6798
Requires: audit
@@ -105,6 +136,7 @@ Requires: pam
105136
Requires: pcre2
106137
Requires: perl
107138
Requires: readline
139+
Requires: python3
108140
Requires: xz
109141
%endif
110142

@@ -145,41 +177,88 @@ project has yet to be fully endorsed by the ASF.
145177
# No prep needed for binary RPM
146178

147179
%build
148-
# No prep needed for binary RPM
180+
# No build needed for binary RPM
149181

150182
%install
151183
rm -rf %{buildroot}
152184

153185
# Create the versioned directory
154186
mkdir -p %{buildroot}%{cloudberry_install_dir}-%{version}
155187

156-
cp -R %{cloudberry_install_dir}/* %{buildroot}%{cloudberry_install_dir}-%{version}
188+
# Use cp -a with /. to include all the files
189+
cp -a %{cloudberry_install_dir}/. %{buildroot}%{cloudberry_install_dir}-%{version}/
157190

158191
# Copy Apache mandatory compliance files from the SOURCES directory into the installation directory
159192
cp %{_sourcedir}/LICENSE %{buildroot}%{cloudberry_install_dir}-%{version}/
160193
cp %{_sourcedir}/NOTICE %{buildroot}%{cloudberry_install_dir}-%{version}/
161194
cp %{_sourcedir}/DISCLAIMER %{buildroot}%{cloudberry_install_dir}-%{version}/
162-
cp -R %{_sourcedir}/licenses %{buildroot}%{cloudberry_install_dir}-%{version}/
163-
164-
# Create the symbolic link
165-
ln -sfn %{cloudberry_install_dir}-%{version} %{buildroot}%{cloudberry_install_dir}
195+
cp -a %{_sourcedir}/licenses %{buildroot}%{cloudberry_install_dir}-%{version}/
166196

167197
%files
168-
%{prefix}-%{version}
169-
%{prefix}
198+
%{cloudberry_install_dir}-%{version}
199+
%config(noreplace) %{cloudberry_install_dir}-%{version}/cloudberry-env.sh
170200

171201
%debug_package
172202

173203
%post
174-
# Change ownership to gpadmin.gpadmin if the gpadmin user exists
175-
if id "gpadmin" &>/dev/null; then
176-
chown -R gpadmin:gpadmin %{cloudberry_install_dir}-%{version}
177-
chown gpadmin:gpadmin %{cloudberry_install_dir}
204+
# RPM_INSTALL_PREFIX is set dynamically by RPM to the actual --prefix value.
205+
# Fall back to cloudberry_base_dir when --prefix was not used.
206+
INSTALL_PREFIX="${RPM_INSTALL_PREFIX:-%{cloudberry_base_dir}}"
207+
INSTALL_BASE="${INSTALL_PREFIX%/}"
208+
209+
LINK_PATH="${INSTALL_BASE}/%{cloudberry_name}"
210+
VERSIONED_DIR="${INSTALL_BASE}/%{cloudberry_name}-%{version}"
211+
LINK_TARGET_REL="%{cloudberry_name}-%{version}"
212+
213+
if [ ! -e "${LINK_PATH}" ] && [ ! -L "${LINK_PATH}" ]; then
214+
# Nothing at the symlink location yet — create it.
215+
ln -s "${LINK_TARGET_REL}" "${LINK_PATH}" || :
216+
elif [ -L "${LINK_PATH}" ]; then
217+
# A symlink already exists. Update it when it points to a
218+
# recognized Cloudberry versioned directory.
219+
EXISTING_TARGET=$(readlink -f -- "${LINK_PATH}" 2>/dev/null || :)
220+
EXISTING_NAME=${EXISTING_TARGET##*/}
221+
222+
case "${EXISTING_NAME}" in
223+
%{cloudberry_name}-*)
224+
EXISTING_VERSION=${EXISTING_NAME#%{cloudberry_name}-}
225+
EXISTING_MAJOR=${EXISTING_VERSION%%.*}
226+
if [ "${EXISTING_MAJOR}" = "%{cloudberry_major_version}" ]; then
227+
# Same major version: move the generic symlink to this build.
228+
ln -sfnT "${LINK_TARGET_REL}" "${LINK_PATH}" || :
229+
else
230+
# Different major version: leave the existing symlink untouched
231+
# so that multiple major versions can coexist under one prefix.
232+
echo "Warning: ${LINK_PATH} points to Cloudberry major version ${EXISTING_MAJOR}; leaving it unchanged so major versions can coexist" >&2
233+
fi
234+
;;
235+
*)
236+
echo "Warning: ${LINK_PATH} does not point to a recognized Cloudberry installation; leaving it unchanged" >&2
237+
;;
238+
esac
239+
else
240+
echo "Warning: ${LINK_PATH} exists and is not a symbolic link; leaving it unchanged" >&2
178241
fi
179242

243+
exit 0
244+
180245
%postun
181-
if [ $1 -eq 0 ] ; then
182-
if [ "$(readlink -f "%{cloudberry_install_dir}")" == "%{cloudberry_install_dir}-%{version}" ]; then
183-
unlink "%{cloudberry_install_dir}" || true
184-
fi
246+
INSTALL_PREFIX="${RPM_INSTALL_PREFIX:-%{cloudberry_base_dir}}"
247+
INSTALL_BASE="${INSTALL_PREFIX%/}"
248+
249+
LINK_PATH="${INSTALL_BASE}/%{cloudberry_name}"
250+
VERSIONED_DIR="${INSTALL_BASE}/%{cloudberry_name}-%{version}"
251+
252+
if [ -L "${LINK_PATH}" ]; then
253+
LINK_TARGET=$(readlink "${LINK_PATH}" 2>/dev/null || :)
254+
255+
# Remove the symlink only when it still points to the version
256+
# being removed (handles both absolute and relative targets).
257+
case "${LINK_TARGET}" in
258+
"${VERSIONED_DIR}"|"%{cloudberry_name}-%{version}")
259+
rm -f -- "${LINK_PATH}" || :
260+
;;
261+
esac
185262
fi
263+
264+
exit 0

0 commit comments

Comments
 (0)