mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-09-10 10:12:50 +02:00
Build and publish RPM packages for Fedora using rpkg and official rust-packaging macros. GPG sign packages with Ed25519 repository key and deploy to Forgejo package registry. Publishes packages to organised groups: - continuwuity (binary): base group (stable/dev/branch-name) - continuwuity-debuginfo: GROUP-debug - continuwuity (source RPM): GROUP-src Workflow triggers on pushes to relevant paths and version tags (v*). Tagged releases use clean version numbers (v1.2.3 becomes 1.2.3-1) while branch builds use git SHA versioning. Include GPG public key for package verification and documentation for RPM repository configuration and installation methods.
480 lines
18 KiB
YAML
480 lines
18 KiB
YAML
name: Build / Fedora RPM
|
|
|
|
concurrency:
|
|
group: "build-fedora-${{ github.ref }}"
|
|
cancel-in-progress: true
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- '**'
|
|
tags:
|
|
- 'v*'
|
|
paths:
|
|
- 'fedora/**'
|
|
- 'src/**'
|
|
- 'Cargo.toml'
|
|
- 'Cargo.lock'
|
|
- '.forgejo/workflows/build-fedora.yml'
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: fedora-latest
|
|
steps:
|
|
- name: Detect Fedora version
|
|
id: fedora
|
|
run: |
|
|
VERSION=$(rpm -E %fedora)
|
|
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
|
echo "Fedora version: $VERSION"
|
|
|
|
- name: Checkout repository with full history
|
|
uses: https://code.forgejo.org/actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
|
|
- name: Cache DNF packages
|
|
uses: https://code.forgejo.org/actions/cache@v4
|
|
with:
|
|
path: |
|
|
/var/cache/dnf
|
|
/var/cache/yum
|
|
key: dnf-fedora${{ steps.fedora.outputs.version }}-${{ hashFiles('fedora/continuwuity.spec.rpkg') }}-v1
|
|
restore-keys: |
|
|
dnf-fedora${{ steps.fedora.outputs.version }}-
|
|
|
|
- name: Cache Cargo registry
|
|
uses: https://code.forgejo.org/actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/.cargo/registry
|
|
~/.cargo/git
|
|
key: cargo-fedora${{ steps.fedora.outputs.version }}-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: |
|
|
cargo-fedora${{ steps.fedora.outputs.version }}-
|
|
|
|
- name: Cache Rust build dependencies
|
|
uses: https://code.forgejo.org/actions/cache@v4
|
|
with:
|
|
path: |
|
|
~/rpmbuild/BUILD/*/target/release/deps
|
|
~/rpmbuild/BUILD/*/target/release/build
|
|
~/rpmbuild/BUILD/*/target/release/.fingerprint
|
|
~/rpmbuild/BUILD/*/target/release/incremental
|
|
key: rust-deps-fedora${{ steps.fedora.outputs.version }}-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: |
|
|
rust-deps-fedora${{ steps.fedora.outputs.version }}-
|
|
|
|
- name: Setup sccache
|
|
uses: https://github.com/mozilla-actions/sccache-action@v0.0.9
|
|
with:
|
|
token: ${{ secrets.GH_PUBLIC_RO }}
|
|
|
|
- name: Configure sccache environment
|
|
run: |
|
|
echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
|
|
echo "CMAKE_C_COMPILER_LAUNCHER=sccache" >> $GITHUB_ENV
|
|
echo "CMAKE_CXX_COMPILER_LAUNCHER=sccache" >> $GITHUB_ENV
|
|
echo "SCCACHE_CACHE_SIZE=10G" >> $GITHUB_ENV
|
|
# Aggressive GC since cache restores don't increment counter
|
|
echo "CARGO_INCREMENTAL_GC_TRIGGER=5" >> $GITHUB_ENV
|
|
|
|
- name: Install build dependencies
|
|
run: |
|
|
dnf install -y --setopt=keepcache=1 \
|
|
wget \
|
|
rpm-build \
|
|
rpm-sign \
|
|
rpmdevtools \
|
|
rpkg \
|
|
cargo-rpm-macros \
|
|
systemd-rpm-macros \
|
|
clang \
|
|
liburing-devel \
|
|
rust \
|
|
cargo \
|
|
gcc \
|
|
gcc-c++ \
|
|
make \
|
|
openssl-devel \
|
|
pkg-config \
|
|
python3-pip
|
|
|
|
- name: Setup build environment and build SRPM
|
|
run: |
|
|
# Configure git for rpkg
|
|
git config --global --add safe.directory "$GITHUB_WORKSPACE"
|
|
git config --global user.email "ci@continuwuity.org"
|
|
git config --global user.name "Continuwuity"
|
|
|
|
# Setup RPM build tree
|
|
rpmdev-setuptree
|
|
|
|
cd "$GITHUB_WORKSPACE"
|
|
|
|
# Determine release suffix and version based on ref type and branch
|
|
if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
|
|
# Tags get clean version numbers for stable releases
|
|
RELEASE_SUFFIX=""
|
|
TAG_NAME="${{ github.ref_name }}"
|
|
# Extract version from tag (remove v prefix if present)
|
|
TAG_VERSION=$(echo "$TAG_NAME" | sed 's/^v//')
|
|
|
|
# Create spec file with tag version
|
|
sed -e "s/^Version:.*$/Version: $TAG_VERSION/" \
|
|
-e "s/^Release:.*$/Release: 1%{?dist}/" \
|
|
fedora/continuwuity.spec.rpkg > continuwuity.spec.rpkg
|
|
elif [ "${{ github.ref_name }}" = "main" ]; then
|
|
# Main branch gets .dev suffix
|
|
RELEASE_SUFFIX=".dev"
|
|
|
|
# Replace the Release line to include our suffix
|
|
sed "s/^Release:.*$/Release: 1${RELEASE_SUFFIX}%{?dist}/" \
|
|
fedora/continuwuity.spec.rpkg > continuwuity.spec.rpkg
|
|
else
|
|
# Other branches get sanitized branch name as suffix
|
|
SAFE_BRANCH=$(echo "${{ github.ref_name }}" | sed 's/[^a-zA-Z0-9]/_/g' | cut -c1-20)
|
|
RELEASE_SUFFIX=".${SAFE_BRANCH}"
|
|
|
|
# Replace the Release line to include our suffix
|
|
sed "s/^Release:.*$/Release: 1${RELEASE_SUFFIX}%{?dist}/" \
|
|
fedora/continuwuity.spec.rpkg > continuwuity.spec.rpkg
|
|
fi
|
|
|
|
# Build the SRPM
|
|
rpkg srpm --outdir "$HOME/rpmbuild/SRPMS"
|
|
|
|
# Show SRPM info
|
|
ls -la $HOME/rpmbuild/SRPMS/
|
|
|
|
- name: Setup GPG for RPM signing
|
|
run: |
|
|
# Skip if no signing key is configured
|
|
if [ -z "${{ secrets.RPM_SIGNING_KEY }}" ]; then
|
|
echo "No RPM signing key configured - skipping signing setup"
|
|
exit 0
|
|
fi
|
|
|
|
# Import the signing key
|
|
echo "${{ secrets.RPM_SIGNING_KEY }}" | gpg --batch --import
|
|
|
|
# Get the key ID (look for the sec line, not the uid line)
|
|
KEY_ID=$(gpg --list-secret-keys --keyid-format=long | grep "^sec" | head -1 | awk '{print $2}' | cut -d'/' -f2)
|
|
echo "Using GPG key: $KEY_ID"
|
|
|
|
# Configure RPM macros for signing
|
|
cat > ~/.rpmmacros << EOF
|
|
%_signature gpg
|
|
%_gpg_name $KEY_ID
|
|
%__gpg /usr/bin/gpg
|
|
EOF
|
|
|
|
- name: Build RPM from SRPM
|
|
run: |
|
|
# Find the SRPM file
|
|
SRPM=$(find "$HOME/rpmbuild/SRPMS" -name "*.src.rpm" | head -1)
|
|
|
|
if [ -z "$SRPM" ]; then
|
|
echo "Error: No SRPM file found"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Building from SRPM: $SRPM"
|
|
|
|
# Build the binary RPM
|
|
rpmbuild --rebuild "$SRPM" \
|
|
--define "_topdir $HOME/rpmbuild" \
|
|
--define "_sourcedir $GITHUB_WORKSPACE" \
|
|
--nocheck # Skip %check section to avoid test dependencies
|
|
|
|
- name: Sign RPM packages
|
|
run: |
|
|
# Skip if no signing key is configured
|
|
if [ -z "${{ secrets.RPM_SIGNING_KEY }}" ]; then
|
|
echo "No RPM signing key configured - skipping package signing"
|
|
exit 0
|
|
fi
|
|
|
|
# Track signing failures
|
|
FAILED_COUNT=0
|
|
TOTAL_COUNT=0
|
|
|
|
# Export GPG_TTY to avoid terminal warnings
|
|
export GPG_TTY=/dev/null
|
|
|
|
# Sign all RPMs (binary and source)
|
|
for rpm in $(find "$HOME/rpmbuild" -name "*.rpm" -type f); do
|
|
echo "Signing: $(basename $rpm)"
|
|
TOTAL_COUNT=$((TOTAL_COUNT + 1))
|
|
|
|
# Use expect or provide empty passphrase via stdin for batch signing
|
|
if ! echo "" | rpmsign --addsign "$rpm" 2>&1; then
|
|
echo "ERROR: Failed to sign $rpm"
|
|
FAILED_COUNT=$((FAILED_COUNT + 1))
|
|
fi
|
|
done
|
|
|
|
# Fail if any RPMs failed to sign
|
|
if [ "$FAILED_COUNT" -gt 0 ]; then
|
|
echo "ERROR: Failed to sign $FAILED_COUNT out of $TOTAL_COUNT RPMs"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Successfully signed all $TOTAL_COUNT RPMs"
|
|
|
|
- name: Verify RPM signatures
|
|
run: |
|
|
# Skip if no signing key is configured
|
|
if [ -z "${{ secrets.RPM_SIGNING_KEY }}" ]; then
|
|
echo "No RPM signing key configured - skipping signature verification"
|
|
exit 0
|
|
fi
|
|
|
|
# Import our public key for verification
|
|
echo "Importing GPG public key for verification..."
|
|
rpm --import fedora/RPM-GPG-KEY-continuwuity.asc
|
|
|
|
# Track verification failures
|
|
FAILED_COUNT=0
|
|
TOTAL_COUNT=0
|
|
|
|
# Verify all RPMs
|
|
for rpm in $(find "$HOME/rpmbuild" -name "*.rpm" -type f); do
|
|
echo -n "Verifying $(basename $rpm): "
|
|
TOTAL_COUNT=$((TOTAL_COUNT + 1))
|
|
|
|
if rpm --checksig "$rpm"; then
|
|
echo " ✓"
|
|
else
|
|
echo " ✗ FAILED"
|
|
FAILED_COUNT=$((FAILED_COUNT + 1))
|
|
fi
|
|
done
|
|
|
|
# Fail if any RPMs failed verification
|
|
if [ "$FAILED_COUNT" -gt 0 ]; then
|
|
echo "ERROR: $FAILED_COUNT out of $TOTAL_COUNT RPMs failed signature verification"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Successfully verified all $TOTAL_COUNT RPM signatures"
|
|
|
|
- name: Test RPM installation
|
|
run: |
|
|
# Find the main binary RPM (exclude debug and source RPMs)
|
|
RPM=$(find "$HOME/rpmbuild/RPMS" -name "continuwuity-*.rpm" \
|
|
! -name "*debuginfo*" \
|
|
! -name "*debugsource*" \
|
|
! -name "*.src.rpm" | head -1)
|
|
|
|
if [ -z "$RPM" ]; then
|
|
echo "Error: No binary RPM file found"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Testing installation of: $RPM"
|
|
|
|
# Dry run first
|
|
rpm -qpi "$RPM"
|
|
echo ""
|
|
rpm -qpl "$RPM"
|
|
|
|
# Actually install it (would need --nodeps if dependencies aren't met)
|
|
dnf install -y "$RPM" || rpm -ivh --nodeps "$RPM"
|
|
|
|
# Verify installation
|
|
rpm -qa | grep continuwuity
|
|
|
|
# Check that the binary exists
|
|
[ -f /usr/bin/conduwuit ] && echo "✅ Binary installed successfully"
|
|
[ -f /usr/lib/systemd/system/conduwuit.service ] && echo "✅ Systemd service installed"
|
|
[ -f /etc/conduwuit/conduwuit.toml ] && echo "✅ Config file installed"
|
|
|
|
- name: List built packages
|
|
run: |
|
|
echo "Binary RPMs:"
|
|
find "$HOME/rpmbuild/RPMS" -name "*.rpm" -type f -exec ls -la {} \;
|
|
|
|
echo ""
|
|
echo "Source RPMs:"
|
|
find "$HOME/rpmbuild/SRPMS" -name "*.rpm" -type f -exec ls -la {} \;
|
|
|
|
- name: Collect artifacts
|
|
run: |
|
|
mkdir -p artifacts
|
|
|
|
# Copy all RPMs to artifacts directory
|
|
find "$HOME/rpmbuild/RPMS" -name "*.rpm" -type f -exec cp {} artifacts/ \;
|
|
find "$HOME/rpmbuild/SRPMS" -name "*.rpm" -type f -exec cp {} artifacts/ \;
|
|
|
|
# Create metadata file
|
|
cd artifacts
|
|
echo "Build Information:" > BUILD_INFO.txt
|
|
echo "==================" >> BUILD_INFO.txt
|
|
echo "Git commit: ${{ github.sha }}" >> BUILD_INFO.txt
|
|
echo "Git branch: ${{ github.ref_name }}" >> BUILD_INFO.txt
|
|
echo "Build date: $(date -u +%Y-%m-%d_%H:%M:%S_UTC)" >> BUILD_INFO.txt
|
|
echo "" >> BUILD_INFO.txt
|
|
echo "Package contents:" >> BUILD_INFO.txt
|
|
echo "-----------------" >> BUILD_INFO.txt
|
|
for rpm in *.rpm; do
|
|
echo "" >> BUILD_INFO.txt
|
|
echo "File: $rpm" >> BUILD_INFO.txt
|
|
rpm -qpi "$rpm" 2>/dev/null | grep -E "^(Name|Version|Release|Architecture|Size)" >> BUILD_INFO.txt
|
|
done
|
|
|
|
ls -la
|
|
|
|
- name: Upload binary RPM artifact
|
|
run: |
|
|
# Find the main binary RPM (exclude debug and source RPMs)
|
|
BIN_RPM=$(find artifacts -name "continuwuity-*.rpm" \
|
|
! -name "*debuginfo*" \
|
|
! -name "*debugsource*" \
|
|
! -name "*.src.rpm" \
|
|
-type f)
|
|
|
|
# Create temp directory for this artifact
|
|
mkdir -p upload-bin
|
|
cp $BIN_RPM upload-bin/
|
|
|
|
- name: Upload binary RPM
|
|
uses: https://code.forgejo.org/actions/upload-artifact@v3
|
|
with:
|
|
name: continuwuity
|
|
path: upload-bin/
|
|
|
|
- name: Upload debug RPM artifact
|
|
uses: https://code.forgejo.org/actions/upload-artifact@v3
|
|
with:
|
|
name: continuwuity-debug
|
|
path: artifacts/*debuginfo*.rpm
|
|
|
|
- name: Publish to RPM Package Registry
|
|
if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}
|
|
run: |
|
|
# Find the main binary RPM (exclude debug and source RPMs)
|
|
RPM=$(find artifacts -name "continuwuity-*.rpm" \
|
|
! -name "*debuginfo*" \
|
|
! -name "*debugsource*" \
|
|
! -name "*.src.rpm" \
|
|
-type f | head -1)
|
|
|
|
if [ -z "$RPM" ]; then
|
|
echo "No binary RPM found to publish"
|
|
exit 0
|
|
fi
|
|
|
|
# Extract version from RPM filename
|
|
RPM_BASENAME=$(basename "$RPM")
|
|
echo "Publishing: $RPM_BASENAME"
|
|
|
|
# Determine the group based on ref type and branch
|
|
if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then
|
|
GROUP="stable"
|
|
# For tags, extract the tag name for version info
|
|
TAG_NAME="${{ github.ref_name }}"
|
|
elif [ "${{ github.ref_name }}" = "main" ]; then
|
|
GROUP="dev"
|
|
else
|
|
# Use sanitized branch name as group for feature branches
|
|
GROUP=$(echo "${{ github.ref_name }}" | sed 's/[^a-zA-Z0-9]/-/g' | tr '[:upper:]' '[:lower:]' | cut -c1-30)
|
|
fi
|
|
|
|
# Extract package info from RPM for deletion
|
|
PACKAGE_INFO=$(rpm -qpi "$RPM" 2>/dev/null)
|
|
PACKAGE_NAME=$(echo "$PACKAGE_INFO" | grep "^Name" | awk '{print $3}')
|
|
PACKAGE_VERSION=$(echo "$PACKAGE_INFO" | grep "^Version" | awk '{print $3}')
|
|
PACKAGE_RELEASE=$(echo "$PACKAGE_INFO" | grep "^Release" | awk '{print $3}')
|
|
PACKAGE_ARCH=$(echo "$PACKAGE_INFO" | grep "^Architecture" | awk '{print $2}')
|
|
|
|
# Full version includes release
|
|
FULL_VERSION="${PACKAGE_VERSION}-${PACKAGE_RELEASE}"
|
|
|
|
# Try to delete existing package first (ignore errors if it doesn't exist)
|
|
echo "Removing any existing package: $PACKAGE_NAME-$FULL_VERSION.$PACKAGE_ARCH"
|
|
curl -X DELETE \
|
|
-H "Authorization: token ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}" \
|
|
"https://forgejo.ellis.link/api/packages/continuwuation/rpm/$GROUP/package/$PACKAGE_NAME/$FULL_VERSION/$PACKAGE_ARCH" \
|
|
|| echo "Package didn't exist or deletion failed (this is OK)"
|
|
|
|
# Upload to Forgejo package registry
|
|
# Using the RPM registry endpoint with group support
|
|
curl --fail-with-body \
|
|
-X PUT \
|
|
-H "Authorization: token ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}" \
|
|
-H "Content-Type: application/x-rpm" \
|
|
-T "$RPM" \
|
|
"https://forgejo.ellis.link/api/packages/continuwuation/rpm/$GROUP/upload"
|
|
|
|
echo ""
|
|
echo "✅ Published binary RPM to: https://forgejo.ellis.link/continuwuation/-/packages/rpm/continuwuity/"
|
|
echo "Group: $GROUP"
|
|
|
|
# Upload debug RPMs to separate group
|
|
DEBUG_RPMS=$(find artifacts -name "*debuginfo*.rpm")
|
|
if [ -n "$DEBUG_RPMS" ]; then
|
|
echo ""
|
|
echo "Publishing debug RPMs to group: ${GROUP}-debug"
|
|
|
|
for DEBUG_RPM in $DEBUG_RPMS; do
|
|
echo "Publishing: $(basename "$DEBUG_RPM")"
|
|
|
|
# Extract debug RPM info
|
|
DEBUG_INFO=$(rpm -qpi "$DEBUG_RPM" 2>/dev/null)
|
|
DEBUG_NAME=$(echo "$DEBUG_INFO" | grep "^Name" | awk '{print $3}')
|
|
DEBUG_VERSION=$(echo "$DEBUG_INFO" | grep "^Version" | awk '{print $3}')
|
|
DEBUG_RELEASE=$(echo "$DEBUG_INFO" | grep "^Release" | awk '{print $3}')
|
|
DEBUG_ARCH=$(echo "$DEBUG_INFO" | grep "^Architecture" | awk '{print $2}')
|
|
DEBUG_FULL_VERSION="${DEBUG_VERSION}-${DEBUG_RELEASE}"
|
|
|
|
# Try to delete existing debug package first
|
|
curl -X DELETE \
|
|
-H "Authorization: token ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}" \
|
|
"https://forgejo.ellis.link/api/packages/continuwuation/rpm/${GROUP}-debug/package/$DEBUG_NAME/$DEBUG_FULL_VERSION/$DEBUG_ARCH" \
|
|
|| echo "Debug package didn't exist or deletion failed (this is OK)"
|
|
|
|
# Upload debug RPM
|
|
curl --fail-with-body \
|
|
-X PUT \
|
|
-H "Authorization: token ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}" \
|
|
-H "Content-Type: application/x-rpm" \
|
|
-T "$DEBUG_RPM" \
|
|
"https://forgejo.ellis.link/api/packages/continuwuation/rpm/${GROUP}-debug/upload"
|
|
done
|
|
|
|
echo "✅ Published debug RPMs to group: ${GROUP}-debug"
|
|
fi
|
|
|
|
# Also upload the SRPM to separate group
|
|
SRPM=$(find artifacts -name "*.src.rpm" | head -1)
|
|
if [ -n "$SRPM" ]; then
|
|
echo ""
|
|
echo "Publishing source RPM: $(basename "$SRPM")"
|
|
echo "Publishing to group: ${GROUP}-src"
|
|
|
|
# Extract SRPM info
|
|
SRPM_INFO=$(rpm -qpi "$SRPM" 2>/dev/null)
|
|
SRPM_NAME=$(echo "$SRPM_INFO" | grep "^Name" | awk '{print $3}')
|
|
SRPM_VERSION=$(echo "$SRPM_INFO" | grep "^Version" | awk '{print $3}')
|
|
SRPM_RELEASE=$(echo "$SRPM_INFO" | grep "^Release" | awk '{print $3}')
|
|
SRPM_FULL_VERSION="${SRPM_VERSION}-${SRPM_RELEASE}"
|
|
|
|
# Try to delete existing SRPM first
|
|
echo "Removing any existing SRPM: $SRPM_NAME-$SRPM_FULL_VERSION.src"
|
|
curl -X DELETE \
|
|
-H "Authorization: token ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}" \
|
|
"https://forgejo.ellis.link/api/packages/continuwuation/rpm/${GROUP}-src/package/$SRPM_NAME/$SRPM_FULL_VERSION/src" \
|
|
|| echo "SRPM didn't exist or deletion failed (this is OK)"
|
|
|
|
curl --fail-with-body \
|
|
-X PUT \
|
|
-H "Authorization: token ${{ secrets.BUILTIN_REGISTRY_PASSWORD || secrets.GITHUB_TOKEN }}" \
|
|
-H "Content-Type: application/x-rpm" \
|
|
-T "$SRPM" \
|
|
"https://forgejo.ellis.link/api/packages/continuwuation/rpm/${GROUP}-src/upload"
|
|
|
|
echo "✅ Published source RPM to group: ${GROUP}-src"
|
|
fi
|