Build New Releases #830
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 New Releases | |
| # Automatically detects and builds new releases | |
| # from cedya77/aiometadata every 6 hours | |
| # Uses native ARM64 runners to avoid QEMU issues | |
| # ============================================ | |
| name: Build New Releases | |
| on: | |
| schedule: | |
| # Run every 6 hours | |
| - cron: '0 */6 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| force_version: | |
| description: 'Force build specific version (leave empty for auto-detect)' | |
| required: false | |
| default: '' | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: jigsawfr/aiometadata-docker | |
| SOURCE_REPO: cedya77/aiometadata | |
| permissions: | |
| contents: write | |
| packages: write | |
| id-token: write # For cosign OIDC | |
| jobs: | |
| # ============================================ | |
| # Job 1: Check for new releases | |
| # ============================================ | |
| check-releases: | |
| name: Check for New Releases | |
| runs-on: ubuntu-latest | |
| outputs: | |
| has_new_stable: ${{ steps.check.outputs.has_new_stable }} | |
| has_new_beta: ${{ steps.check.outputs.has_new_beta }} | |
| stable_version: ${{ steps.check.outputs.stable_version }} | |
| stable_sha: ${{ steps.check.outputs.stable_sha }} | |
| stable_body: ${{ steps.check.outputs.stable_body }} | |
| stable_date: ${{ steps.check.outputs.stable_date }} | |
| beta_version: ${{ steps.check.outputs.beta_version }} | |
| beta_sha: ${{ steps.check.outputs.beta_sha }} | |
| beta_body: ${{ steps.check.outputs.beta_body }} | |
| beta_date: ${{ steps.check.outputs.beta_date }} | |
| steps: | |
| - name: Check for new releases | |
| id: check | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| // Force version override | |
| const forceVersion = '${{ inputs.force_version }}'; | |
| if (forceVersion) { | |
| console.log(`Force building version: ${forceVersion}`); | |
| const isPrerelease = forceVersion.includes('beta') || forceVersion.includes('alpha'); | |
| try { | |
| const { data: release } = await github.rest.repos.getReleaseByTag({ | |
| owner: 'cedya77', | |
| repo: 'aiometadata', | |
| tag: forceVersion.startsWith('v') ? forceVersion : `v${forceVersion}` | |
| }); | |
| const version = release.tag_name.replace(/^v/, ''); | |
| if (isPrerelease) { | |
| core.setOutput('has_new_beta', 'true'); | |
| core.setOutput('has_new_stable', 'false'); | |
| core.setOutput('beta_version', version); | |
| core.setOutput('beta_sha', release.target_commitish); | |
| core.setOutput('beta_body', release.body || ''); | |
| core.setOutput('beta_date', release.published_at); | |
| } else { | |
| core.setOutput('has_new_stable', 'true'); | |
| core.setOutput('has_new_beta', 'false'); | |
| core.setOutput('stable_version', version); | |
| core.setOutput('stable_sha', release.target_commitish); | |
| core.setOutput('stable_body', release.body || ''); | |
| core.setOutput('stable_date', release.published_at); | |
| } | |
| return; | |
| } catch (e) { | |
| console.log(`Release ${forceVersion} not found: ${e.message}`); | |
| } | |
| } | |
| // Get all releases from source | |
| const { data: releases } = await github.rest.repos.listReleases({ | |
| owner: 'cedya77', | |
| repo: 'aiometadata', | |
| per_page: 20 | |
| }); | |
| const latestStable = releases.find(r => !r.prerelease && !r.tag_name.includes('beta')); | |
| const latestBeta = releases.find(r => r.prerelease || r.tag_name.includes('beta')); | |
| // Check if images exist | |
| const checkImageExists = async (version) => { | |
| try { | |
| const response = await fetch( | |
| `https://ghcr.io/v2/jigsawfr/aiometadata-docker/manifests/${version}`, | |
| { method: 'HEAD' } | |
| ); | |
| return response.ok; | |
| } catch { | |
| return false; | |
| } | |
| }; | |
| // Check stable | |
| if (latestStable) { | |
| const stableVersion = latestStable.tag_name.replace(/^v/, ''); | |
| const stableExists = await checkImageExists(stableVersion); | |
| if (!stableExists) { | |
| console.log(`New stable release found: ${stableVersion}`); | |
| core.setOutput('has_new_stable', 'true'); | |
| core.setOutput('stable_version', stableVersion); | |
| core.setOutput('stable_sha', latestStable.target_commitish); | |
| core.setOutput('stable_body', latestStable.body || ''); | |
| core.setOutput('stable_date', latestStable.published_at); | |
| } else { | |
| console.log(`Stable release ${stableVersion} already exists`); | |
| core.setOutput('has_new_stable', 'false'); | |
| } | |
| } else { | |
| core.setOutput('has_new_stable', 'false'); | |
| } | |
| // Check beta | |
| if (latestBeta) { | |
| const betaVersion = latestBeta.tag_name.replace(/^v/, ''); | |
| const betaExists = await checkImageExists(betaVersion); | |
| if (!betaExists) { | |
| console.log(`New beta release found: ${betaVersion}`); | |
| core.setOutput('has_new_beta', 'true'); | |
| core.setOutput('beta_version', betaVersion); | |
| core.setOutput('beta_sha', latestBeta.target_commitish); | |
| core.setOutput('beta_body', latestBeta.body || ''); | |
| core.setOutput('beta_date', latestBeta.published_at); | |
| } else { | |
| console.log(`Beta release ${betaVersion} already exists`); | |
| core.setOutput('has_new_beta', 'false'); | |
| } | |
| } else { | |
| core.setOutput('has_new_beta', 'false'); | |
| } | |
| # ============================================ | |
| # Job 2: Build stable per-arch (native runners) | |
| # ============================================ | |
| build-stable-arch: | |
| name: Build Stable ${{ matrix.arch }} | |
| needs: check-releases | |
| if: needs.check-releases.outputs.has_new_stable == 'true' | |
| runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| arch: [amd64, arm64] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and Push ${{ matrix.arch }} image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| platforms: linux/${{ matrix.arch }} | |
| push: true | |
| tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.check-releases.outputs.stable_version }}-${{ matrix.arch }} | |
| build-args: | | |
| VERSION=${{ needs.check-releases.outputs.stable_version }} | |
| BUILD_DATE=${{ needs.check-releases.outputs.stable_date }} | |
| VCS_REF=${{ needs.check-releases.outputs.stable_sha }} | |
| cache-from: type=gha,scope=stable-${{ matrix.arch }} | |
| cache-to: type=gha,scope=stable-${{ matrix.arch }},mode=max | |
| provenance: false | |
| sbom: false | |
| outputs: type=image,compression=zstd | |
| # ============================================ | |
| # Job 3: Create stable manifest & sign | |
| # ============================================ | |
| create-stable-manifest: | |
| name: Stable Manifest | |
| needs: [check-releases, build-stable-arch] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract version components | |
| id: version | |
| run: | | |
| VERSION="${{ needs.check-releases.outputs.stable_version }}" | |
| MAJOR=$(echo $VERSION | cut -d. -f1) | |
| MINOR=$(echo $VERSION | cut -d. -f2) | |
| echo "major=$MAJOR" >> $GITHUB_OUTPUT | |
| echo "minor=$MINOR" >> $GITHUB_OUTPUT | |
| echo "major_minor=${MAJOR}.${MINOR}" >> $GITHUB_OUTPUT | |
| - name: Create multi-arch manifest | |
| run: | | |
| VERSION="${{ needs.check-releases.outputs.stable_version }}" | |
| IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| echo "📦 Creating manifest for ${VERSION}..." | |
| # Create and push version tag | |
| docker manifest create ${IMAGE}:${VERSION} \ | |
| ${IMAGE}:${VERSION}-amd64 \ | |
| ${IMAGE}:${VERSION}-arm64 | |
| docker manifest push ${IMAGE}:${VERSION} | |
| # Major.minor tag | |
| docker manifest create ${IMAGE}:${{ steps.version.outputs.major_minor }} \ | |
| ${IMAGE}:${VERSION}-amd64 \ | |
| ${IMAGE}:${VERSION}-arm64 | |
| docker manifest push ${IMAGE}:${{ steps.version.outputs.major_minor }} | |
| # Major tag | |
| docker manifest create ${IMAGE}:${{ steps.version.outputs.major }} \ | |
| ${IMAGE}:${VERSION}-amd64 \ | |
| ${IMAGE}:${VERSION}-arm64 | |
| docker manifest push ${IMAGE}:${{ steps.version.outputs.major }} | |
| # Latest tag | |
| docker manifest create ${IMAGE}:latest \ | |
| ${IMAGE}:${VERSION}-amd64 \ | |
| ${IMAGE}:${VERSION}-arm64 | |
| docker manifest push ${IMAGE}:latest | |
| echo "✅ All manifests pushed" | |
| - name: Install Cosign | |
| uses: sigstore/cosign-installer@v3 | |
| - name: Sign manifest | |
| run: | | |
| VERSION="${{ needs.check-releases.outputs.stable_version }}" | |
| IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| # Get the manifest list digest (not individual arch digests) | |
| DIGEST=$(docker buildx imagetools inspect --raw ${IMAGE}:${VERSION} | sha256sum | awk '{print "sha256:"$1}') | |
| echo "🔐 Signing ${IMAGE}@${DIGEST}..." | |
| cosign sign --yes ${IMAGE}@${DIGEST} | |
| echo "✅ Signed successfully" | |
| - name: Test container | |
| continue-on-error: true | |
| run: | | |
| echo "🧪 Testing container..." | |
| docker run -d --name redis-test -p 6379:6379 redis:alpine | |
| sleep 5 | |
| docker run -d --name app-test \ | |
| --link redis-test:redis \ | |
| -p 1337:1337 \ | |
| -e REDIS_URL=redis://redis:6379 \ | |
| -e HOST_NAME=http://localhost:1337 \ | |
| -e DATABASE_URI=sqlite://./addon/data/addon.db \ | |
| -e TMDB_API=test \ | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.check-releases.outputs.stable_version }} | |
| # Wait for container to be ready (non-blocking) | |
| HEALTHY=false | |
| for i in {1..30}; do | |
| # Try multiple health endpoints | |
| if curl -sf http://localhost:1337/health > /dev/null 2>&1 || \ | |
| curl -sf http://localhost:1337/api/cache/health > /dev/null 2>&1 || \ | |
| curl -sf http://localhost:1337/ > /dev/null 2>&1; then | |
| echo "✅ Container is responding!" | |
| HEALTHY=true | |
| break | |
| fi | |
| echo "⏳ Waiting for container... ($i/30)" | |
| sleep 5 | |
| done | |
| if [ "$HEALTHY" != "true" ]; then | |
| echo "⚠️ Container health check timed out (non-blocking)" | |
| echo "📋 Container logs:" | |
| docker logs app-test 2>&1 | tail -50 | |
| fi | |
| docker stop app-test redis-test 2>/dev/null || true | |
| docker rm app-test redis-test 2>/dev/null || true | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ needs.check-releases.outputs.stable_version }} | |
| name: v${{ needs.check-releases.outputs.stable_version }} | |
| body: | | |
| ## 🐳 Docker Image | |
| ```bash | |
| docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.check-releases.outputs.stable_version }} | |
| ``` | |
| ### Available Tags | |
| - `${{ needs.check-releases.outputs.stable_version }}` - This version | |
| - `${{ steps.version.outputs.major_minor }}` - Latest patch | |
| - `${{ steps.version.outputs.major }}` - Latest for major | |
| - `latest` - Latest stable | |
| ### Architectures | |
| - `linux/amd64` | |
| - `linux/arm64` | |
| --- | |
| 📦 **Source**: [cedya77/aiometadata v${{ needs.check-releases.outputs.stable_version }}](https://github.com/cedya77/aiometadata/releases/tag/v${{ needs.check-releases.outputs.stable_version }}) | |
| 🔐 **Image signed with Cosign** | |
| draft: false | |
| prerelease: false | |
| # ============================================ | |
| # Job 4: Build beta per-arch (native runners) | |
| # ============================================ | |
| build-beta-arch: | |
| name: Build Beta ${{ matrix.arch }} | |
| needs: check-releases | |
| if: needs.check-releases.outputs.has_new_beta == 'true' | |
| runs-on: ${{ matrix.arch == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| arch: [amd64, arm64] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and Push ${{ matrix.arch }} image | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: ./Dockerfile | |
| platforms: linux/${{ matrix.arch }} | |
| push: true | |
| tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.check-releases.outputs.beta_version }}-${{ matrix.arch }} | |
| build-args: | | |
| VERSION=${{ needs.check-releases.outputs.beta_version }} | |
| BUILD_DATE=${{ needs.check-releases.outputs.beta_date }} | |
| VCS_REF=${{ needs.check-releases.outputs.beta_sha }} | |
| cache-from: type=gha,scope=beta-${{ matrix.arch }} | |
| cache-to: type=gha,scope=beta-${{ matrix.arch }},mode=max | |
| provenance: false | |
| sbom: false | |
| outputs: type=image,compression=zstd | |
| # ============================================ | |
| # Job 5: Create beta manifest & sign | |
| # ============================================ | |
| create-beta-manifest: | |
| name: Beta Manifest | |
| needs: [check-releases, build-beta-arch] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create multi-arch manifest | |
| run: | | |
| VERSION="${{ needs.check-releases.outputs.beta_version }}" | |
| IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| echo "📦 Creating beta manifest for ${VERSION}..." | |
| # Version tag | |
| docker manifest create ${IMAGE}:${VERSION} \ | |
| ${IMAGE}:${VERSION}-amd64 \ | |
| ${IMAGE}:${VERSION}-arm64 | |
| docker manifest push ${IMAGE}:${VERSION} | |
| # Beta tag | |
| docker manifest create ${IMAGE}:beta \ | |
| ${IMAGE}:${VERSION}-amd64 \ | |
| ${IMAGE}:${VERSION}-arm64 | |
| docker manifest push ${IMAGE}:beta | |
| echo "✅ Beta manifests pushed" | |
| - name: Install Cosign | |
| uses: sigstore/cosign-installer@v3 | |
| - name: Sign manifest | |
| run: | | |
| VERSION="${{ needs.check-releases.outputs.beta_version }}" | |
| IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| # Get the manifest list digest (not individual arch digests) | |
| DIGEST=$(docker buildx imagetools inspect --raw ${IMAGE}:${VERSION} | sha256sum | awk '{print "sha256:"$1}') | |
| echo "🔐 Signing ${IMAGE}@${DIGEST}..." | |
| cosign sign --yes ${IMAGE}@${DIGEST} | |
| echo "✅ Signed successfully" | |
| - name: Create GitHub Pre-Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ needs.check-releases.outputs.beta_version }} | |
| name: v${{ needs.check-releases.outputs.beta_version }} (Beta) | |
| body: | | |
| ## ⚠️ Beta Release | |
| This is a pre-release version and may contain bugs. | |
| ## 🐳 Docker Image | |
| ```bash | |
| docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.check-releases.outputs.beta_version }} | |
| # or | |
| docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:beta | |
| ``` | |
| ### Architectures | |
| - `linux/amd64` | |
| - `linux/arm64` | |
| --- | |
| 📦 **Source**: [cedya77/aiometadata v${{ needs.check-releases.outputs.beta_version }}](https://github.com/cedya77/aiometadata/releases/tag/v${{ needs.check-releases.outputs.beta_version }}) | |
| draft: false | |
| prerelease: true |