From 0e5c9222f1ff12fc8ad48a9f846ba50c0c8937e4 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Tue, 17 Mar 2026 03:27:24 -0400 Subject: [PATCH 01/19] Added rpm package as part of build Signed-off-by: TejaswiniIBM --- bin/zopen-build | 15 + bin/zopen-pax2rpm | 885 ++++++++++++++++++++++++++++++++++++++++++++ cicd/publish.groovy | 17 + zopen-build | 0 4 files changed, 917 insertions(+) create mode 100755 bin/zopen-pax2rpm create mode 100644 zopen-build diff --git a/bin/zopen-build b/bin/zopen-build index c0b44ecb9..4ad0e8699 100755 --- a/bin/zopen-build +++ b/bin/zopen-build @@ -335,6 +335,7 @@ Option: with the .rej extension. -g, --get-source get the source and apply patch without building. -gp, --generate-pax generate a pax.Z file based on the install contents. + -gr, --generate-rpm generate an RPM package from the pax archive. -h, --help, -? print this information. --no-set-active do not change the pinned version. --no-install-deps do not install project's runtime dependencies. @@ -379,6 +380,7 @@ processOptions() buildEnvFile="./buildenv" getSourceOnly=false generatePax=false + generateRPM=false setActive=true signPax=false forcePatchApply=false @@ -476,6 +478,9 @@ processOptions() "-gp" | "--generate-pax") generatePax=true ;; + "-gr" | "--generate-rpm") + generateRPM=true + ;; "-s" | "--shell") startShell=true ;; @@ -2305,6 +2310,16 @@ install() printError "Could not generate pax \"${paxFileName}\"" fi + if ${generateRPM}; then + if [ -f "${paxFileName}" ]; then + printHeader "Generating RPM from ${ZOPEN_INSTALL_DIR}" + rpm_deps=$(echo "${ZOPEN_RUNTIME_DEPS}" | xargs -n1 | sort -u | xargs) + PATH="${ZOPEN_ROOTFS}/usr/local/bin:${PATH}" "${MYDIR}/zopen-pax2rpm" "${paxFileName}" --summary "${ZOPEN_NAME} package" --build + else + printError "Pax file ${paxFileName} not found. Ensure --generate-pax is also used." + fi + fi + #TODO: Hack so that we can use coreutils md5sum without impacting builds ZOPEN_DEPS="${ZOPEN_DEPS} coreutils jq" if [ "${signPax}" = "true" ] && ( [ -z "${ZOPEN_GPG_SECRET_KEY_FILE}" ] || [ -z "${ZOPEN_GPG_SECRET_KEY_PASSPHRASE_FILE}" ] || [ -z "${ZOPEN_GPG_PUBLIC_KEY_FILE}" ] || [ ! -r "${ZOPEN_GPG_SECRET_KEY_FILE}" ] || [ ! -r "${ZOPEN_GPG_SECRET_KEY_PASSPHRASE_FILE}" ] || [ ! -r "${ZOPEN_GPG_PUBLIC_KEY_FILE}" ] ); then diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm new file mode 100755 index 000000000..456db6479 --- /dev/null +++ b/bin/zopen-pax2rpm @@ -0,0 +1,885 @@ +#!/bin/bash +# +# pax2rpm.sh - Generate RPM spec file from z/OS pax archive +# +# Usage: pax2rpm.sh [options] +# +# Options: +# --name Override package name +# --version Override version +# --release Override release number (default: 1) +# --license Specify license (default: Proprietary) +# --summary Package summary +# --description Package description +# --url Project URL +# --output Output spec file (default: .spec) +# --build Build the RPM after generating spec file +# --buildroot RPM build root directory (default: ~/rpmbuild) +# --validate Validate spec file after generation +# --dry-run Show what would be done without doing it +# + +set -e + +# Default values +RELEASE="1" +LICENSE="Proprietary" +BUILD_ARCH=$(uname -m 2>/dev/null || echo "s390x") +PACKAGER_NAME="${USER}" +PACKAGER_EMAIL="${USER}@$(hostname)" + +# Build flag +BUILD_RPM=false +BUILDROOT="${HOME}/rpmbuild" +VALIDATE_SPEC=false +DRY_RUN=false +VERBOSE=false + +# Function to display usage +usage() { + cat << EOF +Usage: $0 [options] + +Generate an RPM spec file from a z/OS pax archive. + +Arguments: + pax_file Path to the pax file (e.g., /path/to/file.pax or file.pax.Z) + +Options: + --name Override package name (default: extracted from filename) + --version Override version (default: extracted from filename) + --release Override release number (default: 1) + --license Specify license (default: Proprietary) + --summary Package summary (required) + --description Package description (default: same as summary) + --url Project URL (default: none) + --requires Package dependencies (e.g., "oef >= 1.1.0") + --output Output spec file (default: .spec) + --build Build the RPM after generating spec file + --buildroot RPM build root directory (default: ~/rpmbuild) + --validate Validate spec file after generation (checks syntax and runs rpmlint) + --dry-run Show what would be done without actually doing it + --verbose Enable verbose debug output + --help Display this help message + +Example: + $0 /nfsmnts/bpidrivers/oefv1r1/os390/latest/HAMN110.runnable.pax.Z \\ + --summary "HAMN110 Runtime Package" \\ + --license "IBM" \\ + --url "https://www.ibm.com" + +EOF + exit 1 +} + +# Function to extract package name and version from filename +parse_filename() { + local filename="$1" + local basename=$(basename "$filename") + + # Remove .pax.Z or .pax extension + basename="${basename%.pax.Z}" + basename="${basename%.pax}" + + # Try to extract name and version + # Pattern 1: NAME-VERSION (e.g., package-1.0) + if [[ "$basename" =~ ^(.+)-([0-9]+\.[0-9]+.*)$ ]]; then + PKG_NAME="${BASH_REMATCH[1]}" + PKG_VERSION="${BASH_REMATCH[2]}" + # Pattern 2: NAMEVERSION.suffix (e.g., HAMN110.runnable) + elif [[ "$basename" =~ ^([A-Za-z]+)([0-9]+)\.(.+)$ ]]; then + PKG_NAME="${BASH_REMATCH[1]}" + PKG_VERSION="${BASH_REMATCH[2]}" + # Don't include suffix like "runnable" in version + # Pattern 3: NAMEVERSION (e.g., HAMN110) + elif [[ "$basename" =~ ^([A-Za-z]+)([0-9]+)$ ]]; then + PKG_NAME="${BASH_REMATCH[1]}" + PKG_VERSION="${BASH_REMATCH[2]}" + else + PKG_NAME="$basename" + PKG_VERSION="1.0" + fi + + # Convert to lowercase and replace invalid characters (keep alphanumeric and hyphens) + PKG_NAME=$(echo "$PKG_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9-]/-/g' | sed 's/^-//;s/-$//') +} + +# Function to analyze pax contents and categorize files +analyze_pax_contents() { + local pax_file="$1" + local temp_dir=$(mktemp -d) + + echo "# Analyzing pax file structure..." >&2 + + # Extract pax listing (limit to first 1000 entries for performance) + local pax_listing + if [[ "$pax_file" == *.pax.Z ]]; then + pax_listing=$(pax -vzf "$pax_file" 2>&1 | head -1000) + else + pax_listing=$(pax -vf "$pax_file" 2>&1 | head -1000) + fi + + # Check if pax listing failed or is empty + # Only treat as error if pax command actually failed (not just listing files) + if [ -z "$pax_listing" ]; then + echo "# Warning: Could not analyze pax contents, using default structure" >&2 + rm -rf "$temp_dir" + return 1 + fi + + # Initialize arrays for different file types + declare -g -A FILE_CATEGORIES + FILE_CATEGORIES[bins]="" + FILE_CATEGORIES[libs]="" + FILE_CATEGORIES[includes]="" + FILE_CATEGORIES[configs]="" + FILE_CATEGORIES[docs]="" + FILE_CATEGORIES[data]="" + FILE_CATEGORIES[scripts]="" + FILE_CATEGORIES[other]="" + + declare -g -A DIR_STRUCTURE + DIR_STRUCTURE[has_bin]=false + DIR_STRUCTURE[has_lib]=false + DIR_STRUCTURE[has_include]=false + DIR_STRUCTURE[has_etc]=false + DIR_STRUCTURE[has_share]=false + DIR_STRUCTURE[has_doc]=false + DIR_STRUCTURE[has_man]=false + + # Analyze each file + while IFS= read -r line; do + # Skip empty lines and headers + [[ -z "$line" ]] && continue + + # Extract filename (last field in pax -v output) + local filename=$(echo "$line" | awk '{print $NF}') + [[ -z "$filename" ]] && continue + + # Detect directory structure (handles top-level dir if present) + # Matches: bin/, ./bin/, package-ver/bin/, ./package-ver/bin/ + # Does NOT match: usr/lpp/IBM/bin/ (too deep) + if [[ "$filename" =~ ^(\./)?([^/]+/)?bin/ ]]; then + DIR_STRUCTURE[has_bin]=true + elif [[ "$filename" =~ ^(\./)?([^/]+/)?lib/ ]]; then + DIR_STRUCTURE[has_lib]=true + elif [[ "$filename" =~ ^(\./)?([^/]+/)?include/ ]]; then + DIR_STRUCTURE[has_include]=true + elif [[ "$filename" =~ ^(\./)?([^/]+/)?etc/ ]]; then + DIR_STRUCTURE[has_etc]=true + elif [[ "$filename" =~ ^(\./)?([^/]+/)?share/ ]]; then + DIR_STRUCTURE[has_share]=true + elif [[ "$filename" =~ ^(\./)?([^/]+/)?(doc|docs)/ ]]; then + DIR_STRUCTURE[has_doc]=true + elif [[ "$filename" =~ ^(\./)?([^/]+/)?man/ ]]; then + DIR_STRUCTURE[has_man]=true + elif [[ "$filename" =~ ^(\./)?([^/]+/)?scripts/ ]]; then + DIR_STRUCTURE[has_scripts]=true + elif [[ "$filename" =~ ^(\./)?usr/lpp/ ]]; then + DIR_STRUCTURE[is_os_layout]=true + fi + + # Categorize by file type + if [[ "$filename" =~ \.(so|so\.[0-9]+|a|dylib)$ ]]; then + FILE_CATEGORIES[libs]+="$filename"$'\n' + elif [[ "$filename" =~ \.(h|hpp|hxx)$ ]]; then + FILE_CATEGORIES[includes]+="$filename"$'\n' + elif [[ "$filename" =~ \.(conf|cfg|ini|yaml|yml|json|xml|properties)$ ]]; then + FILE_CATEGORIES[configs]+="$filename"$'\n' + elif [[ "$filename" =~ \.(md|txt|pdf|html|htm|README|LICENSE|COPYING|CHANGELOG|AUTHORS)$ ]] || [[ "$filename" =~ (README|LICENSE|COPYING|CHANGELOG|AUTHORS|INSTALL|NEWS)$ ]]; then + FILE_CATEGORIES[docs]+="$filename"$'\n' + elif [[ "$filename" =~ \.(sh|bash|ksh|pl|py|rb)$ ]] && [[ "$line" =~ ^-r.x ]]; then + FILE_CATEGORIES[scripts]+="$filename"$'\n' + elif [[ "$line" =~ ^-r.x ]] && [[ ! "$filename" =~ \. ]]; then + # Executable without extension (likely binary) + FILE_CATEGORIES[bins]+="$filename"$'\n' + elif [[ "$filename" =~ \.(dat|data|db|sqlite)$ ]]; then + FILE_CATEGORIES[data]+="$filename"$'\n' + else + FILE_CATEGORIES[other]+="$filename"$'\n' + fi + done <<< "$pax_listing" + + rm -rf "$temp_dir" +} + +# Function to list contents of pax file +list_pax_contents() { + local pax_file="$1" + + echo "# Pax file contents (first 100 entries):" >&2 + + if [[ "$pax_file" == *.pax.Z ]]; then + pax -rvzf "$pax_file" 2>&1 | head -100 + else + pax -rvf "$pax_file" 2>&1 | head -100 + fi +} + +# Function to generate install commands based on analyzed contents +# Function to generate install commands based on analyzed contents +# Function to generate install commands based on analyzed contents +generate_install_commands() { + local pax_file="$1" + + cat << 'EOF' +# Install files based on detected structure + +# Detect the source layout +if [ -d usr ]; then + echo "OS layout (usr/) detected, searching for actual content root..." + # Find the deepest directory that contains bin, lib, pyz, bash, or go + FOUND_PATH=$(find . -type d \( -name bin -o -name lib -o -name pyz -o -name bash -o -name go \) -print -quit) + + if [ -n "$FOUND_PATH" ]; then + FOUND_PATH=${FOUND_PATH#./} + if [[ "$FOUND_PATH" == */pyz ]]; then + ROOT_DIR="$FOUND_PATH" + else + ROOT_DIR=$(echo "$FOUND_PATH" | sed 's/\/\(bin\|lib\|bash\|go\)$//') + fi + else + # Fallback for OS layout: find first dir with more than one entry + ROOT_DIR="." + while [ $(ls -1 "$ROOT_DIR" 2>/dev/null | wc -l) -eq 1 ]; do + SUB_DIR=$(ls -1 "$ROOT_DIR") + if [ -d "$ROOT_DIR/$SUB_DIR" ]; then + ROOT_DIR="$ROOT_DIR/$SUB_DIR" + else + break + fi + done + fi + echo "Detected nested root: $ROOT_DIR" + mkdir -p %{buildroot}%{install_dir} + cp -RP "$ROOT_DIR"/* %{buildroot}%{install_dir}/ +elif [ $(ls -1 | grep -v "^$" | wc -l) -eq 1 ] && [ -d * ]; then + # Standard single-directory layout (e.g., go/) + SUB_DIR=$(ls -1) + echo "Standard single-directory layout detected: $SUB_DIR" + mkdir -p %{buildroot}%{install_dir} + cp -RP "$SUB_DIR"/* %{buildroot}%{install_dir}/ +else + # Flat or mixed layout + echo "Flat or mixed layout detected" + mkdir -p %{buildroot}%{install_dir} + cp -RP * %{buildroot}%{install_dir}/ +fi + +# Install root-level documentation if it hasn't been copied already +# (Matches files in the CURRENT directory, not ROOT_DIR) +for doc in README* LICENSE* COPYING* CHANGELOG* AUTHORS* INSTALL* NEWS*; do + if [ -f "$doc" ]; then + mkdir -p %{buildroot}%{install_dir}/doc + cp -RP "$doc" %{buildroot}%{install_dir}/doc/ + fi +done +EOF +} + + +generate_files_list() { + cat << 'EOF' +%defattr(-,root,root,-) +%{install_dir} +EOF +} + +# Function to generate spec file +generate_spec() { + local output_file="$1" + local source_file="$2" + local date=$(date "+%a %b %d %Y") + + # Analyze the pax contents first (populate global DIR_STRUCTURE) + analyze_pax_contents "$source_file" + + cat > "$output_file" << EOF +# RPM Spec file generated from pax archive +# Source: $source_file +# Generated: $(date) + +%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}-ibm-zos.rpm + +Name: ${PKG_NAME} +Version: ${PKG_VERSION} +Release: ${RELEASE}%{?dist} +Summary: ${SUMMARY} + +%define install_dir /usr/lpp/pkg/IBM/%{name}/%{version} + +License: ${LICENSE} +${URL:+URL: $URL} +Source0: $(basename "$source_file") + +BuildArch: ${BUILD_ARCH} +${REQUIRES:+Requires: $REQUIRES} + +%description +${DESCRIPTION} + +%prep +# Extract pax archive +%setup -c -T +cd %{_builddir} +mkdir -p %{name}-%{version} +cd %{name}-%{version} + +EOF + + if [[ "$source_file" == *.pax.Z ]]; then + cat >> "$output_file" << 'EOF' +# Extract compressed pax archive +pax -rzf %{SOURCE0} +EOF + else + cat >> "$output_file" << 'EOF' +# Extract pax archive +pax -rf %{SOURCE0} +EOF + fi + + cat >> "$output_file" << EOF + +%build +# No build step required for pre-built pax archives + +%install +rm -rf %{buildroot} + +# Create base installation directories +mkdir -p %{buildroot}%{install_dir} + +$(generate_install_commands "$source_file") + +%files +$(generate_files_list) + +%changelog +* ${date} ${PACKAGER_NAME} <${PACKAGER_EMAIL}> - ${PKG_VERSION}-${RELEASE} +- Initial RPM package created from pax archive +- Source: $(basename "$source_file") +EOF + + echo "RPM spec file generated: $output_file" + echo "" + echo "Detected structure:" + [[ "${DIR_STRUCTURE[has_bin]}" == "true" ]] && echo " ✓ Binaries in bin/" + [[ "${DIR_STRUCTURE[has_lib]}" == "true" ]] && echo " ✓ Libraries in lib/" + [[ "${DIR_STRUCTURE[has_include]}" == "true" ]] && echo " ✓ Headers in include/" + [[ "${DIR_STRUCTURE[has_etc]}" == "true" ]] && echo " ✓ Configuration files in etc/" + [[ "${DIR_STRUCTURE[has_share]}" == "true" ]] && echo " ✓ Data files in share/" + [[ "${DIR_STRUCTURE[has_doc]}" == "true" ]] && echo " ✓ Documentation in doc/" + [[ "${DIR_STRUCTURE[has_man]}" == "true" ]] && echo " ✓ Man pages in man/" + [[ "${DIR_STRUCTURE[has_scripts]}" == "true" ]] && echo " ✓ Scripts in scripts/" + + return 0 +} + +# Function to setup rpmbuild directories +setup_rpmbuild() { + local buildroot="$1" + + echo "Setting up RPM build directories in: $buildroot" + + mkdir -p "$buildroot"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} + + if [ ! -f "$HOME/.rpmmacros" ]; then + cat > "$HOME/.rpmmacros" << EOF +%_topdir $buildroot +EOF + echo "Created ~/.rpmmacros with _topdir set to $buildroot" + fi +} + +# Function to check for required tools +check_required_tools() { + local missing_tools=() + local pax_file="$1" + + # Check for pax + if ! command -v pax &> /dev/null; then + missing_tools+=("pax") + fi + + # Check for uncompress if dealing with .Z files + if [[ "$pax_file" == *.pax.Z ]] && ! command -v uncompress &> /dev/null; then + missing_tools+=("uncompress") + fi + + # Check for rpmbuild if building or validating + if [ "$BUILD_RPM" = true ] || [ "$VALIDATE_SPEC" = true ]; then + echo "DEBUG: Current PATH is $PATH" >&2 + if ! command -v rpmbuild &> /dev/null; then + missing_tools+=("rpmbuild") + fi + fi + + if [ ${#missing_tools[@]} -gt 0 ]; then + echo "" >&2 + echo "=========================================" >&2 + echo "ERROR: Missing Required Tools" >&2 + echo "=========================================" >&2 + echo "" >&2 + echo "The following tools are required but not found:" >&2 + echo "" >&2 + + for tool in "${missing_tools[@]}"; do + echo " ✗ $tool" >&2 + + case "$tool" in + pax) + echo " Purpose: Extract pax archives" >&2 + echo " Install: Usually pre-installed on z/OS" >&2 + echo " On Linux: apt-get install pax (Debian/Ubuntu)" >&2 + echo " yum install pax (RHEL/CentOS)" >&2 + ;; + uncompress) + echo " Purpose: Decompress .Z files" >&2 + echo " Install: Usually part of ncompress package" >&2 + echo " On z/OS: May be in /usr/bin or /bin" >&2 + echo " On Linux: apt-get install ncompress (Debian/Ubuntu)" >&2 + echo " yum install ncompress (RHEL/CentOS)" >&2 + ;; + rpmbuild) + echo " Purpose: Build RPM packages and validate spec files" >&2 + echo " Install: Part of rpm-build package" >&2 + echo " On z/OS: zopen install rpm" >&2 + echo " On Linux: apt-get install rpm (Debian/Ubuntu)" >&2 + echo " yum install rpm-build (RHEL/CentOS)" >&2 + ;; + esac + echo "" >&2 + done + + echo "Please install the missing tools and try again." >&2 + echo "" >&2 + return 1 + fi + + # Optional tools check (informational only) + local optional_missing=() + + if [ "$VALIDATE_SPEC" = true ] && ! command -v rpmlint &> /dev/null; then + optional_missing+=("rpmlint") + fi + + if [ ${#optional_missing[@]} -gt 0 ]; then + echo "" >&2 + echo "Note: Optional tools not found (validation will be limited):" >&2 + for tool in "${optional_missing[@]}"; do + echo " - $tool (for enhanced spec file quality checks)" >&2 + case "$tool" in + rpmlint) + echo " Install: On z/OS: zopen install rpmlint" >&2 + echo " On Linux: apt-get install rpmlint (Debian/Ubuntu)" >&2 + echo " yum install rpmlint (RHEL/CentOS)" >&2 + ;; + esac + done + echo "" >&2 + fi + + return 0 +} + +# Function to validate spec file syntax +validate_spec_syntax() { + local spec_file="$1" + + echo "" + echo "==========================================" + echo "Validating spec file syntax..." + echo "==========================================" + echo "" + + # Check if rpmbuild is available + if ! command -v rpmbuild &> /dev/null; then + echo "Warning: rpmbuild not found, skipping syntax validation" >&2 + return 0 + fi + + # Try to use rpmbuild -bp (prep stage) to validate syntax + # This will check syntax without actually building + # Use --nodeps to check syntax/prep logic without requiring local RPM DB to have all deps + local temp_output=$(mktemp) + if rpmbuild -bp --nodeps "$spec_file" &> "$temp_output"; then + echo "✓ Spec file syntax is valid" + rm -f "$temp_output" + return 0 + else + # Check if it's a real syntax error or just missing source files + if grep -q "No such file or directory" "$temp_output" || grep -q "does not exist" "$temp_output"; then + echo "✓ Spec file syntax appears valid (source files not present for full validation)" + rm -f "$temp_output" + return 0 + else + echo "✗ Spec file has syntax errors:" >&2 + cat "$temp_output" | head -20 + rm -f "$temp_output" + return 1 + fi + fi +} + +# Function to run rpmlint on spec file +run_rpmlint() { + local spec_file="$1" + + echo "" + echo "==========================================" + echo "Running rpmlint checks..." + echo "==========================================" + echo "" + + # Check if rpmlint is available + if ! command -v rpmlint &> /dev/null; then + echo "Note: rpmlint not found, skipping quality checks" + echo "Install rpmlint for additional spec file validation" + return 0 + fi + + # Run rpmlint + local rpmlint_output + rpmlint_output=$(rpmlint "$spec_file" 2>&1) + local rpmlint_exit=$? + + echo "$rpmlint_output" + echo "" + + # Count errors and warnings + local errors=$(echo "$rpmlint_output" | grep -c "E:" || true) + local warnings=$(echo "$rpmlint_output" | grep -c "W:" || true) + + if [ $errors -gt 0 ]; then + echo "⚠ Found $errors error(s) and $warnings warning(s)" + echo "Please review and fix errors before building" + return 1 + elif [ $warnings -gt 0 ]; then + echo "⚠ Found $warnings warning(s)" + echo "Warnings can often be ignored, but please review them" + return 0 + else + echo "✓ No errors or warnings found" + return 0 + fi +} + +# Function to validate generated spec file +validate_spec_file() { + local spec_file="$1" + local pax_file="$2" + local buildroot="$3" + local validation_failed=false + + echo "" + echo "==========================================" + echo "Validating generated spec file..." + echo "==========================================" + + # Setup environment for validation (rpmbuild -bp needs source) + setup_rpmbuild "$buildroot" + + # Copy pax file to SOURCES if not already there + local source_name=$(basename "$pax_file") + if [ ! -f "$buildroot/SOURCES/$source_name" ] || [ "$pax_file" -nt "$buildroot/SOURCES/$source_name" ]; then + echo "Copying source file to $buildroot/SOURCES/$source_name for validation" + cp "$pax_file" "$buildroot/SOURCES/" + fi + + # Check syntax + if ! validate_spec_syntax "$spec_file"; then + validation_failed=true + fi + + # Run rpmlint + if ! run_rpmlint "$spec_file"; then + validation_failed=true + fi + + if [ "$validation_failed" = true ]; then + echo "" + echo "==========================================" + echo "✗ Validation failed" + echo "==========================================" + echo "" + echo "Please review the errors above and edit the spec file:" + echo " $spec_file" + echo "" + return 1 + else + echo "" + echo "==========================================" + echo "✓ Validation passed" + echo "==========================================" + echo "" + return 0 + fi +} + +# Function to perform dry run +perform_dry_run() { + local pax_file="$1" + + echo "" + echo "==========================================" + echo "DRY RUN MODE - No files will be created" + echo "==========================================" + echo "" + + echo "Would perform the following actions:" + echo "" + echo "1. Analyze pax file: $pax_file" + echo "2. Extract package information:" + echo " - Name: $PKG_NAME" + echo " - Version: $PKG_VERSION" + echo " - Release: $RELEASE" + echo "3. Generate spec file: $OUTPUT_FILE" + + if [ "$VALIDATE_SPEC" = true ]; then + echo "4. Validate spec file syntax and run rpmlint" + fi + + if [ "$BUILD_RPM" = true ]; then + echo "5. Build RPM package in: $BUILDROOT" + echo " - Copy $pax_file to $BUILDROOT/SOURCES/" + echo " - Copy spec file to $BUILDROOT/SPECS/" + echo " - Run: rpmbuild -ba $OUTPUT_FILE" + fi + + echo "" + echo "To execute these actions, run without --dry-run flag" + echo "" +} + +# Function to build RPM +build_rpm() { + local spec_file="$1" + local pax_file="$2" + local buildroot="$3" + + echo "" + echo "==========================================" + echo "Building RPM package..." + echo "==========================================" + echo "" + + # Setup rpmbuild directories + setup_rpmbuild "$buildroot" + + # Copy pax file to SOURCES + local source_name=$(basename "$pax_file") + echo "Copying source file to $buildroot/SOURCES/$source_name" + cp "$pax_file" "$buildroot/SOURCES/" + + # Copy spec file to SPECS + echo "Copying spec file to $buildroot/SPECS/" + cp "$spec_file" "$buildroot/SPECS/" + + # Build the RPM + echo "" + echo "Running rpmbuild -ba $spec_file" + echo "" + + if rpmbuild -ba "$spec_file"; then + echo "" + echo "==========================================" + echo "✓ RPM build completed successfully!" + echo "==========================================" + echo "" + echo "Generated packages:" + echo "" + echo "Binary RPMs:" + find "$buildroot/RPMS" -name "*.rpm" -type f 2>/dev/null | while read rpm; do + echo " - $rpm" + done + echo "" + echo "Source RPMs:" + find "$buildroot/SRPMS" -name "*.rpm" -type f 2>/dev/null | while read rpm; do + echo " - $rpm" + done + echo "" + return 0 + else + echo "" + echo "==========================================" + echo "✗ RPM build failed!" + echo "==========================================" + echo "" + echo "Please review the spec file and build output above." + echo "Common issues:" + echo " - Missing BuildRequires dependencies" + echo " - Incorrect file paths in %install or %files sections" + echo " - Pax extraction errors" + echo "" + return 1 + fi +} + +# Main script +main() { + if [ $# -eq 0 ]; then + usage + fi + + PAX_FILE="$1" + shift + + # Check if pax file exists + if [ ! -f "$PAX_FILE" ]; then + echo "Error: Pax file not found: $PAX_FILE" >&2 + exit 1 + fi + + # Parse filename to get default name and version + parse_filename "$PAX_FILE" + + # Parse command line options + while [ $# -gt 0 ]; do + case "$1" in + --name) + PKG_NAME="$2" + shift 2 + ;; + --version) + PKG_VERSION="$2" + shift 2 + ;; + --release) + RELEASE="$2" + shift 2 + ;; + --license) + LICENSE="$2" + shift 2 + ;; + --summary) + SUMMARY="$2" + shift 2 + ;; + --description) + DESCRIPTION="$2" + shift 2 + ;; + --url) + URL="$2" + shift 2 + ;; + --output) + OUTPUT_FILE="$2" + shift 2 + ;; + --requires) + REQUIRES="$2" + shift 2 + ;; + --build) + BUILD_RPM=true + shift + ;; + --buildroot) + BUILDROOT="$2" + shift 2 + ;; + --validate) + VALIDATE_SPEC=true + shift + ;; + --dry-run) + DRY_RUN=true + shift + ;; + --verbose) + VERBOSE=true + set -x + shift + ;; + --help) + usage + ;; + *) + echo "Error: Unknown option: $1" >&2 + usage + ;; + esac + done + + # Set defaults for required fields + if [ -z "$SUMMARY" ]; then + echo "Error: --summary is required" >&2 + exit 1 + fi + + if [ -z "$DESCRIPTION" ]; then + DESCRIPTION="$SUMMARY" + fi + + if [ -z "$OUTPUT_FILE" ]; then + OUTPUT_FILE="${PKG_NAME}.spec" + fi + + # Check for required tools + if ! check_required_tools "$PAX_FILE"; then + exit 1 + fi + + # Display extracted information + echo "Package Information:" + echo " Name: $PKG_NAME" + echo " Version: $PKG_VERSION" + echo " Release: $RELEASE" + echo " License: $LICENSE" + echo " Summary: $SUMMARY" + echo " Source: $(basename "$PAX_FILE")" + echo " Output: $OUTPUT_FILE" + echo "" + + # Handle dry-run mode + if [ "$DRY_RUN" = true ]; then + perform_dry_run "$PAX_FILE" + exit 0 + fi + + # List pax contents for reference + list_pax_contents "$PAX_FILE" || echo " (Could not list contents)" + echo "" + + # Generate spec file + generate_spec "$OUTPUT_FILE" "$PAX_FILE" + + # Validate spec file if requested + if [ "$VALIDATE_SPEC" = true ]; then + if ! validate_spec_file "$OUTPUT_FILE" "$PAX_FILE" "$BUILDROOT"; then + echo "Validation failed. Please fix the errors and try again." >&2 + exit 1 + fi + fi + + # Build RPM if requested + if [ "$BUILD_RPM" = true ]; then + if build_rpm "$OUTPUT_FILE" "$PAX_FILE" "$BUILDROOT"; then + echo "RPM package built successfully!" + else + echo "Warning: RPM build failed. Please review the spec file and try again." >&2 + exit 1 + fi + else + echo "" + echo "Next steps:" + echo " 1. Review and edit the generated spec file: $OUTPUT_FILE" + echo " 2. Adjust the %install and %files sections if needed" + if [ "$VALIDATE_SPEC" = false ]; then + echo " 3. Validate the spec: $0 $PAX_FILE --summary \"$SUMMARY\" --validate" + echo " 4. Copy the pax file to rpmbuild/SOURCES/" + echo " 5. Build the RPM: rpmbuild -ba $OUTPUT_FILE" + else + echo " 3. Copy the pax file to rpmbuild/SOURCES/" + echo " 4. Build the RPM: rpmbuild -ba $OUTPUT_FILE" + fi + echo "" + echo "Or run with --build flag to build automatically:" + echo " $0 $PAX_FILE --summary \"$SUMMARY\" --build" + fi +} + +main "$@" diff --git a/cicd/publish.groovy b/cicd/publish.groovy index 9336e696a..16e2bea83 100755 --- a/cicd/publish.groovy +++ b/cicd/publish.groovy @@ -22,6 +22,7 @@ GITHUB_REPO=$RELEASE_PREFIX # PAX file should be a copied artifact PAX=`find . -type f -path "*install/*zos.pax.Z"` +RPM=`find rpmbuild/RPMS -type f -name "*.rpm"` METADATA=`find . -type f -path "*install/metadata.json"` BUILD_STATUS=`find . -name "test.status" | xargs cat` DEPENDENCIES=`find . -name ".runtimedeps" | xargs cat` @@ -33,6 +34,11 @@ if [ ! -f "$PAX" ]; then exit 1; fi +if [ ! -f "$RPM" ]; then + echo "Port RPM file does not exist"; + exit 1; +fi + if [ ! -f "$METADATA" ]; then echo "Port metadata.json file does not exist"; exit 1; @@ -57,6 +63,8 @@ PAX_BASENAME=$(basename "${PAX}") DIR_NAME=${PAX_BASENAME%%.pax.Z} DIR_NAME=$(echo "$DIR_NAME" | sed -e "s/\.202[0-9]*_[0-9]*\.zos/.zos/g" -e "s/\.zos//g") BUILD_ID=${BUILD_NUMBER} +RPM_BASENAME=$(basename "${RPM}") + # Check for python dependencies if pip3 show numpy &> /dev/null; then @@ -198,6 +206,15 @@ else exit 1 fi +echo "Uploading the RPM artifacts into github" +github-release -v upload --user ${GITHUB_ORGANIZATION} --repo ${GITHUB_REPO} --tag "${TAG}" --name "${RPM_BASENAME}" --file "${RPM}" +if [ $? -eq 0 ]; then + echo "RPM Artifact uploaded successfully!" +else + echo "Failed to upload RPM artifact!" + exit 1 +fi + echo "Uploading metadata artifacts into github" github-release -v upload --user ${GITHUB_ORGANIZATION} --repo ${GITHUB_REPO} --tag "${TAG}" --name "metadata.json" --file "${METADATA}" if [ $? -eq 0 ]; then diff --git a/zopen-build b/zopen-build new file mode 100644 index 000000000..e69de29bb From 993c3ea6401442863be78e0e5487f46a9609223b Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Mon, 23 Mar 2026 01:14:07 -0400 Subject: [PATCH 02/19] Resolved augment comments Signed-off-by: TejaswiniIBM --- bin/zopen-build | 20 ++++++++++++-------- bin/zopen-pax2rpm | 23 +++++++++++++---------- cicd/publish.groovy | 6 +++--- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/bin/zopen-build b/bin/zopen-build index 4ad0e8699..9b5fe3ae2 100755 --- a/bin/zopen-build +++ b/bin/zopen-build @@ -480,12 +480,14 @@ processOptions() ;; "-gr" | "--generate-rpm") generateRPM=true + generatePax=true ;; "-s" | "--shell") startShell=true ;; "-sp" | "--sign-pax") signPax=true + generatePax=true ;; *) printError "Unknown option ${1} specified" @@ -2309,17 +2311,19 @@ install() if ! runAndLog "${ZOPEN_PAX_CMD}"; then printError "Could not generate pax \"${paxFileName}\"" fi + fi - if ${generateRPM}; then - if [ -f "${paxFileName}" ]; then - printHeader "Generating RPM from ${ZOPEN_INSTALL_DIR}" - rpm_deps=$(echo "${ZOPEN_RUNTIME_DEPS}" | xargs -n1 | sort -u | xargs) - PATH="${ZOPEN_ROOTFS}/usr/local/bin:${PATH}" "${MYDIR}/zopen-pax2rpm" "${paxFileName}" --summary "${ZOPEN_NAME} package" --build - else - printError "Pax file ${paxFileName} not found. Ensure --generate-pax is also used." - fi + if ${generateRPM}; then + if [ -f "${paxFileName}" ]; then + printHeader "Generating RPM from ${ZOPEN_INSTALL_DIR}" + rpm_deps=$(echo "${ZOPEN_RUNTIME_DEPS}" | xargs -n1 | sort -u | xargs) + PATH="${ZOPEN_ROOTFS}/usr/local/bin:${PATH}" "${MYDIR}/zopen-pax2rpm" "${paxFileName}" --summary "${ZOPEN_NAME} package" --requires "${rpm_deps}" --build + else + printError "Pax file ${paxFileName} not found. Ensure --generate-pax is also used." fi + fi + if ${generatePax}; then #TODO: Hack so that we can use coreutils md5sum without impacting builds ZOPEN_DEPS="${ZOPEN_DEPS} coreutils jq" if [ "${signPax}" = "true" ] && ( [ -z "${ZOPEN_GPG_SECRET_KEY_FILE}" ] || [ -z "${ZOPEN_GPG_SECRET_KEY_PASSPHRASE_FILE}" ] || [ -z "${ZOPEN_GPG_PUBLIC_KEY_FILE}" ] || [ ! -r "${ZOPEN_GPG_SECRET_KEY_FILE}" ] || [ ! -r "${ZOPEN_GPG_SECRET_KEY_PASSPHRASE_FILE}" ] || [ ! -r "${ZOPEN_GPG_PUBLIC_KEY_FILE}" ] ); then diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index 456db6479..56a36a35c 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -233,11 +233,14 @@ if [ -d usr ]; then if [ -n "$FOUND_PATH" ]; then FOUND_PATH=${FOUND_PATH#./} - if [[ "$FOUND_PATH" == */pyz ]]; then - ROOT_DIR="$FOUND_PATH" - else - ROOT_DIR=$(echo "$FOUND_PATH" | sed 's/\/\(bin\|lib\|bash\|go\)$//') - fi + case "$FOUND_PATH" in + */pyz) + ROOT_DIR="$FOUND_PATH" + ;; + *) + ROOT_DIR=$(echo "$FOUND_PATH" | sed 's/\/\(bin\|lib\|bash\|go\)$//') + ;; + esac else # Fallback for OS layout: find first dir with more than one entry ROOT_DIR="." @@ -328,14 +331,14 @@ cd %{name}-%{version} EOF if [[ "$source_file" == *.pax.Z ]]; then - cat >> "$output_file" << 'EOF' + cat >> "$output_file" << EOF # Extract compressed pax archive -pax -rzf %{SOURCE0} +pax -rzf %{_sourcedir}/$(basename "$source_file") EOF else - cat >> "$output_file" << 'EOF' + cat >> "$output_file" << EOF # Extract pax archive -pax -rf %{SOURCE0} +pax -rf %{_sourcedir}/$(basename "$source_file") EOF fi @@ -541,7 +544,7 @@ run_rpmlint() { # Run rpmlint local rpmlint_output - rpmlint_output=$(rpmlint "$spec_file" 2>&1) + rpmlint_output=$(rpmlint "$spec_file" 2>&1 || true) local rpmlint_exit=$? echo "$rpmlint_output" diff --git a/cicd/publish.groovy b/cicd/publish.groovy index 16e2bea83..08bf34c95 100755 --- a/cicd/publish.groovy +++ b/cicd/publish.groovy @@ -21,9 +21,9 @@ PORT_NAME=${RELEASE_PREFIX%%port} GITHUB_REPO=$RELEASE_PREFIX # PAX file should be a copied artifact -PAX=`find . -type f -path "*install/*zos.pax.Z"` -RPM=`find rpmbuild/RPMS -type f -name "*.rpm"` -METADATA=`find . -type f -path "*install/metadata.json"` +PAX=`find . -type f -path "*install/*zos.pax.Z" | head -n 1` +RPM=`find rpmbuild/RPMS -type f -name "*.rpm" | head -n 1` +METADATA=`find . -type f -path "*install/metadata.json" | head -n 1` BUILD_STATUS=`find . -name "test.status" | xargs cat` DEPENDENCIES=`find . -name ".runtimedeps" | xargs cat` BUILD_DEPENDENCIES=`find . -name ".builddeps" | xargs cat` From f271bac1c2428dbc68ac5fd5a3eb47d13c925781 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Mon, 23 Mar 2026 01:47:04 -0400 Subject: [PATCH 03/19] Resolved PR reviews in zopen-pax2rpm, pubilsh.groovy Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 19 +++++++++++++------ cicd/publish.groovy | 25 +++++++++++++++++-------- zopen-build | 0 zopen-diagnostics | 0 4 files changed, 30 insertions(+), 14 deletions(-) delete mode 100644 zopen-build delete mode 100644 zopen-diagnostics diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index 56a36a35c..f8bf3d205 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -392,6 +392,13 @@ setup_rpmbuild() { %_topdir $buildroot EOF echo "Created ~/.rpmmacros with _topdir set to $buildroot" + else + # Check if _topdir is already defined and different + local existing_topdir=$(grep "%_topdir" "$HOME/.rpmmacros" | awk '{print $2}') + if [ "$existing_topdir" != "$buildroot" ]; then + echo "Warning: ~/.rpmmacros exists and has _topdir set to '$existing_topdir'." + echo "The script will use '$buildroot' by overriding it with --define \"_topdir $buildroot\"." + fi fi } @@ -506,7 +513,7 @@ validate_spec_syntax() { # This will check syntax without actually building # Use --nodeps to check syntax/prep logic without requiring local RPM DB to have all deps local temp_output=$(mktemp) - if rpmbuild -bp --nodeps "$spec_file" &> "$temp_output"; then + if rpmbuild --define "_topdir $buildroot" -bp --nodeps "$spec_file" &> "$temp_output"; then echo "✓ Spec file syntax is valid" rm -f "$temp_output" return 0 @@ -647,7 +654,7 @@ perform_dry_run() { echo "5. Build RPM package in: $BUILDROOT" echo " - Copy $pax_file to $BUILDROOT/SOURCES/" echo " - Copy spec file to $BUILDROOT/SPECS/" - echo " - Run: rpmbuild -ba $OUTPUT_FILE" + echo " - Run: rpmbuild --define \"_topdir $buildroot\" -ba $OUTPUT_FILE" fi echo "" @@ -681,10 +688,10 @@ build_rpm() { # Build the RPM echo "" - echo "Running rpmbuild -ba $spec_file" + echo "Running rpmbuild --define \"_topdir $buildroot\" -ba $spec_file" echo "" - if rpmbuild -ba "$spec_file"; then + if rpmbuild --define "_topdir $buildroot" -ba "$spec_file"; then echo "" echo "==========================================" echo "✓ RPM build completed successfully!" @@ -874,10 +881,10 @@ main() { if [ "$VALIDATE_SPEC" = false ]; then echo " 3. Validate the spec: $0 $PAX_FILE --summary \"$SUMMARY\" --validate" echo " 4. Copy the pax file to rpmbuild/SOURCES/" - echo " 5. Build the RPM: rpmbuild -ba $OUTPUT_FILE" + echo " 5. Build the RPM: rpmbuild --define \"_topdir $BUILDROOT\" -ba $OUTPUT_FILE" else echo " 3. Copy the pax file to rpmbuild/SOURCES/" - echo " 4. Build the RPM: rpmbuild -ba $OUTPUT_FILE" + echo " 4. Build the RPM: rpmbuild --define \"_topdir $BUILDROOT\" -ba $OUTPUT_FILE" fi echo "" echo "Or run with --build flag to build automatically:" diff --git a/cicd/publish.groovy b/cicd/publish.groovy index 08bf34c95..104343099 100755 --- a/cicd/publish.groovy +++ b/cicd/publish.groovy @@ -21,9 +21,23 @@ PORT_NAME=${RELEASE_PREFIX%%port} GITHUB_REPO=$RELEASE_PREFIX # PAX file should be a copied artifact -PAX=`find . -type f -path "*install/*zos.pax.Z" | head -n 1` -RPM=`find rpmbuild/RPMS -type f -name "*.rpm" | head -n 1` -METADATA=`find . -type f -path "*install/metadata.json" | head -n 1` +PAX=`find . -type f -path "*install/*zos.pax.Z"` +if [ $(echo "$PAX" | grep -c /) -ne 1 ]; then + echo "Error: Expected exactly 1 PAX file, found: $PAX" + exit 1 +fi + +RPM=`find rpmbuild/RPMS -type f -name "*.rpm"` +if [ $(echo "$RPM" | grep -c /) -ne 1 ]; then + echo "Error: Expected exactly 1 RPM file, found: $RPM" + exit 1 +fi + +METADATA=`find . -type f -path "*install/metadata.json"` +if [ $(echo "$METADATA" | grep -c /) -ne 1 ]; then + echo "Error: Expected exactly 1 metadata.json file, found: $METADATA" + exit 1 +fi BUILD_STATUS=`find . -name "test.status" | xargs cat` DEPENDENCIES=`find . -name ".runtimedeps" | xargs cat` BUILD_DEPENDENCIES=`find . -name ".builddeps" | xargs cat` @@ -34,11 +48,6 @@ if [ ! -f "$PAX" ]; then exit 1; fi -if [ ! -f "$RPM" ]; then - echo "Port RPM file does not exist"; - exit 1; -fi - if [ ! -f "$METADATA" ]; then echo "Port metadata.json file does not exist"; exit 1; diff --git a/zopen-build b/zopen-build deleted file mode 100644 index e69de29bb..000000000 diff --git a/zopen-diagnostics b/zopen-diagnostics deleted file mode 100644 index e69de29bb..000000000 From d9b16e592dd3edc8c447ba152459e3be39c9e71d Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Mon, 23 Mar 2026 05:01:21 -0400 Subject: [PATCH 04/19] Resolved PR reviews in zopen-pax2rpm Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index f8bf3d205..0be49bfea 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -124,7 +124,7 @@ analyze_pax_contents() { if [ -z "$pax_listing" ]; then echo "# Warning: Could not analyze pax contents, using default structure" >&2 rm -rf "$temp_dir" - return 1 + return 0 fi # Initialize arrays for different file types @@ -496,6 +496,7 @@ check_required_tools() { # Function to validate spec file syntax validate_spec_syntax() { local spec_file="$1" + local buildroot="$2" echo "" echo "==========================================" @@ -598,7 +599,7 @@ validate_spec_file() { fi # Check syntax - if ! validate_spec_syntax "$spec_file"; then + if ! validate_spec_syntax "$spec_file" "$buildroot"; then validation_failed=true fi @@ -654,7 +655,7 @@ perform_dry_run() { echo "5. Build RPM package in: $BUILDROOT" echo " - Copy $pax_file to $BUILDROOT/SOURCES/" echo " - Copy spec file to $BUILDROOT/SPECS/" - echo " - Run: rpmbuild --define \"_topdir $buildroot\" -ba $OUTPUT_FILE" + echo " - Run: rpmbuild --define \"_topdir $BUILDROOT\" -ba $OUTPUT_FILE" fi echo "" From 5531b9b2691febc5d099fcad0a863255d70d5cf5 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Thu, 26 Mar 2026 01:23:37 -0400 Subject: [PATCH 05/19] Resolved PR comments Signed-off-by: TejaswiniIBM --- bin/zopen-build | 2 +- bin/zopen-pax2rpm | 9 +++++++-- cicd/publish.groovy | 32 +++++++++++++++++++++----------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/bin/zopen-build b/bin/zopen-build index 9b5fe3ae2..611f25e20 100755 --- a/bin/zopen-build +++ b/bin/zopen-build @@ -2317,7 +2317,7 @@ install() if [ -f "${paxFileName}" ]; then printHeader "Generating RPM from ${ZOPEN_INSTALL_DIR}" rpm_deps=$(echo "${ZOPEN_RUNTIME_DEPS}" | xargs -n1 | sort -u | xargs) - PATH="${ZOPEN_ROOTFS}/usr/local/bin:${PATH}" "${MYDIR}/zopen-pax2rpm" "${paxFileName}" --summary "${ZOPEN_NAME} package" --requires "${rpm_deps}" --build + PATH="${ZOPEN_ROOTFS}/usr/local/bin:${PATH}" "${MYDIR}/zopen-pax2rpm" "${paxFileName}" --summary "${ZOPEN_NAME} package" --requires "${rpm_deps}" --build --buildroot "${ZOPEN_ROOT}/rpmbuild" else printError "Pax file ${paxFileName} not found. Ensure --generate-pax is also used." fi diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index 0be49bfea..b4e3b0f01 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -388,10 +388,15 @@ setup_rpmbuild() { mkdir -p "$buildroot"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} if [ ! -f "$HOME/.rpmmacros" ]; then - cat > "$HOME/.rpmmacros" << EOF + if touch "$HOME/.rpmmacros" 2>/dev/null; then + cat > "$HOME/.rpmmacros" << EOF %_topdir $buildroot EOF - echo "Created ~/.rpmmacros with _topdir set to $buildroot" + echo "Created ~/.rpmmacros with _topdir set to $buildroot" + else + echo "Warning: could not create ~/.rpmmacros. This is normal in some CI environments." + echo "The script will use '$buildroot' by overriding it with --define \"_topdir $buildroot\"." + fi else # Check if _topdir is already defined and different local existing_topdir=$(grep "%_topdir" "$HOME/.rpmmacros" | awk '{print $2}') diff --git a/cicd/publish.groovy b/cicd/publish.groovy index 104343099..9343a16e6 100755 --- a/cicd/publish.groovy +++ b/cicd/publish.groovy @@ -27,10 +27,13 @@ if [ $(echo "$PAX" | grep -c /) -ne 1 ]; then exit 1 fi -RPM=`find rpmbuild/RPMS -type f -name "*.rpm"` -if [ $(echo "$RPM" | grep -c /) -ne 1 ]; then - echo "Error: Expected exactly 1 RPM file, found: $RPM" - exit 1 +RPM_FILES=($(find rpmbuild/RPMS -type f -name "*.rpm" 2>/dev/null)) +NUM_RPMS=${#RPM_FILES[@]} + +if [ $NUM_RPMS -gt 0 ]; then + echo "Found $NUM_RPMS RPM file(s): ${RPM_FILES[@]}" +else + echo "No RPM files found. Skipping RPM upload." fi METADATA=`find . -type f -path "*install/metadata.json"` @@ -72,7 +75,6 @@ PAX_BASENAME=$(basename "${PAX}") DIR_NAME=${PAX_BASENAME%%.pax.Z} DIR_NAME=$(echo "$DIR_NAME" | sed -e "s/\.202[0-9]*_[0-9]*\.zos/.zos/g" -e "s/\.zos//g") BUILD_ID=${BUILD_NUMBER} -RPM_BASENAME=$(basename "${RPM}") # Check for python dependencies @@ -215,13 +217,21 @@ else exit 1 fi -echo "Uploading the RPM artifacts into github" -github-release -v upload --user ${GITHUB_ORGANIZATION} --repo ${GITHUB_REPO} --tag "${TAG}" --name "${RPM_BASENAME}" --file "${RPM}" -if [ $? -eq 0 ]; then - echo "RPM Artifact uploaded successfully!" +if [ $NUM_RPMS -gt 0 ]; then + echo "Uploading the RPM artifacts into github" + for RPM in "${RPM_FILES[@]}"; do + RPM_BASENAME=$(basename "${RPM}") + echo "Uploading ${RPM_BASENAME}..." + github-release -v upload --user ${GITHUB_ORGANIZATION} --repo ${GITHUB_REPO} --tag "${TAG}" --name "${RPM_BASENAME}" --file "${RPM}" + if [ $? -eq 0 ]; then + echo "RPM Artifact ${RPM_BASENAME} uploaded successfully!" + else + echo "Failed to upload RPM artifact ${RPM_BASENAME}!" + exit 1 + fi + done else - echo "Failed to upload RPM artifact!" - exit 1 + echo "No RPM artifacts to upload." fi echo "Uploading metadata artifacts into github" From 9da910e9731ade2e4749e0e4adb4d3e6501d7a46 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Tue, 31 Mar 2026 07:12:24 -0400 Subject: [PATCH 06/19] Fixing the review comments Signed-off-by: TejaswiniIBM --- bin/zopen-build | 4 +++- bin/zopen-pax2rpm | 17 +++++++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/bin/zopen-build b/bin/zopen-build index 611f25e20..216ec2baa 100755 --- a/bin/zopen-build +++ b/bin/zopen-build @@ -2317,7 +2317,9 @@ install() if [ -f "${paxFileName}" ]; then printHeader "Generating RPM from ${ZOPEN_INSTALL_DIR}" rpm_deps=$(echo "${ZOPEN_RUNTIME_DEPS}" | xargs -n1 | sort -u | xargs) - PATH="${ZOPEN_ROOTFS}/usr/local/bin:${PATH}" "${MYDIR}/zopen-pax2rpm" "${paxFileName}" --summary "${ZOPEN_NAME} package" --requires "${rpm_deps}" --build --buildroot "${ZOPEN_ROOT}/rpmbuild" + if ! runAndLog "PATH=\"${ZOPEN_ROOTFS}/usr/local/bin:${PATH}\" \"${MYDIR}/zopen-pax2rpm\" \"${paxFileName}\" --summary \"${ZOPEN_NAME} package\" --requires \"${rpm_deps}\" --build --buildroot \"${ZOPEN_ROOT}/rpmbuild\""; then + printError "Could not generate RPM from \"${paxFileName}\"" + fi else printError "Pax file ${paxFileName} not found. Ensure --generate-pax is also used." fi diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index b4e3b0f01..e76de855e 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -229,7 +229,8 @@ generate_install_commands() { if [ -d usr ]; then echo "OS layout (usr/) detected, searching for actual content root..." # Find the deepest directory that contains bin, lib, pyz, bash, or go - FOUND_PATH=$(find . -type d \( -name bin -o -name lib -o -name pyz -o -name bash -o -name go \) -print -quit) + # We sort by the number of slashes to find the deepest one + FOUND_PATH=$(find . -type d \( -name bin -o -name lib -o -name pyz -o -name bash -o -name go \) -print | awk -F/ '{print NF, $0}' | sort -rn | head -n 1 | cut -d' ' -f2-) if [ -n "$FOUND_PATH" ]; then FOUND_PATH=${FOUND_PATH#./} @@ -424,7 +425,9 @@ check_required_tools() { # Check for rpmbuild if building or validating if [ "$BUILD_RPM" = true ] || [ "$VALIDATE_SPEC" = true ]; then - echo "DEBUG: Current PATH is $PATH" >&2 + if [ "$VERBOSE" = true ]; then + echo "DEBUG: Current PATH is $PATH" >&2 + fi if ! command -v rpmbuild &> /dev/null; then missing_tools+=("rpmbuild") fi @@ -754,38 +757,47 @@ main() { while [ $# -gt 0 ]; do case "$1" in --name) + [ -n "$2" ] || { echo "Error: --name requires a value" >&2; usage; } PKG_NAME="$2" shift 2 ;; --version) + [ -n "$2" ] || { echo "Error: --version requires a value" >&2; usage; } PKG_VERSION="$2" shift 2 ;; --release) + [ -n "$2" ] || { echo "Error: --release requires a value" >&2; usage; } RELEASE="$2" shift 2 ;; --license) + [ -n "$2" ] || { echo "Error: --license requires a value" >&2; usage; } LICENSE="$2" shift 2 ;; --summary) + [ -n "$2" ] || { echo "Error: --summary requires a value" >&2; usage; } SUMMARY="$2" shift 2 ;; --description) + [ -n "$2" ] || { echo "Error: --description requires a value" >&2; usage; } DESCRIPTION="$2" shift 2 ;; --url) + [ -n "$2" ] || { echo "Error: --url requires a value" >&2; usage; } URL="$2" shift 2 ;; --output) + [ -n "$2" ] || { echo "Error: --output requires a value" >&2; usage; } OUTPUT_FILE="$2" shift 2 ;; --requires) + [ -n "$2" ] || { echo "Error: --requires requires a value" >&2; usage; } REQUIRES="$2" shift 2 ;; @@ -794,6 +806,7 @@ main() { shift ;; --buildroot) + [ -n "$2" ] || { echo "Error: --buildroot requires a value" >&2; usage; } BUILDROOT="$2" shift 2 ;; From 962d0b3187cbd7529a39aae9e129bb3043559153 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Tue, 31 Mar 2026 07:53:51 -0400 Subject: [PATCH 07/19] Resolved the pr review comments v2 Signed-off-by: TejaswiniIBM --- bin/zopen-build | 6 +++++- bin/zopen-pax2rpm | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/bin/zopen-build b/bin/zopen-build index 216ec2baa..d03dfa877 100755 --- a/bin/zopen-build +++ b/bin/zopen-build @@ -2317,7 +2317,11 @@ install() if [ -f "${paxFileName}" ]; then printHeader "Generating RPM from ${ZOPEN_INSTALL_DIR}" rpm_deps=$(echo "${ZOPEN_RUNTIME_DEPS}" | xargs -n1 | sort -u | xargs) - if ! runAndLog "PATH=\"${ZOPEN_ROOTFS}/usr/local/bin:${PATH}\" \"${MYDIR}/zopen-pax2rpm\" \"${paxFileName}\" --summary \"${ZOPEN_NAME} package\" --requires \"${rpm_deps}\" --build --buildroot \"${ZOPEN_ROOT}/rpmbuild\""; then + cmd="PATH=\"${ZOPEN_ROOTFS}/usr/local/bin:${PATH}\" \"${MYDIR}/zopen-pax2rpm\" \"${paxFileName}\" --summary \"${ZOPEN_NAME} package\" --build --buildroot \"${ZOPEN_ROOT}/rpmbuild\"" + if [ -n "${rpm_deps}" ]; then + cmd="${cmd} --requires \"${rpm_deps}\"" + fi + if ! runAndLog "${cmd}"; then printError "Could not generate RPM from \"${paxFileName}\"" fi else diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index e76de855e..4c51a8e25 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -210,9 +210,9 @@ list_pax_contents() { echo "# Pax file contents (first 100 entries):" >&2 if [[ "$pax_file" == *.pax.Z ]]; then - pax -rvzf "$pax_file" 2>&1 | head -100 + pax -vzf "$pax_file" 2>&1 | head -100 else - pax -rvf "$pax_file" 2>&1 | head -100 + pax -vf "$pax_file" 2>&1 | head -100 fi } From 541aa2e91e7bfa03e414f3aae4bb6dfb225dd4e1 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Tue, 31 Mar 2026 11:07:39 -0400 Subject: [PATCH 08/19] resolved pr comments 3 Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 82 +++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index 4c51a8e25..89b889109 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -19,14 +19,31 @@ # --dry-run Show what would be done without doing it # -set -e +# +# All zopen-* scripts MUST start with this code to maintain consistency. +# +setupMyself() +{ + ME=$(basename "$0") + MYDIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 && pwd -P )" + INCDIR="${MYDIR}/../include" + if ! [ -d "${INCDIR}" ] && ! [ -f "${INCDIR}/common.sh" ]; then + echo "Internal Error. Unable to find common.sh file to source." >&2 + exit 8 + fi + . "${INCDIR}/common.sh" +} +setupMyself # Default values RELEASE="1" LICENSE="Proprietary" BUILD_ARCH=$(uname -m 2>/dev/null || echo "s390x") -PACKAGER_NAME="${USER}" -PACKAGER_EMAIL="${USER}@$(hostname)" + +# Get current user safely for packager info +CURRENT_USER="${USER:-$(whoami 2>/dev/null || echo "zopen-community")}" +PACKAGER_NAME="${CURRENT_USER}" +PACKAGER_EMAIL="${CURRENT_USER}@$(hostname 2>/dev/null || echo "localhost")" # Build flag BUILD_RPM=false @@ -37,6 +54,7 @@ VERBOSE=false # Function to display usage usage() { + local exit_code="${1:-1}" cat << EOF Usage: $0 [options] @@ -69,7 +87,7 @@ Example: --url "https://www.ibm.com" EOF - exit 1 + exit "$exit_code" } # Function to extract package name and version from filename @@ -80,17 +98,19 @@ parse_filename() { # Remove .pax.Z or .pax extension basename="${basename%.pax.Z}" basename="${basename%.pax}" + + # Handle .zos suffix commonly used in zopen packages + basename="${basename%.zos}" # Try to extract name and version - # Pattern 1: NAME-VERSION (e.g., package-1.0) - if [[ "$basename" =~ ^(.+)-([0-9]+\.[0-9]+.*)$ ]]; then + # Pattern 1: NAME-VERSION (e.g., package-1.0, curl-8.10.1.20241001_214340) + if [[ "$basename" =~ ^(.+)-([0-9].*)$ ]]; then PKG_NAME="${BASH_REMATCH[1]}" PKG_VERSION="${BASH_REMATCH[2]}" # Pattern 2: NAMEVERSION.suffix (e.g., HAMN110.runnable) elif [[ "$basename" =~ ^([A-Za-z]+)([0-9]+)\.(.+)$ ]]; then PKG_NAME="${BASH_REMATCH[1]}" PKG_VERSION="${BASH_REMATCH[2]}" - # Don't include suffix like "runnable" in version # Pattern 3: NAMEVERSION (e.g., HAMN110) elif [[ "$basename" =~ ^([A-Za-z]+)([0-9]+)$ ]]; then PKG_NAME="${BASH_REMATCH[1]}" @@ -107,23 +127,21 @@ parse_filename() { # Function to analyze pax contents and categorize files analyze_pax_contents() { local pax_file="$1" - local temp_dir=$(mktemp -d) + local temp_dir=$(mktempdir "pax2rpm") - echo "# Analyzing pax file structure..." >&2 + printInfo "# Analyzing pax file structure..." - # Extract pax listing (limit to first 1000 entries for performance) + # Extract pax listing (limit to first 5000 entries for performance/accuracy balance) local pax_listing if [[ "$pax_file" == *.pax.Z ]]; then - pax_listing=$(pax -vzf "$pax_file" 2>&1 | head -1000) + pax_listing=$(pax -vzf "$pax_file" 2>&1 | head -5000) else - pax_listing=$(pax -vf "$pax_file" 2>&1 | head -1000) + pax_listing=$(pax -vf "$pax_file" 2>&1 | head -5000) fi # Check if pax listing failed or is empty - # Only treat as error if pax command actually failed (not just listing files) if [ -z "$pax_listing" ]; then - echo "# Warning: Could not analyze pax contents, using default structure" >&2 - rm -rf "$temp_dir" + printWarning "Could not analyze pax contents, using default structure" return 0 fi @@ -207,7 +225,7 @@ analyze_pax_contents() { list_pax_contents() { local pax_file="$1" - echo "# Pax file contents (first 100 entries):" >&2 + printHeader "# Pax file contents (first 100 entries):" if [[ "$pax_file" == *.pax.Z ]]; then pax -vzf "$pax_file" 2>&1 | head -100 @@ -418,11 +436,6 @@ check_required_tools() { missing_tools+=("pax") fi - # Check for uncompress if dealing with .Z files - if [[ "$pax_file" == *.pax.Z ]] && ! command -v uncompress &> /dev/null; then - missing_tools+=("uncompress") - fi - # Check for rpmbuild if building or validating if [ "$BUILD_RPM" = true ] || [ "$VALIDATE_SPEC" = true ]; then if [ "$VERBOSE" = true ]; then @@ -435,10 +448,7 @@ check_required_tools() { if [ ${#missing_tools[@]} -gt 0 ]; then echo "" >&2 - echo "=========================================" >&2 - echo "ERROR: Missing Required Tools" >&2 - echo "=========================================" >&2 - echo "" >&2 + printError "Missing Required Tools" echo "The following tools are required but not found:" >&2 echo "" >&2 @@ -449,29 +459,19 @@ check_required_tools() { pax) echo " Purpose: Extract pax archives" >&2 echo " Install: Usually pre-installed on z/OS" >&2 - echo " On Linux: apt-get install pax (Debian/Ubuntu)" >&2 - echo " yum install pax (RHEL/CentOS)" >&2 - ;; - uncompress) - echo " Purpose: Decompress .Z files" >&2 - echo " Install: Usually part of ncompress package" >&2 - echo " On z/OS: May be in /usr/bin or /bin" >&2 - echo " On Linux: apt-get install ncompress (Debian/Ubuntu)" >&2 - echo " yum install ncompress (RHEL/CentOS)" >&2 + echo " On Linux: apt-get install pax (Debian/Ubuntu/RHEL/CentOS)" >&2 ;; rpmbuild) echo " Purpose: Build RPM packages and validate spec files" >&2 echo " Install: Part of rpm-build package" >&2 echo " On z/OS: zopen install rpm" >&2 - echo " On Linux: apt-get install rpm (Debian/Ubuntu)" >&2 - echo " yum install rpm-build (RHEL/CentOS)" >&2 + echo " On Linux: apt-get install rpm (Debian/Ubuntu/RHEL/CentOS)" >&2 ;; esac echo "" >&2 done - echo "Please install the missing tools and try again." >&2 - echo "" >&2 + printInfo "Please install the missing tools and try again." return 1 fi @@ -741,6 +741,10 @@ main() { usage fi + if [ "$1" == "--help" ]; then + usage 0 + fi + PAX_FILE="$1" shift @@ -824,7 +828,7 @@ main() { shift ;; --help) - usage + usage 0 ;; *) echo "Error: Unknown option: $1" >&2 From 057745dea4e61c988798bce8b97c1e73d5ae6f98 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Thu, 2 Apr 2026 00:01:48 -0400 Subject: [PATCH 09/19] versioning added Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index 89b889109..a97b9a3ea 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -66,6 +66,7 @@ Arguments: Options: --name Override package name (default: extracted from filename) --version Override version (default: extracted from filename) + --pkg-version Override version (alternative to --version) --release Override release number (default: 1) --license Specify license (default: Proprietary) --summary Package summary (required) @@ -79,6 +80,7 @@ Options: --dry-run Show what would be done without actually doing it --verbose Enable verbose debug output --help Display this help message + --version Display tool version Example: $0 /nfsmnts/bpidrivers/oefv1r1/os390/latest/HAMN110.runnable.pax.Z \\ @@ -745,6 +747,15 @@ main() { usage 0 fi + if [ "$1" == "--version" ]; then + if [ -x "${MYDIR}/zopen-version" ]; then + "${MYDIR}/zopen-version" "${ME}" + else + echo "${ME} version $(cat "${INCDIR}/zopen_version" 2>/dev/null || echo "unknown")" + fi + exit 0 + fi + PAX_FILE="$1" shift @@ -766,7 +777,19 @@ main() { shift 2 ;; --version) - [ -n "$2" ] || { echo "Error: --version requires a value" >&2; usage; } + if [ -z "$2" ] || [[ "$2" == --* ]]; then + if [ -x "${MYDIR}/zopen-version" ]; then + "${MYDIR}/zopen-version" "${ME}" + else + echo "${ME} version $(cat "${INCDIR}/zopen_version" 2>/dev/null || echo "unknown")" + fi + exit 0 + fi + PKG_VERSION="$2" + shift 2 + ;; + --pkg-version) + [ -n "$2" ] || { echo "Error: --pkg-version requires a value" >&2; usage; } PKG_VERSION="$2" shift 2 ;; From edb48242a2b37f28ff17aabed929d269c6c5b2e5 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Mon, 6 Apr 2026 09:49:00 -0400 Subject: [PATCH 10/19] refactored the script to matchother zopen tools Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 297 +++++++++++++++++++++------------------------- 1 file changed, 132 insertions(+), 165 deletions(-) diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index a97b9a3ea..c3aeb6b45 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -1,22 +1,6 @@ -#!/bin/bash +#!/bin/sh # -# pax2rpm.sh - Generate RPM spec file from z/OS pax archive -# -# Usage: pax2rpm.sh [options] -# -# Options: -# --name Override package name -# --version Override version -# --release Override release number (default: 1) -# --license Specify license (default: Proprietary) -# --summary Package summary -# --description Package description -# --url Project URL -# --output Output spec file (default: .spec) -# --build Build the RPM after generating spec file -# --buildroot RPM build root directory (default: ~/rpmbuild) -# --validate Validate spec file after generation -# --dry-run Show what would be done without doing it +# RPM generation utility for zopen community - https://github.com/zopencommunity # # @@ -24,8 +8,8 @@ # setupMyself() { - ME=$(basename "$0") - MYDIR="$( cd "$(dirname "$0")" >/dev/null 2>&1 && pwd -P )" + ME=$(basename $0) + MYDIR="$(cd "$(dirname "$0")" > /dev/null 2>&1 && pwd -P)" INCDIR="${MYDIR}/../include" if ! [ -d "${INCDIR}" ] && ! [ -f "${INCDIR}/common.sh" ]; then echo "Internal Error. Unable to find common.sh file to source." >&2 @@ -54,7 +38,7 @@ VERBOSE=false # Function to display usage usage() { - local exit_code="${1:-1}" +exit_code="${1:-1}" cat << EOF Usage: $0 [options] @@ -94,8 +78,8 @@ EOF # Function to extract package name and version from filename parse_filename() { - local filename="$1" - local basename=$(basename "$filename") + filename="$1" + basename=$(basename "$filename") # Remove .pax.Z or .pax extension basename="${basename%.pax.Z}" @@ -104,19 +88,19 @@ parse_filename() { # Handle .zos suffix commonly used in zopen packages basename="${basename%.zos}" - # Try to extract name and version + # Try to extract name and version using sed since BASH_REMATCH is a bashism # Pattern 1: NAME-VERSION (e.g., package-1.0, curl-8.10.1.20241001_214340) - if [[ "$basename" =~ ^(.+)-([0-9].*)$ ]]; then - PKG_NAME="${BASH_REMATCH[1]}" - PKG_VERSION="${BASH_REMATCH[2]}" + if echo "$basename" | grep -qE '^(.+)-([0-9].*)$'; then + PKG_NAME=$(echo "$basename" | sed -E 's/^(.+)-([0-9].*)$/\1/') + PKG_VERSION=$(echo "$basename" | sed -E 's/^(.+)-([0-9].*)$/\2/') # Pattern 2: NAMEVERSION.suffix (e.g., HAMN110.runnable) - elif [[ "$basename" =~ ^([A-Za-z]+)([0-9]+)\.(.+)$ ]]; then - PKG_NAME="${BASH_REMATCH[1]}" - PKG_VERSION="${BASH_REMATCH[2]}" + elif echo "$basename" | grep -qE '^([A-Za-z]+)([0-9]+)\.(.+)$'; then + PKG_NAME=$(echo "$basename" | sed -E 's/^([A-Za-z]+)([0-9]+)\.(.+)$/\1/') + PKG_VERSION=$(echo "$basename" | sed -E 's/^([A-Za-z]+)([0-9]+)\.(.+)$/\2/') # Pattern 3: NAMEVERSION (e.g., HAMN110) - elif [[ "$basename" =~ ^([A-Za-z]+)([0-9]+)$ ]]; then - PKG_NAME="${BASH_REMATCH[1]}" - PKG_VERSION="${BASH_REMATCH[2]}" + elif echo "$basename" | grep -qE '^([A-Za-z]+)([0-9]+)$'; then + PKG_NAME=$(echo "$basename" | sed -E 's/^([A-Za-z]+)([0-9]+)$/\1/') + PKG_VERSION=$(echo "$basename" | sed -E 's/^([A-Za-z]+)([0-9]+)$/\2/') else PKG_NAME="$basename" PKG_VERSION="1.0" @@ -128,18 +112,20 @@ parse_filename() { # Function to analyze pax contents and categorize files analyze_pax_contents() { - local pax_file="$1" - local temp_dir=$(mktempdir "pax2rpm") + pax_file="$1" + temp_dir=$(mktempdir "pax2rpm") printInfo "# Analyzing pax file structure..." # Extract pax listing (limit to first 5000 entries for performance/accuracy balance) - local pax_listing - if [[ "$pax_file" == *.pax.Z ]]; then - pax_listing=$(pax -vzf "$pax_file" 2>&1 | head -5000) - else - pax_listing=$(pax -vf "$pax_file" 2>&1 | head -5000) - fi + case "$pax_file" in + *.pax.Z) + pax_listing=$(pax -vzf "$pax_file" 2>&1 | head -5000) + ;; + *) + pax_listing=$(pax -vf "$pax_file" 2>&1 | head -5000) + ;; + esac # Check if pax listing failed or is empty if [ -z "$pax_listing" ]; then @@ -147,100 +133,74 @@ analyze_pax_contents() { return 0 fi - # Initialize arrays for different file types - declare -g -A FILE_CATEGORIES - FILE_CATEGORIES[bins]="" - FILE_CATEGORIES[libs]="" - FILE_CATEGORIES[includes]="" - FILE_CATEGORIES[configs]="" - FILE_CATEGORIES[docs]="" - FILE_CATEGORIES[data]="" - FILE_CATEGORIES[scripts]="" - FILE_CATEGORIES[other]="" - - declare -g -A DIR_STRUCTURE - DIR_STRUCTURE[has_bin]=false - DIR_STRUCTURE[has_lib]=false - DIR_STRUCTURE[has_include]=false - DIR_STRUCTURE[has_etc]=false - DIR_STRUCTURE[has_share]=false - DIR_STRUCTURE[has_doc]=false - DIR_STRUCTURE[has_man]=false + # Initialize variables for different file types (removed unused FILE_CATEGORIES) + has_bin=false + has_lib=false + has_include=false + has_etc=false + has_share=false + has_doc=false + has_man=false + has_scripts=false + is_os_layout=false # Analyze each file - while IFS= read -r line; do + echo "$pax_listing" | while IFS= read -r line; do # Skip empty lines and headers - [[ -z "$line" ]] && continue + [ -z "$line" ] && continue # Extract filename (last field in pax -v output) - local filename=$(echo "$line" | awk '{print $NF}') - [[ -z "$filename" ]] && continue + filename=$(echo "$line" | awk '{print $NF}') + [ -z "$filename" ] && continue # Detect directory structure (handles top-level dir if present) # Matches: bin/, ./bin/, package-ver/bin/, ./package-ver/bin/ # Does NOT match: usr/lpp/IBM/bin/ (too deep) - if [[ "$filename" =~ ^(\./)?([^/]+/)?bin/ ]]; then - DIR_STRUCTURE[has_bin]=true - elif [[ "$filename" =~ ^(\./)?([^/]+/)?lib/ ]]; then - DIR_STRUCTURE[has_lib]=true - elif [[ "$filename" =~ ^(\./)?([^/]+/)?include/ ]]; then - DIR_STRUCTURE[has_include]=true - elif [[ "$filename" =~ ^(\./)?([^/]+/)?etc/ ]]; then - DIR_STRUCTURE[has_etc]=true - elif [[ "$filename" =~ ^(\./)?([^/]+/)?share/ ]]; then - DIR_STRUCTURE[has_share]=true - elif [[ "$filename" =~ ^(\./)?([^/]+/)?(doc|docs)/ ]]; then - DIR_STRUCTURE[has_doc]=true - elif [[ "$filename" =~ ^(\./)?([^/]+/)?man/ ]]; then - DIR_STRUCTURE[has_man]=true - elif [[ "$filename" =~ ^(\./)?([^/]+/)?scripts/ ]]; then - DIR_STRUCTURE[has_scripts]=true - elif [[ "$filename" =~ ^(\./)?usr/lpp/ ]]; then - DIR_STRUCTURE[is_os_layout]=true - fi - - # Categorize by file type - if [[ "$filename" =~ \.(so|so\.[0-9]+|a|dylib)$ ]]; then - FILE_CATEGORIES[libs]+="$filename"$'\n' - elif [[ "$filename" =~ \.(h|hpp|hxx)$ ]]; then - FILE_CATEGORIES[includes]+="$filename"$'\n' - elif [[ "$filename" =~ \.(conf|cfg|ini|yaml|yml|json|xml|properties)$ ]]; then - FILE_CATEGORIES[configs]+="$filename"$'\n' - elif [[ "$filename" =~ \.(md|txt|pdf|html|htm|README|LICENSE|COPYING|CHANGELOG|AUTHORS)$ ]] || [[ "$filename" =~ (README|LICENSE|COPYING|CHANGELOG|AUTHORS|INSTALL|NEWS)$ ]]; then - FILE_CATEGORIES[docs]+="$filename"$'\n' - elif [[ "$filename" =~ \.(sh|bash|ksh|pl|py|rb)$ ]] && [[ "$line" =~ ^-r.x ]]; then - FILE_CATEGORIES[scripts]+="$filename"$'\n' - elif [[ "$line" =~ ^-r.x ]] && [[ ! "$filename" =~ \. ]]; then - # Executable without extension (likely binary) - FILE_CATEGORIES[bins]+="$filename"$'\n' - elif [[ "$filename" =~ \.(dat|data|db|sqlite)$ ]]; then - FILE_CATEGORIES[data]+="$filename"$'\n' - else - FILE_CATEGORIES[other]+="$filename"$'\n' - fi - done <<< "$pax_listing" + case "$filename" in + bin/* | */bin/*) + has_bin=true ;; + lib/* | */lib/*) + has_lib=true ;; + include/* | */include/*) + has_include=true ;; + etc/* | */etc/*) + has_etc=true ;; + share/* | */share/*) + has_share=true ;; + doc/* | docs/* | */doc/* | */docs/*) + has_doc=true ;; + man/* | */man/*) + has_man=true ;; + scripts/* | */scripts/*) + has_scripts=true ;; + usr/lpp/* | */usr/lpp/*) + is_os_layout=true ;; + esac + done rm -rf "$temp_dir" } # Function to list contents of pax file list_pax_contents() { - local pax_file="$1" - + pax_file="$1" + printHeader "# Pax file contents (first 100 entries):" - - if [[ "$pax_file" == *.pax.Z ]]; then - pax -vzf "$pax_file" 2>&1 | head -100 - else - pax -vf "$pax_file" 2>&1 | head -100 - fi -} + case "$pax_file" in + *.pax.Z) + pax -vzf "$pax_file" 2>&1 | head -100 + ;; + *) + pax -vf "$pax_file" 2>&1 | head -100 + ;; + esac +} # Function to generate install commands based on analyzed contents # Function to generate install commands based on analyzed contents # Function to generate install commands based on analyzed contents generate_install_commands() { - local pax_file="$1" +pax_file="$1" cat << 'EOF' # Install files based on detected structure @@ -311,9 +271,9 @@ EOF # Function to generate spec file generate_spec() { - local output_file="$1" - local source_file="$2" - local date=$(date "+%a %b %d %Y") +output_file="$1" +source_file="$2" +date=$(date "+%a %b %d %Y") # Analyze the pax contents first (populate global DIR_STRUCTURE) analyze_pax_contents "$source_file" @@ -351,17 +311,20 @@ cd %{name}-%{version} EOF - if [[ "$source_file" == *.pax.Z ]]; then - cat >> "$output_file" << EOF + case "$source_file" in + *.pax.Z) + cat >> "$output_file" << EOF # Extract compressed pax archive pax -rzf %{_sourcedir}/$(basename "$source_file") EOF - else - cat >> "$output_file" << EOF + ;; + *) + cat >> "$output_file" << EOF # Extract pax archive pax -rf %{_sourcedir}/$(basename "$source_file") EOF - fi + ;; + esac cat >> "$output_file" << EOF @@ -388,21 +351,21 @@ EOF echo "RPM spec file generated: $output_file" echo "" echo "Detected structure:" - [[ "${DIR_STRUCTURE[has_bin]}" == "true" ]] && echo " ✓ Binaries in bin/" - [[ "${DIR_STRUCTURE[has_lib]}" == "true" ]] && echo " ✓ Libraries in lib/" - [[ "${DIR_STRUCTURE[has_include]}" == "true" ]] && echo " ✓ Headers in include/" - [[ "${DIR_STRUCTURE[has_etc]}" == "true" ]] && echo " ✓ Configuration files in etc/" - [[ "${DIR_STRUCTURE[has_share]}" == "true" ]] && echo " ✓ Data files in share/" - [[ "${DIR_STRUCTURE[has_doc]}" == "true" ]] && echo " ✓ Documentation in doc/" - [[ "${DIR_STRUCTURE[has_man]}" == "true" ]] && echo " ✓ Man pages in man/" - [[ "${DIR_STRUCTURE[has_scripts]}" == "true" ]] && echo " ✓ Scripts in scripts/" + [ "$has_bin" = "true" ] && echo " ✓ Binaries in bin/" + [ "$has_lib" = "true" ] && echo " ✓ Libraries in lib/" + [ "$has_include" = "true" ] && echo " ✓ Headers in include/" + [ "$has_etc" = "true" ] && echo " ✓ Configuration files in etc/" + [ "$has_share" = "true" ] && echo " ✓ Data files in share/" + [ "$has_doc" = "true" ] && echo " ✓ Documentation in doc/" + [ "$has_man" = "true" ] && echo " ✓ Man pages in man/" + [ "$has_scripts" = "true" ] && echo " ✓ Scripts in scripts/" return 0 } # Function to setup rpmbuild directories setup_rpmbuild() { - local buildroot="$1" +buildroot="$1" echo "Setting up RPM build directories in: $buildroot" @@ -420,7 +383,7 @@ EOF fi else # Check if _topdir is already defined and different - local existing_topdir=$(grep "%_topdir" "$HOME/.rpmmacros" | awk '{print $2}') +existing_topdir=$(grep "%_topdir" "$HOME/.rpmmacros" | awk '{print $2}') if [ "$existing_topdir" != "$buildroot" ]; then echo "Warning: ~/.rpmmacros exists and has _topdir set to '$existing_topdir'." echo "The script will use '$buildroot' by overriding it with --define \"_topdir $buildroot\"." @@ -430,8 +393,8 @@ EOF # Function to check for required tools check_required_tools() { - local missing_tools=() - local pax_file="$1" +missing_tools=() +pax_file="$1" # Check for pax if ! command -v pax &> /dev/null; then @@ -478,7 +441,7 @@ check_required_tools() { fi # Optional tools check (informational only) - local optional_missing=() +optional_missing=() if [ "$VALIDATE_SPEC" = true ] && ! command -v rpmlint &> /dev/null; then optional_missing+=("rpmlint") @@ -505,8 +468,8 @@ check_required_tools() { # Function to validate spec file syntax validate_spec_syntax() { - local spec_file="$1" - local buildroot="$2" +spec_file="$1" +buildroot="$2" echo "" echo "==========================================" @@ -523,7 +486,7 @@ validate_spec_syntax() { # Try to use rpmbuild -bp (prep stage) to validate syntax # This will check syntax without actually building # Use --nodeps to check syntax/prep logic without requiring local RPM DB to have all deps - local temp_output=$(mktemp) +temp_output=$(mktemp) if rpmbuild --define "_topdir $buildroot" -bp --nodeps "$spec_file" &> "$temp_output"; then echo "✓ Spec file syntax is valid" rm -f "$temp_output" @@ -545,7 +508,7 @@ validate_spec_syntax() { # Function to run rpmlint on spec file run_rpmlint() { - local spec_file="$1" +spec_file="$1" echo "" echo "==========================================" @@ -561,16 +524,16 @@ run_rpmlint() { fi # Run rpmlint - local rpmlint_output +rpmlint_output rpmlint_output=$(rpmlint "$spec_file" 2>&1 || true) - local rpmlint_exit=$? +rpmlint_exit=$? echo "$rpmlint_output" echo "" # Count errors and warnings - local errors=$(echo "$rpmlint_output" | grep -c "E:" || true) - local warnings=$(echo "$rpmlint_output" | grep -c "W:" || true) +errors=$(echo "$rpmlint_output" | grep -c "E:" || true) +warnings=$(echo "$rpmlint_output" | grep -c "W:" || true) if [ $errors -gt 0 ]; then echo "⚠ Found $errors error(s) and $warnings warning(s)" @@ -588,10 +551,10 @@ run_rpmlint() { # Function to validate generated spec file validate_spec_file() { - local spec_file="$1" - local pax_file="$2" - local buildroot="$3" - local validation_failed=false +spec_file="$1" +pax_file="$2" +buildroot="$3" +validation_failed=false echo "" echo "==========================================" @@ -602,7 +565,7 @@ validate_spec_file() { setup_rpmbuild "$buildroot" # Copy pax file to SOURCES if not already there - local source_name=$(basename "$pax_file") +source_name=$(basename "$pax_file") if [ ! -f "$buildroot/SOURCES/$source_name" ] || [ "$pax_file" -nt "$buildroot/SOURCES/$source_name" ]; then echo "Copying source file to $buildroot/SOURCES/$source_name for validation" cp "$pax_file" "$buildroot/SOURCES/" @@ -640,7 +603,7 @@ validate_spec_file() { # Function to perform dry run perform_dry_run() { - local pax_file="$1" +pax_file="$1" echo "" echo "==========================================" @@ -675,9 +638,9 @@ perform_dry_run() { # Function to build RPM build_rpm() { - local spec_file="$1" - local pax_file="$2" - local buildroot="$3" +spec_file="$1" +pax_file="$2" +buildroot="$3" echo "" echo "==========================================" @@ -689,7 +652,7 @@ build_rpm() { setup_rpmbuild "$buildroot" # Copy pax file to SOURCES - local source_name=$(basename "$pax_file") +source_name=$(basename "$pax_file") echo "Copying source file to $buildroot/SOURCES/$source_name" cp "$pax_file" "$buildroot/SOURCES/" @@ -743,11 +706,11 @@ main() { usage fi - if [ "$1" == "--help" ]; then + if [ "$1" = "--help" ]; then usage 0 fi - if [ "$1" == "--version" ]; then + if [ "$1" = "--version" ]; then if [ -x "${MYDIR}/zopen-version" ]; then "${MYDIR}/zopen-version" "${ME}" else @@ -777,16 +740,20 @@ main() { shift 2 ;; --version) - if [ -z "$2" ] || [[ "$2" == --* ]]; then - if [ -x "${MYDIR}/zopen-version" ]; then - "${MYDIR}/zopen-version" "${ME}" - else - echo "${ME} version $(cat "${INCDIR}/zopen_version" 2>/dev/null || echo "unknown")" - fi - exit 0 - fi - PKG_VERSION="$2" - shift 2 + case "$2" in + "" | --*) + if [ -x "${MYDIR}/zopen-version" ]; then + "${MYDIR}/zopen-version" "${ME}" + else + echo "${ME} version $(cat "${INCDIR}/zopen_version" 2>/dev/null || echo "unknown")" + fi + exit 0 + ;; + *) + PKG_VERSION="$2" + shift 2 + ;; + esac ;; --pkg-version) [ -n "$2" ] || { echo "Error: --pkg-version requires a value" >&2; usage; } From 2fa9f22b14954ad021acb86bf3ef8323db4c4ec9 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Mon, 6 Apr 2026 10:28:30 -0400 Subject: [PATCH 11/19] Fixed the build errors Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 90 +++++++++++++++++++++++------------------------ 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index c3aeb6b45..fad80a50b 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -369,7 +369,7 @@ buildroot="$1" echo "Setting up RPM build directories in: $buildroot" - mkdir -p "$buildroot"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} + mkdir -p "$buildroot/BUILD" "$buildroot/BUILDROOT" "$buildroot/RPMS" "$buildroot/SOURCES" "$buildroot/SPECS" "$buildroot/SRPMS" if [ ! -f "$HOME/.rpmmacros" ]; then if touch "$HOME/.rpmmacros" 2>/dev/null; then @@ -393,12 +393,12 @@ existing_topdir=$(grep "%_topdir" "$HOME/.rpmmacros" | awk '{print $2}') # Function to check for required tools check_required_tools() { -missing_tools=() -pax_file="$1" + missing_tools="" + pax_file="$1" # Check for pax - if ! command -v pax &> /dev/null; then - missing_tools+=("pax") + if ! command -v pax > /dev/null 2>&1; then + missing_tools="${missing_tools} pax" fi # Check for rpmbuild if building or validating @@ -406,18 +406,18 @@ pax_file="$1" if [ "$VERBOSE" = true ]; then echo "DEBUG: Current PATH is $PATH" >&2 fi - if ! command -v rpmbuild &> /dev/null; then - missing_tools+=("rpmbuild") + if ! command -v rpmbuild > /dev/null 2>&1; then + missing_tools="${missing_tools} rpmbuild" fi fi - if [ ${#missing_tools[@]} -gt 0 ]; then + if [ -n "$missing_tools" ]; then echo "" >&2 printError "Missing Required Tools" echo "The following tools are required but not found:" >&2 echo "" >&2 - for tool in "${missing_tools[@]}"; do + for tool in $missing_tools; do echo " ✗ $tool" >&2 case "$tool" in @@ -441,16 +441,16 @@ pax_file="$1" fi # Optional tools check (informational only) -optional_missing=() + optional_missing="" - if [ "$VALIDATE_SPEC" = true ] && ! command -v rpmlint &> /dev/null; then - optional_missing+=("rpmlint") + if [ "$VALIDATE_SPEC" = true ] && ! command -v rpmlint > /dev/null 2>&1; then + optional_missing="${optional_missing} rpmlint" fi - if [ ${#optional_missing[@]} -gt 0 ]; then + if [ -n "$optional_missing" ]; then echo "" >&2 echo "Note: Optional tools not found (validation will be limited):" >&2 - for tool in "${optional_missing[@]}"; do + for tool in $optional_missing; do echo " - $tool (for enhanced spec file quality checks)" >&2 case "$tool" in rpmlint) @@ -468,26 +468,26 @@ optional_missing=() # Function to validate spec file syntax validate_spec_syntax() { -spec_file="$1" -buildroot="$2" - + spec_file="$1" + buildroot="$2" + echo "" echo "==========================================" echo "Validating spec file syntax..." echo "==========================================" echo "" - + # Check if rpmbuild is available - if ! command -v rpmbuild &> /dev/null; then + if ! command -v rpmbuild > /dev/null 2>&1; then echo "Warning: rpmbuild not found, skipping syntax validation" >&2 return 0 fi - + # Try to use rpmbuild -bp (prep stage) to validate syntax # This will check syntax without actually building # Use --nodeps to check syntax/prep logic without requiring local RPM DB to have all deps -temp_output=$(mktemp) - if rpmbuild --define "_topdir $buildroot" -bp --nodeps "$spec_file" &> "$temp_output"; then + temp_output=$(mktemp) + if rpmbuild --define "_topdir $buildroot" -bp --nodeps "$spec_file" > "$temp_output" 2>&1; then echo "✓ Spec file syntax is valid" rm -f "$temp_output" return 0 @@ -508,33 +508,32 @@ temp_output=$(mktemp) # Function to run rpmlint on spec file run_rpmlint() { -spec_file="$1" - + spec_file="$1" + echo "" echo "==========================================" echo "Running rpmlint checks..." echo "==========================================" echo "" - + # Check if rpmlint is available - if ! command -v rpmlint &> /dev/null; then + if ! command -v rpmlint > /dev/null 2>&1; then echo "Note: rpmlint not found, skipping quality checks" echo "Install rpmlint for additional spec file validation" return 0 fi - + # Run rpmlint -rpmlint_output rpmlint_output=$(rpmlint "$spec_file" 2>&1 || true) -rpmlint_exit=$? + rpmlint_exit=$? echo "$rpmlint_output" echo "" - + # Count errors and warnings -errors=$(echo "$rpmlint_output" | grep -c "E:" || true) -warnings=$(echo "$rpmlint_output" | grep -c "W:" || true) - + errors=$(echo "$rpmlint_output" | grep -c "E:" || true) + warnings=$(echo "$rpmlint_output" | grep -c "W:" || true) + if [ $errors -gt 0 ]; then echo "⚠ Found $errors error(s) and $warnings warning(s)" echo "Please review and fix errors before building" @@ -706,18 +705,19 @@ main() { usage fi - if [ "$1" = "--help" ]; then - usage 0 - fi - - if [ "$1" = "--version" ]; then - if [ -x "${MYDIR}/zopen-version" ]; then - "${MYDIR}/zopen-version" "${ME}" - else - echo "${ME} version $(cat "${INCDIR}/zopen_version" 2>/dev/null || echo "unknown")" - fi - exit 0 - fi + case "$1" in + --help) + usage 0 + ;; + --version) + if [ -x "${MYDIR}/zopen-version" ]; then + "${MYDIR}/zopen-version" "${ME}" + else + echo "${ME} version $(cat "${INCDIR}/zopen_version" 2>/dev/null || echo "unknown")" + fi + exit 0 + ;; + esac PAX_FILE="$1" shift From 703956e53af66cf9a039f489ee5464fc3485158c Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Mon, 6 Apr 2026 10:57:03 -0400 Subject: [PATCH 12/19] Fixed the build issues Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index fad80a50b..2668391fe 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -1,6 +1,6 @@ #!/bin/sh # -# RPM generation utility for zopen community - https://github.com/zopencommunity +# RPM generation from the PAX generated file utility for zopen # # From 5eb36a2c80cf7f8336f07116233a74ef474fccad Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Tue, 7 Apr 2026 00:02:14 -0400 Subject: [PATCH 13/19] Fixed the review comments Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index 2668391fe..03f35b0a8 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -145,7 +145,7 @@ analyze_pax_contents() { is_os_layout=false # Analyze each file - echo "$pax_listing" | while IFS= read -r line; do + while IFS= read -r line; do # Skip empty lines and headers [ -z "$line" ] && continue @@ -176,7 +176,9 @@ analyze_pax_contents() { usr/lpp/* | */usr/lpp/*) is_os_layout=true ;; esac - done + done << EOF +$pax_listing +EOF rm -rf "$temp_dir" } From c04a5146557d760d56ba9deb04d9abef4f87418b Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Tue, 7 Apr 2026 00:09:59 -0400 Subject: [PATCH 14/19] Updated the build command to test rpm Signed-off-by: TejaswiniIBM --- cicd/build.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cicd/build.groovy b/cicd/build.groovy index 75cfdcdb3..0ae38d20b 100755 --- a/cicd/build.groovy +++ b/cicd/build.groovy @@ -55,7 +55,7 @@ fi git clone -b "${PORT_BRANCH}" "${PORT_GITHUB_REPO}" ${PORT_NAME} && cd ${PORT_NAME} # Always run tests and update dependencies and generate pax file -zopen build -v -b release -u -gp -sp --no-set-active $extraOptions +zopen build -v -b release -u -gr -sp --no-set-active $extraOptions # Clean the cache after build is complete zopen clean -c -v From eacfd784372ceacd608e69f4650942fe8e920583 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Tue, 7 Apr 2026 04:32:05 -0400 Subject: [PATCH 15/19] Changed the portbuild to portbuild test for testing the command Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 4 ++-- cicd/pipeline.jenkins | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index 03f35b0a8..2324880f8 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -385,8 +385,8 @@ EOF fi else # Check if _topdir is already defined and different -existing_topdir=$(grep "%_topdir" "$HOME/.rpmmacros" | awk '{print $2}') - if [ "$existing_topdir" != "$buildroot" ]; then + existing_topdir=$(grep "%_topdir" "$HOME/.rpmmacros" | awk '{print $2}') + if [ -n "$existing_topdir" ] && [ "$existing_topdir" != "$buildroot" ]; then echo "Warning: ~/.rpmmacros exists and has _topdir set to '$existing_topdir'." echo "The script will use '$buildroot' by overriding it with --define \"_topdir $buildroot\"." fi diff --git a/cicd/pipeline.jenkins b/cicd/pipeline.jenkins index 9dbe318bf..7c0ec0f6c 100644 --- a/cicd/pipeline.jenkins +++ b/cicd/pipeline.jenkins @@ -27,7 +27,7 @@ node('linux') { stage('Build and Test') { // Build Job : https://cicd.zopen.community/view/Framework/job/Port-Build/ - buildResult = build job: 'Port-Build', propagate: false, parameters: [string(name: 'PORT_GITHUB_REPO', value: "${repo}"), + buildResult = build job: 'Port-Build-Test', propagate: false, parameters: [string(name: 'PORT_GITHUB_REPO', value: "${repo}"), string(name: 'PORT_BRANCH', value: "${branch}"), string(name: 'node', value: "${node_label}"), booleanParam(name: 'FORCE_CLANG', value: params.FORCE_CLANG), @@ -39,7 +39,7 @@ node('linux') } copyArtifacts filter: '**/test.status', fingerprintArtifacts: true, - projectName: 'Port-Build', + projectName: 'Port-Build-Test', selector: specific(buildResult.number.toString()), optional: true def testStatusFile = sh(script: 'find . -name test.status | head -n 1', returnStdout: true).trim() From eb996718b227ab1941e2030973a6db797545b98a Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Wed, 8 Apr 2026 05:35:46 -0400 Subject: [PATCH 16/19] Fixed the test build error No compatible architectures found for build Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index 2324880f8..fc020352f 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -22,7 +22,15 @@ setupMyself # Default values RELEASE="1" LICENSE="Proprietary" -BUILD_ARCH=$(uname -m 2>/dev/null || echo "s390x") +BUILD_ARCH=$(rpm --eval "%{_target_cpu}" 2>/dev/null || uname -m 2>/dev/null || echo "s390x") + +# On z/OS, uname -m returns numeric machine types (e.g., 8561, 3931). +# RPM expects 's390x' for the build architecture. +if [ "$(uname -s)" = "OS/390" ]; then + case "${BUILD_ARCH}" in + [0-9]*) BUILD_ARCH="s390x" ;; + esac +fi # Get current user safely for packager info CURRENT_USER="${USER:-$(whoami 2>/dev/null || echo "zopen-community")}" From 36ee648b93df00f99b6afdadf83000a5561e87ff Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Wed, 8 Apr 2026 06:11:25 -0400 Subject: [PATCH 17/19] Fixed the test build error - RPM build errors: Signed-off-by: TejaswiniIBM --- bin/zopen-pax2rpm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/bin/zopen-pax2rpm b/bin/zopen-pax2rpm index fc020352f..d4a016057 100755 --- a/bin/zopen-pax2rpm +++ b/bin/zopen-pax2rpm @@ -309,6 +309,17 @@ Source0: $(basename "$source_file") BuildArch: ${BUILD_ARCH} ${REQUIRES:+Requires: $REQUIRES} +EOF + + if [ "$(uname -s)" = "OS/390" ]; then + cat >> "$output_file" << EOF +# On z/OS, disable broken post-install processing (stripping, etc.) +%define __os_install_post %{nil} + +EOF + fi + + cat >> "$output_file" << EOF %description ${DESCRIPTION} From e7315bb9d6fe7c08e45cd96995eaf13d1b19fdd6 Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Wed, 8 Apr 2026 07:33:05 -0400 Subject: [PATCH 18/19] Reverted the Port-Build-Test to Port-Build Signed-off-by: TejaswiniIBM --- cicd/pipeline.jenkins | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cicd/pipeline.jenkins b/cicd/pipeline.jenkins index 7c0ec0f6c..9dbe318bf 100644 --- a/cicd/pipeline.jenkins +++ b/cicd/pipeline.jenkins @@ -27,7 +27,7 @@ node('linux') { stage('Build and Test') { // Build Job : https://cicd.zopen.community/view/Framework/job/Port-Build/ - buildResult = build job: 'Port-Build-Test', propagate: false, parameters: [string(name: 'PORT_GITHUB_REPO', value: "${repo}"), + buildResult = build job: 'Port-Build', propagate: false, parameters: [string(name: 'PORT_GITHUB_REPO', value: "${repo}"), string(name: 'PORT_BRANCH', value: "${branch}"), string(name: 'node', value: "${node_label}"), booleanParam(name: 'FORCE_CLANG', value: params.FORCE_CLANG), @@ -39,7 +39,7 @@ node('linux') } copyArtifacts filter: '**/test.status', fingerprintArtifacts: true, - projectName: 'Port-Build-Test', + projectName: 'Port-Build', selector: specific(buildResult.number.toString()), optional: true def testStatusFile = sh(script: 'find . -name test.status | head -n 1', returnStdout: true).trim() From a5b764f6d3af1fe62b83b2364ef363c50bfa1c2f Mon Sep 17 00:00:00 2001 From: TejaswiniIBM Date: Mon, 20 Apr 2026 10:06:45 +0530 Subject: [PATCH 19/19] Update publish.groovy Added command to push the RPM to pulp repo Signed-off-by: TejaswiniIBM --- cicd/publish.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/cicd/publish.groovy b/cicd/publish.groovy index 9343a16e6..73811baa6 100755 --- a/cicd/publish.groovy +++ b/cicd/publish.groovy @@ -223,6 +223,7 @@ if [ $NUM_RPMS -gt 0 ]; then RPM_BASENAME=$(basename "${RPM}") echo "Uploading ${RPM_BASENAME}..." github-release -v upload --user ${GITHUB_ORGANIZATION} --repo ${GITHUB_REPO} --tag "${TAG}" --name "${RPM_BASENAME}" --file "${RPM}" + pulp artifact upload --file "${RPM}" if [ $? -eq 0 ]; then echo "RPM Artifact ${RPM_BASENAME} uploaded successfully!" else