Merge branch 'release/20260101-1' #31
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
| name: Build and Release Cores | ||
| on: | ||
| push: | ||
| tags: | ||
| - "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9]*" # Match YYYYMMDD-N format (e.g., 20251115-0) | ||
| concurrency: | ||
| group: release-${{ github.ref }} | ||
| cancel-in-progress: false # Don't cancel releases mid-build | ||
| permissions: | ||
| contents: write | ||
| packages: write | ||
| jobs: | ||
| docker-build: | ||
| runs-on: sprinters:aws:ubuntu-24.04-arm:c7gd.xlarge:temp=32 | ||
| timeout-minutes: 30 | ||
| concurrency: | ||
| group: docker-build-${{ hashFiles('Dockerfile') }} | ||
| cancel-in-progress: false | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| submodules: recursive | ||
| fetch-depth: 0 | ||
| - name: Calculate Dockerfile hash | ||
| id: dockerfile-hash | ||
| run: | | ||
| HASH=$(sha256sum Dockerfile | awk '{print $1}') | ||
| echo "hash=$HASH" >> $GITHUB_OUTPUT | ||
| echo "Dockerfile hash: $HASH" | ||
| - name: Login to GitHub Container Registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Check if image exists in GHCR | ||
| id: check-image | ||
| run: | | ||
| IMAGE="ghcr.io/${{ github.repository_owner }}/lessui-cores-builder:${{ steps.dockerfile-hash.outputs.hash }}" | ||
| if docker pull "$IMAGE" 2>/dev/null; then | ||
| echo "✓ Image found in GHCR (skipped 28 min build!)" | ||
| echo "exists=true" >> $GITHUB_OUTPUT | ||
| else | ||
| echo "Image not found, will build" | ||
| echo "exists=false" >> $GITHUB_OUTPUT | ||
| fi | ||
| - name: Set up Docker Buildx | ||
| if: steps.check-image.outputs.exists == 'false' | ||
| uses: docker/setup-buildx-action@v3 | ||
| - name: Build and push Docker image | ||
| if: steps.check-image.outputs.exists == 'false' | ||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| context: . | ||
| push: true | ||
| tags: | | ||
| ghcr.io/${{ github.repository_owner }}/lessui-cores-builder:${{ steps.dockerfile-hash.outputs.hash }} | ||
| ghcr.io/${{ github.repository_owner }}/lessui-cores-builder:latest | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max | ||
| build: | ||
| needs: docker-build | ||
| runs-on: sprinters:aws:ubuntu-24.04-arm:c7gd.xlarge:temp=32 | ||
| timeout-minutes: 120 # 2 hours max per architecture | ||
| strategy: | ||
| matrix: | ||
| cpu: [arm32, arm64] | ||
| fail-fast: false # Continue other builds even if one fails | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| submodules: recursive | ||
| fetch-depth: 0 | ||
| - name: Login to GitHub Container Registry | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Pull Docker image from GHCR | ||
| run: | | ||
| docker pull ghcr.io/${{ github.repository_owner }}/lessui-cores-builder:latest | ||
| docker tag ghcr.io/${{ github.repository_owner }}/lessui-cores-builder:latest lessui-cores-builder:latest | ||
| - name: Clean previous builds | ||
| run: make clean | ||
| - name: Build ${{ matrix.cpu }} | ||
| env: | ||
| JOBS: 4 | ||
| SKIP_DOCKER_BUILD: 1 | ||
| run: | | ||
| echo "Starting build for ${{ matrix.cpu }} at $(date)" | ||
| make build-${{ matrix.cpu }} | ||
| echo "Build completed at $(date)" | ||
| - name: Verify build | ||
| run: | | ||
| count=$(ls output/${{ matrix.cpu }}/*.so 2>/dev/null | wc -l) | ||
| echo "${{ matrix.cpu }}: $count cores built" | ||
| if [ "$count" -eq 0 ]; then | ||
| echo "ERROR: No cores built for ${{ matrix.cpu }}" | ||
| exit 1 | ||
| fi | ||
| - name: Package build | ||
| run: make package-${{ matrix.cpu }} | ||
| - name: Verify package | ||
| run: | | ||
| if [ -f "output/dist/linux-${{ matrix.cpu }}.zip" ]; then | ||
| size=$(du -h "output/dist/linux-${{ matrix.cpu }}.zip" | cut -f1) | ||
| echo "linux-${{ matrix.cpu }}.zip: $size" | ||
| else | ||
| echo "ERROR: output/dist/linux-${{ matrix.cpu }}.zip not found" | ||
| exit 1 | ||
| fi | ||
| - name: Upload package artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: linux-${{ matrix.cpu }} | ||
| path: output/dist/linux-${{ matrix.cpu }}.zip | ||
| retention-days: 7 | ||
| - name: Save build info | ||
| run: | | ||
| count=$(ls output/${{ matrix.cpu }}/*.so 2>/dev/null | wc -l) | ||
| echo "$count" > core-count-${{ matrix.cpu }}.txt | ||
| - name: Upload build info artifact | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: build-info-${{ matrix.cpu }} | ||
| path: core-count-${{ matrix.cpu }}.txt | ||
| retention-days: 7 | ||
| release: | ||
| needs: build | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| fetch-depth: 1 # Only need current commit for release | ||
| - name: Download build artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| pattern: linux-* | ||
| path: artifacts | ||
| - name: Download build info artifacts | ||
| uses: actions/download-artifact@v4 | ||
| with: | ||
| pattern: build-info-* | ||
| path: artifacts | ||
| - name: Verify downloads | ||
| run: | | ||
| echo "Checking downloaded artifacts..." | ||
| ls -lR artifacts/ | ||
| # Verify packages exist | ||
| if [ ! -f "artifacts/linux-arm32/linux-arm32.zip" ]; then | ||
| echo "ERROR: linux-arm32.zip not found" | ||
| exit 1 | ||
| fi | ||
| if [ ! -f "artifacts/linux-arm64/linux-arm64.zip" ]; then | ||
| echo "ERROR: linux-arm64.zip not found" | ||
| exit 1 | ||
| fi | ||
| # Verify build info exists | ||
| if [ ! -f "artifacts/build-info-arm32/core-count-arm32.txt" ]; then | ||
| echo "ERROR: core-count-arm32.txt not found" | ||
| exit 1 | ||
| fi | ||
| if [ ! -f "artifacts/build-info-arm64/core-count-arm64.txt" ]; then | ||
| echo "ERROR: core-count-arm64.txt not found" | ||
| exit 1 | ||
| fi | ||
| echo "✓ All required artifacts downloaded" | ||
| - name: Organize artifacts | ||
| run: | | ||
| mkdir -p release-packages | ||
| mv artifacts/linux-arm32/linux-arm32.zip release-packages/ | ||
| mv artifacts/linux-arm64/linux-arm64.zip release-packages/ | ||
| ls -lh release-packages/ | ||
| - name: Generate release notes | ||
| id: release_notes | ||
| run: | | ||
| # Get core counts | ||
| arm32_count=$(cat artifacts/build-info-arm32/core-count-arm32.txt) | ||
| arm64_count=$(cat artifacts/build-info-arm64/core-count-arm64.txt) | ||
| cat << EOF > release_notes.md | ||
| # lessui-cores ${GITHUB_REF_NAME} | ||
| Libretro cores for ARM-based retro handhelds running MinUI. | ||
| | Package | Cores | Devices | | ||
| |---------|-------|---------| | ||
| | **linux-arm32.zip** | ${arm32_count} | Miyoo Mini, RG35XX, Trimui Smart | | ||
| | **linux-arm64.zip** | ${arm64_count} | RG28xx/40xx, CubeXX, Trimui | | ||
| Built $(date -u '+%Y-%m-%d') from commit ${GITHUB_SHA:0:8} | ||
| EOF | ||
| echo "notes_file=release_notes.md" >> $GITHUB_OUTPUT | ||
| - name: Create GitHub Release | ||
| uses: softprops/action-gh-release@v2 | ||
| with: | ||
| files: release-packages/*.zip | ||
| body_path: release_notes.md | ||
| prerelease: false | ||
| make_latest: true | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| - name: Update 'latest' tag | ||
| run: | | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| git tag -f latest | ||
| git push -f origin latest | ||