Release #25
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: Release | |
| on: | |
| workflow_run: | |
| workflows: ["CI"] | |
| types: | |
| - completed | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository }} | |
| IMAGE_DESCRIPTION: > | |
| CinePro Core is the central scraping and streaming engine of the CinePro | |
| ecosystem. It exposes an OMSS-compliant HTTP API for resolving movie and TV | |
| show streams from multiple providers. | |
| concurrency: | |
| group: docker-build-${{ github.event.workflow_run.head_branch || github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| prepare: | |
| if: >- | |
| github.event_name == 'workflow_dispatch' || | |
| (github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.head_branch == 'main') | |
| runs-on: ubuntu-latest | |
| outputs: | |
| app_version: ${{ steps.meta.outputs.app_version }} | |
| arch_tag_amd64: ${{ steps.meta.outputs.arch_tag_amd64 }} | |
| arch_tag_arm64: ${{ steps.meta.outputs.arch_tag_arm64 }} | |
| created: ${{ steps.meta.outputs.created }} | |
| steps: | |
| - id: meta | |
| run: | | |
| SHA="${{ github.event.workflow_run.head_sha || github.sha }}" | |
| SHORT_SHA="${SHA::7}" | |
| APP_VERSION="main-${SHORT_SHA}" | |
| CREATED="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" | |
| { | |
| echo "app_version=${APP_VERSION}" | |
| echo "arch_tag_amd64=${APP_VERSION}-amd64" | |
| echo "arch_tag_arm64=${APP_VERSION}-arm64" | |
| echo "created=${CREATED}" | |
| } >> "$GITHUB_OUTPUT" | |
| build-amd64: | |
| runs-on: ubuntu-latest | |
| needs: [prepare] | |
| permissions: | |
| contents: read | |
| packages: write | |
| outputs: | |
| digest: ${{ steps.build-push.outputs.digest }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set lowercase image name | |
| run: echo "IMAGE_NAME_LC=${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV" | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v3 | |
| if: github.event_name != 'pull_request' | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build test image | |
| run: docker buildx build --load -t cinepro-core:test . | |
| - name: Test Docker image | |
| run: | | |
| docker run -d --name cinepro-core-test \ | |
| -p 3000:3000 \ | |
| -e NODE_ENV=production \ | |
| -e HOST=0.0.0.0 \ | |
| -e PORT=3000 \ | |
| -e CACHE_TYPE=memory \ | |
| -e TMDB_API_KEY=dummy-ci-key \ | |
| cinepro-core:test | |
| sleep 10 | |
| docker logs cinepro-core-test | |
| curl -sf http://localhost:3000 || true | |
| - name: Cleanup test container | |
| if: always() | |
| run: docker rm -f cinepro-core-test || true | |
| - name: Build and push Docker image (amd64) | |
| id: build-push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| platforms: linux/amd64 | |
| push: ${{ github.event_name != 'pull_request' }} | |
| provenance: false | |
| sbom: false | |
| tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ needs.prepare.outputs.arch_tag_amd64 }} | |
| cache-from: type=gha,scope=amd64 | |
| cache-to: type=gha,mode=max,scope=amd64 | |
| build-args: | | |
| VERSION=${{ needs.prepare.outputs.app_version }} | |
| REVISION=${{ github.event.workflow_run.head_sha }} | |
| CREATED=${{ needs.prepare.outputs.created }} | |
| DESCRIPTION=${{ env.IMAGE_DESCRIPTION }} | |
| SOURCE=${{ github.server_url }}/${{ github.repository }} | |
| build-arm64: | |
| runs-on: ubuntu-24.04-arm | |
| needs: [prepare] | |
| permissions: | |
| contents: read | |
| packages: write | |
| outputs: | |
| digest: ${{ steps.build-push.outputs.digest }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Set lowercase image name | |
| run: echo "IMAGE_NAME_LC=${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV" | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v3 | |
| if: github.event_name != 'pull_request' | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push Docker image (arm64) | |
| id: build-push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| platforms: linux/arm64 | |
| push: ${{ github.event_name != 'pull_request' }} | |
| provenance: false | |
| sbom: false | |
| tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:${{ needs.prepare.outputs.arch_tag_arm64 }} | |
| cache-from: type=gha,scope=arm64 | |
| cache-to: type=gha,mode=max,scope=arm64 | |
| build-args: | | |
| VERSION=${{ needs.prepare.outputs.app_version }} | |
| REVISION=${{ github.event.workflow_run.head_sha }} | |
| CREATED=${{ needs.prepare.outputs.created }} | |
| DESCRIPTION=${{ env.IMAGE_DESCRIPTION }} | |
| SOURCE=${{ github.server_url }}/${{ github.repository }} | |
| manifest: | |
| runs-on: ubuntu-latest | |
| needs: [prepare, build-amd64, build-arm64] | |
| if: >- | |
| github.event_name != 'pull_request' && | |
| needs.prepare.result == 'success' && | |
| needs.build-amd64.result == 'success' && | |
| needs.build-arm64.result == 'success' | |
| permissions: | |
| contents: write | |
| packages: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Set lowercase image name | |
| run: echo "IMAGE_NAME_LC=${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV" | |
| - uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - uses: docker/setup-buildx-action@v3 | |
| - name: Create and push multi-arch manifests with description | |
| run: | | |
| IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}" | |
| AMD64_IMAGE="$IMAGE:${{ needs.prepare.outputs.arch_tag_amd64 }}" | |
| ARM64_IMAGE="$IMAGE:${{ needs.prepare.outputs.arch_tag_arm64 }}" | |
| DESC="${{ env.IMAGE_DESCRIPTION }}" | |
| # Versioned tag | |
| docker buildx imagetools create \ | |
| --annotation "index:org.opencontainers.image.description=${DESC}" \ | |
| -t "$IMAGE:${{ needs.prepare.outputs.app_version }}" \ | |
| "$AMD64_IMAGE" "$ARM64_IMAGE" | |
| # Moving tags | |
| docker buildx imagetools create \ | |
| --annotation "index:org.opencontainers.image.description=${DESC}" \ | |
| -t "$IMAGE:main" \ | |
| "$AMD64_IMAGE" "$ARM64_IMAGE" | |
| docker buildx imagetools create \ | |
| --annotation "index:org.opencontainers.image.description=${DESC}" \ | |
| -t "$IMAGE:latest" \ | |
| "$AMD64_IMAGE" "$ARM64_IMAGE" | |
| - name: Generate categorized release notes | |
| id: release_notes | |
| run: | | |
| CURRENT_TAG="${{ needs.prepare.outputs.app_version }}" | |
| PREV_TAG="${{ needs.prepare.outputs.previous_tag }}" | |
| if [ -n "$PREV_TAG" ]; then | |
| HASHES=$(git log --pretty=format:"%H" "$PREV_TAG"..origin/main) | |
| echo "Commits since $PREV_TAG:" | |
| else | |
| HASHES=$(git log --pretty=format:"%H" origin/main -n 50) | |
| echo "No previous tag found, using last 50 commits on main" | |
| fi | |
| FEATURES="" | |
| FIXES="" | |
| DOCS="" | |
| CHORES="" | |
| BREAKING="" | |
| OTHERS="" | |
| while IFS= read -r hash; do | |
| # Get first line only, escape for bash/YAML safely | |
| message=$(git log --format=%s -n 1 "$hash" | head -n1 | sed 's/"/\\"/g; s/`/\\`/g; s/$/\n/g') | |
| short_hash=$(git log --format=%h -n 1 "$hash") | |
| echo "Processing: '$message' ($short_hash)" | |
| # Safe prefix extraction | |
| if echo "$message" | grep -q '^[^:]*:'; then | |
| clean_message=$(echo "$message" | sed 's/^[^:]*: *//') | |
| else | |
| clean_message="$message" | |
| fi | |
| REPO_URL="${{ github.server_url }}/${{ github.repository }}" | |
| LINK="[${clean_message}](${REPO_URL}/commit/${short_hash})" | |
| case "$message" in | |
| feat:*|feat\(*|feature:*) | |
| FEATURES="${FEATURES}- feat: ${LINK}" | |
| ;; | |
| fix:*|fix\(*) | |
| FIXES="${FIXES}- fix: ${LINK}" | |
| ;; | |
| docs:*|docs\(*) | |
| DOCS="${DOCS}- docs: ${LINK}" | |
| ;; | |
| chore:*|chore\(*|ci:*|ci\(*|build:*|build\(*) | |
| CHORES="${CHORES}- chore: ${LINK}" | |
| ;; | |
| *BREAKING*|*breaking*|*!:*) | |
| BREAKING="${BREAKING}- breaking change: [${message}](${REPO_URL}/commit/${short_hash})" | |
| ;; | |
| *) | |
| OTHERS="${OTHERS}- ${LINK}" | |
| ;; | |
| esac | |
| done <<< "$HASHES" | |
| NOTES="## Release $CURRENT_TAG" | |
| if [ -n "$PREV_TAG" ]; then | |
| NOTES="${NOTES}Changes since \`${PREV_TAG}\`:" | |
| fi | |
| NOTES="${NOTES}Release commit: [\`${{ github.event.workflow_run.head_sha || github.sha }}\`](${REPO_URL}/commit/${{ github.event.workflow_run.head_sha || github.sha }})." | |
| [ -n "$BREAKING" ] && NOTES="${NOTES}### ⚠️ Breaking Changes$(printf %s "$BREAKING")" | |
| [ -n "$FEATURES" ] && NOTES="${NOTES}### ✨ Features$(printf %s "$FEATURES")" | |
| [ -n "$FIXES" ] && NOTES="${NOTES}### 🐛 Bug Fixes$(printf %s "$FIXES")" | |
| [ -n "$DOCS" ] && NOTES="${NOTES}### 📚 Documentation$(printf %s "$DOCS")" | |
| [ -n "$CHORES" ] && NOTES="${NOTES}### 🔧 Maintenance$(printf %s "$CHORES")" | |
| [ -n "$OTHERS" ] && NOTES="${NOTES}### 📦 Other Changes$(printf %s "$OTHERS")" | |
| NOTES="${NOTES}## Quick startStart the latest version:\`\`\`bashdocker run --name cinepro-core-${{ needs.prepare.outputs.app_version }} -p 3000:3000 -e TMDB_API_KEY=your-tmdb-key ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_LC }}:latest\`\`\`## For more details, check the [CinePro documentation](https://docs.cinepro.cc) and the [Version History](https://docs.cinepro.cc/core/versions)." | |
| { | |
| echo "notes<<EOF" | |
| printf '%s\n' "$NOTES" | |
| echo "EOF" | |
| } >> "$GITHUB_OUTPUT" | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v2.5.0 | |
| with: | |
| name: Release ${{ needs.prepare.outputs.app_version }} | |
| body: ${{ steps.release_notes.outputs.notes }} | |
| draft: false | |
| generate_release_notes: false | |
| tag_name: ${{ needs.prepare.outputs.app_version }} | |
| prerelease: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |