Release Stage 2 - Scheduled - alpha Run #147
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 Stage 2 - Build, Prerelease, Validate, and Publish | |
| run-name: Release Stage 2 - ${{ github.event.inputs.Scheduled}} - ${{ github.event.inputs.release_type}} Run | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| release_type: | |
| description: 'Release type to build' | |
| required: true | |
| type: choice | |
| options: | |
| - alpha | |
| - beta | |
| - stable | |
| default: 'alpha' | |
| Scheduled: | |
| description: 'Whether this is a scheduled run' | |
| required: false | |
| type: choice | |
| options: | |
| - Scheduled | |
| - Manual | |
| default: Manual | |
| release_increment: | |
| description: 'Version increment for stable releases' | |
| required: false | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| default: patch | |
| permissions: | |
| contents: write | |
| actions: write | |
| id-token: write | |
| concurrency: | |
| group: release-stage-2 | |
| cancel-in-progress: false | |
| jobs: | |
| # For alpha/beta releases, generate version using date-based approach | |
| generate_version: | |
| if: ${{ inputs.release_type!= 'stable' }} | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.version_output.outputs.version }} | |
| npm_version: ${{ steps.version_output.outputs.npm_version }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Get base version | |
| uses: reecetech/[email protected] | |
| id: release_version | |
| with: | |
| scheme: semver | |
| increment: patch | |
| - name: Generate version | |
| id: version_output | |
| run: | | |
| BASE_VERSION=${{ steps.release_version.outputs.version }} | |
| BASE_VERSION_CLEAN=$(echo "$BASE_VERSION" | sed -E 's/-[a-z0-9.]+//') | |
| DATE=$(date +%Y%m%d) | |
| VERSION="${BASE_VERSION_CLEAN}~${{ inputs.release_type}}.${DATE}" | |
| NPM_VERSION="${BASE_VERSION_CLEAN}-${{ inputs.release_type}}.${DATE}" | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "npm_version=$NPM_VERSION" >> $GITHUB_OUTPUT | |
| echo "::notice::📦 ${{ inputs.release_type}} version: $VERSION" | |
| # For stable releases, generate version using semver increment | |
| generate_stable_version: | |
| if: ${{ inputs.release_type== 'stable' }} | |
| runs-on: ubuntu-latest | |
| outputs: | |
| version: ${{ steps.release_version.outputs.version }} | |
| npm_version: ${{ steps.release_version.outputs.version }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Get next stable version | |
| uses: reecetech/[email protected] | |
| id: release_version | |
| with: | |
| scheme: semver | |
| increment: ${{ inputs.release_increment }} | |
| build_packages: | |
| name: Build Packages for (${{ matrix.name }}) v${{ needs.generate_version.outputs.version || needs.generate_stable_version.outputs.version }} | |
| needs: [generate_version, generate_stable_version] | |
| if: always() && (needs.generate_version.result == 'success' || needs.generate_stable_version.result == 'success') | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| name: [ | |
| debian-x86_64, | |
| debian-arm32v6, | |
| debian-arm64v8, | |
| ] | |
| include: | |
| - name: debian-x86_64 | |
| os: ubuntu-latest | |
| BASE_IMAGE: library/debian:bullseye | |
| QEMU_ARCH: x86_64 | |
| - name: debian-arm32v6 | |
| os: ubuntu-24.04-arm | |
| BASE_IMAGE: balenalib/raspberrypi3-debian:bullseye | |
| QEMU_ARCH: arm | |
| - name: debian-arm64v8 | |
| os: ubuntu-24.04-arm | |
| BASE_IMAGE: arm64v8/debian:bullseye | |
| QEMU_ARCH: aarch64 | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup build environment X64 | |
| if: runner.os == 'Linux' && runner.arch == 'X64' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get --yes --no-install-recommends install binfmt-support qemu-user-static | |
| docker run --rm --privileged multiarch/qemu-user-static:register --reset | |
| continue-on-error: false | |
| - name: Setup build environment ARM64 | |
| if: runner.os == 'Linux' && runner.arch == 'ARM64' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get --yes --no-install-recommends install binfmt-support | |
| continue-on-error: false | |
| - name: Build Docker image | |
| run: | | |
| docker build -f build/Dockerfile \ | |
| --build-arg BASE_IMAGE=${{ matrix.BASE_IMAGE }} \ | |
| --build-arg QEMU_ARCH=${{ matrix.QEMU_ARCH }} \ | |
| -t ${{ inputs.release_type}}-package-build \ | |
| --platform=linux/${{ matrix.QEMU_ARCH }} \ | |
| --cache-from=${{ inputs.release_type}}-package-build:latest \ | |
| --build-arg BUILDKIT_INLINE_CACHE=1 . | |
| continue-on-error: false | |
| - name: Build package | |
| run: | | |
| docker run --rm -v $(pwd):/repo \ | |
| -e PKG_RELEASE_TYPE="${{ inputs.release_type}}" \ | |
| -e PKG_RELEASE_VERSION="${{ needs.generate_version.outputs.version || needs.generate_stable_version.outputs.version }}" \ | |
| ${{ inputs.release_type}}-package-build | |
| continue-on-error: false | |
| - name: Rename package to include v | |
| run: | | |
| DEB_FILE=$(ls homebridge*.deb 2>/dev/null || echo "") | |
| if [ -z "$DEB_FILE" ]; then | |
| echo "No .deb file found. Exiting." | |
| exit 1 | |
| fi | |
| UPDATED=$(echo "$DEB_FILE" | sed -e 's/homebridge_/homebridge_v/g') | |
| mv "$DEB_FILE" "$UPDATED" | |
| - name: Rename manifest to include v | |
| run: | | |
| MANIFEST_FILE=$(ls homebridge*.manifest 2>/dev/null || echo "") | |
| if [ -z "$MANIFEST_FILE" ]; then | |
| echo "No .manifest file found. Exiting." | |
| exit 1 | |
| fi | |
| UPDATED=$(echo "$MANIFEST_FILE" | sed -e 's/homebridge_/homebridge_v/g') | |
| mv "$MANIFEST_FILE" "$UPDATED" | |
| - uses: actions/upload-artifact@v5 | |
| with: | |
| name: artifacts-${{ matrix.name }} | |
| retention-days: 7 | |
| path: | | |
| *.deb | |
| *.manifest | |
| # Step 1: Create prerelease for all release types | |
| create_prerelease: | |
| name: Create GitHub Prerelease v${{ needs.generate_version.outputs.npm_version || needs.generate_stable_version.outputs.npm_version }} | |
| needs: [generate_version, generate_stable_version, build_packages] | |
| if: always() && (needs.generate_version.result == 'success' || needs.generate_stable_version.result == 'success') && needs.build_packages.result == 'success' | |
| runs-on: ubuntu-latest | |
| outputs: | |
| release_tag: ${{ steps.release_details.outputs.tag_name }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/download-artifact@v6 | |
| with: | |
| pattern: artifacts-* | |
| merge-multiple: true | |
| - name: Display structure of downloaded files | |
| run: ls -R | |
| - name: Get the date | |
| id: date | |
| run: | | |
| echo "builddate=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT | |
| - name: Create Release Body | |
| id: release_body | |
| run: | | |
| echo "BODY_FILE=$(ls *.manifest | head -n 1)" >> $GITHUB_OUTPUT | |
| - name: Determine release details | |
| id: release_details | |
| run: | | |
| RELEASE_TYPE="${{ inputs.release_type}}" | |
| if [[ "$RELEASE_TYPE" == "stable" ]]; then | |
| VERSION="${{ needs.generate_stable_version.outputs.version }}" | |
| NPM_VERSION="${{ needs.generate_stable_version.outputs.npm_version }}" | |
| TITLE="v$VERSION - ${{ steps.date.outputs.builddate }}" | |
| else | |
| VERSION="${{ needs.generate_version.outputs.version }}" | |
| NPM_VERSION="${{ needs.generate_version.outputs.npm_version }}" | |
| TITLE_PREFIX=$(echo "$RELEASE_TYPE" | tr '[:lower:]' '[:upper:]') | |
| TITLE="$TITLE_PREFIX: Homebridge APT Pkg Release v$NPM_VERSION" | |
| fi | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "npm_version=$NPM_VERSION" >> $GITHUB_OUTPUT | |
| echo "release_title=$TITLE" >> $GITHUB_OUTPUT | |
| echo "tag_name=v$NPM_VERSION" >> $GITHUB_OUTPUT | |
| - name: Create GitHub Prerelease | |
| id: create_release | |
| uses: docker://ghcr.io/mini-bomba/create-github-release:v1.2.0 | |
| with: | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| tag: ${{ steps.release_details.outputs.tag_name }} | |
| prerelease: true | |
| draft: false | |
| name: ${{ steps.release_details.outputs.release_title }} | |
| files: | | |
| *.deb | |
| *.manifest | |
| body: ${{ steps.release_body.outputs.BODY_FILE }} | |
| clear_attachments: true | |
| fail_on_no_files: true | |
| - name: Set release tag output | |
| run: | | |
| echo "release_tag=${{ steps.release_details.outputs.tag_name }}" >> $GITHUB_OUTPUT | |
| - name: Update release body | |
| uses: tubone24/[email protected] | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAG_NAME: ${{ steps.release_details.outputs.tag_name }} | |
| with: | |
| body_path: ${{ steps.release_body.outputs.BODY_FILE }} | |
| prelease_created_notice: | |
| name: Prerelease Created Notice | |
| needs: [create_prerelease] | |
| if: always() && needs.create_prerelease.result == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Output Success Notice | |
| run: echo "::notice::📦 Created prerelease ${{ needs.create_prerelease.outputs.release_tag }} with ${{ inputs.release_type}} changes" | |
| # Step 2: Validate prerelease artifacts | |
| validate_prerelease: | |
| name: Validate GitHub Prerelease Artifacts | |
| needs: [create_prerelease, prelease_created_notice] | |
| if: always() && needs.create_prerelease.result == 'success' | |
| uses: ./.github/workflows/reusable-validate-homebridge.yml | |
| with: | |
| validation_type: "prerelease" | |
| release_tag: ${{ needs.create_prerelease.outputs.release_tag }} | |
| prerelease: true | |
| latest: false | |
| # Step 3: Promote prerelease to full release after validation passes | |
| promote_to_release: | |
| name: Promote to Full Release | |
| needs: [validate_prerelease, create_prerelease] | |
| if: always() && needs.validate_prerelease.result == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Promote prerelease to full release | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const { data: release } = await github.rest.repos.getReleaseByTag({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| tag: '${{ needs.create_prerelease.outputs.release_tag }}' | |
| }); | |
| await github.rest.repos.updateRelease({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| release_id: release.id, | |
| prerelease: false | |
| }); | |
| console.log('Successfully promoted prerelease to full release: ${{ needs.create_prerelease.outputs.release_tag }}'); | |
| # Step 4: Publish to APT repository | |
| publish_apt: | |
| name: Publish to APT Repository | |
| needs: [promote_to_release, create_prerelease, generate_version, generate_stable_version] | |
| if: always() && needs.promote_to_release.result == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Download from GitHub Release | |
| uses: robinraju/[email protected] | |
| with: | |
| tag: ${{ needs.create_prerelease.outputs.release_tag }} | |
| fileName: 'homebridge*.deb' | |
| out-file-path: 'repo/' | |
| - name: Display structure of downloaded files | |
| run: ls -R | |
| - name: Import GPG Key | |
| uses: crazy-max/ghaction-import-gpg@v6 | |
| with: | |
| gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} | |
| passphrase: ${{ secrets.GPG_PASSPHRASE }} | |
| - name: Install deb-s3 | |
| run: | | |
| curl -sLO https://github.com/deb-s3/deb-s3/releases/download/0.11.8/deb-s3-0.11.8.gem | |
| sudo gem install deb-s3-0.11.8.gem | |
| - name: Upload to APT Repository | |
| run: | | |
| sudo chown -R $USER: repo/ | |
| deb-s3 upload \ | |
| --codename=${{ inputs.release_type== 'stable' && 'stable' || inputs.release_type}} \ | |
| --suite=${{ inputs.release_type== 'stable' && 'stable' || inputs.release_type}} \ | |
| --preserve-versions \ | |
| --s3-region=us-west-2 \ | |
| --bucket repo.homebridge.io \ | |
| --access-key-id=${{ secrets.AWS_ACCESS_KEY_ID }} \ | |
| --secret-access-key=${{ secrets.AWS_SECRET_ACCESS_KEY }} \ | |
| --sign=${{ secrets.GPG_KEY_ID }} \ | |
| repo/homebridge_v*.deb repo/*.deb | |
| # Step 5: Purge CloudFlare cache | |
| purge_cloudflare_cache: | |
| name: Purge Cloudflare Cache | |
| needs: [publish_apt] | |
| if: always() && needs.publish_apt.result == 'success' | |
| uses: ./.github/workflows/reusable-purge_cloudflare_cache.yml | |
| secrets: | |
| CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} | |
| CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} | |
| # Step 6: Validate APT installation after publishing | |
| validate_apt: | |
| name: Validate APT Installation | |
| needs: [purge_cloudflare_cache, publish_apt] | |
| if: always() && needs.publish_apt.result == 'success' | |
| uses: ./.github/workflows/reusable-validate-homebridge.yml | |
| with: | |
| validation_type: "apt" | |
| release_channel: ${{ inputs.release_type== 'stable' && 'stable' || inputs.release_type}} | |
| # Step 7: Publish to NPM | |
| publish_to_npm: | |
| name: Publish to NPM | |
| needs: [validate_apt, create_prerelease, generate_version, generate_stable_version] | |
| if: always() && needs.validate_apt.result == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout Repo | |
| uses: actions/checkout@v6 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: latest | |
| registry-url: 'https://registry.npmjs.org' | |
| - name: Set package.json version | |
| run: | | |
| NPM_VERSION="${{ needs.generate_version.outputs.npm_version || needs.generate_stable_version.outputs.npm_version }}" | |
| echo "Setting version to $NPM_VERSION" | |
| jq ".version = \"$NPM_VERSION\"" package.json > tmp.$$.json && mv tmp.$$.json package.json | |
| cat package.json | |
| - name: Publish to npm | |
| run: npm publish --access public --tag ${{ inputs.release_type== 'stable' && 'latest' || inputs.release_type}} | |
| env: | |
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Output Success Notice | |
| run: echo "::notice::Published @homebridge/homebridge-apt-pkg as version ${{ needs.generate_version.outputs.npm_version || needs.generate_stable_version.outputs.npm_version }} with ${{ inputs.release_type== 'stable' && 'latest' || inputs.release_type}} tag" | |
| # Step 8: Notify Discord (for stable releases only) | |
| notify_discord: | |
| name: Notify Discord | |
| needs: [publish_to_npm, create_prerelease] | |
| if: always() && needs.publish_to_npm.result == 'success' && inputs.release_type== 'stable' | |
| uses: homebridge/.github/.github/workflows/discord-webhooks.yml@latest | |
| with: | |
| title: "Homebridge APT Package Released" | |
| description: "Version `${{ needs.create_prerelease.outputs.release_tag }}` has been released." | |
| url: "https://github.com/homebridge/homebridge-apt-pkg/releases/tag/${{ needs.create_prerelease.outputs.release_tag }}" | |
| secrets: | |
| DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL_LATEST }} | |
| # stable release should be marked as non-prerelease, and latest | |
| stable-release-is-latest: | |
| needs: [create_prerelease,promote_to_release] | |
| if: ${{ inputs.release_type == 'stable' }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| # Mark stable release as non-prerelease and latest | |
| - name: Mark Stable Release as Non-Prerelease and Latest | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| gh release edit ${{ needs.create_prerelease.outputs.release_tag }} \ | |
| --prerelease=false \ | |
| --latest |