Skip to content

Buildfixes (#74)

Buildfixes (#74) #42

# 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: Install jq
run: sudo apt-get update && sudo apt-get install -y jq
- name: Detect changed packages
id: detect
env:
GH_TOKEN: ${{ github.token }}
run: |
# Source shared package configuration
source ./scripts/packages/config.sh
# Package dependencies are now available via PACKAGE_DEPS
# Build batches are available via BUILD_BATCHES
# Function to get all downstream dependents of a package
get_dependents() {
local pkg=$1
local dependents=""
for p in "${!PACKAGE_DEPS[@]}"; do
if [[ " ${PACKAGE_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="${!PACKAGE_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 ""
# Use the check-copr-versions script to filter packages
if [[ -z "$CHANGED_PKGS" ]]; then
NEEDS_BUILD=""
else
# Run the version check script
SCRIPT_OUTPUT=$(./scripts/packages/check-copr-versions.sh $CHANGED_PKGS 2>&1)
echo "$SCRIPT_OUTPUT"
# Extract packages that need building from the script output
NEEDS_BUILD=$(echo "$SCRIPT_OUTPUT" | grep "^NEEDS_BUILD_JSON=" | sed 's/^NEEDS_BUILD_JSON=//' | jq -r '.[]' | tr '\n' ' ')
fi
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
# Source shared package configuration
source ./scripts/packages/config.sh
# Build batches JSON from BUILD_BATCHES array (in sorted order)
echo '{"batch":[' > /tmp/batches.json
first=true
# Sort batch IDs numerically to ensure correct order
for batch_id in $(echo "${!BUILD_BATCHES[@]}" | tr ' ' '\n' | sort -n); do
if [[ $first == false ]]; then
echo "," >> /tmp/batches.json
fi
first=false
# Convert space-separated packages to JSON array
packages_json=$(echo "${BUILD_BATCHES[$batch_id]}" | tr ' ' '\n' | jq -R . | jq -sc .)
jq -n \
--argjson id "$batch_id" \
--argjson packages "$packages_json" \
'{id: $id, packages: $packages}' >> /tmp/batches.json
done
echo ']}' >> /tmp/batches.json
# 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