Lets try to only find copr versions #22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # 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 |