Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
f137546
feat(packaging): add RPM packaging support for RHEL/Rocky/OpenEuler
shiptux Feb 25, 2026
973b257
refactor(packaging): unify RPM spec and Dockerfile with parameterized…
shiptux Mar 8, 2026
4c7340f
ci(build): remove Docker Buildx setup from RPM workflow
shiptux Mar 22, 2026
e38bfb5
fix(packaging): fail loud on missing JSON dep; verify RPM artifacts; …
shiptux May 21, 2026
0ecf806
fix(packaging): drop '|| true' on dh_shlibdeps and gate make-clean on…
shiptux May 21, 2026
bf3b403
fix(packaging): preserve stderr/aggregate failures; fail loud on patc…
shiptux May 21, 2026
7fd1bf3
fix(packaging): switch RPM License to SPDX Apache-2.0 (was legacy 'AS…
shiptux May 21, 2026
22e8d6d
fix(packaging): allow empty main %files; Standards-Version 4.6.2; TOD…
shiptux May 21, 2026
0070429
fix(packaging): qualify RPM Source0 with %{url} (round-7 batch missed…
shiptux May 22, 2026
6211013
fix(packaging): tighten libnccl >= 2.10 (group-call API minimum); Sou…
shiptux May 22, 2026
0a11c91
fix(packaging): ExclusiveArch by backend (ascend=aarch64, others=x86_…
shiptux May 22, 2026
700fa49
fix(packaging): drop Debian changes from RPM PR
shiptux May 26, 2026
b834b29
refactor(packaging): split RPM Dockerfiles by backend
shiptux May 26, 2026
e7f84bc
fix(packaging): allow x86_64 Ascend RPM builds
shiptux May 26, 2026
aa8cc37
ci(packaging): upload RPM artifacts to Nexus
shiptux May 27, 2026
4c4a859
ci(packaging): defer RPM Nexus upload
shiptux May 27, 2026
79c7984
ci(packaging): publish RPMs to Nexus directly from build job
shiptux May 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/build-rpm.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Build RPM Packages

on:
push:
tags:
- 'v*'
pull_request:
branches: [ main ]
paths:
- 'flagcx/**'
- 'packaging/rpm/**'
- '.github/workflows/build-rpm.yml'
workflow_dispatch:

jobs:
build-rpm-packages:
runs-on: h20

strategy:
fail-fast: false
matrix:
backend: [nvidia, metax, ascend]

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive

- name: Build ${{ matrix.backend }} RPM packages
run: ./packaging/rpm/build-flagcx-rpm.sh ${{ matrix.backend }}

- name: Upload ${{ matrix.backend }} RPM packages
uses: actions/upload-artifact@v4
with:
name: flagcx-${{ matrix.backend }}-rpm-packages
path: rpm-packages/${{ matrix.backend }}/**/*.rpm
Comment thread
shiptux marked this conversation as resolved.
retention-days: 7

# Publish straight from the locally-built artifacts on the same self-hosted
# runner: h20 can reach the internal Nexus but not codeload/api.github.com
# reliably, so we deliberately avoid any cross-workflow artifact round-trip.
- name: Publish ${{ matrix.backend }} RPMs to Nexus YUM repository
if: startsWith(github.ref, 'refs/tags/v')
env:
NEXUS_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
NEXUS_PASSWORD: ${{ secrets.CONTAINER_REGISTRY }}
NEXUS_REPO_URL: https://resource.flagos.net/repository/flagos-yum-hosted
BACKEND: ${{ matrix.backend }}
run: |
set -euo pipefail

uploaded=0
while IFS= read -r -d '' rpm; do
# rel keeps the RPMS/<arch>/<file> or SRPMS/<file> layout under the backend
rel="${rpm#rpm-packages/${BACKEND}/}"
echo "Uploading ${rpm} -> ${BACKEND}/${rel}"
curl -f -u "${NEXUS_USERNAME}:${NEXUS_PASSWORD}" \
--upload-file "$rpm" \
"${NEXUS_REPO_URL}/${BACKEND}/${rel}"
uploaded=$((uploaded + 1))
done < <(find "rpm-packages/${BACKEND}" -name '*.rpm' -print0)

if [ "$uploaded" -eq 0 ]; then
echo "No RPMs found for ${BACKEND}"
exit 1
fi
echo "Uploaded ${uploaded} ${BACKEND} RPM(s) to Nexus YUM repository"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ build
plugin/*/build
test/*/build
debian-packages
rpm-packages

# Ignore compiled Python files and shared object files
plugin/*/*.so
Expand Down
106 changes: 106 additions & 0 deletions packaging/rpm/build-flagcx-rpm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/bin/bash
set -e

# FlagCX RPM package build script
# Usage: ./build-flagcx-rpm.sh <backend> [base_image_version]
# Supported backends: nvidia, metax, ascend

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
BACKEND="${1:-}"
BASE_IMAGE_VERSION="${2:-}"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_step() { echo -e "${BLUE}[STEP]${NC} $1"; }

# Show usage
if [ -z "$BACKEND" ]; then
log_error "No backend specified"
echo ""
echo "Usage: $0 <backend> [base_image_version]"
echo ""
echo "Supported backends:"
echo " nvidia - Build RPM packages for NVIDIA GPUs"
echo " metax - Build RPM packages for MetaX accelerators"
echo " ascend - Build RPM packages for Ascend NPUs"
echo ""
echo "Examples:"
echo " $0 nvidia"
echo " $0 ascend 8.5.0-910-openeuler24.03-py3.11"
exit 1
fi

# Validate backend and set base image
case "$BACKEND" in
nvidia)
BASE_IMAGE="nvcr.io/nvidia/cuda"
[ -z "$BASE_IMAGE_VERSION" ] && BASE_IMAGE_VERSION="12.4.1-devel-rockylinux8"
DOCKERFILE="${SCRIPT_DIR}/dockerfiles/Dockerfile.rpm.nvidia"
;;
metax)
BASE_IMAGE="rockylinux"
[ -z "$BASE_IMAGE_VERSION" ] && BASE_IMAGE_VERSION="8"
DOCKERFILE="${SCRIPT_DIR}/dockerfiles/Dockerfile.rpm.metax"
;;
ascend)
BASE_IMAGE="ascendai/cann"
[ -z "$BASE_IMAGE_VERSION" ] && BASE_IMAGE_VERSION="8.5.0-910-openeuler24.03-py3.11"
DOCKERFILE="${SCRIPT_DIR}/dockerfiles/Dockerfile.rpm.ascend"
;;
*)
log_error "Invalid backend: $BACKEND"
echo "Supported backends: nvidia, metax, ascend"
exit 1
;;
esac

log_info "Building FlagCX RPM packages for $BACKEND backend"
log_info "Using base image: ${BASE_IMAGE}:${BASE_IMAGE_VERSION}"

# Sync changelog from CHANGELOG.md
log_step "Synchronizing changelog..."
if [ -f "${PROJECT_DIR}/packaging/sync-changelog.py" ]; then
python3 "${PROJECT_DIR}/packaging/sync-changelog.py" || log_warn "Failed to sync changelog"
else
log_warn "sync-changelog.py not found, skipping changelog sync"
fi

# Build Docker image using backend-specific Dockerfile with shared RPM logic.
log_step "Building Docker image..."
docker build \
--network=host \
--build-arg BASE_IMAGE_VERSION="${BASE_IMAGE_VERSION}" \
-f "${DOCKERFILE}" \
-t "flagcx-rpm-${BACKEND}:${BASE_IMAGE_VERSION}" \
"${PROJECT_DIR}"

# Extract RPM packages
log_step "Extracting RPM packages..."
OUTPUT_DIR="${PROJECT_DIR}/rpm-packages/${BACKEND}"
mkdir -p "${OUTPUT_DIR}"

CONTAINER_ID=$(docker create "flagcx-rpm-${BACKEND}:${BASE_IMAGE_VERSION}")
docker cp "${CONTAINER_ID}:/root/rpmbuild/RPMS/" "${OUTPUT_DIR}/"
docker cp "${CONTAINER_ID}:/root/rpmbuild/SRPMS/" "${OUTPUT_DIR}/"
docker rm "${CONTAINER_ID}"

# Fail loudly if no RPMs were extracted, so CI doesn't silently upload empty artifacts.
if ! find "${OUTPUT_DIR}" -name '*.rpm' | grep -q .; then
log_error "No RPM packages found under ${OUTPUT_DIR}"
exit 1
fi

log_info "✓ Packages built successfully for ${BACKEND}:"
echo ""
find "${OUTPUT_DIR}" -name "*.rpm" -exec ls -lh {} \;

log_info "Build complete! Packages in: ${OUTPUT_DIR}"
11 changes: 11 additions & 0 deletions packaging/rpm/dockerfiles/Dockerfile.rpm.ascend
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Build FlagCX Ascend RPM packages on CANN OpenEuler images.

ARG BASE_IMAGE_VERSION=8.5.0-910-openeuler24.03-py3.11
FROM ascendai/cann:${BASE_IMAGE_VERSION}

WORKDIR /workspace
COPY . /workspace/

RUN bash packaging/rpm/dockerfiles/build-rpm-common.sh ascend

CMD ["/bin/bash"]
20 changes: 20 additions & 0 deletions packaging/rpm/dockerfiles/Dockerfile.rpm.metax
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Build FlagCX MetaX RPM packages on Rocky Linux.

ARG BASE_IMAGE_VERSION=8
FROM rockylinux:${BASE_IMAGE_VERSION}

WORKDIR /workspace
COPY . /workspace/

# MetaX packages are served from the public MACA yum repository.
# TODO: switch gpgcheck=1 once MetaX publishes a stable GPG key for
# repos.metax-tech.com. Today this repo serves unsigned packages.
RUN printf '[maca-sdk]\nname=MACA SDK Yum Repository\nbaseurl=https://repos.metax-tech.com/r/maca-sdk-rpm-x86_64/\nenabled=1\ngpgcheck=0\n' \
> /etc/yum.repos.d/maca-sdk-rpm.repo && \
yum makecache && \
yum install -y maca_sdk && \
yum clean all

RUN bash packaging/rpm/dockerfiles/build-rpm-common.sh metax

CMD ["/bin/bash"]
11 changes: 11 additions & 0 deletions packaging/rpm/dockerfiles/Dockerfile.rpm.nvidia
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Build FlagCX NVIDIA RPM packages on Rocky Linux based CUDA images.

ARG BASE_IMAGE_VERSION=12.4.1-devel-rockylinux8
FROM nvcr.io/nvidia/cuda:${BASE_IMAGE_VERSION}

WORKDIR /workspace
COPY . /workspace/

RUN bash packaging/rpm/dockerfiles/build-rpm-common.sh nvidia

CMD ["/bin/bash"]
58 changes: 58 additions & 0 deletions packaging/rpm/dockerfiles/build-rpm-common.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/bash
set -euo pipefail

BACKEND="${1:-}"

if [ -z "${BACKEND}" ]; then
echo "ERROR: backend is required" >&2
exit 1
fi

case "${BACKEND}" in
nvidia|metax|ascend)
;;
*)
echo "ERROR: unsupported backend: ${BACKEND}" >&2
exit 1
;;
esac

PKG_MANAGER="$(command -v dnf || command -v yum || true)"
if [ -z "${PKG_MANAGER}" ]; then
echo "ERROR: neither dnf nor yum is available in the base image" >&2
exit 1
fi

"${PKG_MANAGER}" install -y epel-release || \
echo "EPEL not available for this base image, continuing without it"

"${PKG_MANAGER}" install -y \
rpm-build \
rpmdevtools \
gcc-c++ \
make \
cmake \
patchelf

"${PKG_MANAGER}" install -y json-devel 2>/dev/null \
|| "${PKG_MANAGER}" install -y nlohmann-json-devel 2>/dev/null \
|| { echo "ERROR: neither json-devel nor nlohmann-json-devel is available; rpmbuild requires nlohmann::json headers" >&2; exit 1; }

"${PKG_MANAGER}" clean all

rpmdev-setuptree

SPEC_VERSION="$(awk '/^Version:/ {print $2; exit}' /workspace/packaging/rpm/specs/flagcx.spec)"
tar czf "/root/rpmbuild/SOURCES/flagcx-${SPEC_VERSION}.tar.gz" \
--transform "s,^\.,flagcx-${SPEC_VERSION}," \
--exclude='.git' \
--exclude='build' \
--exclude='debian-packages' \
--exclude='rpm-packages' \
.

rpmbuild -ba \
--define "backend ${BACKEND}" \
/workspace/packaging/rpm/specs/flagcx.spec

ls -lh /root/rpmbuild/RPMS/*/*.rpm
Loading