Skip to content

Lets try to only find copr versions #22

Lets try to only find copr versions

Lets try to only find copr versions #22

# Build COPR packages when package specs change
# Triggers builds in dependency order based on packages/README.md
name: Build COPR Packages
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false # Don't cancel - we need to cleanup COPR builds properly
on:
push:
branches:
- main
paths:
- 'packages/**'
- '.github/workflows/build-copr-packages.yml'
workflow_dispatch:
inputs:
packages:
description: 'Comma-separated list of packages to build (empty = auto-detect from changes)'
required: false
type: string
rebuild_all:
description: 'Rebuild all packages in dependency order'
required: false
type: boolean
default: false
env:
COPR_PROJECT: binarypie/hypercube
jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
packages: ${{ steps.detect.outputs.packages }}
matrix: ${{ steps.build-matrix.outputs.matrix }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0 # Full history to find last successful build
- name: Get last successful build commit
id: last-success
env:
GH_TOKEN: ${{ github.token }}
run: |
# Find the last successful workflow run on this branch
LAST_SHA=$(gh run list \
--workflow="Build COPR Packages" \
--branch=main \
--status=success \
--limit=1 \
--json headSha \
--jq '.[0].headSha // empty' 2>/dev/null) || LAST_SHA=""
if [[ -n "$LAST_SHA" ]]; then
echo "Last successful build: $LAST_SHA"
echo "sha=$LAST_SHA" >> $GITHUB_OUTPUT
else
echo "No previous successful build found, will use HEAD~1"
echo "sha=" >> $GITHUB_OUTPUT
fi
- name: Detect changed packages
id: detect
env:
GH_TOKEN: ${{ github.token }}
run: |
# Define all packages and their dependencies
declare -A DEPS
DEPS[hyprutils]=""
DEPS[hyprwayland-scanner]=""
DEPS[hyprland-protocols]=""
DEPS[hyprwire]=""
DEPS[glaze]=""
DEPS[uwsm]=""
DEPS[eza]=""
DEPS[starship]=""
DEPS[lazygit]=""
DEPS[quickshell]=""
DEPS[livesys-scripts]=""
DEPS[wifitui]=""
DEPS[regreet]=""
DEPS[hyprlang]="hyprutils"
DEPS[hyprgraphics]="hyprutils"
DEPS[aquamarine]="hyprutils hyprwayland-scanner"
DEPS[hyprcursor]="hyprlang"
DEPS[hyprland-qt-support]="hyprlang"
DEPS[hyprland]="aquamarine hyprcursor hyprgraphics hyprlang hyprutils glaze"
DEPS[hyprlock]="hyprgraphics hyprlang hyprutils hyprwayland-scanner"
DEPS[hypridle]="hyprland-protocols hyprlang hyprutils hyprwayland-scanner"
DEPS[hyprpaper]="hyprgraphics hyprlang hyprutils hyprwayland-scanner hyprwire hyprtoolkit"
DEPS[xdg-desktop-portal-hyprland]="hyprland-protocols hyprlang hyprutils hyprwayland-scanner"
DEPS[hyprpolkitagent]="hyprutils hyprland-qt-support"
DEPS[hyprtoolkit]="aquamarine hyprgraphics hyprlang hyprutils hyprwayland-scanner"
DEPS[hyprland-guiutils]="aquamarine hyprtoolkit hyprlang hyprutils"
# Define batch order (packages in same batch can build in parallel)
BATCH1="hyprutils hyprwayland-scanner hyprland-protocols hyprwire glaze uwsm eza starship lazygit quickshell livesys-scripts wifitui regreet"
BATCH2="hyprlang hyprgraphics aquamarine"
BATCH3="hyprcursor hyprland-qt-support"
BATCH4="hyprland hyprlock hypridle xdg-desktop-portal-hyprland hyprpolkitagent hyprtoolkit"
BATCH5="hyprpaper hyprland-guiutils"
# Function to get all downstream dependents of a package
get_dependents() {
local pkg=$1
local dependents=""
for p in "${!DEPS[@]}"; do
if [[ " ${DEPS[$p]} " == *" $pkg "* ]]; then
dependents="$dependents $p"
# Recursively get dependents of dependents
dependents="$dependents $(get_dependents $p)"
fi
done
echo "$dependents"
}
# Determine which packages to build
if [[ "${{ inputs.rebuild_all }}" == "true" ]]; then
echo "Rebuilding all packages..."
CHANGED_PKGS="${!DEPS[*]}"
elif [[ -n "${{ inputs.packages }}" ]]; then
echo "Using manually specified packages..."
CHANGED_PKGS=$(echo "${{ inputs.packages }}" | tr ',' ' ')
else
echo "Detecting changed packages..."
# Compare against last successful build, or HEAD~1 if none
LAST_SHA="${{ steps.last-success.outputs.sha }}"
if [[ -n "$LAST_SHA" ]]; then
echo "Comparing HEAD against last successful build: $LAST_SHA"
COMPARE_REF="$LAST_SHA"
else
echo "Comparing HEAD against HEAD~1"
COMPARE_REF="HEAD~1"
fi
CHANGED_PKGS=""
for dir in packages/*/; do
pkg=$(basename "$dir")
if [[ "$pkg" != "README.md" ]]; then
if git diff --name-only "$COMPARE_REF" HEAD | grep -q "^packages/$pkg/"; then
CHANGED_PKGS="$CHANGED_PKGS $pkg"
fi
fi
done
fi
echo "Initially changed packages: $CHANGED_PKGS"
echo ""
echo "Checking which packages actually need building..."
echo ""
# Filter out packages that are already at the correct version in COPR
NEEDS_BUILD=""
for pkg in $CHANGED_PKGS; do
SPEC_FILE="packages/$pkg/$pkg.spec"
if [[ ! -f "$SPEC_FILE" ]]; then
echo " $pkg: spec file not found, skipping"
continue
fi
SPEC_VERSION=$(grep "^Version:" "$SPEC_FILE" | awk '{print $2}')
SPEC_RELEASE=$(grep "^Release:" "$SPEC_FILE" | awk '{print $2}' | sed 's/%{?dist}//')
if [[ -z "$SPEC_VERSION" ]]; then
echo " $pkg: could not determine spec version, will build"
NEEDS_BUILD="$NEEDS_BUILD $pkg"
continue
fi
echo " $pkg: spec has version $SPEC_VERSION-$SPEC_RELEASE"
# Query COPR build history API for the latest successful build
echo " Querying COPR API for latest successful build..."
COPR_API_URL="https://copr.fedorainfracloud.org/api_3/package?ownername=binarypie&projectname=hypercube&packagename=$pkg&with_latest_build=True"
COPR_API_RESPONSE=$(curl -sf "$COPR_API_URL" 2>/dev/null)
if [[ -z "$COPR_API_RESPONSE" ]]; then
echo " COPR: Failed to query API, will build"
NEEDS_BUILD="$NEEDS_BUILD $pkg"
continue
fi
# Extract the latest succeeded build's source_package (which contains NVR)
# The API returns builds array, we want the first one with state "succeeded"
BUILDS_JSON=$(echo "$COPR_API_RESPONSE" | jq -r '.builds' 2>/dev/null)
if [[ -z "$BUILDS_JSON" || "$BUILDS_JSON" == "null" ]]; then
echo " COPR: No builds found in API response, will build"
NEEDS_BUILD="$NEEDS_BUILD $pkg"
continue
fi
# Find the latest successful build and extract its NVR
LATEST_NVR=""
while IFS= read -r build; do
STATE=$(echo "$build" | jq -r '.state' 2>/dev/null)
if [[ "$STATE" == "succeeded" ]]; then
SOURCE_PKG=$(echo "$build" | jq -r '.source_package.name' 2>/dev/null)
if [[ -n "$SOURCE_PKG" && "$SOURCE_PKG" != "null" ]]; then
# source_package.name is the NVR (e.g., "starship-1.24.2-1.fc43")
LATEST_NVR="$SOURCE_PKG"
break
fi
fi
done < <(echo "$BUILDS_JSON" | jq -c '.[]' 2>/dev/null)
if [[ -z "$LATEST_NVR" ]]; then
echo " COPR: No successful builds found, will build"
NEEDS_BUILD="$NEEDS_BUILD $pkg"
continue
fi
# Extract version-release from NVR (format: package-version-release.fc43)
# Remove package name prefix and .fc43 suffix
COPR_VR=$(echo "$LATEST_NVR" | sed "s/^$pkg-//" | sed 's/\.fc43$//')
echo " COPR: has version $COPR_VR (from latest successful build)"
COPR_VERSION="$COPR_VR"
# Compare spec version-release with COPR version-release
SPEC_VR="${SPEC_VERSION}-${SPEC_RELEASE}"
if [[ "$COPR_VERSION" == "$SPEC_VR" ]]; then
echo " -> Versions match, skipping build"
else
echo " -> Versions differ (spec: $SPEC_VR vs copr: $COPR_VERSION), will build"
NEEDS_BUILD="$NEEDS_BUILD $pkg"
fi
done
echo ""
echo "Packages that need building: $NEEDS_BUILD"
# Add all downstream dependents
TO_BUILD="$NEEDS_BUILD"
for pkg in $NEEDS_BUILD; do
dependents=$(get_dependents "$pkg")
TO_BUILD="$TO_BUILD $dependents"
done
# Deduplicate
TO_BUILD=$(echo "$TO_BUILD" | tr ' ' '\n' | sort -u | tr '\n' ' ')
echo "Packages to build (including dependents): $TO_BUILD"
# Output as JSON array
if [[ -z "$(echo $TO_BUILD | tr -d ' ')" ]]; then
echo "packages=[]" >> $GITHUB_OUTPUT
else
JSON=$(echo "$TO_BUILD" | tr -s ' ' '\n' | grep -v '^$' | jq -R . | jq -sc .)
echo "packages=$JSON" >> $GITHUB_OUTPUT
fi
- name: Build batch matrix
id: build-matrix
run: |
PACKAGES='${{ steps.detect.outputs.packages }}'
if [[ "$PACKAGES" == "[]" ]]; then
echo "matrix={\"batch\":[]}" >> $GITHUB_OUTPUT
exit 0
fi
# Define batches with their packages
cat > /tmp/batches.json << 'EOF'
{
"batch": [
{"id": 1, "packages": ["hyprutils", "hyprwayland-scanner", "hyprland-protocols", "hyprwire", "glaze", "uwsm", "eza", "starship", "lazygit", "quickshell", "livesys-scripts", "wifitui", "regreet"]},
{"id": 2, "packages": ["hyprlang", "hyprgraphics", "aquamarine"]},
{"id": 3, "packages": ["hyprcursor", "hyprland-qt-support"]},
{"id": 4, "packages": ["hyprland", "hyprlock", "hypridle", "xdg-desktop-portal-hyprland", "hyprpolkitagent", "hyprtoolkit"]},
{"id": 5, "packages": ["hyprpaper", "hyprland-guiutils"]}
]
}
EOF
# Filter batches to only include packages that need building
FILTERED=$(jq -c --argjson pkgs "$PACKAGES" '
.batch | map(
.packages = (.packages | map(select(. as $p | $pkgs | index($p))))
| select(.packages | length > 0)
) | {batch: .}
' /tmp/batches.json)
echo "matrix=$FILTERED" >> $GITHUB_OUTPUT
echo "Build matrix:"
echo "$FILTERED" | jq .
build-batch:
needs: detect-changes
if: ${{ needs.detect-changes.outputs.matrix != '{"batch":[]}' && needs.detect-changes.outputs.matrix != '' }}
runs-on: ubuntu-latest
strategy:
max-parallel: 1 # Run batches sequentially
fail-fast: true # Stop subsequent batches if one fails (dependencies would fail anyway)
matrix: ${{ fromJson(needs.detect-changes.outputs.matrix) }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install COPR CLI
run: |
sudo apt-get update
sudo apt-get install -y python3-pip
pip3 install copr-cli
- name: Configure COPR CLI
run: |
mkdir -p ~/.config
cat > ~/.config/copr << EOF
[copr-cli]
login = ${{ secrets.COPR_API_LOGIN }}
username = binarypie
token = ${{ secrets.COPR_API_TOKEN }}
copr_url = https://copr.fedorainfracloud.org
EOF
- name: Build batch ${{ matrix.batch.id }} packages
id: build
run: |
echo "Building batch ${{ matrix.batch.id }}: ${{ join(matrix.batch.packages, ', ') }}"
FAILED=""
BUILD_IDS=""
# Trigger all builds in this batch
for pkg in ${{ join(matrix.batch.packages, ' ') }}; do
echo ""
echo "========================================"
echo "Triggering build for: $pkg"
echo "========================================"
# Trigger SCM build
BUILD_OUTPUT=$(copr-cli build-package \
--name "$pkg" \
"${{ env.COPR_PROJECT }}" 2>&1) || {
echo "::error::Failed to trigger build for $pkg"
echo "$BUILD_OUTPUT"
FAILED="$FAILED $pkg"
continue
}
echo "$BUILD_OUTPUT"
# Extract build ID
BUILD_ID=$(echo "$BUILD_OUTPUT" | grep -oP 'Build was added to .+builds/\K\d+' || echo "")
if [[ -n "$BUILD_ID" ]]; then
BUILD_IDS="$BUILD_IDS $BUILD_ID"
echo "Build ID: $BUILD_ID"
fi
done
# Save build IDs for potential cleanup
echo "build_ids=$BUILD_IDS" >> $GITHUB_OUTPUT
# Wait for all builds in this batch to complete
echo ""
echo "========================================"
echo "Waiting for batch ${{ matrix.batch.id }} builds to complete..."
echo "========================================"
for build_id in $BUILD_IDS; do
echo "Waiting for build $build_id..."
copr-cli watch-build "$build_id" || {
echo "::warning::Build $build_id failed or was cancelled"
# Get package name for this build
FAILED="$FAILED (build:$build_id)"
}
done
if [[ -n "$FAILED" ]]; then
echo ""
echo "::error::Some packages failed to build:$FAILED"
exit 1
fi
echo ""
echo "Batch ${{ matrix.batch.id }} completed successfully!"
- name: Cancel COPR builds on workflow cancellation
if: cancelled()
run: |
echo "Workflow cancelled - cancelling COPR builds..."
BUILD_IDS="${{ steps.build.outputs.build_ids }}"
for build_id in $BUILD_IDS; do
echo "Cancelling COPR build $build_id..."
copr-cli cancel "$build_id" || echo "Could not cancel build $build_id (may have already finished)"
done
echo "COPR build cancellation complete"
- name: Generate batch summary
if: always()
run: |
echo "## Batch ${{ matrix.batch.id }} Build Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Packages:** ${{ join(matrix.batch.packages, ', ') }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "[View builds on COPR](https://copr.fedorainfracloud.org/coprs/${{ env.COPR_PROJECT }}/builds/)" >> $GITHUB_STEP_SUMMARY
summary:
needs: [detect-changes, build-batch]
if: always()
runs-on: ubuntu-latest
steps:
- name: Generate final summary
run: |
echo "## COPR Package Build Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Packages built:** ${{ needs.detect-changes.outputs.packages }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Status:** ${{ needs.build-batch.result }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "[View all builds](https://copr.fedorainfracloud.org/coprs/${{ env.COPR_PROJECT }}/builds/)" >> $GITHUB_STEP_SUMMARY