Working through hyprwire build #17
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 | |
| 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" | |
| 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 hyprpaper xdg-desktop-portal-hyprland hyprpolkitagent hyprtoolkit" | |
| BATCH5="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" | |
| # Add all downstream dependents | |
| TO_BUILD="$CHANGED_PKGS" | |
| for pkg in $CHANGED_PKGS; 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", "hyprpaper", "xdg-desktop-portal-hyprland", "hyprpolkitagent", "hyprtoolkit"]}, | |
| {"id": 5, "packages": ["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 |