From 091df401e637fc06f164c79643f74cfbf8f3f66a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 01:20:11 +0000 Subject: [PATCH 01/16] Initial plan From ef562d62805a93703dbfd2519a6e6673647c8069 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 01:27:37 +0000 Subject: [PATCH 02/16] Create reusable GitHub Actions workflows and update beta, alpha, and stable workflows to use them Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- ...-stage-2_build_alpha_release_and_store.yml | 240 +++--------------- ...a-stage-2_build_beta_release_and_store.yml | 240 +++--------------- .github/workflows/reusable-build-package.yml | 84 ++++++ .../reusable-create-github-release.yml | 69 +++++ .../workflows/reusable-generate-version.yml | 50 ++++ .github/workflows/reusable-publish-apt.yml | 89 +++++++ .github/workflows/reusable-publish-npm.yml | 51 ++++ .../stage-1_create_a_release_and_store.yml | 67 +---- .../stage-3_promote_release_to_apt.yml | 50 +--- 9 files changed, 422 insertions(+), 518 deletions(-) create mode 100644 .github/workflows/reusable-build-package.yml create mode 100644 .github/workflows/reusable-create-github-release.yml create mode 100644 .github/workflows/reusable-generate-version.yml create mode 100644 .github/workflows/reusable-publish-apt.yml create mode 100644 .github/workflows/reusable-publish-npm.yml diff --git a/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml b/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml index a9aa8a0..9e23e7b 100644 --- a/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml +++ b/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml @@ -49,198 +49,40 @@ jobs: generate_alpha_version: needs: check-changes if: ${{ needs.check-changes.outputs.alpha_only == 'true' }} - name: Generate Alpha Package Version - runs-on: ubuntu-latest - outputs: - version: ${{ steps.alpha_version.outputs.version }} - npm_version: ${{ steps.alpha_version.outputs.npm_version }} - steps: - - uses: actions/checkout@v4 - - - name: Get base version - uses: reecetech/version-increment@2023.10.1 - id: release_version - with: - scheme: semver - increment: patch - - - name: Generate alpha package version - id: alpha_version - 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}~alpha.${DATE}" - NPM_VERSION="${BASE_VERSION_CLEAN}-alpha.${DATE}" - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "npm_version=$NPM_VERSION" >> $GITHUB_OUTPUT - echo "::notice::📦 Alpha version: $VERSION" + uses: ./.github/workflows/reusable-generate-version.yml + with: + release_type: alpha build_alpha_and_store: - name: Build Alpha Packages for (${{ matrix.name }}) needs: [generate_alpha_version] - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - name: [debian-x86_64, debian-arm64v8, debian-arm32v6] - include: - - name: debian-x86_64 - os: ubuntu-latest - BASE_IMAGE: library/debian:bullseye - QEMU_ARCH: x86_64 - - - name: debian-arm64v8 - os: ubuntu-24.04-arm - BASE_IMAGE: arm64v8/debian:bullseye - QEMU_ARCH: aarch64 - - - name: debian-arm32v6 - os: ubuntu-24.04-arm - BASE_IMAGE: balenalib/raspberrypi3-debian:bullseye - QEMU_ARCH: arm - - steps: - - uses: actions/checkout@v4 - - - 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 alpha-package-build \ - --platform=linux/${{ matrix.QEMU_ARCH }} \ - --cache-from=alpha-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="alpha" \ - -e PKG_RELEASE_VERSION="${{ needs.generate_alpha_version.outputs.version }}" \ - alpha-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@v4 - with: - name: artifacts-${{ matrix.name }} - retention-days: 7 - path: | - homebridge_v*.deb - homebridge_v*.manifest + uses: ./.github/workflows/reusable-build-package.yml + with: + release_type: alpha + release_version: ${{ needs.generate_alpha_version.outputs.version }} + docker_tag_prefix: alpha-package-build publish_apt_alpha: - name: APT Alpha Repo Publish ${{ needs.generate_alpha_version.outputs.version }} - runs-on: ubuntu-latest needs: [generate_alpha_version, build_alpha_and_store] - steps: - - uses: actions/checkout@v4 - - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - merge-multiple: true - 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 Alpha APT Repo - run: | - sudo chown -R $USER: repo/ - deb-s3 upload \ - --codename=alpha \ - --suite=alpha \ - --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 + uses: ./.github/workflows/reusable-publish-apt.yml + with: + codename: alpha + suite: alpha + release_version: ${{ needs.generate_alpha_version.outputs.version }} + secrets: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} publish_github_alpha_release: - name: Publish GitHub Alpha Release v${{ needs.generate_alpha_version.outputs.npm_version }} needs: [check-changes, publish_apt_alpha, generate_alpha_version] - runs-on: ubuntu-latest - steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - merge-multiple: true - path: artifacts/ - - - name: Read amd64 manifest content - id: read_manifest - run: | - echo "MANIFEST_FILE=$(ls artifacts/*.manifest | head -n 1)" >> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ needs.generate_alpha_version.outputs.npm_version }} - name: Alpha Release v${{ needs.generate_alpha_version.outputs.npm_version }} - prerelease: true - files: | - artifacts/homebridge_v*.deb - artifacts/homebridge_v*.manifest - body_path: ${{ steps.read_manifest.outputs.MANIFEST_FILE }} - body: | - Homebridge Apt Package Manifest - - Release Version: v${{ needs.generate_alpha_version.outputs.npm_version }} - Release Type: alpha - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: ./.github/workflows/reusable-create-github-release.yml + with: + tag_name: v${{ needs.generate_alpha_version.outputs.npm_version }} + release_name: Alpha Release v${{ needs.generate_alpha_version.outputs.npm_version }} + release_type: alpha + prerelease: true purge_cloudflare_cache: name: Purge Cloudflare Cache @@ -252,29 +94,9 @@ jobs: publish_to_npm: needs: [generate_alpha_version, build_alpha_and_store] - name: NPM Publish ${{ needs.generate_alpha_version.outputs.npm_version }} - runs-on: ubuntu-latest - steps: - - name: Checkout Repo - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: latest - registry-url: 'https://registry.npmjs.org' - - - name: Set package.json version - run: | - ALPHA_VERSION="${{ needs.generate_alpha_version.outputs.npm_version }}" - echo "Setting version to $ALPHA_VERSION" - jq ".version = \"$ALPHA_VERSION\"" package.json > tmp.$$.json && mv tmp.$$.json package.json - cat package.json - - - name: Publish to npm with alpha tag - run: npm publish --access public --tag alpha - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Output Success Notice - run: echo "::notice::Published @homebridge/homebridge-apt-pkg as version ${{ needs.generate_alpha_version.outputs.npm_version }} with alpha tag" \ No newline at end of file + uses: ./.github/workflows/reusable-publish-npm.yml + with: + npm_version: ${{ needs.generate_alpha_version.outputs.npm_version }} + npm_tag: alpha + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/beta-stage-2_build_beta_release_and_store.yml b/.github/workflows/beta-stage-2_build_beta_release_and_store.yml index 763245d..8bfd58a 100644 --- a/.github/workflows/beta-stage-2_build_beta_release_and_store.yml +++ b/.github/workflows/beta-stage-2_build_beta_release_and_store.yml @@ -49,198 +49,40 @@ jobs: generate_beta_version: needs: check-changes if: ${{ needs.check-changes.outputs.beta_only == 'true' }} - name: Generate Beta Package Version - runs-on: ubuntu-latest - outputs: - version: ${{ steps.beta_version.outputs.version }} - npm_version: ${{ steps.beta_version.outputs.npm_version }} - steps: - - uses: actions/checkout@v4 - - - name: Get base version - uses: reecetech/version-increment@2023.10.1 - id: release_version - with: - scheme: semver - increment: patch - - - name: Generate beta package version - id: beta_version - 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}~beta.${DATE}" - NPM_VERSION="${BASE_VERSION_CLEAN}-beta.${DATE}" - echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "npm_version=$NPM_VERSION" >> $GITHUB_OUTPUT - echo "::notice::📦 Beta version: $VERSION" + uses: ./.github/workflows/reusable-generate-version.yml + with: + release_type: beta build_beta_and_store: - name: Build Beta Packages for (${{ matrix.name }}) needs: [generate_beta_version] - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - name: [debian-x86_64, debian-arm64v8, debian-arm32v6] - include: - - name: debian-x86_64 - os: ubuntu-latest - BASE_IMAGE: library/debian:bullseye - QEMU_ARCH: x86_64 - - - name: debian-arm64v8 - os: ubuntu-24.04-arm - BASE_IMAGE: arm64v8/debian:bullseye - QEMU_ARCH: aarch64 - - - name: debian-arm32v6 - os: ubuntu-24.04-arm - BASE_IMAGE: balenalib/raspberrypi3-debian:bullseye - QEMU_ARCH: arm - - steps: - - uses: actions/checkout@v4 - - - 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 beta-package-build \ - --platform=linux/${{ matrix.QEMU_ARCH }} \ - --cache-from=beta-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="beta" \ - -e PKG_RELEASE_VERSION="${{ needs.generate_beta_version.outputs.version }}" \ - beta-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@v4 - with: - name: artifacts-${{ matrix.name }} - retention-days: 7 - path: | - homebridge_v*.deb - homebridge_v*.manifest + uses: ./.github/workflows/reusable-build-package.yml + with: + release_type: beta + release_version: ${{ needs.generate_beta_version.outputs.version }} + docker_tag_prefix: beta-package-build publish_apt_beta: - name: APT Beta Repo Publish ${{ needs.generate_beta_version.outputs.version }} - runs-on: ubuntu-latest needs: [generate_beta_version, build_beta_and_store] - steps: - - uses: actions/checkout@v4 - - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - merge-multiple: true - 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 Beta APT Repo - run: | - sudo chown -R $USER: repo/ - deb-s3 upload \ - --codename=beta \ - --suite=beta \ - --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 + uses: ./.github/workflows/reusable-publish-apt.yml + with: + codename: beta + suite: beta + release_version: ${{ needs.generate_beta_version.outputs.version }} + secrets: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} publish_github_beta_release: - name: Publish GitHub Beta Release v${{ needs.generate_beta_version.outputs.npm_version }} needs: [check-changes, publish_apt_beta, generate_beta_version] - runs-on: ubuntu-latest - steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - merge-multiple: true - path: artifacts/ - - - name: Read amd64 manifest content - id: read_manifest - run: | - echo "MANIFEST_FILE=$(ls artifacts/*.manifest | head -n 1)" >> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ needs.generate_beta_version.outputs.npm_version }} - name: Beta Release v${{ needs.generate_beta_version.outputs.npm_version }} - prerelease: true - files: | - artifacts/homebridge_v*.deb - artifacts/homebridge_v*.manifest - body_path: ${{ steps.read_manifest.outputs.MANIFEST_FILE }} - body: | - Homebridge Apt Package Manifest - - Release Version: v${{ needs.generate_beta_version.outputs.npm_version }} - Release Type: beta - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: ./.github/workflows/reusable-create-github-release.yml + with: + tag_name: v${{ needs.generate_beta_version.outputs.npm_version }} + release_name: Beta Release v${{ needs.generate_beta_version.outputs.npm_version }} + release_type: beta + prerelease: true purge_cloudflare_cache: name: Purge Cloudflare Cache @@ -252,29 +94,9 @@ jobs: publish_to_npm: needs: [generate_beta_version, build_beta_and_store] - name: NPM Publish ${{ needs.generate_beta_version.outputs.npm_version }} - runs-on: ubuntu-latest - steps: - - name: Checkout Repo - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: latest - registry-url: 'https://registry.npmjs.org' - - - name: Set package.json version - run: | - BETA_VERSION="${{ needs.generate_beta_version.outputs.npm_version }}" - echo "Setting version to $BETA_VERSION" - jq ".version = \"$BETA_VERSION\"" package.json > tmp.$$.json && mv tmp.$$.json package.json - cat package.json - - - name: Publish to npm with beta tag - run: npm publish --access public --tag beta - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Output Success Notice - run: echo "::notice::Published @homebridge/homebridge-apt-pkg as version ${{ needs.generate_beta_version.outputs.npm_version }} with beta tag" \ No newline at end of file + uses: ./.github/workflows/reusable-publish-npm.yml + with: + npm_version: ${{ needs.generate_beta_version.outputs.npm_version }} + npm_tag: beta + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/reusable-build-package.yml b/.github/workflows/reusable-build-package.yml new file mode 100644 index 0000000..76d51d5 --- /dev/null +++ b/.github/workflows/reusable-build-package.yml @@ -0,0 +1,84 @@ +name: "Reusable - Build APT Package" + +on: + workflow_call: + inputs: + release_type: + description: 'Release type (stable, beta, alpha)' + required: true + type: string + release_version: + description: 'Release version string' + required: true + type: string + docker_tag_prefix: + description: 'Docker tag prefix for build image' + required: false + type: string + default: 'package-build' + +jobs: + build_package_and_store: + name: Build Packages for (${{ matrix.name }}) v${{ inputs.release_version }} + 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-latest + BASE_IMAGE: balenalib/raspberrypi3-debian:bullseye + QEMU_ARCH: arm + + - name: debian-arm64v8 + os: ubuntu-latest + BASE_IMAGE: arm64v8/debian:bullseye + QEMU_ARCH: aarch64 + + steps: + - uses: actions/checkout@v4 + + - name: Linux - Setup Dependencies + if: runner.os == 'Linux' + 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 + + - name: Linux - Build Docker Image + if: runner.os == 'Linux' + run: | + docker build -f build/Dockerfile --build-arg BASE_IMAGE=${{ matrix.BASE_IMAGE }} --build-arg QEMU_ARCH=${{ matrix.QEMU_ARCH }} -t ${{ inputs.docker_tag_prefix }}-${{ inputs.release_type }} --platform=linux/${{ matrix.QEMU_ARCH }} . + + - name: Linux - Build Package + if: runner.os == 'Linux' + run: | + docker run --rm -v $(pwd):/repo -e PKG_RELEASE_TYPE="${{ inputs.release_type }}" -e PKG_RELEASE_VERSION="${{ inputs.release_version }}" ${{ inputs.docker_tag_prefix }}-${{ inputs.release_type }} + + - name: Rename package to include v + run: | + UPDATED=$(ls homebridge*.deb | sed -e 's/homebridge_/homebridge_v/g') + mv homebridge_*.deb ${UPDATED} + + - name: Rename manifest to include v + run: | + UPDATED=$(ls homebridge*.manifest | sed -e 's/homebridge_/homebridge_v/g') + mv homebridge_*.manifest ${UPDATED} + + - uses: actions/upload-artifact@v4 + with: + name: artifacts-${{ matrix.name }} + retention-days: 7 + path: | + *.deb + *.manifest \ No newline at end of file diff --git a/.github/workflows/reusable-create-github-release.yml b/.github/workflows/reusable-create-github-release.yml new file mode 100644 index 0000000..073871e --- /dev/null +++ b/.github/workflows/reusable-create-github-release.yml @@ -0,0 +1,69 @@ +name: "Reusable - Create GitHub Release" + +on: + workflow_call: + inputs: + tag_name: + description: 'Release tag name' + required: true + type: string + release_name: + description: 'Release name/title' + required: true + type: string + release_type: + description: 'Release type (stable, beta, alpha)' + required: true + type: string + prerelease: + description: 'Mark as prerelease' + required: false + type: boolean + default: false + draft: + description: 'Mark as draft' + required: false + type: boolean + default: false + body_prefix: + description: 'Additional content to prepend to release body' + required: false + type: string + default: '' + +jobs: + create_github_release: + name: Create GitHub Release ${{ inputs.tag_name }} + runs-on: ubuntu-latest + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + pattern: artifacts-* + merge-multiple: true + path: artifacts/ + + - name: Read manifest content + id: read_manifest + run: | + echo "MANIFEST_FILE=$(ls artifacts/*.manifest | head -n 1)" >> $GITHUB_OUTPUT + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ inputs.tag_name }} + name: ${{ inputs.release_name }} + prerelease: ${{ inputs.prerelease }} + draft: ${{ inputs.draft }} + files: | + artifacts/homebridge_v*.deb + artifacts/homebridge_v*.manifest + body_path: ${{ steps.read_manifest.outputs.MANIFEST_FILE }} + body: | + ${{ inputs.body_prefix }} + Homebridge Apt Package Manifest + + Release Version: ${{ inputs.tag_name }} + Release Type: ${{ inputs.release_type }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/reusable-generate-version.yml b/.github/workflows/reusable-generate-version.yml new file mode 100644 index 0000000..4f42ef8 --- /dev/null +++ b/.github/workflows/reusable-generate-version.yml @@ -0,0 +1,50 @@ +name: "Reusable - Generate Version" + +on: + workflow_call: + inputs: + release_type: + description: 'Release type (beta, alpha)' + required: true + type: string + increment: + description: 'Version increment type' + required: false + type: string + default: 'patch' + outputs: + version: + description: 'APT package version' + value: ${{ jobs.generate_version.outputs.version }} + npm_version: + description: 'NPM version' + value: ${{ jobs.generate_version.outputs.npm_version }} + +jobs: + generate_version: + name: Generate ${{ inputs.release_type }} Package Version + runs-on: ubuntu-latest + outputs: + version: ${{ steps.version_output.outputs.version }} + npm_version: ${{ steps.version_output.outputs.npm_version }} + steps: + - uses: actions/checkout@v4 + + - name: Get base version + uses: reecetech/version-increment@2023.10.1 + id: release_version + with: + scheme: semver + increment: ${{ inputs.increment }} + + - 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" \ No newline at end of file diff --git a/.github/workflows/reusable-publish-apt.yml b/.github/workflows/reusable-publish-apt.yml new file mode 100644 index 0000000..4538e56 --- /dev/null +++ b/.github/workflows/reusable-publish-apt.yml @@ -0,0 +1,89 @@ +name: "Reusable - Publish to APT Repository" + +on: + workflow_call: + inputs: + codename: + description: 'APT repository codename (stable, beta, alpha, test)' + required: true + type: string + suite: + description: 'APT repository suite' + required: false + type: string + default: 'stable' + release_version: + description: 'Release version for display' + required: true + type: string + download_from_release: + description: 'Download files from GitHub release instead of artifacts' + required: false + type: boolean + default: false + release_tag: + description: 'Release tag to download from (when download_from_release is true)' + required: false + type: string + secrets: + GPG_PRIVATE_KEY: + required: true + GPG_PASSPHRASE: + required: true + GPG_KEY_ID: + required: true + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + +jobs: + publish_to_apt: + name: APT Repo Publish (${{ inputs.codename }}) ${{ inputs.release_version }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Download from GitHub Release + if: ${{ inputs.download_from_release }} + uses: robinraju/release-downloader@v1.11 + with: + tag: ${{ inputs.release_tag }} + fileName: 'homebridge*.deb' + out-file-path: 'repo/' + + - name: Download artifacts + if: ${{ !inputs.download_from_release }} + uses: actions/download-artifact@v4 + with: + pattern: artifacts-* + merge-multiple: true + 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.codename }} \ + --suite=${{ inputs.suite }} \ + --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 \ No newline at end of file diff --git a/.github/workflows/reusable-publish-npm.yml b/.github/workflows/reusable-publish-npm.yml new file mode 100644 index 0000000..e5dcfb5 --- /dev/null +++ b/.github/workflows/reusable-publish-npm.yml @@ -0,0 +1,51 @@ +name: "Reusable - Publish to NPM" + +on: + workflow_call: + inputs: + npm_version: + description: 'NPM version string' + required: true + type: string + npm_tag: + description: 'NPM tag (latest, beta, alpha)' + required: false + type: string + default: 'latest' + package_name: + description: 'NPM package name' + required: false + type: string + default: '@homebridge/homebridge-apt-pkg' + secrets: + NPM_TOKEN: + required: true + +jobs: + publish_to_npm: + name: NPM Publish ${{ inputs.npm_version }} + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: latest + registry-url: 'https://registry.npmjs.org' + + - name: Set package.json version + run: | + NPM_VERSION="${{ inputs.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.npm_tag }} + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Output Success Notice + run: echo "::notice::Published ${{ inputs.package_name }} as version ${{ inputs.npm_version }} with ${{ inputs.npm_tag }} tag" \ No newline at end of file diff --git a/.github/workflows/stage-1_create_a_release_and_store.yml b/.github/workflows/stage-1_create_a_release_and_store.yml index 4a3e21d..da71c86 100644 --- a/.github/workflows/stage-1_create_a_release_and_store.yml +++ b/.github/workflows/stage-1_create_a_release_and_store.yml @@ -92,69 +92,10 @@ jobs: build_package_and_store: needs: [create_draft_prerelease] - name: Build Packages for (${{ matrix.name }}) v${{ needs.create_draft_prerelease.outputs.version }} - 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-latest - BASE_IMAGE: balenalib/raspberrypi3-debian:bullseye - QEMU_ARCH: arm - - - name: debian-arm64v8 - os: ubuntu-latest - BASE_IMAGE: arm64v8/debian:bullseye - QEMU_ARCH: aarch64 - - steps: - - uses: actions/checkout@v4 - - - name: Linux - Setup Dependencies - if: runner.os == 'Linux' - 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 - - - name: Linux - Build Docker Image - if: runner.os == 'Linux' - run: | - docker build -f build/Dockerfile --build-arg BASE_IMAGE=${{ matrix.BASE_IMAGE }} --build-arg QEMU_ARCH=${{ matrix.QEMU_ARCH }} -t package-build --platform=linux/${{ matrix.QEMU_ARCH }} . - - - name: Linux - Build Package - if: runner.os == 'Linux' - run: | - docker run --rm -v $(pwd):/repo -e PKG_RELEASE_TYPE="${{ needs.create_draft_prerelease.outputs.release_type }}" -e PKG_RELEASE_VERSION="${{ needs.create_draft_prerelease.outputs.version }}" package-build - - - name: Rename package to include v - run: | - UPDATED=$(ls homebridge*.deb | sed -e 's/homebridge_/homebridge_v/g') - mv homebridge_*.deb ${UPDATED} - - - name: Rename manifest to include v - run: | - UPDATED=$(ls homebridge*.manifest | sed -e 's/homebridge_/homebridge_v/g') - mv homebridge_*.manifest ${UPDATED} - - - uses: actions/upload-artifact@v4 - with: - name: artifacts-${{ matrix.name }} - retention-days: 7 - path: | - *.deb - *.manifest + uses: ./.github/workflows/reusable-build-package.yml + with: + release_type: ${{ needs.create_draft_prerelease.outputs.release_type }} + release_version: ${{ needs.create_draft_prerelease.outputs.version }} publish_prerelease: # Publish the pre-release to the GitHub Releases page diff --git a/.github/workflows/stage-3_promote_release_to_apt.yml b/.github/workflows/stage-3_promote_release_to_apt.yml index 057eaae..9bce577 100644 --- a/.github/workflows/stage-3_promote_release_to_apt.yml +++ b/.github/workflows/stage-3_promote_release_to_apt.yml @@ -43,44 +43,20 @@ jobs: fi update_apt_repo: - name: Update APT Repo - runs-on: ubuntu-latest needs: [determine_release_tag, release_type] - steps: - - uses: actions/checkout@v4 - - - name: Download Release Assets - uses: robinraju/release-downloader@v1.11 - with: - tag: ${{ needs.determine_release_tag.outputs.release_tag }} - fileName: 'homebridge*.deb' - out-file-path: 'repo/' - - - name: Import GPG Key - id: import_gpg - uses: crazy-max/ghaction-import-gpg@v4 - 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 Package to APT Repo - run: | - sudo chown -R $USER: repo/ - deb-s3 upload \ - --codename=${{ needs.release_type.outputs.release_type }} \ - --suite=stable \ - --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/*.deb + uses: ./.github/workflows/reusable-publish-apt.yml + with: + codename: ${{ needs.release_type.outputs.release_type }} + suite: stable + release_version: ${{ needs.determine_release_tag.outputs.release_tag }} + download_from_release: true + release_tag: ${{ needs.determine_release_tag.outputs.release_tag }} + secrets: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} purge_cloudflare_cache: name: Purge Cloudflare Cache From 3ef87b57fcd0a915022242ca377073c90190c87d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 01:29:52 +0000 Subject: [PATCH 03/16] Add documentation for consolidated GitHub Actions workflow structure Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .github/workflows/README.md | 120 ++++++++++++++++++++++++++++++++++++ BUILD.md | 27 +++++++- 2 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/README.md diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..fbc52f1 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,120 @@ +# Consolidated GitHub Actions Workflows + +This directory contains the GitHub Actions workflows for the Homebridge APT package repository. As of the consolidation effort (Issue #190), the workflows have been reorganized to reduce duplication and improve maintainability. + +## Reusable Workflows + +The following reusable workflows contain common functionality that is shared across multiple release streams: + +### `reusable-build-package.yml` +Builds APT packages for all supported architectures using Docker and QEMU emulation. + +**Inputs:** +- `release_type`: Release type (stable, beta, alpha) +- `release_version`: Release version string +- `docker_tag_prefix`: Docker tag prefix for build image + +**Outputs:** Artifacts containing .deb and .manifest files for each architecture + +### `reusable-publish-apt.yml` +Publishes packages to the APT repository hosted on S3. + +**Inputs:** +- `codename`: APT repository codename (stable, beta, alpha, test) +- `suite`: APT repository suite +- `release_version`: Release version for display +- `download_from_release`: Whether to download from GitHub release vs artifacts +- `release_tag`: Release tag to download from (when downloading from release) + +**Secrets:** GPG keys and AWS credentials + +### `reusable-create-github-release.yml` +Creates GitHub releases with packages and manifests. + +**Inputs:** +- `tag_name`: Release tag name +- `release_name`: Release name/title +- `release_type`: Release type (stable, beta, alpha) +- `prerelease`: Mark as prerelease (boolean) +- `draft`: Mark as draft (boolean) +- `body_prefix`: Additional content for release body + +### `reusable-publish-npm.yml` +Publishes the package to NPM with specified version and tag. + +**Inputs:** +- `npm_version`: NPM version string +- `npm_tag`: NPM tag (latest, beta, alpha) +- `package_name`: NPM package name + +**Secrets:** NPM token + +### `reusable-generate-version.yml` +Generates version strings for beta and alpha releases using date stamps. + +**Inputs:** +- `release_type`: Release type (beta, alpha) +- `increment`: Version increment type + +**Outputs:** +- `version`: APT package version +- `npm_version`: NPM version + +## Release Stream Workflows + +### Stable Release (4 stages) +1. **`stage-1_create_a_release_and_store.yml`** - Creates pre-release and builds packages + - Uses: `reusable-build-package.yml` + - Triggered by: Dependabot updates to package.json files + +2. **`stage-2_pre-release_validation.yml`** - Validates pre-release packages + - Downloads and tests AMD64 package installation + +3. **`stage-3_promote_release_to_apt.yml`** - Promotes to APT repository + - Uses: `reusable-publish-apt.yml` + - Triggered by: Release publication + +4. **`Stage-4_post_release_validation.yml`** - Post-release validation + - Tests APT installation from repository + +### Beta Release (2 stages) +1. **`beta-stage-1_update_beta_dependencies.yml`** - Updates beta dependencies + - Managed by homebridge-beta-bot + +2. **`beta-stage-2_build_beta_release_and_store.yml`** - Builds and releases beta packages + - Uses: `reusable-generate-version.yml`, `reusable-build-package.yml`, `reusable-publish-apt.yml`, `reusable-create-github-release.yml`, `reusable-publish-npm.yml` + +### Alpha Release (2 stages) +1. **`alpha-stage-1_update_alpha_dependencies.yml`** - Updates alpha dependencies + - Managed by homebridge-alpha-bot + +2. **`alpha-stage-2_build_alpha_release_and_store.yml`** - Builds and releases alpha packages + - Uses: `reusable-generate-version.yml`, `reusable-build-package.yml`, `reusable-publish-apt.yml`, `reusable-create-github-release.yml`, `reusable-publish-npm.yml` + +## Utility Workflows + +- **`stage-3_5_purge_cloudflare_cache.yml`** - Reusable CloudFlare cache purging +- **`stage-5_update_version_on_npm.yml`** - NPM version updates for stable releases +- **`purge.yml`** - Manual cache purging +- **`stale.yml`** - Stale issue management +- **`pr-labeler.yml`** - PR labeling + +## Benefits of Consolidation + +1. **Reduced Duplication**: ~400 lines of duplicate YAML eliminated +2. **Consistency**: All builds use identical Docker setup and architecture matrix +3. **Maintainability**: Common changes only need to be made in reusable workflows +4. **Flexibility**: Reusable workflows are parameterized for different use cases +5. **Reliability**: Shared logic reduces the chance of inconsistencies between release streams + +## Making Changes + +When modifying build or publishing logic: + +1. **Package Building**: Edit `reusable-build-package.yml` +2. **APT Publishing**: Edit `reusable-publish-apt.yml` +3. **GitHub Releases**: Edit `reusable-create-github-release.yml` +4. **NPM Publishing**: Edit `reusable-publish-npm.yml` +5. **Version Generation**: Edit `reusable-generate-version.yml` + +The changes will automatically apply to all release streams that use these workflows. \ No newline at end of file diff --git a/BUILD.md b/BUILD.md index 259e715..d1d6177 100644 --- a/BUILD.md +++ b/BUILD.md @@ -65,13 +65,27 @@ For detailed usage instructions, see [`scripts/README.md`](scripts/README.md). ## Actions +The repository uses a consolidated set of GitHub Actions workflows that share common functionality through reusable workflows to reduce duplication and improve maintainability. + +### Workflow Architecture + +The workflow system consists of: +- **5 Reusable Workflows**: Common functionality shared across release streams +- **Stable Release Stream**: 4-stage workflow for production releases +- **Beta Release Stream**: 2-stage workflow for beta releases +- **Alpha Release Stream**: 2-stage workflow for alpha releases + +For detailed workflow documentation, see [`.github/workflows/README.md`](.github/workflows/README.md). + +### Stable Release Process + ### Stage 1 - Create a pre-release and build APT package >Average Execution time: Approx 40 minutes This job is triggered when any package.json file (stable/32bit, stable/64bit, or legacy root) is updated on the latest branch, and the author of the change is dependabot. 1. Determine release version based on either manual workflow input or the latest release. -2. Build apt packages for x86_64, Arm ( RPI 32 bit), and aarch64 ( RPI 64 bit ). +2. Build apt packages for x86_64, Arm ( RPI 32 bit), and aarch64 ( RPI 64 bit ) using the reusable build workflow. 3. Apt packages are stored as an artifact against the workflow. 4. Create a Pre-Release, and attach the artifacts. @@ -89,7 +103,7 @@ This job is triggered by the the publishing of a prerelease or the completion of This job is triggered by the changing of the Release status from `pre-release` to `released`. Changing the prerelease to release is a manual step. 1. Release assets are downloaded from the latest release -2. Assets are promoted to `repo.homebridge.io` +2. Assets are promoted to `repo.homebridge.io` using the reusable APT publishing workflow 3. Cloud flare cache is purged ### Stage 4 - Post Release Validation @@ -100,6 +114,15 @@ This job is triggered by the successful completion of step 3 1. Download the current homebridge-apt-pkg for x86 and install. 2. Check that homebridge starts +### Beta and Alpha Release Processes + +Both beta and alpha releases follow a similar 2-stage process: + +1. **Dependency Updates**: Automated bot updates to package.json files in beta/ or alpha/ directories +2. **Build and Release**: Automated build, APT publishing, GitHub release creation, and NPM publishing using consolidated reusable workflows + +These processes automatically generate date-stamped versions and publish to their respective channels (beta/alpha) in the APT repository and NPM registry. + ## Package Manifest Each release includes a Package Manifest file that contains: From 0dd9a0b46f38fa95301d710b18cfb4457906a63c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 01:39:00 +0000 Subject: [PATCH 04/16] Further consolidate alpha and beta workflows by creating reusable components - Create reusable-update-dependencies.yml for Stage 1 dependency update logic - Create reusable-build-and-release-prerelease.yml for Stage 2 build and release logic - Update alpha and beta Stage 1 workflows to use reusable-update-dependencies.yml - Update alpha and beta Stage 2 workflows to use reusable-build-and-release-prerelease.yml - Update workflow documentation to reflect additional consolidation - Eliminate ~200 additional lines of duplicate YAML between alpha and beta streams Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .github/workflows/README.md | 30 +++- ...lpha-stage-1_update_alpha_dependencies.yml | 35 +---- ...-stage-2_build_alpha_release_and_store.yml | 87 +----------- .../beta-stage-1_update_beta_dependencies.yml | 34 +---- ...a-stage-2_build_beta_release_and_store.yml | 87 +----------- .../reusable-build-and-release-prerelease.yml | 132 ++++++++++++++++++ .../reusable-update-dependencies.yml | 56 ++++++++ 7 files changed, 233 insertions(+), 228 deletions(-) create mode 100644 .github/workflows/reusable-build-and-release-prerelease.yml create mode 100644 .github/workflows/reusable-update-dependencies.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index fbc52f1..91e1daa 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -60,6 +60,25 @@ Generates version strings for beta and alpha releases using date stamps. - `version`: APT package version - `npm_version`: NPM version +### `reusable-update-dependencies.yml` +Handles dependency updates and triggering of Stage 2 workflows for prerelease streams. + +**Inputs:** +- `release_type`: Release type (alpha, beta) +- `config_file`: Configuration file for homebridge bot +- `trigger_workflow`: Workflow file to trigger for Stage 2 +- `cron_schedule`: Cron schedule for display purposes + +### `reusable-build-and-release-prerelease.yml` +Consolidated build and release logic for alpha and beta packages, including all steps from version generation through NPM publishing. + +**Inputs:** +- `release_type`: Release type (alpha, beta) +- `event_name`: Event name for conditional execution +- `pr_merged`: Whether PR was merged (for pull_request events) + +**Secrets:** All required secrets for GPG, AWS, CloudFlare, and NPM + ## Release Stream Workflows ### Stable Release (4 stages) @@ -79,17 +98,19 @@ Generates version strings for beta and alpha releases using date stamps. ### Beta Release (2 stages) 1. **`beta-stage-1_update_beta_dependencies.yml`** - Updates beta dependencies + - Uses: `reusable-update-dependencies.yml` - Managed by homebridge-beta-bot 2. **`beta-stage-2_build_beta_release_and_store.yml`** - Builds and releases beta packages - - Uses: `reusable-generate-version.yml`, `reusable-build-package.yml`, `reusable-publish-apt.yml`, `reusable-create-github-release.yml`, `reusable-publish-npm.yml` + - Uses: `reusable-build-and-release-prerelease.yml` ### Alpha Release (2 stages) 1. **`alpha-stage-1_update_alpha_dependencies.yml`** - Updates alpha dependencies + - Uses: `reusable-update-dependencies.yml` - Managed by homebridge-alpha-bot 2. **`alpha-stage-2_build_alpha_release_and_store.yml`** - Builds and releases alpha packages - - Uses: `reusable-generate-version.yml`, `reusable-build-package.yml`, `reusable-publish-apt.yml`, `reusable-create-github-release.yml`, `reusable-publish-npm.yml` + - Uses: `reusable-build-and-release-prerelease.yml` ## Utility Workflows @@ -101,11 +122,12 @@ Generates version strings for beta and alpha releases using date stamps. ## Benefits of Consolidation -1. **Reduced Duplication**: ~400 lines of duplicate YAML eliminated +1. **Reduced Duplication**: ~600 lines of duplicate YAML eliminated across all workflow consolidations 2. **Consistency**: All builds use identical Docker setup and architecture matrix 3. **Maintainability**: Common changes only need to be made in reusable workflows 4. **Flexibility**: Reusable workflows are parameterized for different use cases 5. **Reliability**: Shared logic reduces the chance of inconsistencies between release streams +6. **Simplified Alpha/Beta**: Complete alpha and beta workflow logic consolidated into shared components ## Making Changes @@ -116,5 +138,7 @@ When modifying build or publishing logic: 3. **GitHub Releases**: Edit `reusable-create-github-release.yml` 4. **NPM Publishing**: Edit `reusable-publish-npm.yml` 5. **Version Generation**: Edit `reusable-generate-version.yml` +6. **Alpha/Beta Dependency Updates**: Edit `reusable-update-dependencies.yml` +7. **Alpha/Beta Build and Release**: Edit `reusable-build-and-release-prerelease.yml` The changes will automatically apply to all release streams that use these workflows. \ No newline at end of file diff --git a/.github/workflows/alpha-stage-1_update_alpha_dependencies.yml b/.github/workflows/alpha-stage-1_update_alpha_dependencies.yml index ec24540..d3a75ed 100644 --- a/.github/workflows/alpha-stage-1_update_alpha_dependencies.yml +++ b/.github/workflows/alpha-stage-1_update_alpha_dependencies.yml @@ -6,35 +6,10 @@ on: run-name: Alpha Stage 1 - ${{ github.event_name == 'workflow_dispatch' && 'Manual' || 'Scheduled' }} Run jobs: - homebridge-alpha-bot: - name: Run Homebridge Alpha Bot - uses: homebridge/.github/.github/workflows/homebridge-beta-bot.yml@latest + update_dependencies: + uses: ./.github/workflows/reusable-update-dependencies.yml with: + release_type: alpha config_file: '.github/homebridge-alpha-bot.json' - release_stream: 'alpha' - secrets: inherit - - log-skipped-trigger: - name: Log Skipped Alpha Stage 2 Trigger - needs: homebridge-alpha-bot - if: needs.homebridge-alpha-bot.outputs.changes_detected != 'true' || needs.homebridge-alpha-bot.outputs.auto_merge != 'true' - runs-on: ubuntu-latest - steps: - - name: Log Skipped Trigger - run: | - echo "::warning::Alpha Stage 2 not triggered: Changes Detected=${{ needs.homebridge-alpha-bot.outputs.changes_detected }}, Auto Merge=${{ needs.homebridge-alpha-bot.outputs.auto_merge }}" - - trigger-alpha-stage-2: - name: Trigger Build and Release Alpha Package - needs: homebridge-alpha-bot - if: needs.homebridge-alpha-bot.outputs.changes_detected == 'true' && needs.homebridge-alpha-bot.outputs.auto_merge == 'true' - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Trigger Alpha Stage 2 Workflow - run: | - echo "::notice::Triggering Alpha Stage 2 - Build and Release Alpha Package" - gh workflow run alpha-stage-2_build_alpha_release_and_store.yml --ref latest || { echo "::error::Failed to trigger Alpha Stage 2 workflow"; exit 1; } - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + trigger_workflow: 'alpha-stage-2_build_alpha_release_and_store.yml' + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml b/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml index 9e23e7b..1c9f1aa 100644 --- a/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml +++ b/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml @@ -1,102 +1,23 @@ name: Alpha Stage 2 - Build and Release Alpha Package -permissions: - contents: write - actions: write - id-token: write - on: pull_request: types: [closed] workflow_dispatch: jobs: - check-changes: - if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - outputs: - alpha_only: ${{ steps.verify_changes.outputs.alpha_only }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Log event context for debugging - if: github.event_name == 'pull_request' - run: | - echo "Event: ${{ github.event_name }}" - echo "Merged: ${{ github.event.pull_request.merged }}" - - - name: Verify alpha-only changes - id: verify_changes - run: | - if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - echo "Workflow dispatch detected, setting alpha_only=true" - echo "alpha_only=true" >> $GITHUB_OUTPUT - else - alpha_only=true - changed_files=$(git diff --name-only HEAD^ HEAD) - for file in $changed_files; do - if [[ ! $file == alpha/* ]]; then - echo "Non-alpha changes detected in $file. Setting alpha_only=false" - alpha_only=false - fi - done - echo "alpha_only=$alpha_only" >> $GITHUB_OUTPUT - fi - - generate_alpha_version: - needs: check-changes - if: ${{ needs.check-changes.outputs.alpha_only == 'true' }} - uses: ./.github/workflows/reusable-generate-version.yml + build_and_release: + uses: ./.github/workflows/reusable-build-and-release-prerelease.yml with: release_type: alpha - - build_alpha_and_store: - needs: [generate_alpha_version] - uses: ./.github/workflows/reusable-build-package.yml - with: - release_type: alpha - release_version: ${{ needs.generate_alpha_version.outputs.version }} - docker_tag_prefix: alpha-package-build - - publish_apt_alpha: - needs: [generate_alpha_version, build_alpha_and_store] - uses: ./.github/workflows/reusable-publish-apt.yml - with: - codename: alpha - suite: alpha - release_version: ${{ needs.generate_alpha_version.outputs.version }} + event_name: ${{ github.event_name }} + pr_merged: ${{ github.event.pull_request.merged }} secrets: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - publish_github_alpha_release: - needs: [check-changes, publish_apt_alpha, generate_alpha_version] - uses: ./.github/workflows/reusable-create-github-release.yml - with: - tag_name: v${{ needs.generate_alpha_version.outputs.npm_version }} - release_name: Alpha Release v${{ needs.generate_alpha_version.outputs.npm_version }} - release_type: alpha - prerelease: true - - purge_cloudflare_cache: - name: Purge Cloudflare Cache - needs: [publish_apt_alpha] - uses: ./.github/workflows/stage-3_5_purge_cloudflare_cache.yml - secrets: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} - - publish_to_npm: - needs: [generate_alpha_version, build_alpha_and_store] - uses: ./.github/workflows/reusable-publish-npm.yml - with: - npm_version: ${{ needs.generate_alpha_version.outputs.npm_version }} - npm_tag: alpha - secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/beta-stage-1_update_beta_dependencies.yml b/.github/workflows/beta-stage-1_update_beta_dependencies.yml index 90a0339..6c9e80c 100644 --- a/.github/workflows/beta-stage-1_update_beta_dependencies.yml +++ b/.github/workflows/beta-stage-1_update_beta_dependencies.yml @@ -6,34 +6,10 @@ on: run-name: Beta Stage 1 - ${{ github.event_name == 'workflow_dispatch' && 'Manual' || 'Scheduled' }} Run jobs: - homebridge-beta-bot: - name: Run Homebridge Beta Bot - uses: homebridge/.github/.github/workflows/homebridge-beta-bot.yml@latest + update_dependencies: + uses: ./.github/workflows/reusable-update-dependencies.yml with: + release_type: beta config_file: '.github/homebridge-beta-bot.json' - secrets: inherit - - log-skipped-trigger: - name: Log Skipped Beta Stage 2 Trigger - needs: homebridge-beta-bot - if: needs.homebridge-beta-bot.outputs.changes_detected != 'true' || needs.homebridge-beta-bot.outputs.auto_merge != 'true' - runs-on: ubuntu-latest - steps: - - name: Log Skipped Trigger - run: | - echo "::warning::Beta Stage 2 not triggered: Changes Detected=${{ needs.homebridge-beta-bot.outputs.changes_detected }}, Auto Merge=${{ needs.homebridge-beta-bot.outputs.auto_merge }}" - - trigger-beta-stage-2: - name: Trigger Build and Release Beta Package - needs: homebridge-beta-bot - if: needs.homebridge-beta-bot.outputs.changes_detected == 'true' && needs.homebridge-beta-bot.outputs.auto_merge == 'true' - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Trigger Beta Stage 2 Workflow - run: | - echo "::notice::Triggering Beta Stage 2 - Build and Release Beta Package" - gh workflow run beta-stage-2_build_beta_release_and_store.yml --ref latest || { echo "::error::Failed to trigger Beta Stage 2 workflow"; exit 1; } - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + trigger_workflow: 'beta-stage-2_build_beta_release_and_store.yml' + secrets: inherit \ No newline at end of file diff --git a/.github/workflows/beta-stage-2_build_beta_release_and_store.yml b/.github/workflows/beta-stage-2_build_beta_release_and_store.yml index 8bfd58a..37ce5bc 100644 --- a/.github/workflows/beta-stage-2_build_beta_release_and_store.yml +++ b/.github/workflows/beta-stage-2_build_beta_release_and_store.yml @@ -1,102 +1,23 @@ name: Beta Stage 2 - Build and Release Beta Package -permissions: - contents: write - actions: write - id-token: write - on: pull_request: types: [closed] workflow_dispatch: jobs: - check-changes: - if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' - runs-on: ubuntu-latest - outputs: - beta_only: ${{ steps.verify_changes.outputs.beta_only }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Log event context for debugging - if: github.event_name == 'pull_request' - run: | - echo "Event: ${{ github.event_name }}" - echo "Merged: ${{ github.event.pull_request.merged }}" - - - name: Verify beta-only changes - id: verify_changes - run: | - if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - echo "Workflow dispatch detected, setting beta_only=true" - echo "beta_only=true" >> $GITHUB_OUTPUT - else - beta_only=true - changed_files=$(git diff --name-only HEAD^ HEAD) - for file in $changed_files; do - if [[ ! $file == beta/* ]]; then - echo "Non-beta changes detected in $file. Setting beta_only=false" - beta_only=false - fi - done - echo "beta_only=$beta_only" >> $GITHUB_OUTPUT - fi - - generate_beta_version: - needs: check-changes - if: ${{ needs.check-changes.outputs.beta_only == 'true' }} - uses: ./.github/workflows/reusable-generate-version.yml + build_and_release: + uses: ./.github/workflows/reusable-build-and-release-prerelease.yml with: release_type: beta - - build_beta_and_store: - needs: [generate_beta_version] - uses: ./.github/workflows/reusable-build-package.yml - with: - release_type: beta - release_version: ${{ needs.generate_beta_version.outputs.version }} - docker_tag_prefix: beta-package-build - - publish_apt_beta: - needs: [generate_beta_version, build_beta_and_store] - uses: ./.github/workflows/reusable-publish-apt.yml - with: - codename: beta - suite: beta - release_version: ${{ needs.generate_beta_version.outputs.version }} + event_name: ${{ github.event_name }} + pr_merged: ${{ github.event.pull_request.merged }} secrets: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - publish_github_beta_release: - needs: [check-changes, publish_apt_beta, generate_beta_version] - uses: ./.github/workflows/reusable-create-github-release.yml - with: - tag_name: v${{ needs.generate_beta_version.outputs.npm_version }} - release_name: Beta Release v${{ needs.generate_beta_version.outputs.npm_version }} - release_type: beta - prerelease: true - - purge_cloudflare_cache: - name: Purge Cloudflare Cache - needs: [publish_apt_beta] - uses: ./.github/workflows/stage-3_5_purge_cloudflare_cache.yml - secrets: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} - - publish_to_npm: - needs: [generate_beta_version, build_beta_and_store] - uses: ./.github/workflows/reusable-publish-npm.yml - with: - npm_version: ${{ needs.generate_beta_version.outputs.npm_version }} - npm_tag: beta - secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/reusable-build-and-release-prerelease.yml b/.github/workflows/reusable-build-and-release-prerelease.yml new file mode 100644 index 0000000..99234e4 --- /dev/null +++ b/.github/workflows/reusable-build-and-release-prerelease.yml @@ -0,0 +1,132 @@ +name: Reusable - Build and Release Prerelease Package + +on: + workflow_call: + inputs: + release_type: + description: 'Release type (alpha or beta)' + required: true + type: string + event_name: + description: 'Event name for conditional execution' + required: false + type: string + default: 'workflow_call' + pr_merged: + description: 'Whether PR was merged (for pull_request events)' + required: false + type: boolean + default: true + secrets: + GPG_PRIVATE_KEY: + required: true + GPG_PASSPHRASE: + required: true + GPG_KEY_ID: + required: true + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + CLOUDFLARE_API_TOKEN: + required: true + CLOUDFLARE_ZONE_ID: + required: true + NPM_TOKEN: + required: true + +permissions: + contents: write + actions: write + id-token: write + +jobs: + check-changes: + if: ${{ inputs.pr_merged == true || inputs.event_name == 'workflow_dispatch' }} + runs-on: ubuntu-latest + outputs: + changes_only: ${{ steps.verify_changes.outputs.changes_only }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Log event context for debugging + if: ${{ inputs.event_name == 'pull_request' }} + run: | + echo "Event: ${{ inputs.event_name }}" + echo "Merged: ${{ inputs.pr_merged }}" + + - name: Verify ${{ inputs.release_type }}-only changes + id: verify_changes + run: | + if [ "${{ inputs.event_name }}" == "workflow_dispatch" ]; then + echo "Workflow dispatch detected, setting changes_only=true" + echo "changes_only=true" >> $GITHUB_OUTPUT + else + changes_only=true + changed_files=$(git diff --name-only HEAD^ HEAD) + for file in $changed_files; do + if [[ ! $file == ${{ inputs.release_type }}/* ]]; then + echo "Non-${{ inputs.release_type }} changes detected in $file. Setting changes_only=false" + changes_only=false + fi + done + echo "changes_only=$changes_only" >> $GITHUB_OUTPUT + fi + + generate_version: + needs: check-changes + if: ${{ needs.check-changes.outputs.changes_only == 'true' }} + uses: ./.github/workflows/reusable-generate-version.yml + with: + release_type: ${{ inputs.release_type }} + + build_and_store: + needs: [generate_version] + uses: ./.github/workflows/reusable-build-package.yml + with: + release_type: ${{ inputs.release_type }} + release_version: ${{ needs.generate_version.outputs.version }} + docker_tag_prefix: ${{ inputs.release_type }}-package-build + + publish_apt: + needs: [generate_version, build_and_store] + uses: ./.github/workflows/reusable-publish-apt.yml + with: + codename: ${{ inputs.release_type }} + suite: ${{ inputs.release_type }} + release_version: ${{ needs.generate_version.outputs.version }} + secrets: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + + publish_github_release: + needs: [check-changes, publish_apt, generate_version] + uses: ./.github/workflows/reusable-create-github-release.yml + with: + tag_name: v${{ needs.generate_version.outputs.npm_version }} + release_name: ${{ inputs.release_type == 'alpha' && 'Alpha' || 'Beta' }} Release v${{ needs.generate_version.outputs.npm_version }} + release_type: ${{ inputs.release_type }} + prerelease: true + + purge_cloudflare_cache: + name: Purge Cloudflare Cache + needs: [publish_apt] + uses: ./.github/workflows/stage-3_5_purge_cloudflare_cache.yml + secrets: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} + + publish_to_npm: + needs: [generate_version, build_and_store] + uses: ./.github/workflows/reusable-publish-npm.yml + with: + npm_version: ${{ needs.generate_version.outputs.npm_version }} + npm_tag: ${{ inputs.release_type }} + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/reusable-update-dependencies.yml b/.github/workflows/reusable-update-dependencies.yml new file mode 100644 index 0000000..fe2ef70 --- /dev/null +++ b/.github/workflows/reusable-update-dependencies.yml @@ -0,0 +1,56 @@ +name: Reusable - Update Dependencies and Trigger Build + +on: + workflow_call: + inputs: + release_type: + description: 'Release type (alpha or beta)' + required: true + type: string + config_file: + description: 'Configuration file for the homebridge bot' + required: true + type: string + trigger_workflow: + description: 'Workflow file to trigger for Stage 2' + required: true + type: string + cron_schedule: + description: 'Cron schedule for display purposes' + required: false + type: string + default: 'Manual' + +jobs: + homebridge-bot: + name: Run Homebridge ${{ inputs.release_type }} Bot + uses: homebridge/.github/.github/workflows/homebridge-beta-bot.yml@latest + with: + config_file: ${{ inputs.config_file }} + release_stream: ${{ inputs.release_type }} + secrets: inherit + + log-skipped-trigger: + name: Log Skipped ${{ inputs.release_type }} Stage 2 Trigger + needs: homebridge-bot + if: needs.homebridge-bot.outputs.changes_detected != 'true' || needs.homebridge-bot.outputs.auto_merge != 'true' + runs-on: ubuntu-latest + steps: + - name: Log Skipped Trigger + run: | + echo "::warning::${{ inputs.release_type }} Stage 2 not triggered: Changes Detected=${{ needs.homebridge-bot.outputs.changes_detected }}, Auto Merge=${{ needs.homebridge-bot.outputs.auto_merge }}" + + trigger-stage-2: + name: Trigger Build and Release ${{ inputs.release_type }} Package + needs: homebridge-bot + if: needs.homebridge-bot.outputs.changes_detected == 'true' && needs.homebridge-bot.outputs.auto_merge == 'true' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Trigger ${{ inputs.release_type }} Stage 2 Workflow + run: | + echo "::notice::Triggering ${{ inputs.release_type }} Stage 2 - Build and Release ${{ inputs.release_type }} Package" + gh workflow run ${{ inputs.trigger_workflow }} --ref latest || { echo "::error::Failed to trigger ${{ inputs.release_type }} Stage 2 workflow"; exit 1; } + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From d201cafbdff883d260040a9e820df2d6dbb2753b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 01:49:47 +0000 Subject: [PATCH 05/16] Combine alpha and beta streams into consolidated prerelease workflows Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .github/workflows/README.md | 29 +++--- ...lpha-stage-1_update_alpha_dependencies.yml | 15 --- ...-stage-2_build_alpha_release_and_store.yml | 23 ----- .../beta-stage-1_update_beta_dependencies.yml | 15 --- ...a-stage-2_build_beta_release_and_store.yml | 23 ----- ...prerelease-stage-1_update_dependencies.yml | 65 +++++++++++++ .../prerelease-stage-2_build_and_release.yml | 96 +++++++++++++++++++ 7 files changed, 175 insertions(+), 91 deletions(-) delete mode 100644 .github/workflows/alpha-stage-1_update_alpha_dependencies.yml delete mode 100644 .github/workflows/alpha-stage-2_build_alpha_release_and_store.yml delete mode 100644 .github/workflows/beta-stage-1_update_beta_dependencies.yml delete mode 100644 .github/workflows/beta-stage-2_build_beta_release_and_store.yml create mode 100644 .github/workflows/prerelease-stage-1_update_dependencies.yml create mode 100644 .github/workflows/prerelease-stage-2_build_and_release.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 91e1daa..d8ee4c8 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -96,21 +96,18 @@ Consolidated build and release logic for alpha and beta packages, including all 4. **`Stage-4_post_release_validation.yml`** - Post-release validation - Tests APT installation from repository -### Beta Release (2 stages) -1. **`beta-stage-1_update_beta_dependencies.yml`** - Updates beta dependencies +### Prerelease (Alpha and Beta) Workflows (2 stages) +1. **`prerelease-stage-1_update_dependencies.yml`** - Updates dependencies for both alpha and beta - Uses: `reusable-update-dependencies.yml` - - Managed by homebridge-beta-bot + - Supports both scheduled runs (separate cron schedules for alpha/beta) and manual dispatch + - Managed by homebridge-alpha-bot and homebridge-beta-bot + - Consolidates both alpha and beta dependency updates into a single workflow -2. **`beta-stage-2_build_beta_release_and_store.yml`** - Builds and releases beta packages - - Uses: `reusable-build-and-release-prerelease.yml` - -### Alpha Release (2 stages) -1. **`alpha-stage-1_update_alpha_dependencies.yml`** - Updates alpha dependencies - - Uses: `reusable-update-dependencies.yml` - - Managed by homebridge-alpha-bot - -2. **`alpha-stage-2_build_alpha_release_and_store.yml`** - Builds and releases alpha packages +2. **`prerelease-stage-2_build_and_release.yml`** - Builds and releases alpha and beta packages - Uses: `reusable-build-and-release-prerelease.yml` + - Automatically detects release type based on changed directories in PRs + - Supports manual dispatch with release type selection + - Consolidates both alpha and beta build/release logic into a single workflow ## Utility Workflows @@ -127,7 +124,9 @@ Consolidated build and release logic for alpha and beta packages, including all 3. **Maintainability**: Common changes only need to be made in reusable workflows 4. **Flexibility**: Reusable workflows are parameterized for different use cases 5. **Reliability**: Shared logic reduces the chance of inconsistencies between release streams -6. **Simplified Alpha/Beta**: Complete alpha and beta workflow logic consolidated into shared components +6. **Complete Prerelease Consolidation**: Alpha and beta workflows combined into 2 shared workflows instead of 4 separate ones +7. **Intelligent Release Detection**: Automatic detection of release type based on changed directories +8. **Simplified Management**: Single workflow files handle both alpha and beta with appropriate scheduling ## Making Changes @@ -138,7 +137,7 @@ When modifying build or publishing logic: 3. **GitHub Releases**: Edit `reusable-create-github-release.yml` 4. **NPM Publishing**: Edit `reusable-publish-npm.yml` 5. **Version Generation**: Edit `reusable-generate-version.yml` -6. **Alpha/Beta Dependency Updates**: Edit `reusable-update-dependencies.yml` -7. **Alpha/Beta Build and Release**: Edit `reusable-build-and-release-prerelease.yml` +6. **Prerelease Dependency Updates**: Edit `reusable-update-dependencies.yml` +7. **Prerelease Build and Release**: Edit `reusable-build-and-release-prerelease.yml` The changes will automatically apply to all release streams that use these workflows. \ No newline at end of file diff --git a/.github/workflows/alpha-stage-1_update_alpha_dependencies.yml b/.github/workflows/alpha-stage-1_update_alpha_dependencies.yml deleted file mode 100644 index d3a75ed..0000000 --- a/.github/workflows/alpha-stage-1_update_alpha_dependencies.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Alpha Stage 1 - Update Alpha Dependencies, Create PR, Merge PR and Trigger Alpha Stage 2 -on: - schedule: - - cron: '0 9 * * *' # 5 AM Eastern (11 AM UTC), Docker updates at 6 AM Eastern (12 PM UTC) - workflow_dispatch: -run-name: Alpha Stage 1 - ${{ github.event_name == 'workflow_dispatch' && 'Manual' || 'Scheduled' }} Run - -jobs: - update_dependencies: - uses: ./.github/workflows/reusable-update-dependencies.yml - with: - release_type: alpha - config_file: '.github/homebridge-alpha-bot.json' - trigger_workflow: 'alpha-stage-2_build_alpha_release_and_store.yml' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml b/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml deleted file mode 100644 index 1c9f1aa..0000000 --- a/.github/workflows/alpha-stage-2_build_alpha_release_and_store.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Alpha Stage 2 - Build and Release Alpha Package - -on: - pull_request: - types: [closed] - workflow_dispatch: - -jobs: - build_and_release: - uses: ./.github/workflows/reusable-build-and-release-prerelease.yml - with: - release_type: alpha - event_name: ${{ github.event_name }} - pr_merged: ${{ github.event.pull_request.merged }} - secrets: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/beta-stage-1_update_beta_dependencies.yml b/.github/workflows/beta-stage-1_update_beta_dependencies.yml deleted file mode 100644 index 6c9e80c..0000000 --- a/.github/workflows/beta-stage-1_update_beta_dependencies.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Beta Stage 1 - Update Beta Dependencies, Create PR, Merge PR and Trigger Beta Stage 2 -on: - schedule: - - cron: '0 8 * * *' # 4 AM Eastern (10 AM UTC), Docker updates at 6 AM Eastern (12 PM UTC) - workflow_dispatch: -run-name: Beta Stage 1 - ${{ github.event_name == 'workflow_dispatch' && 'Manual' || 'Scheduled' }} Run - -jobs: - update_dependencies: - uses: ./.github/workflows/reusable-update-dependencies.yml - with: - release_type: beta - config_file: '.github/homebridge-beta-bot.json' - trigger_workflow: 'beta-stage-2_build_beta_release_and_store.yml' - secrets: inherit \ No newline at end of file diff --git a/.github/workflows/beta-stage-2_build_beta_release_and_store.yml b/.github/workflows/beta-stage-2_build_beta_release_and_store.yml deleted file mode 100644 index 37ce5bc..0000000 --- a/.github/workflows/beta-stage-2_build_beta_release_and_store.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Beta Stage 2 - Build and Release Beta Package - -on: - pull_request: - types: [closed] - workflow_dispatch: - -jobs: - build_and_release: - uses: ./.github/workflows/reusable-build-and-release-prerelease.yml - with: - release_type: beta - event_name: ${{ github.event_name }} - pr_merged: ${{ github.event.pull_request.merged }} - secrets: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/prerelease-stage-1_update_dependencies.yml b/.github/workflows/prerelease-stage-1_update_dependencies.yml new file mode 100644 index 0000000..b435bd2 --- /dev/null +++ b/.github/workflows/prerelease-stage-1_update_dependencies.yml @@ -0,0 +1,65 @@ +name: Prerelease Stage 1 - Update Alpha and Beta Dependencies, Create PRs, Merge PRs and Trigger Stage 2 + +on: + schedule: + # Beta: 4 AM Eastern (8 AM UTC), Docker updates at 6 AM Eastern (10 AM UTC) + - cron: '0 8 * * *' + # Alpha: 5 AM Eastern (9 AM UTC), Docker updates at 6 AM Eastern (11 AM UTC) + - cron: '0 9 * * *' + workflow_dispatch: + inputs: + release_type: + description: 'Release type to update' + required: true + type: choice + options: + - alpha + - beta + - both + default: 'both' + +run-name: Prerelease Stage 1 - ${{ github.event_name == 'workflow_dispatch' && format('Manual - {0}', github.event.inputs.release_type) || 'Scheduled' }} Run + +jobs: + determine-release-types: + name: Determine Release Types + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Set matrix based on trigger + id: set-matrix + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + if [[ "${{ github.event.inputs.release_type }}" == "both" ]]; then + echo 'matrix={"release_type":["alpha","beta"],"include":[{"release_type":"alpha","config_file":".github/homebridge-alpha-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"},{"release_type":"beta","config_file":".github/homebridge-beta-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"}]}' >> $GITHUB_OUTPUT + else + CONFIG_FILE=".github/homebridge-${{ github.event.inputs.release_type }}-bot.json" + echo "matrix={\"release_type\":[\"${{ github.event.inputs.release_type }}\"],\"include\":[{\"release_type\":\"${{ github.event.inputs.release_type }}\",\"config_file\":\"$CONFIG_FILE\",\"trigger_workflow\":\"prerelease-stage-2_build_and_release.yml\"}]}" >> $GITHUB_OUTPUT + fi + else + # Scheduled run - determine based on cron time + CURRENT_HOUR=$(date -u +%H) + if [[ "$CURRENT_HOUR" == "08" ]]; then + # Beta cron at 8 AM UTC + echo 'matrix={"release_type":["beta"],"include":[{"release_type":"beta","config_file":".github/homebridge-beta-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"}]}' >> $GITHUB_OUTPUT + elif [[ "$CURRENT_HOUR" == "09" ]]; then + # Alpha cron at 9 AM UTC + echo 'matrix={"release_type":["alpha"],"include":[{"release_type":"alpha","config_file":".github/homebridge-alpha-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"}]}' >> $GITHUB_OUTPUT + else + echo "::error::Unexpected scheduled run time: $CURRENT_HOUR" + exit 1 + fi + fi + + update_dependencies: + name: Update ${{ matrix.release_type }} Dependencies + needs: determine-release-types + strategy: + matrix: ${{ fromJson(needs.determine-release-types.outputs.matrix) }} + uses: ./.github/workflows/reusable-update-dependencies.yml + with: + release_type: ${{ matrix.release_type }} + config_file: ${{ matrix.config_file }} + trigger_workflow: ${{ matrix.trigger_workflow }} + secrets: inherit diff --git a/.github/workflows/prerelease-stage-2_build_and_release.yml b/.github/workflows/prerelease-stage-2_build_and_release.yml new file mode 100644 index 0000000..c06c94e --- /dev/null +++ b/.github/workflows/prerelease-stage-2_build_and_release.yml @@ -0,0 +1,96 @@ +name: Prerelease Stage 2 - Build and Release Alpha and Beta Packages + +on: + pull_request: + types: [closed] + paths: + - 'alpha/**' + - 'beta/**' + workflow_dispatch: + inputs: + release_type: + description: 'Release type to build' + required: true + type: choice + options: + - alpha + - beta + default: 'beta' + +jobs: + determine-release-type: + name: Determine Release Type + runs-on: ubuntu-latest + outputs: + release_type: ${{ steps.detect-type.outputs.release_type }} + should_run: ${{ steps.detect-type.outputs.should_run }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Detect release type + id: detect-type + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + echo "release_type=${{ github.event.inputs.release_type }}" >> $GITHUB_OUTPUT + echo "should_run=true" >> $GITHUB_OUTPUT + echo "::notice::Manual dispatch for ${{ github.event.inputs.release_type }} release" + elif [[ "${{ github.event_name }}" == "pull_request" ]]; then + if [[ "${{ github.event.pull_request.merged }}" != "true" ]]; then + echo "should_run=false" >> $GITHUB_OUTPUT + echo "::notice::PR was not merged, skipping build" + exit 0 + fi + + # Check which directories were changed in the merged PR + CHANGED_FILES=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.number }}/files --jq '.[].filename') + + ALPHA_CHANGES=$(echo "$CHANGED_FILES" | grep -c "^alpha/" || true) + BETA_CHANGES=$(echo "$CHANGED_FILES" | grep -c "^beta/" || true) + + echo "Alpha files changed: $ALPHA_CHANGES" + echo "Beta files changed: $BETA_CHANGES" + + if [[ $ALPHA_CHANGES -gt 0 && $BETA_CHANGES -gt 0 ]]; then + echo "::error::PR contains changes to both alpha and beta directories. This should not happen." + echo "should_run=false" >> $GITHUB_OUTPUT + exit 1 + elif [[ $ALPHA_CHANGES -gt 0 ]]; then + echo "release_type=alpha" >> $GITHUB_OUTPUT + echo "should_run=true" >> $GITHUB_OUTPUT + echo "::notice::Detected alpha release based on changed files" + elif [[ $BETA_CHANGES -gt 0 ]]; then + echo "release_type=beta" >> $GITHUB_OUTPUT + echo "should_run=true" >> $GITHUB_OUTPUT + echo "::notice::Detected beta release based on changed files" + else + echo "should_run=false" >> $GITHUB_OUTPUT + echo "::notice::No relevant changes detected, skipping build" + fi + else + echo "should_run=false" >> $GITHUB_OUTPUT + echo "::error::Unexpected event type: ${{ github.event_name }}" + fi + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + build_and_release: + name: Build and Release ${{ needs.determine-release-type.outputs.release_type }} Package + needs: determine-release-type + if: needs.determine-release-type.outputs.should_run == 'true' + uses: ./.github/workflows/reusable-build-and-release-prerelease.yml + with: + release_type: ${{ needs.determine-release-type.outputs.release_type }} + event_name: ${{ github.event_name }} + pr_merged: ${{ github.event.pull_request.merged }} + secrets: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} From fe60b6d4cc4ed26f5a19291933782eeca5ebe843 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 01:59:57 +0000 Subject: [PATCH 06/16] Further consolidate GitHub Actions workflows by extracting common patterns into reusable components Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .github/workflows/README.md | 21 ++ .../Stage-4_post_release_validation.yml | 36 +--- ...-beta-stage-1_update_beta_dependencies.yml | 202 ------------------ .github/workflows/purge.yml | 2 +- .github/workflows/reusable-debug-context.yml | 46 ++++ .../reusable-validate-homebridge.yml | 115 ++++++++++ .../stage-1_create_a_release_and_store.yml | 39 +--- .../stage-2_pre-release_validation.yml | 71 +----- .../stage-3_promote_release_to_apt.yml | 39 +--- 9 files changed, 196 insertions(+), 375 deletions(-) delete mode 100644 .github/workflows/old-beta-stage-1_update_beta_dependencies.yml create mode 100644 .github/workflows/reusable-debug-context.yml create mode 100644 .github/workflows/reusable-validate-homebridge.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index d8ee4c8..8e68d42 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -49,6 +49,27 @@ Publishes the package to NPM with specified version and tag. **Secrets:** NPM token +### `reusable-debug-context.yml` +Provides comprehensive debug output for GitHub Actions contexts. + +**Usage:** Call this workflow from any workflow that needs debug context information. + +### `reusable-validate-homebridge.yml` +Validates Homebridge package installations from different sources. + +**Inputs:** +- `validation_type`: Type of validation ("prerelease" for GitHub releases, "apt" for APT installations) +- `release_tag`: GitHub release tag (for prerelease validation) +- `release_channel`: APT release channel (for APT validation) +- `prerelease`: Whether to download from prerelease +- `latest`: Whether to download latest release + +**Features:** +- Multi-architecture validation for APT installations +- Package download and installation verification +- Service status validation +- File count verification for prerelease packages + ### `reusable-generate-version.yml` Generates version strings for beta and alpha releases using date stamps. diff --git a/.github/workflows/Stage-4_post_release_validation.yml b/.github/workflows/Stage-4_post_release_validation.yml index ae41700..8dfb51f 100644 --- a/.github/workflows/Stage-4_post_release_validation.yml +++ b/.github/workflows/Stage-4_post_release_validation.yml @@ -42,36 +42,8 @@ jobs: fi validate: - name: Validate APT Install ${{ matrix.github-action-runner }} needs: determine-channel - runs-on: ${{ matrix.github-action-runner }} - strategy: - fail-fast: false - matrix: - github-action-runner: [ubuntu-latest, ubuntu-24.04-arm] - steps: - - name: Install Homebridge from APT - run: | - curl -sSfL https://repo.homebridge.io/KEY.gpg | sudo gpg --dearmor -o /usr/share/keyrings/homebridge.gpg - echo "deb [signed-by=/usr/share/keyrings/homebridge.gpg] https://repo.homebridge.io ${{ needs.determine-channel.outputs.channel }} main" | \ - sudo tee /etc/apt/sources.list.d/homebridge.list > /dev/null - sudo apt-get update - sudo apt-get install -y homebridge - - - name: Display Installed Version - id: installed-version - run: | - dpkg -l homebridge - echo "homebridge-version=\"$(dpkg -l homebridge | tail -1 | awk '{ print $3 }')\"" >> "$GITHUB_OUTPUT" - - - name: Validate Homebridge - run: | - sudo hb-service status - sudo hb-service view - - - name: List Available Versions - run: | - apt-cache madison homebridge - - - name: Publish Notice - run: echo "::notice::APT Package Installed from ${{ needs.determine-channel.outputs.channel }} - Version ${{ steps.installed-version.outputs.homebridge-version }} - ${{ matrix.github-action-runner }}" + uses: ./.github/workflows/reusable-validate-homebridge.yml + with: + validation_type: "apt" + release_channel: ${{ needs.determine-channel.outputs.channel }} diff --git a/.github/workflows/old-beta-stage-1_update_beta_dependencies.yml b/.github/workflows/old-beta-stage-1_update_beta_dependencies.yml deleted file mode 100644 index 86d032f..0000000 --- a/.github/workflows/old-beta-stage-1_update_beta_dependencies.yml +++ /dev/null @@ -1,202 +0,0 @@ -name: OLD Beta Stage 1 - Update Beta Dependencies, Create PR, Merge PR and Trigger Beta Stage 2 - -on: -# schedule: -# - cron: '0 8 * * *' # 4 AM Eastern (10 AM UTC), Docker updates at 6 AM Eastern (12 PM UTC) - workflow_dispatch: - -jobs: - update: - runs-on: ubuntu-latest - outputs: - changes_detected: ${{ steps.check-changes.outputs.changes_detected }} - changed_dirs: ${{ steps.check-changes.outputs.changed_dirs }} - branch_name: ${{ steps.create-branch.outputs.branch_name }} - pr_number: ${{ steps.create-pr.outputs.pr_number }} - auto_merge: ${{ steps.load-config.outputs.auto_merge }} - user_name: ${{ steps.load-config.outputs.user_name }} - user_email: ${{ steps.load-config.outputs.user_email }} - dirs_length: ${{ steps.load-config.outputs.dirs_length }} - steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 - - - uses: actions/setup-node@v4 - with: - node-version: 'latest' - - - name: Install jq - run: sudo apt-get install -y jq - - - name: Load config file - id: load-config - run: | - config=".github/beta-bot.json" - config_abs="$(pwd)/$config" - if [ ! -f "$config_abs" ]; then - echo "::error::Config file not found: $config_abs. Please ensure .github/beta-bot.json exists in the repository." - exit 1 - fi - user_name=$(jq -r '.git_user.name // "GitHub Actions"' "$config_abs") - user_email=$(jq -r '.git_user.email // "actions@github.com"' "$config_abs") - auto_merge=$(jq -r '.auto_merge // false' "$config_abs") - dirs_length=$(jq '.directories | length' "$config_abs") - for i in $(seq 0 $(($dirs_length - 1))); do - pkgs_length=$(jq ".directories[$i].packages | length" "$config_abs") - for j in $(seq 0 $(($pkgs_length - 1))); do - pkg=$(jq -r ".directories[$i].packages[$j].name" "$config_abs") - has_tag=$(jq -r ".directories[$i].packages[$j].tag // null" "$config_abs") - has_pattern=$(jq -r ".directories[$i].packages[$j].pattern // null" "$config_abs") - if [ "$has_tag" != "null" ] && [ "$has_pattern" != "null" ]; then - echo "::error::Package $pkg in directory $i has both tag and pattern defined. Specify only one." - exit 1 - fi - if [ "$has_tag" = "null" ] && [ "$has_pattern" = "null" ]; then - echo "::error::Package $pkg in directory $i has neither tag nor pattern defined. Specify one." - exit 1 - fi - done - done - echo "Config file loaded: $config_abs" - echo "config_abs=$config_abs" >> $GITHUB_OUTPUT - echo "user_name=$user_name" >> $GITHUB_OUTPUT - echo "user_email=$user_email" >> $GITHUB_OUTPUT - echo "auto_merge=$auto_merge" >> $GITHUB_OUTPUT - echo "dirs_length=$dirs_length" >> $GITHUB_OUTPUT - - - name: Configure Git identity - run: | - git config user.name "${{ steps.load-config.outputs.user_name }}" - git config user.email "${{ steps.load-config.outputs.user_email }}" - echo "Git identity configured: ${{ steps.load-config.outputs.user_name }} <${{ steps.load-config.outputs.user_email }}>" - - - name: Process directories and packages - id: process-dirs - run: | - config="${{ steps.load-config.outputs.config_abs }}" - dirs_length="${{ steps.load-config.outputs.dirs_length }}" - echo "Found $dirs_length directories in config" - for i in $(seq 0 $(($dirs_length - 1))); do - dir=$(jq -r ".directories[$i].directory" "$config") - if [ ! -d "$dir" ]; then - echo "Directory not found: $dir" - continue - fi - echo "Processing directory: $dir" - cd "$dir" - pkgs_length=$(jq ".directories[$i].packages | length" "$config") - echo "Found $pkgs_length packages in $dir" - for j in $(seq 0 $(($pkgs_length - 1))); do - pkg=$(jq -r ".directories[$i].packages[$j].name" "$config") - tag=$(jq -r ".directories[$i].packages[$j].tag // null" "$config") - pattern=$(jq -r ".directories[$i].packages[$j].pattern // null" "$config") - if [ "$tag" != "null" ]; then - echo "::notice::Installing $pkg@$tag in $dir" - npm install "$pkg@$tag" || { echo "::error::Failed to install $pkg@$tag"; exit 1; } - else - unescaped_pattern=$(echo "$pattern" | sed 's/\\\+/\\/g') - latest_version=$(npm view "$pkg" versions --json | jq -r ".[] | select(test(\"$unescaped_pattern\"))" | sort -V | tail -n 1) - if [ -z "$latest_version" ]; then - echo "::error::No versions found for $pkg matching pattern $unescaped_pattern" - exit 1 - fi - echo "::notice::Installing $pkg@$latest_version in $dir" - npm install "$pkg@$latest_version" || { echo "::error::Failed to install $pkg@$latest_version"; exit 1; } - fi - done - cd - > /dev/null - echo "Completed processing $dir" - done - - - name: Check for changes - id: check-changes - run: | - config="${{ steps.load-config.outputs.config_abs }}" - dirs_length="${{ steps.load-config.outputs.dirs_length }}" - changes_detected=false - changed_dirs="" - for i in $(seq 0 $(($dirs_length - 1))); do - dir=$(jq -r ".directories[$i].directory" "$config") - if [ ! -d "$dir" ]; then - continue - fi - cd "$dir" - if git diff --quiet -- package.json package-lock.json; then - echo "No changes in $dir" - else - echo "Changes detected in $dir" - changes_detected=true - if [ -z "$changed_dirs" ]; then - changed_dirs="$dir" - else - changed_dirs="$changed_dirs,$dir" - fi - git add package.json package-lock.json - fi - cd - > /dev/null - done - if [ "$changes_detected" = "true" ]; then - echo "::notice::Changes detected in directories: $changed_dirs" - else - echo "::notice::No changes detected in any directories" - fi - echo "changes_detected=$changes_detected" >> $GITHUB_OUTPUT - echo "changed_dirs=$changed_dirs" >> $GITHUB_OUTPUT - - - name: Create and push branch - id: create-branch - if: steps.check-changes.outputs.changes_detected == 'true' - run: | - changed_dirs="${{ steps.check-changes.outputs.changed_dirs }}" - branch="update/beta-$(date +%s)" - git checkout -b "$branch" - git commit -m "Update beta dependencies in $changed_dirs" - git push origin "$branch" - echo "::notice::Branch pushed: $branch" - echo "branch_name=$branch" >> $GITHUB_OUTPUT - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Create pull request - id: create-pr - if: steps.check-changes.outputs.changes_detected == 'true' - run: | - branch="${{ steps.create-branch.outputs.branch_name }}" - changed_dirs="${{ steps.check-changes.outputs.changed_dirs }}" - pr_url=$(gh pr create --title "BETA: Update beta dependencies in $changed_dirs" --body "Automated dependency update" --label beta) - pr_number="${pr_url##*/}" - echo "::notice::Pull request created: #$pr_number ($pr_url)" - echo "PR created: $pr_url" - echo "pr_number=$pr_number" >> $GITHUB_OUTPUT - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Approve pull request - id: approve-pr - if: steps.check-changes.outputs.changes_detected == 'true' && steps.load-config.outputs.auto_merge == 'true' - run: | - pr_number="${{ steps.create-pr.outputs.pr_number }}" - echo "::notice::Approving PR #$pr_number" - gh pr review "$pr_number" --approve || { echo "::error::Failed to approve PR #$pr_number"; exit 1; } - env: - GH_TOKEN: ${{ secrets.GH_TOKEN }} - - - name: Auto-merge PR - id: auto-merge - if: steps.check-changes.outputs.changes_detected == 'true' && steps.load-config.outputs.auto_merge == 'true' - run: | - pr_number="${{ steps.create-pr.outputs.pr_number }}" - echo "::notice::Auto-merging PR #$pr_number" - gh pr merge "$pr_number" --squash --delete-branch || { echo "::error::Failed to merge PR #$pr_number"; exit 1; } - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Trigger Beta Stage 2 Workflow - if: steps.check-changes.outputs.changes_detected == 'true' && steps.load-config.outputs.auto_merge == 'true' - run: | - echo "::notice::Triggering Beta Stage 2 - Build and Release Beta Package" - gh workflow run beta-stage-2_build_beta_release_and_store.yml --ref latest || { echo "::error::Failed to trigger Beta Stage 2 workflow"; exit 1; } - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/purge.yml b/.github/workflows/purge.yml index 8771167..d21dfa2 100644 --- a/.github/workflows/purge.yml +++ b/.github/workflows/purge.yml @@ -57,7 +57,7 @@ jobs: purge_cloudflare_cache: name: Clear Cache needs: purge_old_releases - uses: ./.github/workflows/purge-cf-cache.yml + uses: ./.github/workflows/stage-3_5_purge_cloudflare_cache.yml secrets: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} diff --git a/.github/workflows/reusable-debug-context.yml b/.github/workflows/reusable-debug-context.yml new file mode 100644 index 0000000..471c528 --- /dev/null +++ b/.github/workflows/reusable-debug-context.yml @@ -0,0 +1,46 @@ +name: "Reusable - Debug Context" + +on: + workflow_call: + +jobs: + print_context: + name: Print Debug Context + runs-on: ubuntu-latest + steps: + - name: Dump Inputs context + env: + INPUTS_CONTEXT: ${{ toJson(inputs) }} + run: echo "$INPUTS_CONTEXT" + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + - name: Dump job context + env: + JOB_CONTEXT: ${{ toJson(job) }} + run: echo "$JOB_CONTEXT" + - name: Dump steps context + env: + STEPS_CONTEXT: ${{ toJson(steps) }} + run: echo "$STEPS_CONTEXT" + - name: Dump runner context + env: + RUNNER_CONTEXT: ${{ toJson(runner) }} + run: echo "$RUNNER_CONTEXT" + - name: Dump strategy context + env: + STRATEGY_CONTEXT: ${{ toJson(strategy) }} + run: echo "$STRATEGY_CONTEXT" + - name: Dump matrix context + env: + MATRIX_CONTEXT: ${{ toJson(matrix) }} + run: echo "$MATRIX_CONTEXT" + + - name: Show default environment variables + run: | + echo "The job_id is: $GITHUB_JOB" + echo "The id of this action is: $GITHUB_ACTION" + echo "The run id is: $GITHUB_RUN_ID" + echo "The GitHub Actor's username is: $GITHUB_ACTOR" + echo "GitHub SHA: $GITHUB_SHA" \ No newline at end of file diff --git a/.github/workflows/reusable-validate-homebridge.yml b/.github/workflows/reusable-validate-homebridge.yml new file mode 100644 index 0000000..2576bf3 --- /dev/null +++ b/.github/workflows/reusable-validate-homebridge.yml @@ -0,0 +1,115 @@ +name: "Reusable - Validate Homebridge Package" + +on: + workflow_call: + inputs: + validation_type: + description: "Type of validation to perform" + required: true + type: string + # Options: "prerelease" (validate GitHub release), "apt" (validate APT installation) + release_tag: + description: "Release tag for GitHub release validation" + required: false + type: string + release_channel: + description: "APT release channel for APT validation" + required: false + type: string + # Options: "stable", "beta", "alpha" + prerelease: + description: "Whether to download from prerelease" + required: false + type: boolean + default: false + latest: + description: "Whether to download latest release" + required: false + type: boolean + default: false + +jobs: + validate_github_release: + name: Validate GitHub Release Package + runs-on: ubuntu-latest + if: ${{ inputs.validation_type == 'prerelease' }} + steps: + - name: Download prerelease assets + id: download_assets + uses: robinraju/release-downloader@v1.11 + with: + tag: ${{ inputs.release_tag }} + preRelease: ${{ inputs.prerelease }} + latest: ${{ inputs.latest }} + fileName: 'homebridge*.deb' + + - name: List the downloaded files + run: ls -l + + - name: Check the number of downloaded files + id: check_file_count + run: | + FILE_COUNT=$(ls -1 | wc -l) + echo "Number of files downloaded: $FILE_COUNT" + if [ "$FILE_COUNT" -ne 3 ]; then + echo "::error::Expected 3 files, but found $FILE_COUNT" + exit 1 + fi + + - name: Download and install AMD64 package + uses: robinraju/release-downloader@v1.11 + with: + tag: ${{ inputs.release_tag }} + preRelease: ${{ inputs.prerelease }} + latest: ${{ inputs.latest }} + fileName: 'homebridge_*_amd64.deb' + + - name: List downloaded files + run: | + echo "Downloaded files:" + ls -l + + - name: Install the downloaded package + run: | + sudo dpkg -i homebridge_*_amd64.deb || sudo apt-get install -f -y + sleep 30 + + - name: Validate Homebridge Service + run: | + sudo hb-service view + sudo hb-service status + + validate_apt_installation: + name: Validate APT Installation on ${{ matrix.github-action-runner }} + runs-on: ${{ matrix.github-action-runner }} + if: ${{ inputs.validation_type == 'apt' }} + strategy: + fail-fast: false + matrix: + github-action-runner: [ubuntu-latest, ubuntu-24.04-arm] + steps: + - name: Install Homebridge from APT + run: | + curl -sSfL https://repo.homebridge.io/KEY.gpg | sudo gpg --dearmor -o /usr/share/keyrings/homebridge.gpg + echo "deb [signed-by=/usr/share/keyrings/homebridge.gpg] https://repo.homebridge.io ${{ inputs.release_channel }} main" | \ + sudo tee /etc/apt/sources.list.d/homebridge.list > /dev/null + sudo apt-get update + sudo apt-get install -y homebridge + + - name: Display Installed Version + id: installed-version + run: | + dpkg -l homebridge + echo "homebridge-version=\"$(dpkg -l homebridge | tail -1 | awk '{ print $3 }')\"" >> "$GITHUB_OUTPUT" + + - name: Validate Homebridge + run: | + sudo hb-service status + sudo hb-service view + + - name: List Available Versions + run: | + apt-cache madison homebridge + + - name: Publish Notice + run: echo "::notice::APT Package Installed from ${{ inputs.release_channel }} - Version ${{ steps.installed-version.outputs.homebridge-version }} - ${{ matrix.github-action-runner }}" \ No newline at end of file diff --git a/.github/workflows/stage-1_create_a_release_and_store.yml b/.github/workflows/stage-1_create_a_release_and_store.yml index da71c86..285428f 100644 --- a/.github/workflows/stage-1_create_a_release_and_store.yml +++ b/.github/workflows/stage-1_create_a_release_and_store.yml @@ -151,41 +151,4 @@ jobs: body_path: ${{ steps.release_body.outputs.BODY_FILE }} print_context: - runs-on: ubuntu-latest - steps: - - name: Dump Inputs context - env: - INPUTS_CONTEXT: ${{ toJson(inputs) }} - run: echo "$INPUTS_CONTEXT" - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Dump job context - env: - JOB_CONTEXT: ${{ toJson(job) }} - run: echo "$JOB_CONTEXT" - - name: Dump steps context - env: - STEPS_CONTEXT: ${{ toJson(steps) }} - run: echo "$STEPS_CONTEXT" - - name: Dump runner context - env: - RUNNER_CONTEXT: ${{ toJson(runner) }} - run: echo "$RUNNER_CONTEXT" - - name: Dump strategy context - env: - STRATEGY_CONTEXT: ${{ toJson(strategy) }} - run: echo "$STRATEGY_CONTEXT" - - name: Dump matrix context - env: - MATRIX_CONTEXT: ${{ toJson(matrix) }} - run: echo "$MATRIX_CONTEXT" - - - name: Show default environment variables - run: | - echo "The job_id is: $GITHUB_JOB" # reference the default environment variables - echo "The id of this action is: $GITHUB_ACTION" # reference the default environment variables - echo "The run id is: $GITHUB_RUN_ID" - echo "The GitHub Actor's username is: $GITHUB_ACTOR" - echo "GitHub SHA: $GITHUB_SHA" + uses: ./.github/workflows/reusable-debug-context.yml diff --git a/.github/workflows/stage-2_pre-release_validation.yml b/.github/workflows/stage-2_pre-release_validation.yml index 2e17a0f..68a5abe 100644 --- a/.github/workflows/stage-2_pre-release_validation.yml +++ b/.github/workflows/stage-2_pre-release_validation.yml @@ -78,69 +78,12 @@ jobs: validate_prerelease_x86_package: needs: [determine_release_tag, count_prerelease_assets] - name: Download, Install, and Validate Homebridge Package - runs-on: ubuntu-latest - steps: - - name: Download release asset - id: download_release - uses: robinraju/release-downloader@v1.11 - with: - tag: ${{ needs.determine_release_tag.outputs.release_tag }} - preRelease: ${{ needs.determine_release_tag.outputs.preRelease }} - latest: ${{ needs.determine_release_tag.outputs.latest }} - fileName: 'homebridge_*_amd64.deb' - - - name: List downloaded files - run: | - echo "Downloaded files:" - ls -l - - - name: Install the downloaded package - run: | - sudo dpkg -i homebridge_*_amd64.deb || sudo apt-get install -f -y - sleep 30 - - - name: Validate Homebridge Service - run: | - sudo hb-service view - sudo hb-service status + uses: ./.github/workflows/reusable-validate-homebridge.yml + with: + validation_type: "prerelease" + release_tag: ${{ needs.determine_release_tag.outputs.release_tag }} + prerelease: ${{ needs.determine_release_tag.outputs.preRelease == 'true' }} + latest: ${{ needs.determine_release_tag.outputs.latest == 'true' }} print_context: - runs-on: ubuntu-latest - steps: - - name: Dump Inputs context - env: - INPUTS_CONTEXT: ${{ toJson(inputs) }} - run: echo "$INPUTS_CONTEXT" - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Dump job context - env: - JOB_CONTEXT: ${{ toJson(job) }} - run: echo "$JOB_CONTEXT" - - name: Dump steps context - env: - STEPS_CONTEXT: ${{ toJson(steps) }} - run: echo "$STEPS_CONTEXT" - - name: Dump runner context - env: - RUNNER_CONTEXT: ${{ toJson(runner) }} - run: echo "$RUNNER_CONTEXT" - - name: Dump strategy context - env: - STRATEGY_CONTEXT: ${{ toJson(strategy) }} - run: echo "$STRATEGY_CONTEXT" - - name: Dump matrix context - env: - MATRIX_CONTEXT: ${{ toJson(matrix) }} - run: echo "$MATRIX_CONTEXT" - - - name: Show default environment variables - run: | - echo "The job_id is: $GITHUB_JOB" - echo "The id of this action is: $GITHUB_ACTION" - echo "The run id is: $GITHUB_RUN_ID" - echo "The GitHub Actor's username is: $GITHUB_ACTOR" - echo "GitHub SHA: $GITHUB_SHA" + uses: ./.github/workflows/reusable-debug-context.yml diff --git a/.github/workflows/stage-3_promote_release_to_apt.yml b/.github/workflows/stage-3_promote_release_to_apt.yml index 9bce577..6f2c541 100644 --- a/.github/workflows/stage-3_promote_release_to_apt.yml +++ b/.github/workflows/stage-3_promote_release_to_apt.yml @@ -78,41 +78,4 @@ jobs: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL_LATEST }} print_context: - runs-on: ubuntu-latest - steps: - - name: Dump Inputs context - env: - INPUTS_CONTEXT: ${{ toJson(inputs) }} - run: echo "$INPUTS_CONTEXT" - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Dump job context - env: - JOB_CONTEXT: ${{ toJson(job) }} - run: echo "$JOB_CONTEXT" - - name: Dump steps context - env: - STEPS_CONTEXT: ${{ toJson(steps) }} - run: echo "$STEPS_CONTEXT" - - name: Dump runner context - env: - RUNNER_CONTEXT: ${{ toJson(runner) }} - run: echo "$RUNNER_CONTEXT" - - name: Dump strategy context - env: - STRATEGY_CONTEXT: ${{ toJson(strategy) }} - run: echo "$STRATEGY_CONTEXT" - - name: Dump matrix context - env: - MATRIX_CONTEXT: ${{ toJson(matrix) }} - run: echo "$MATRIX_CONTEXT" - - - name: Show default environment variables - run: | - echo "The job_id is: $GITHUB_JOB" # reference the default environment variables - echo "The id of this action is: $GITHUB_ACTION" # reference the default environment variables - echo "The run id is: $GITHUB_RUN_ID" - echo "The GitHub Actor's username is: $GITHUB_ACTOR" - echo "GitHub SHA: $GITHUB_SHA" \ No newline at end of file + uses: ./.github/workflows/reusable-debug-context.yml \ No newline at end of file From 67df554dc06d987b013d317297f5f07777062db3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 02:12:55 +0000 Subject: [PATCH 07/16] Update reusable-build-package.yml to use optimized beta/alpha build process Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .github/workflows/reusable-build-package.yml | 55 +++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/.github/workflows/reusable-build-package.yml b/.github/workflows/reusable-build-package.yml index 76d51d5..06806bb 100644 --- a/.github/workflows/reusable-build-package.yml +++ b/.github/workflows/reusable-build-package.yml @@ -36,44 +36,71 @@ jobs: QEMU_ARCH: x86_64 - name: debian-arm32v6 - os: ubuntu-latest + os: ubuntu-24.04-arm BASE_IMAGE: balenalib/raspberrypi3-debian:bullseye QEMU_ARCH: arm - name: debian-arm64v8 - os: ubuntu-latest + os: ubuntu-24.04-arm BASE_IMAGE: arm64v8/debian:bullseye QEMU_ARCH: aarch64 steps: - uses: actions/checkout@v4 - - name: Linux - Setup Dependencies - if: runner.os == 'Linux' + - 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: Linux - Build Docker Image - if: runner.os == 'Linux' + - 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.docker_tag_prefix }}-${{ inputs.release_type }} --platform=linux/${{ matrix.QEMU_ARCH }} . + docker build -f build/Dockerfile \ + --build-arg BASE_IMAGE=${{ matrix.BASE_IMAGE }} \ + --build-arg QEMU_ARCH=${{ matrix.QEMU_ARCH }} \ + -t ${{ inputs.docker_tag_prefix }}-${{ inputs.release_type }} \ + --platform=linux/${{ matrix.QEMU_ARCH }} \ + --cache-from=${{ inputs.docker_tag_prefix }}-${{ inputs.release_type }}:latest \ + --build-arg BUILDKIT_INLINE_CACHE=1 . + continue-on-error: false - - name: Linux - Build Package - if: runner.os == 'Linux' + - name: Build package run: | - docker run --rm -v $(pwd):/repo -e PKG_RELEASE_TYPE="${{ inputs.release_type }}" -e PKG_RELEASE_VERSION="${{ inputs.release_version }}" ${{ inputs.docker_tag_prefix }}-${{ inputs.release_type }} + docker run --rm -v $(pwd):/repo \ + -e PKG_RELEASE_TYPE="${{ inputs.release_type }}" \ + -e PKG_RELEASE_VERSION="${{ inputs.release_version }}" \ + ${{ inputs.docker_tag_prefix }}-${{ inputs.release_type }} + continue-on-error: false - name: Rename package to include v run: | - UPDATED=$(ls homebridge*.deb | sed -e 's/homebridge_/homebridge_v/g') - mv homebridge_*.deb ${UPDATED} + 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: | - UPDATED=$(ls homebridge*.manifest | sed -e 's/homebridge_/homebridge_v/g') - mv homebridge_*.manifest ${UPDATED} + 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@v4 with: From eb60daa20026893bd8fb4c592e3a88c8b1b4371c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 02:40:07 +0000 Subject: [PATCH 08/16] Consolidate stable workflow stages (1-5) into 2 unified workflows Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .../Stage-4_post_release_validation.yml | 49 ---------- ...> stable-stage-1_build_and_prerelease.yml} | 22 +++-- ...> stable-stage-2_promote_and_finalize.yml} | 52 ++++++++++- .../stage-2_pre-release_validation.yml | 89 ------------------- .../stage-5_update_version_on_npm.yml | 52 ----------- 5 files changed, 62 insertions(+), 202 deletions(-) delete mode 100644 .github/workflows/Stage-4_post_release_validation.yml rename .github/workflows/{stage-1_create_a_release_and_store.yml => stable-stage-1_build_and_prerelease.yml} (90%) rename .github/workflows/{stage-3_promote_release_to_apt.yml => stable-stage-2_promote_and_finalize.yml} (61%) delete mode 100644 .github/workflows/stage-2_pre-release_validation.yml delete mode 100644 .github/workflows/stage-5_update_version_on_npm.yml diff --git a/.github/workflows/Stage-4_post_release_validation.yml b/.github/workflows/Stage-4_post_release_validation.yml deleted file mode 100644 index 8dfb51f..0000000 --- a/.github/workflows/Stage-4_post_release_validation.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Stage 4 - Post Release Validation - -on: - workflow_dispatch: - inputs: - release_channel: - description: "APT release channel to validate" - required: true - default: "stable" - type: choice - options: - - stable - - beta - - alpha - - workflow_run: - workflows: - - "Stage 3 - Promote Release Package to APT Stores" - - "Beta Stage 2 - Build and Release Beta Package" - - "Alpha Stage 2 - Build and Release Alpha Package" - types: - - completed - -jobs: - determine-channel: - name: Determine Release Channel - runs-on: ubuntu-latest - if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }} - outputs: - channel: ${{ steps.set.outputs.release_channel }} - steps: - - id: set - run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - echo "release_channel=${{ github.event.inputs.release_channel }}" >> $GITHUB_OUTPUT - elif [[ "${{ github.event.workflow_run.name }}" == "Beta Stage 2 - Build and Release Beta Package" ]]; then - echo "release_channel=beta" >> $GITHUB_OUTPUT - elif [[ "${{ github.event.workflow_run.name }}" == "Alpha Stage 2 - Build and Release Alpha Package" ]]; then - echo "release_channel=alpha" >> $GITHUB_OUTPUT - else - echo "release_channel=stable" >> $GITHUB_OUTPUT - fi - - validate: - needs: determine-channel - uses: ./.github/workflows/reusable-validate-homebridge.yml - with: - validation_type: "apt" - release_channel: ${{ needs.determine-channel.outputs.channel }} diff --git a/.github/workflows/stage-1_create_a_release_and_store.yml b/.github/workflows/stable-stage-1_build_and_prerelease.yml similarity index 90% rename from .github/workflows/stage-1_create_a_release_and_store.yml rename to .github/workflows/stable-stage-1_build_and_prerelease.yml index 285428f..3928889 100644 --- a/.github/workflows/stage-1_create_a_release_and_store.yml +++ b/.github/workflows/stable-stage-1_build_and_prerelease.yml @@ -1,4 +1,4 @@ -name: "Stage 1 - Create a pre-release and build APT package" +name: "Stable Stage 1 - Build Package and Create Pre-Release" on: workflow_dispatch: @@ -23,7 +23,7 @@ jobs: merge_inputs: # allows for both the workflow_dispatch and push events to trigger this job runs-on: ubuntu-latest - outputs: + outputs: username: ${{ steps.merge_username.outputs.username }} increment: ${{ steps.merge_increment.outputs.increment }} steps: @@ -38,7 +38,7 @@ jobs: echo "Using USERNAME: $USERNAME" echo "username=$USERNAME" >> "$GITHUB_OUTPUT" - - name: Merge Username + - name: Merge Increment id: merge_increment run: | if [ "${{ inputs.increment }}" ]; then @@ -76,7 +76,7 @@ jobs: increment: ${{ needs.merge_inputs.outputs.increment }} message_about_not_running: - # Only run this job if the username is dependabot[bot] + # Only run this job if the username is not dependabot[bot] needs: merge_inputs if: ${{ needs.merge_inputs.outputs.username != 'dependabot[bot]' }} permissions: @@ -122,11 +122,6 @@ jobs: run: | echo "BODY_FILE=$(ls *.manifest | head -n 1)" >> $GITHUB_OUTPUT - - name: Dump Steps context - env: - STEPS_CONTEXT: ${{ toJson(steps) }} - run: echo "$STEPS_CONTEXT" - - name: Create Pre-Release uses: docker://ghcr.io/mini-bomba/create-github-release:v1.2.0 with: @@ -150,5 +145,14 @@ jobs: with: body_path: ${{ steps.release_body.outputs.BODY_FILE }} + validate_prerelease: + needs: [publish_prerelease, create_draft_prerelease] + uses: ./.github/workflows/reusable-validate-homebridge.yml + with: + validation_type: "prerelease" + release_tag: ${{ needs.create_draft_prerelease.outputs.v-version }} + prerelease: true + latest: false + print_context: uses: ./.github/workflows/reusable-debug-context.yml diff --git a/.github/workflows/stage-3_promote_release_to_apt.yml b/.github/workflows/stable-stage-2_promote_and_finalize.yml similarity index 61% rename from .github/workflows/stage-3_promote_release_to_apt.yml rename to .github/workflows/stable-stage-2_promote_and_finalize.yml index 6f2c541..c11b380 100644 --- a/.github/workflows/stage-3_promote_release_to_apt.yml +++ b/.github/workflows/stable-stage-2_promote_and_finalize.yml @@ -1,4 +1,4 @@ -name: "Stage 3 - Promote Release Package to APT Stores" +name: "Stable Stage 2 - Promote Release to APT and Finalize" on: release: @@ -6,7 +6,7 @@ on: workflow_dispatch: inputs: release_tag: - description: "The tag of the release to Package to APT Stores." + description: "The tag of the release to promote to APT stores." required: true type: string @@ -66,6 +66,52 @@ jobs: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} + validate_apt_installation: + needs: [purge_cloudflare_cache] + uses: ./.github/workflows/reusable-validate-homebridge.yml + with: + validation_type: "apt" + release_channel: "stable" + + publish_to_npm: + needs: [determine_release_tag, update_apt_repo] + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: latest + registry-url: 'https://registry.npmjs.org' + + - name: Extract and validate version from tag + id: version + run: | + RAW_TAG="${{ needs.determine_release_tag.outputs.release_tag }}" + if [[ "$RAW_TAG" =~ ^v([0-9]+\.[0-9]+\.[0-9]+.*)$ ]]; then + CLEAN_VERSION="${BASH_REMATCH[1]}" + echo "CLEAN_VERSION=$CLEAN_VERSION" >> "$GITHUB_ENV" + else + echo "::error::Invalid tag format: '$RAW_TAG'. Expected format 'v1.2.3'" + exit 1 + fi + + - name: Set package.json version + run: | + echo "Setting version to $CLEAN_VERSION" + jq ".version = \"$CLEAN_VERSION\"" package.json > tmp.$$.json && mv tmp.$$.json package.json + cat package.json + + - name: Publish to npm + run: npm publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Output Success Notice + run: echo "::notice::Published @homebridge/homebridge-apt-pkg as version $CLEAN_VERSION" + notify_discord: name: Notify Discord needs: [update_apt_repo, purge_cloudflare_cache, determine_release_tag] @@ -78,4 +124,4 @@ jobs: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL_LATEST }} print_context: - uses: ./.github/workflows/reusable-debug-context.yml \ No newline at end of file + uses: ./.github/workflows/reusable-debug-context.yml diff --git a/.github/workflows/stage-2_pre-release_validation.yml b/.github/workflows/stage-2_pre-release_validation.yml deleted file mode 100644 index 68a5abe..0000000 --- a/.github/workflows/stage-2_pre-release_validation.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: "Stage 2 - Pre-Release Validation Workflow" - -on: - workflow_dispatch: - inputs: - release_tag: - description: "The tag of the release to validate." - required: true - type: string - release: - types: [prereleased] - # The workflow run trigger is a workaround due to mini-bomba/create-github-release@v1.2.0 in Stage 1 - not triggering the release event - workflow_run: - workflows: ["Stage 1 - Create a pre-release and build APT package", - "Test - Stage 1 - Create a pre-release and build APT package"] - types: - - completed - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - determine_release_tag: - name: Determine Release Tag - runs-on: ubuntu-latest - outputs: - release_tag: ${{ steps.set_release_tag.outputs.release_tag }} - preRelease: ${{ steps.set_release_tag.outputs.preRelease }} - latest: ${{ steps.set_release_tag.outputs.latest }} - steps: - - name: Set Release Tag - id: set_release_tag - run: | - if [ "${{ github.event_name }}" == "release" ]; then - echo "release_tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT" - echo "preRelease=false" >> "$GITHUB_OUTPUT" - echo "latest=false" >> "$GITHUB_OUTPUT" - elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - echo "release_tag=${{ inputs.release_tag }}" >> "$GITHUB_OUTPUT" - echo "preRelease=false" >> "$GITHUB_OUTPUT" - echo "latest=false" >> "$GITHUB_OUTPUT" - elif [ "${{ github.event_name }}" == "workflow_run" ] && [ "${{ github.event.workflow_run.conclusion }}" == "success" ]; then - echo "release_tag=''" >> "$GITHUB_OUTPUT" - echo "preRelease=true" >> "$GITHUB_OUTPUT" - echo "latest=true" >> "$GITHUB_OUTPUT" - else - echo "::error::Unexpected event: ${{ github.event_name }}" - exit 1 - fi - - count_prerelease_assets: - name: Download Latest Pre-Release Assets - runs-on: ubuntu-latest - needs: determine_release_tag - steps: - - name: Download prerelease assets - id: download_assets - uses: robinraju/release-downloader@v1.11 - with: - tag: ${{ needs.determine_release_tag.outputs.release_tag }} - preRelease: ${{ needs.determine_release_tag.outputs.preRelease }} - latest: ${{ needs.determine_release_tag.outputs.latest }} - fileName: 'homebridge*.deb' - - - name: List the downloaded files - run: ls -l - - - name: Check the number of downloaded files - id: check_file_count - run: | - FILE_COUNT=$(ls -1 | wc -l) - echo "Number of files downloaded: $FILE_COUNT" - if [ "$FILE_COUNT" -ne 3 ]; then - echo "::error::Expected 3 files, but found $FILE_COUNT" - exit 1 - fi - - validate_prerelease_x86_package: - needs: [determine_release_tag, count_prerelease_assets] - uses: ./.github/workflows/reusable-validate-homebridge.yml - with: - validation_type: "prerelease" - release_tag: ${{ needs.determine_release_tag.outputs.release_tag }} - prerelease: ${{ needs.determine_release_tag.outputs.preRelease == 'true' }} - latest: ${{ needs.determine_release_tag.outputs.latest == 'true' }} - - print_context: - uses: ./.github/workflows/reusable-debug-context.yml diff --git a/.github/workflows/stage-5_update_version_on_npm.yml b/.github/workflows/stage-5_update_version_on_npm.yml deleted file mode 100644 index 092f26a..0000000 --- a/.github/workflows/stage-5_update_version_on_npm.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Stage 5 - Update Version on npm -# This workflow updates the version of the stub package on npm when a new tag is pushed. -# It extracts the version from the tag and updates the package.json file accordingly -# before publishing the package to npm. -# It is triggered by the completion of the "Stage 3 - Promote Release Package to APT Stores" workflow. - -on: - workflow_run: - workflows: [Stage 3 - Promote Release Package to APT Stores] - types: - - completed - -jobs: - publish: - name: Publish ${{ github.event.workflow_run.head_branch }} - runs-on: ubuntu-latest - - steps: - - name: Checkout Repo - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: latest - registry-url: 'https://registry.npmjs.org' - - - name: Extract and validate version from tag - id: version - run: | - RAW_TAG="${{ github.event.workflow_run.head_branch }}" - if [[ "$RAW_TAG" =~ ^v([0-9]+\.[0-9]+\.[0-9]+.*)$ ]]; then - CLEAN_VERSION="${BASH_REMATCH[1]}" - echo "CLEAN_VERSION=$CLEAN_VERSION" >> "$GITHUB_ENV" - else - echo "::error::Invalid tag format: '$RAW_TAG'. Expected format 'v1.2.3'" - exit 1 - fi - - - name: Set package.json version - run: | - echo "Setting version to $CLEAN_VERSION" - jq ".version = \"$CLEAN_VERSION\"" package.json > tmp.$$.json && mv tmp.$$.json package.json - cat package.json - - - name: Publish to npm - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Output Success Notice - run: echo "::notice::Published @homebridge/homebridge-apt-pkg as version $CLEAN_VERSION" From 2118e6a6b71b5f33cd634e4231fcf266eaeaa0ee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 02:55:19 +0000 Subject: [PATCH 09/16] refactor: use reusable-publish-npm workflow in stable release Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .../stable-stage-2_promote_and_finalize.yml | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/.github/workflows/stable-stage-2_promote_and_finalize.yml b/.github/workflows/stable-stage-2_promote_and_finalize.yml index c11b380..6678f46 100644 --- a/.github/workflows/stable-stage-2_promote_and_finalize.yml +++ b/.github/workflows/stable-stage-2_promote_and_finalize.yml @@ -73,44 +73,33 @@ jobs: validation_type: "apt" release_channel: "stable" - publish_to_npm: - needs: [determine_release_tag, update_apt_repo] + extract_npm_version: + name: Extract NPM Version + needs: [determine_release_tag] runs-on: ubuntu-latest + outputs: + npm_version: ${{ steps.extract_version.outputs.npm_version }} steps: - - name: Checkout Repo - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: latest - registry-url: 'https://registry.npmjs.org' - - name: Extract and validate version from tag - id: version + id: extract_version run: | RAW_TAG="${{ needs.determine_release_tag.outputs.release_tag }}" if [[ "$RAW_TAG" =~ ^v([0-9]+\.[0-9]+\.[0-9]+.*)$ ]]; then CLEAN_VERSION="${BASH_REMATCH[1]}" - echo "CLEAN_VERSION=$CLEAN_VERSION" >> "$GITHUB_ENV" + echo "npm_version=$CLEAN_VERSION" >> "$GITHUB_OUTPUT" else echo "::error::Invalid tag format: '$RAW_TAG'. Expected format 'v1.2.3'" exit 1 fi - - name: Set package.json version - run: | - echo "Setting version to $CLEAN_VERSION" - jq ".version = \"$CLEAN_VERSION\"" package.json > tmp.$$.json && mv tmp.$$.json package.json - cat package.json - - - name: Publish to npm - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Output Success Notice - run: echo "::notice::Published @homebridge/homebridge-apt-pkg as version $CLEAN_VERSION" + publish_to_npm: + needs: [extract_npm_version, update_apt_repo] + uses: ./.github/workflows/reusable-publish-npm.yml + with: + npm_version: ${{ needs.extract_npm_version.outputs.npm_version }} + npm_tag: 'latest' + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} notify_discord: name: Notify Discord From a4b8c942728a39f9539af98492ff23a1c061818a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 13:32:33 +0000 Subject: [PATCH 10/16] Consolidate cron schedule to single entry that processes both alpha and beta streams Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .../prerelease-stage-1_update_dependencies.yml | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/.github/workflows/prerelease-stage-1_update_dependencies.yml b/.github/workflows/prerelease-stage-1_update_dependencies.yml index b435bd2..c6e2d7e 100644 --- a/.github/workflows/prerelease-stage-1_update_dependencies.yml +++ b/.github/workflows/prerelease-stage-1_update_dependencies.yml @@ -2,10 +2,8 @@ name: Prerelease Stage 1 - Update Alpha and Beta Dependencies, Create PRs, Merge on: schedule: - # Beta: 4 AM Eastern (8 AM UTC), Docker updates at 6 AM Eastern (10 AM UTC) + # Process both alpha and beta streams at 8 AM UTC daily - cron: '0 8 * * *' - # Alpha: 5 AM Eastern (9 AM UTC), Docker updates at 6 AM Eastern (11 AM UTC) - - cron: '0 9 * * *' workflow_dispatch: inputs: release_type: @@ -38,18 +36,8 @@ jobs: echo "matrix={\"release_type\":[\"${{ github.event.inputs.release_type }}\"],\"include\":[{\"release_type\":\"${{ github.event.inputs.release_type }}\",\"config_file\":\"$CONFIG_FILE\",\"trigger_workflow\":\"prerelease-stage-2_build_and_release.yml\"}]}" >> $GITHUB_OUTPUT fi else - # Scheduled run - determine based on cron time - CURRENT_HOUR=$(date -u +%H) - if [[ "$CURRENT_HOUR" == "08" ]]; then - # Beta cron at 8 AM UTC - echo 'matrix={"release_type":["beta"],"include":[{"release_type":"beta","config_file":".github/homebridge-beta-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"}]}' >> $GITHUB_OUTPUT - elif [[ "$CURRENT_HOUR" == "09" ]]; then - # Alpha cron at 9 AM UTC - echo 'matrix={"release_type":["alpha"],"include":[{"release_type":"alpha","config_file":".github/homebridge-alpha-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"}]}' >> $GITHUB_OUTPUT - else - echo "::error::Unexpected scheduled run time: $CURRENT_HOUR" - exit 1 - fi + # Scheduled run - process both alpha and beta streams + echo 'matrix={"release_type":["alpha","beta"],"include":[{"release_type":"alpha","config_file":".github/homebridge-alpha-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"},{"release_type":"beta","config_file":".github/homebridge-beta-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"}]}' >> $GITHUB_OUTPUT fi update_dependencies: From 54728ad968e5699752da0c9a0b3183326977ef85 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 20:35:31 +0000 Subject: [PATCH 11/16] Enable homebridge-bot for stable releases and reduce workflow count by inlining single-use reusables Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .github/homebridge-stable-bot.json | 43 +++ ...prerelease-stage-1_update_dependencies.yml | 53 ---- .../release-stage-1_update_dependencies.yml | 54 ++++ ... => release-stage-2_build_and_release.yml} | 38 ++- .../reusable-build-and-release-prerelease.yml | 132 --------- .../workflows/reusable-build-and-release.yml | 269 ++++++++++++++++++ .../reusable-create-github-release.yml | 69 ----- .github/workflows/reusable-debug-context.yml | 46 --- .../workflows/reusable-generate-version.yml | 50 ---- ..._and_finalize.yml => stable-promotion.yml} | 38 ++- .../stable-stage-1_build_and_prerelease.yml | 158 ---------- 11 files changed, 431 insertions(+), 519 deletions(-) create mode 100644 .github/homebridge-stable-bot.json delete mode 100644 .github/workflows/prerelease-stage-1_update_dependencies.yml create mode 100644 .github/workflows/release-stage-1_update_dependencies.yml rename .github/workflows/{prerelease-stage-2_build_and_release.yml => release-stage-2_build_and_release.yml} (69%) delete mode 100644 .github/workflows/reusable-build-and-release-prerelease.yml create mode 100644 .github/workflows/reusable-build-and-release.yml delete mode 100644 .github/workflows/reusable-create-github-release.yml delete mode 100644 .github/workflows/reusable-debug-context.yml delete mode 100644 .github/workflows/reusable-generate-version.yml rename .github/workflows/{stable-stage-2_promote_and_finalize.yml => stable-promotion.yml} (76%) delete mode 100644 .github/workflows/stable-stage-1_build_and_prerelease.yml diff --git a/.github/homebridge-stable-bot.json b/.github/homebridge-stable-bot.json new file mode 100644 index 0000000..fdf3085 --- /dev/null +++ b/.github/homebridge-stable-bot.json @@ -0,0 +1,43 @@ +{ + "auto_merge": true, + "git_user": { + "name": "Homebridge Stable Bot", + "email": "actions@github.com" + }, + "directories": [ + { + "directory": "stable/32bit", + "packages": [ + { + "name": "homebridge-config-ui-x", + "tag": "latest" + }, + { + "name": "homebridge", + "pattern": "^1\\.[0-9]+\\.[0-9]+" + }, + { + "name": "node", + "tag": "^22.x.x" + } + ] + }, + { + "directory": "stable/64bit", + "packages": [ + { + "name": "homebridge-config-ui-x", + "tag": "latest" + }, + { + "name": "homebridge", + "pattern": "^1\\.[0-9]+\\.[0-9]+" + }, + { + "name": "node", + "tag": "^24.x.x" + } + ] + } + ] +} \ No newline at end of file diff --git a/.github/workflows/prerelease-stage-1_update_dependencies.yml b/.github/workflows/prerelease-stage-1_update_dependencies.yml deleted file mode 100644 index c6e2d7e..0000000 --- a/.github/workflows/prerelease-stage-1_update_dependencies.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Prerelease Stage 1 - Update Alpha and Beta Dependencies, Create PRs, Merge PRs and Trigger Stage 2 - -on: - schedule: - # Process both alpha and beta streams at 8 AM UTC daily - - cron: '0 8 * * *' - workflow_dispatch: - inputs: - release_type: - description: 'Release type to update' - required: true - type: choice - options: - - alpha - - beta - - both - default: 'both' - -run-name: Prerelease Stage 1 - ${{ github.event_name == 'workflow_dispatch' && format('Manual - {0}', github.event.inputs.release_type) || 'Scheduled' }} Run - -jobs: - determine-release-types: - name: Determine Release Types - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - name: Set matrix based on trigger - id: set-matrix - run: | - if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then - if [[ "${{ github.event.inputs.release_type }}" == "both" ]]; then - echo 'matrix={"release_type":["alpha","beta"],"include":[{"release_type":"alpha","config_file":".github/homebridge-alpha-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"},{"release_type":"beta","config_file":".github/homebridge-beta-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"}]}' >> $GITHUB_OUTPUT - else - CONFIG_FILE=".github/homebridge-${{ github.event.inputs.release_type }}-bot.json" - echo "matrix={\"release_type\":[\"${{ github.event.inputs.release_type }}\"],\"include\":[{\"release_type\":\"${{ github.event.inputs.release_type }}\",\"config_file\":\"$CONFIG_FILE\",\"trigger_workflow\":\"prerelease-stage-2_build_and_release.yml\"}]}" >> $GITHUB_OUTPUT - fi - else - # Scheduled run - process both alpha and beta streams - echo 'matrix={"release_type":["alpha","beta"],"include":[{"release_type":"alpha","config_file":".github/homebridge-alpha-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"},{"release_type":"beta","config_file":".github/homebridge-beta-bot.json","trigger_workflow":"prerelease-stage-2_build_and_release.yml"}]}' >> $GITHUB_OUTPUT - fi - - update_dependencies: - name: Update ${{ matrix.release_type }} Dependencies - needs: determine-release-types - strategy: - matrix: ${{ fromJson(needs.determine-release-types.outputs.matrix) }} - uses: ./.github/workflows/reusable-update-dependencies.yml - with: - release_type: ${{ matrix.release_type }} - config_file: ${{ matrix.config_file }} - trigger_workflow: ${{ matrix.trigger_workflow }} - secrets: inherit diff --git a/.github/workflows/release-stage-1_update_dependencies.yml b/.github/workflows/release-stage-1_update_dependencies.yml new file mode 100644 index 0000000..3b67b61 --- /dev/null +++ b/.github/workflows/release-stage-1_update_dependencies.yml @@ -0,0 +1,54 @@ +name: Release Stage 1 - Update Alpha, Beta, and Stable Dependencies, Create PRs, Merge PRs and Trigger Stage 2 + +on: + schedule: + # Process alpha, beta, and stable streams at 8 AM UTC daily + - cron: '0 8 * * *' + workflow_dispatch: + inputs: + release_type: + description: 'Release type to update' + required: true + type: choice + options: + - alpha + - beta + - stable + - all + default: 'all' + +run-name: Release Stage 1 - ${{ github.event_name == 'workflow_dispatch' && format('Manual - {0}', github.event.inputs.release_type) || 'Scheduled' }} Run + +jobs: + determine-release-types: + name: Determine Release Types + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Set matrix based on trigger + id: set-matrix + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + if [[ "${{ github.event.inputs.release_type }}" == "all" ]]; then + echo 'matrix={"release_type":["alpha","beta","stable"],"include":[{"release_type":"alpha","config_file":".github/homebridge-alpha-bot.json","trigger_workflow":"release-stage-2_build_and_release.yml"},{"release_type":"beta","config_file":".github/homebridge-beta-bot.json","trigger_workflow":"release-stage-2_build_and_release.yml"},{"release_type":"stable","config_file":".github/homebridge-stable-bot.json","trigger_workflow":"release-stage-2_build_and_release.yml"}]}' >> $GITHUB_OUTPUT + else + CONFIG_FILE=".github/homebridge-${{ github.event.inputs.release_type }}-bot.json" + echo "matrix={\"release_type\":[\"${{ github.event.inputs.release_type }}\"],\"include\":[{\"release_type\":\"${{ github.event.inputs.release_type }}\",\"config_file\":\"$CONFIG_FILE\",\"trigger_workflow\":\"release-stage-2_build_and_release.yml\"}]}" >> $GITHUB_OUTPUT + fi + else + # Scheduled run - process alpha, beta, and stable streams + echo 'matrix={"release_type":["alpha","beta","stable"],"include":[{"release_type":"alpha","config_file":".github/homebridge-alpha-bot.json","trigger_workflow":"release-stage-2_build_and_release.yml"},{"release_type":"beta","config_file":".github/homebridge-beta-bot.json","trigger_workflow":"release-stage-2_build_and_release.yml"},{"release_type":"stable","config_file":".github/homebridge-stable-bot.json","trigger_workflow":"release-stage-2_build_and_release.yml"}]}' >> $GITHUB_OUTPUT + fi + + update_dependencies: + name: Update ${{ matrix.release_type }} Dependencies + needs: determine-release-types + strategy: + matrix: ${{ fromJson(needs.determine-release-types.outputs.matrix) }} + uses: ./.github/workflows/reusable-update-dependencies.yml + with: + release_type: ${{ matrix.release_type }} + config_file: ${{ matrix.config_file }} + trigger_workflow: ${{ matrix.trigger_workflow }} + secrets: inherit diff --git a/.github/workflows/prerelease-stage-2_build_and_release.yml b/.github/workflows/release-stage-2_build_and_release.yml similarity index 69% rename from .github/workflows/prerelease-stage-2_build_and_release.yml rename to .github/workflows/release-stage-2_build_and_release.yml index c06c94e..62e668d 100644 --- a/.github/workflows/prerelease-stage-2_build_and_release.yml +++ b/.github/workflows/release-stage-2_build_and_release.yml @@ -1,4 +1,4 @@ -name: Prerelease Stage 2 - Build and Release Alpha and Beta Packages +name: Release Stage 2 - Build and Release Alpha, Beta, and Stable Packages on: pull_request: @@ -6,6 +6,7 @@ on: paths: - 'alpha/**' - 'beta/**' + - 'stable/**' workflow_dispatch: inputs: release_type: @@ -15,6 +16,7 @@ on: options: - alpha - beta + - stable default: 'beta' jobs: @@ -49,15 +51,32 @@ jobs: ALPHA_CHANGES=$(echo "$CHANGED_FILES" | grep -c "^alpha/" || true) BETA_CHANGES=$(echo "$CHANGED_FILES" | grep -c "^beta/" || true) + STABLE_CHANGES=$(echo "$CHANGED_FILES" | grep -c "^stable/" || true) echo "Alpha files changed: $ALPHA_CHANGES" echo "Beta files changed: $BETA_CHANGES" + echo "Stable files changed: $STABLE_CHANGES" - if [[ $ALPHA_CHANGES -gt 0 && $BETA_CHANGES -gt 0 ]]; then - echo "::error::PR contains changes to both alpha and beta directories. This should not happen." + TOTAL_CHANGES=$((ALPHA_CHANGES + BETA_CHANGES + STABLE_CHANGES)) + + if [[ $TOTAL_CHANGES -eq 0 ]]; then echo "should_run=false" >> $GITHUB_OUTPUT - exit 1 - elif [[ $ALPHA_CHANGES -gt 0 ]]; then + echo "::notice::No relevant changes detected, skipping build" + elif [[ $TOTAL_CHANGES -gt 1 ]] && [[ $ALPHA_CHANGES -gt 0 || $BETA_CHANGES -gt 0 || $STABLE_CHANGES -gt 0 ]]; then + # Check if more than one release type has changes + CHANGE_COUNT=0 + [[ $ALPHA_CHANGES -gt 0 ]] && CHANGE_COUNT=$((CHANGE_COUNT + 1)) + [[ $BETA_CHANGES -gt 0 ]] && CHANGE_COUNT=$((CHANGE_COUNT + 1)) + [[ $STABLE_CHANGES -gt 0 ]] && CHANGE_COUNT=$((CHANGE_COUNT + 1)) + + if [[ $CHANGE_COUNT -gt 1 ]]; then + echo "::error::PR contains changes to multiple release type directories. This should not happen." + echo "should_run=false" >> $GITHUB_OUTPUT + exit 1 + fi + fi + + if [[ $ALPHA_CHANGES -gt 0 ]]; then echo "release_type=alpha" >> $GITHUB_OUTPUT echo "should_run=true" >> $GITHUB_OUTPUT echo "::notice::Detected alpha release based on changed files" @@ -65,9 +84,10 @@ jobs: echo "release_type=beta" >> $GITHUB_OUTPUT echo "should_run=true" >> $GITHUB_OUTPUT echo "::notice::Detected beta release based on changed files" - else - echo "should_run=false" >> $GITHUB_OUTPUT - echo "::notice::No relevant changes detected, skipping build" + elif [[ $STABLE_CHANGES -gt 0 ]]; then + echo "release_type=stable" >> $GITHUB_OUTPUT + echo "should_run=true" >> $GITHUB_OUTPUT + echo "::notice::Detected stable release based on changed files" fi else echo "should_run=false" >> $GITHUB_OUTPUT @@ -80,7 +100,7 @@ jobs: name: Build and Release ${{ needs.determine-release-type.outputs.release_type }} Package needs: determine-release-type if: needs.determine-release-type.outputs.should_run == 'true' - uses: ./.github/workflows/reusable-build-and-release-prerelease.yml + uses: ./.github/workflows/reusable-build-and-release.yml with: release_type: ${{ needs.determine-release-type.outputs.release_type }} event_name: ${{ github.event_name }} diff --git a/.github/workflows/reusable-build-and-release-prerelease.yml b/.github/workflows/reusable-build-and-release-prerelease.yml deleted file mode 100644 index 99234e4..0000000 --- a/.github/workflows/reusable-build-and-release-prerelease.yml +++ /dev/null @@ -1,132 +0,0 @@ -name: Reusable - Build and Release Prerelease Package - -on: - workflow_call: - inputs: - release_type: - description: 'Release type (alpha or beta)' - required: true - type: string - event_name: - description: 'Event name for conditional execution' - required: false - type: string - default: 'workflow_call' - pr_merged: - description: 'Whether PR was merged (for pull_request events)' - required: false - type: boolean - default: true - secrets: - GPG_PRIVATE_KEY: - required: true - GPG_PASSPHRASE: - required: true - GPG_KEY_ID: - required: true - AWS_ACCESS_KEY_ID: - required: true - AWS_SECRET_ACCESS_KEY: - required: true - CLOUDFLARE_API_TOKEN: - required: true - CLOUDFLARE_ZONE_ID: - required: true - NPM_TOKEN: - required: true - -permissions: - contents: write - actions: write - id-token: write - -jobs: - check-changes: - if: ${{ inputs.pr_merged == true || inputs.event_name == 'workflow_dispatch' }} - runs-on: ubuntu-latest - outputs: - changes_only: ${{ steps.verify_changes.outputs.changes_only }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Log event context for debugging - if: ${{ inputs.event_name == 'pull_request' }} - run: | - echo "Event: ${{ inputs.event_name }}" - echo "Merged: ${{ inputs.pr_merged }}" - - - name: Verify ${{ inputs.release_type }}-only changes - id: verify_changes - run: | - if [ "${{ inputs.event_name }}" == "workflow_dispatch" ]; then - echo "Workflow dispatch detected, setting changes_only=true" - echo "changes_only=true" >> $GITHUB_OUTPUT - else - changes_only=true - changed_files=$(git diff --name-only HEAD^ HEAD) - for file in $changed_files; do - if [[ ! $file == ${{ inputs.release_type }}/* ]]; then - echo "Non-${{ inputs.release_type }} changes detected in $file. Setting changes_only=false" - changes_only=false - fi - done - echo "changes_only=$changes_only" >> $GITHUB_OUTPUT - fi - - generate_version: - needs: check-changes - if: ${{ needs.check-changes.outputs.changes_only == 'true' }} - uses: ./.github/workflows/reusable-generate-version.yml - with: - release_type: ${{ inputs.release_type }} - - build_and_store: - needs: [generate_version] - uses: ./.github/workflows/reusable-build-package.yml - with: - release_type: ${{ inputs.release_type }} - release_version: ${{ needs.generate_version.outputs.version }} - docker_tag_prefix: ${{ inputs.release_type }}-package-build - - publish_apt: - needs: [generate_version, build_and_store] - uses: ./.github/workflows/reusable-publish-apt.yml - with: - codename: ${{ inputs.release_type }} - suite: ${{ inputs.release_type }} - release_version: ${{ needs.generate_version.outputs.version }} - secrets: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - publish_github_release: - needs: [check-changes, publish_apt, generate_version] - uses: ./.github/workflows/reusable-create-github-release.yml - with: - tag_name: v${{ needs.generate_version.outputs.npm_version }} - release_name: ${{ inputs.release_type == 'alpha' && 'Alpha' || 'Beta' }} Release v${{ needs.generate_version.outputs.npm_version }} - release_type: ${{ inputs.release_type }} - prerelease: true - - purge_cloudflare_cache: - name: Purge Cloudflare Cache - needs: [publish_apt] - uses: ./.github/workflows/stage-3_5_purge_cloudflare_cache.yml - secrets: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} - - publish_to_npm: - needs: [generate_version, build_and_store] - uses: ./.github/workflows/reusable-publish-npm.yml - with: - npm_version: ${{ needs.generate_version.outputs.npm_version }} - npm_tag: ${{ inputs.release_type }} - secrets: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/reusable-build-and-release.yml b/.github/workflows/reusable-build-and-release.yml new file mode 100644 index 0000000..d9887cf --- /dev/null +++ b/.github/workflows/reusable-build-and-release.yml @@ -0,0 +1,269 @@ +name: Reusable - Build and Release Package (All Release Types) + +on: + workflow_call: + inputs: + release_type: + description: 'Release type (alpha, beta, or stable)' + required: true + type: string + event_name: + description: 'Event name for conditional execution' + required: false + type: string + default: 'workflow_call' + pr_merged: + description: 'Whether PR was merged (for pull_request events)' + required: false + type: boolean + default: true + secrets: + GPG_PRIVATE_KEY: + required: true + GPG_PASSPHRASE: + required: true + GPG_KEY_ID: + required: true + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + CLOUDFLARE_API_TOKEN: + required: true + CLOUDFLARE_ZONE_ID: + required: true + NPM_TOKEN: + required: true + +permissions: + contents: write + actions: write + id-token: write + +jobs: + check-changes: + if: ${{ inputs.pr_merged == true || inputs.event_name == 'workflow_dispatch' }} + runs-on: ubuntu-latest + outputs: + changes_only: ${{ steps.verify_changes.outputs.changes_only }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Log event context for debugging + if: ${{ inputs.event_name == 'pull_request' }} + run: | + echo "Event: ${{ inputs.event_name }}" + echo "Merged: ${{ inputs.pr_merged }}" + + - name: Verify ${{ inputs.release_type }}-only changes + id: verify_changes + run: | + if [ "${{ inputs.event_name }}" == "workflow_dispatch" ]; then + echo "Workflow dispatch detected, setting changes_only=true" + echo "changes_only=true" >> $GITHUB_OUTPUT + else + changes_only=true + changed_files=$(git diff --name-only HEAD^ HEAD) + for file in $changed_files; do + if [[ ! $file == ${{ inputs.release_type }}/* ]]; then + echo "Non-${{ inputs.release_type }} changes detected in $file. Setting changes_only=false" + changes_only=false + fi + done + echo "changes_only=$changes_only" >> $GITHUB_OUTPUT + fi + + # For alpha/beta releases, generate version using date-based approach + generate_version: + needs: check-changes + if: ${{ needs.check-changes.outputs.changes_only == 'true' && 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@v4 + + - name: Get base version + uses: reecetech/version-increment@2023.10.1 + 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: + needs: check-changes + if: ${{ needs.check-changes.outputs.changes_only == 'true' && 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@v4 + + - name: Get next stable version + uses: reecetech/version-increment@2023.10.1 + id: release_version + with: + scheme: semver + increment: patch + + build_and_store: + needs: [generate_version, generate_stable_version] + if: always() && (needs.generate_version.result == 'success' || needs.generate_stable_version.result == 'success') + uses: ./.github/workflows/reusable-build-package.yml + with: + release_type: ${{ inputs.release_type }} + release_version: ${{ inputs.release_type == 'stable' && needs.generate_stable_version.outputs.version || needs.generate_version.outputs.version }} + docker_tag_prefix: ${{ inputs.release_type }}-package-build + + # For alpha/beta releases, publish directly to APT + publish_apt: + needs: [generate_version, build_and_store] + if: ${{ needs.generate_version.result == 'success' && inputs.release_type != 'stable' }} + uses: ./.github/workflows/reusable-publish-apt.yml + with: + codename: ${{ inputs.release_type }} + suite: ${{ inputs.release_type }} + release_version: ${{ needs.generate_version.outputs.version }} + secrets: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + + # For stable releases, create prerelease instead of publishing to APT + create_stable_prerelease: + needs: [generate_stable_version, build_and_store] + if: ${{ needs.generate_stable_version.result == 'success' && inputs.release_type == 'stable' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + 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: Create Stable Pre-Release + uses: docker://ghcr.io/mini-bomba/create-github-release:v1.2.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: v${{ needs.generate_stable_version.outputs.version }} + prerelease: true + draft: false + name: "v${{ needs.generate_stable_version.outputs.version }} - ${{ steps.date.outputs.builddate }}" + files: | + *.deb + *.manifest + body: ${{ steps.release_body.outputs.BODY_FILE }} + clear_attachments: true + fail_on_no_files: true + + - name: update release + uses: tubone24/update_release@v1.3.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG_NAME: v${{ needs.generate_stable_version.outputs.version }} + with: + body_path: ${{ steps.release_body.outputs.BODY_FILE }} + + # For alpha/beta releases, create GitHub release + publish_github_release: + needs: [check-changes, publish_apt, generate_version] + if: ${{ needs.generate_version.result == 'success' && inputs.release_type != 'stable' }} + runs-on: ubuntu-latest + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + pattern: artifacts-* + merge-multiple: true + path: artifacts/ + + - name: Read manifest content + id: read_manifest + run: | + echo "MANIFEST_FILE=$(ls artifacts/*.manifest | head -n 1)" >> $GITHUB_OUTPUT + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ needs.generate_version.outputs.npm_version }} + name: ${{ inputs.release_type == 'alpha' && 'Alpha' || 'Beta' }} Release v${{ needs.generate_version.outputs.npm_version }} + prerelease: true + draft: false + files: | + artifacts/homebridge_v*.deb + artifacts/homebridge_v*.manifest + body_path: ${{ steps.read_manifest.outputs.MANIFEST_FILE }} + body: | + Homebridge Apt Package Manifest + + Release Version: v${{ needs.generate_version.outputs.npm_version }} + Release Type: ${{ inputs.release_type }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # For alpha/beta releases, purge CloudFlare cache + purge_cloudflare_cache: + name: Purge Cloudflare Cache + needs: [publish_apt] + if: ${{ needs.publish_apt.result == 'success' && inputs.release_type != 'stable' }} + uses: ./.github/workflows/stage-3_5_purge_cloudflare_cache.yml + secrets: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} + + # For alpha/beta releases, publish to NPM + publish_to_npm: + needs: [generate_version, build_and_store] + if: ${{ needs.generate_version.result == 'success' && inputs.release_type != 'stable' }} + uses: ./.github/workflows/reusable-publish-npm.yml + with: + npm_version: ${{ needs.generate_version.outputs.npm_version }} + npm_tag: ${{ inputs.release_type }} + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + # For stable releases, validate prerelease + validate_stable_prerelease: + needs: [create_stable_prerelease, generate_stable_version] + if: ${{ needs.create_stable_prerelease.result == 'success' && inputs.release_type == 'stable' }} + uses: ./.github/workflows/reusable-validate-homebridge.yml + with: + validation_type: "prerelease" + release_tag: v${{ needs.generate_stable_version.outputs.version }} + prerelease: true + latest: false \ No newline at end of file diff --git a/.github/workflows/reusable-create-github-release.yml b/.github/workflows/reusable-create-github-release.yml deleted file mode 100644 index 073871e..0000000 --- a/.github/workflows/reusable-create-github-release.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: "Reusable - Create GitHub Release" - -on: - workflow_call: - inputs: - tag_name: - description: 'Release tag name' - required: true - type: string - release_name: - description: 'Release name/title' - required: true - type: string - release_type: - description: 'Release type (stable, beta, alpha)' - required: true - type: string - prerelease: - description: 'Mark as prerelease' - required: false - type: boolean - default: false - draft: - description: 'Mark as draft' - required: false - type: boolean - default: false - body_prefix: - description: 'Additional content to prepend to release body' - required: false - type: string - default: '' - -jobs: - create_github_release: - name: Create GitHub Release ${{ inputs.tag_name }} - runs-on: ubuntu-latest - steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - merge-multiple: true - path: artifacts/ - - - name: Read manifest content - id: read_manifest - run: | - echo "MANIFEST_FILE=$(ls artifacts/*.manifest | head -n 1)" >> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ inputs.tag_name }} - name: ${{ inputs.release_name }} - prerelease: ${{ inputs.prerelease }} - draft: ${{ inputs.draft }} - files: | - artifacts/homebridge_v*.deb - artifacts/homebridge_v*.manifest - body_path: ${{ steps.read_manifest.outputs.MANIFEST_FILE }} - body: | - ${{ inputs.body_prefix }} - Homebridge Apt Package Manifest - - Release Version: ${{ inputs.tag_name }} - Release Type: ${{ inputs.release_type }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/reusable-debug-context.yml b/.github/workflows/reusable-debug-context.yml deleted file mode 100644 index 471c528..0000000 --- a/.github/workflows/reusable-debug-context.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: "Reusable - Debug Context" - -on: - workflow_call: - -jobs: - print_context: - name: Print Debug Context - runs-on: ubuntu-latest - steps: - - name: Dump Inputs context - env: - INPUTS_CONTEXT: ${{ toJson(inputs) }} - run: echo "$INPUTS_CONTEXT" - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Dump job context - env: - JOB_CONTEXT: ${{ toJson(job) }} - run: echo "$JOB_CONTEXT" - - name: Dump steps context - env: - STEPS_CONTEXT: ${{ toJson(steps) }} - run: echo "$STEPS_CONTEXT" - - name: Dump runner context - env: - RUNNER_CONTEXT: ${{ toJson(runner) }} - run: echo "$RUNNER_CONTEXT" - - name: Dump strategy context - env: - STRATEGY_CONTEXT: ${{ toJson(strategy) }} - run: echo "$STRATEGY_CONTEXT" - - name: Dump matrix context - env: - MATRIX_CONTEXT: ${{ toJson(matrix) }} - run: echo "$MATRIX_CONTEXT" - - - name: Show default environment variables - run: | - echo "The job_id is: $GITHUB_JOB" - echo "The id of this action is: $GITHUB_ACTION" - echo "The run id is: $GITHUB_RUN_ID" - echo "The GitHub Actor's username is: $GITHUB_ACTOR" - echo "GitHub SHA: $GITHUB_SHA" \ No newline at end of file diff --git a/.github/workflows/reusable-generate-version.yml b/.github/workflows/reusable-generate-version.yml deleted file mode 100644 index 4f42ef8..0000000 --- a/.github/workflows/reusable-generate-version.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: "Reusable - Generate Version" - -on: - workflow_call: - inputs: - release_type: - description: 'Release type (beta, alpha)' - required: true - type: string - increment: - description: 'Version increment type' - required: false - type: string - default: 'patch' - outputs: - version: - description: 'APT package version' - value: ${{ jobs.generate_version.outputs.version }} - npm_version: - description: 'NPM version' - value: ${{ jobs.generate_version.outputs.npm_version }} - -jobs: - generate_version: - name: Generate ${{ inputs.release_type }} Package Version - runs-on: ubuntu-latest - outputs: - version: ${{ steps.version_output.outputs.version }} - npm_version: ${{ steps.version_output.outputs.npm_version }} - steps: - - uses: actions/checkout@v4 - - - name: Get base version - uses: reecetech/version-increment@2023.10.1 - id: release_version - with: - scheme: semver - increment: ${{ inputs.increment }} - - - 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" \ No newline at end of file diff --git a/.github/workflows/stable-stage-2_promote_and_finalize.yml b/.github/workflows/stable-promotion.yml similarity index 76% rename from .github/workflows/stable-stage-2_promote_and_finalize.yml rename to .github/workflows/stable-promotion.yml index 6678f46..b12117d 100644 --- a/.github/workflows/stable-stage-2_promote_and_finalize.yml +++ b/.github/workflows/stable-promotion.yml @@ -1,4 +1,4 @@ -name: "Stable Stage 2 - Promote Release to APT and Finalize" +name: "Stable Release Promotion - Promote to APT and Finalize" on: release: @@ -113,4 +113,38 @@ jobs: DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL_LATEST }} print_context: - uses: ./.github/workflows/reusable-debug-context.yml + name: Print Debug Context + runs-on: ubuntu-latest + steps: + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJson(github) }} + run: echo "$GITHUB_CONTEXT" + - name: Dump job context + env: + JOB_CONTEXT: ${{ toJson(job) }} + run: echo "$JOB_CONTEXT" + - name: Dump steps context + env: + STEPS_CONTEXT: ${{ toJson(steps) }} + run: echo "$STEPS_CONTEXT" + - name: Dump runner context + env: + RUNNER_CONTEXT: ${{ toJson(runner) }} + run: echo "$RUNNER_CONTEXT" + - name: Dump strategy context + env: + STRATEGY_CONTEXT: ${{ toJson(strategy) }} + run: echo "$STRATEGY_CONTEXT" + - name: Dump matrix context + env: + MATRIX_CONTEXT: ${{ toJson(matrix) }} + run: echo "$MATRIX_CONTEXT" + + - name: Show default environment variables + run: | + echo "The job_id is: $GITHUB_JOB" + echo "The id of this action is: $GITHUB_ACTION" + echo "The run id is: $GITHUB_RUN_ID" + echo "The GitHub Actor's username is: $GITHUB_ACTOR" + echo "GitHub SHA: $GITHUB_SHA" \ No newline at end of file diff --git a/.github/workflows/stable-stage-1_build_and_prerelease.yml b/.github/workflows/stable-stage-1_build_and_prerelease.yml deleted file mode 100644 index 3928889..0000000 --- a/.github/workflows/stable-stage-1_build_and_prerelease.yml +++ /dev/null @@ -1,158 +0,0 @@ -name: "Stable Stage 1 - Build Package and Create Pre-Release" - -on: - workflow_dispatch: - inputs: - increment: - description: 'Release Level' - required: true - default: 'patch' - type: choice - options: - - major - - minor - - patch - push: - branches: - - latest - paths: - - stable/32bit/package.json - - stable/64bit/package.json - -jobs: - merge_inputs: - # allows for both the workflow_dispatch and push events to trigger this job - runs-on: ubuntu-latest - outputs: - username: ${{ steps.merge_username.outputs.username }} - increment: ${{ steps.merge_increment.outputs.increment }} - steps: - - name: Merge Username - id: merge_username - run: | - if [ "${{ github.event.commits[0].author.username }}" ]; then - USERNAME=${{ github.event.commits[0].author.username }} - else - USERNAME='dependabot[bot]' - fi - echo "Using USERNAME: $USERNAME" - echo "username=$USERNAME" >> "$GITHUB_OUTPUT" - - - name: Merge Increment - id: merge_increment - run: | - if [ "${{ inputs.increment }}" ]; then - INCREMENT=${{ inputs.increment }} - else - INCREMENT=patch - fi - echo "Using INCREMENT: $INCREMENT" - echo "increment=$INCREMENT" >> "$GITHUB_OUTPUT" - - create_draft_prerelease: - # Only run this job if the username is dependabot[bot] - needs: merge_inputs - if: ${{ needs.merge_inputs.outputs.username == 'dependabot[bot]' }} - permissions: - # write permission is required to create a github release - contents: write - # write permission is required for autolabeler - # otherwise, read permission is required at least - pull-requests: write - runs-on: ubuntu-latest - outputs: - version: ${{ steps.release_version.outputs.version }} # Without a V - v-version: ${{ steps.release_version.outputs.v-version }} # With a V - release_type: 'stable' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Get next version - uses: reecetech/version-increment@2023.10.1 - id: release_version - with: - scheme: semver - increment: ${{ needs.merge_inputs.outputs.increment }} - - message_about_not_running: - # Only run this job if the username is not dependabot[bot] - needs: merge_inputs - if: ${{ needs.merge_inputs.outputs.username != 'dependabot[bot]' }} - permissions: - # write permission is required to create a github release - contents: write - # write permission is required for autolabeler - # otherwise, read permission is required at least - pull-requests: write - runs-on: ubuntu-latest - steps: - - name: Display Message - run: echo "::error::This job is not running automatically because the invoker is not 'dependabot[bot]'." && exit 1 - - build_package_and_store: - needs: [create_draft_prerelease] - uses: ./.github/workflows/reusable-build-package.yml - with: - release_type: ${{ needs.create_draft_prerelease.outputs.release_type }} - release_version: ${{ needs.create_draft_prerelease.outputs.version }} - - publish_prerelease: - # Publish the pre-release to the GitHub Releases page - needs: [build_package_and_store, create_draft_prerelease] - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 - 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: Create Pre-Release - uses: docker://ghcr.io/mini-bomba/create-github-release:v1.2.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - tag: ${{ needs.create_draft_prerelease.outputs.v-version }} - prerelease: true - draft: false - name: "${{ needs.create_draft_prerelease.outputs.v-version }} - ${{ steps.date.outputs.builddate }}" - files: | - *.deb - *.manifest - body: ${{ steps.release_body.outputs.BODY_FILE }} - clear_attachments: true - fail_on_no_files: true - - - name: update release - uses: tubone24/update_release@v1.3.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG_NAME: ${{ needs.create_draft_prerelease.outputs.v-version }} - with: - body_path: ${{ steps.release_body.outputs.BODY_FILE }} - - validate_prerelease: - needs: [publish_prerelease, create_draft_prerelease] - uses: ./.github/workflows/reusable-validate-homebridge.yml - with: - validation_type: "prerelease" - release_tag: ${{ needs.create_draft_prerelease.outputs.v-version }} - prerelease: true - latest: false - - print_context: - uses: ./.github/workflows/reusable-debug-context.yml From 51c16c6f0a4aa9e7dc11beef988daf8ea2afbda1 Mon Sep 17 00:00:00 2001 From: Northern Man <19808920+NorthernMan54@users.noreply.github.com> Date: Thu, 4 Sep 2025 17:54:05 -0400 Subject: [PATCH 12/16] Update homebridge-stable-bot.json --- .github/homebridge-stable-bot.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/homebridge-stable-bot.json b/.github/homebridge-stable-bot.json index fdf3085..3e3b1dc 100644 --- a/.github/homebridge-stable-bot.json +++ b/.github/homebridge-stable-bot.json @@ -14,7 +14,7 @@ }, { "name": "homebridge", - "pattern": "^1\\.[0-9]+\\.[0-9]+" + "tag": "latest" }, { "name": "node", @@ -31,7 +31,7 @@ }, { "name": "homebridge", - "pattern": "^1\\.[0-9]+\\.[0-9]+" + "tag": "latest" }, { "name": "node", From 7bcd1041cb41eefb6d2611636c0463d31aa324d0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 21:58:50 +0000 Subject: [PATCH 13/16] Merge single-use reusable workflows and remove dependabot configuration - Remove dependabot.yml since homebridge-beta-bot now manages stable releases - Merge reusable-update-dependencies.yml into release-stage-1_update_dependencies.yml - Merge both reusable-build-and-release.yml and reusable-build-package.yml into release-stage-2_build_and_release.yml - Remove 3 single-use reusable workflow files - Reduce total workflow count from 15 to 12 files (3 fewer) Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .github/dependabot.yml | 37 --- .../release-stage-1_update_dependencies.yml | 32 +- .../release-stage-2_build_and_release.yml | 312 +++++++++++++++++- .../workflows/reusable-build-and-release.yml | 269 --------------- .github/workflows/reusable-build-package.yml | 111 ------- .../reusable-update-dependencies.yml | 56 ---- 6 files changed, 332 insertions(+), 485 deletions(-) delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/reusable-build-and-release.yml delete mode 100644 .github/workflows/reusable-build-package.yml delete mode 100644 .github/workflows/reusable-update-dependencies.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index efc228d..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,37 +0,0 @@ -version: 2 -updates: - # Stable 32-bit package.json (Node.js ≤22 only) - - package-ecosystem: npm - directory: "/stable/32bit" - schedule: - interval: daily - time: "10:00" - open-pull-requests-limit: 10 - versioning-strategy: increase - ignore: - - dependency-name: "node" - versions: [">22.x"] - labels: - - "Production" - - "32-bit" - commit-message: - prefix: "Production(32-bit)" - include: "scope" - - # Stable 64-bit package.json (Node.js ≤22 only) - - package-ecosystem: npm - directory: "/stable/64bit" - schedule: - interval: daily - time: "10:00" - open-pull-requests-limit: 10 - versioning-strategy: increase - ignore: - - dependency-name: "node" - versions: [">22.x"] - labels: - - "Production" - - "64-bit" - commit-message: - prefix: "Production(64-bit)" - include: "scope" \ No newline at end of file diff --git a/.github/workflows/release-stage-1_update_dependencies.yml b/.github/workflows/release-stage-1_update_dependencies.yml index 3b67b61..6536418 100644 --- a/.github/workflows/release-stage-1_update_dependencies.yml +++ b/.github/workflows/release-stage-1_update_dependencies.yml @@ -46,9 +46,29 @@ jobs: needs: determine-release-types strategy: matrix: ${{ fromJson(needs.determine-release-types.outputs.matrix) }} - uses: ./.github/workflows/reusable-update-dependencies.yml - with: - release_type: ${{ matrix.release_type }} - config_file: ${{ matrix.config_file }} - trigger_workflow: ${{ matrix.trigger_workflow }} - secrets: inherit + runs-on: ubuntu-latest + steps: + - name: Run Homebridge ${{ matrix.release_type }} Bot + id: homebridge-bot + uses: homebridge/.github/.github/workflows/homebridge-beta-bot.yml@latest + with: + config_file: ${{ matrix.config_file }} + release_stream: ${{ matrix.release_type }} + secrets: inherit + + - name: Log Skipped ${{ matrix.release_type }} Stage 2 Trigger + if: steps.homebridge-bot.outputs.changes_detected != 'true' || steps.homebridge-bot.outputs.auto_merge != 'true' + run: | + echo "::warning::${{ matrix.release_type }} Stage 2 not triggered: Changes Detected=${{ steps.homebridge-bot.outputs.changes_detected }}, Auto Merge=${{ steps.homebridge-bot.outputs.auto_merge }}" + + - name: Trigger Build and Release ${{ matrix.release_type }} Package + if: steps.homebridge-bot.outputs.changes_detected == 'true' && steps.homebridge-bot.outputs.auto_merge == 'true' + run: | + echo "::notice::Triggering ${{ matrix.release_type }} Stage 2 - Build and Release ${{ matrix.release_type }} Package" + gh workflow run ${{ matrix.trigger_workflow }} --ref latest || { echo "::error::Failed to trigger ${{ matrix.release_type }} Stage 2 workflow"; exit 1; } + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Checkout repository (for trigger step) + if: steps.homebridge-bot.outputs.changes_detected == 'true' && steps.homebridge-bot.outputs.auto_merge == 'true' + uses: actions/checkout@v4 diff --git a/.github/workflows/release-stage-2_build_and_release.yml b/.github/workflows/release-stage-2_build_and_release.yml index 62e668d..3dc0772 100644 --- a/.github/workflows/release-stage-2_build_and_release.yml +++ b/.github/workflows/release-stage-2_build_and_release.yml @@ -19,6 +19,11 @@ on: - stable default: 'beta' +permissions: + contents: write + actions: write + id-token: write + jobs: determine-release-type: name: Determine Release Type @@ -96,21 +101,316 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - build_and_release: - name: Build and Release ${{ needs.determine-release-type.outputs.release_type }} Package + check-changes: + name: Check Changes needs: determine-release-type if: needs.determine-release-type.outputs.should_run == 'true' - uses: ./.github/workflows/reusable-build-and-release.yml + runs-on: ubuntu-latest + outputs: + changes_only: ${{ steps.verify_changes.outputs.changes_only }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Log event context for debugging + if: ${{ github.event_name == 'pull_request' }} + run: | + echo "Event: ${{ github.event_name }}" + echo "Merged: ${{ github.event.pull_request.merged }}" + + - name: Verify ${{ needs.determine-release-type.outputs.release_type }}-only changes + id: verify_changes + run: | + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + echo "Workflow dispatch detected, setting changes_only=true" + echo "changes_only=true" >> $GITHUB_OUTPUT + else + changes_only=true + changed_files=$(git diff --name-only HEAD^ HEAD) + for file in $changed_files; do + if [[ ! $file == ${{ needs.determine-release-type.outputs.release_type }}/* ]]; then + echo "Non-${{ needs.determine-release-type.outputs.release_type }} changes detected in $file. Setting changes_only=false" + changes_only=false + fi + done + echo "changes_only=$changes_only" >> $GITHUB_OUTPUT + fi + + # For alpha/beta releases, generate version using date-based approach + generate_version: + needs: [determine-release-type, check-changes] + if: ${{ needs.check-changes.outputs.changes_only == 'true' && needs.determine-release-type.outputs.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@v4 + + - name: Get base version + uses: reecetech/version-increment@2023.10.1 + 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}~${{ needs.determine-release-type.outputs.release_type }}.${DATE}" + NPM_VERSION="${BASE_VERSION_CLEAN}-${{ needs.determine-release-type.outputs.release_type }}.${DATE}" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "npm_version=$NPM_VERSION" >> $GITHUB_OUTPUT + echo "::notice::📦 ${{ needs.determine-release-type.outputs.release_type }} version: $VERSION" + + # For stable releases, generate version using semver increment + generate_stable_version: + needs: [determine-release-type, check-changes] + if: ${{ needs.check-changes.outputs.changes_only == 'true' && needs.determine-release-type.outputs.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@v4 + + - name: Get next stable version + uses: reecetech/version-increment@2023.10.1 + id: release_version + with: + scheme: semver + increment: patch + + build_package_and_store: + name: Build Packages for (${{ matrix.name }}) v${{ needs.generate_version.outputs.version || needs.generate_stable_version.outputs.version }} + needs: [determine-release-type, 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@v4 + + - 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 ${{ needs.determine-release-type.outputs.release_type }}-package-build \ + --platform=linux/${{ matrix.QEMU_ARCH }} \ + --cache-from=${{ needs.determine-release-type.outputs.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="${{ needs.determine-release-type.outputs.release_type }}" \ + -e PKG_RELEASE_VERSION="${{ needs.generate_version.outputs.version || needs.generate_stable_version.outputs.version }}" \ + ${{ needs.determine-release-type.outputs.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@v4 + with: + name: artifacts-${{ matrix.name }} + retention-days: 7 + path: | + *.deb + *.manifest + + # For alpha/beta releases, publish directly to APT + publish_apt: + needs: [determine-release-type, generate_version, build_package_and_store] + if: ${{ needs.generate_version.result == 'success' && needs.determine-release-type.outputs.release_type != 'stable' }} + uses: ./.github/workflows/reusable-publish-apt.yml with: - release_type: ${{ needs.determine-release-type.outputs.release_type }} - event_name: ${{ github.event_name }} - pr_merged: ${{ github.event.pull_request.merged }} + codename: ${{ needs.determine-release-type.outputs.release_type }} + suite: ${{ needs.determine-release-type.outputs.release_type }} + release_version: ${{ needs.generate_version.outputs.version }} secrets: GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + + # For stable releases, create prerelease instead of publishing to APT + create_stable_prerelease: + needs: [determine-release-type, generate_stable_version, build_package_and_store] + if: ${{ needs.generate_stable_version.result == 'success' && needs.determine-release-type.outputs.release_type == 'stable' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/download-artifact@v4 + 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: Create Stable Pre-Release + uses: docker://ghcr.io/mini-bomba/create-github-release:v1.2.0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: v${{ needs.generate_stable_version.outputs.version }} + prerelease: true + draft: false + name: "v${{ needs.generate_stable_version.outputs.version }} - ${{ steps.date.outputs.builddate }}" + files: | + *.deb + *.manifest + body: ${{ steps.release_body.outputs.BODY_FILE }} + clear_attachments: true + fail_on_no_files: true + + - name: update release + uses: tubone24/update_release@v1.3.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG_NAME: v${{ needs.generate_stable_version.outputs.version }} + with: + body_path: ${{ steps.release_body.outputs.BODY_FILE }} + + # For alpha/beta releases, create GitHub release + publish_github_release: + needs: [determine-release-type, publish_apt, generate_version] + if: ${{ needs.generate_version.result == 'success' && needs.determine-release-type.outputs.release_type != 'stable' }} + runs-on: ubuntu-latest + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + pattern: artifacts-* + merge-multiple: true + path: artifacts/ + + - name: Read manifest content + id: read_manifest + run: | + echo "MANIFEST_FILE=$(ls artifacts/*.manifest | head -n 1)" >> $GITHUB_OUTPUT + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ needs.generate_version.outputs.npm_version }} + name: "${{ needs.determine-release-type.outputs.release_type == 'alpha' && 'ALPHA' || 'BETA' }}: Homebridge APT Pkg Release v${{ needs.generate_version.outputs.npm_version }}" + prerelease: true + draft: false + files: | + artifacts/homebridge_v*.deb + artifacts/homebridge_v*.manifest + body_path: ${{ steps.read_manifest.outputs.MANIFEST_FILE }} + body: | + Homebridge Apt Package Manifest + + Release Version: v${{ needs.generate_version.outputs.npm_version }} + Release Type: ${{ needs.determine-release-type.outputs.release_type }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # For alpha/beta releases, purge CloudFlare cache + purge_cloudflare_cache: + name: Purge Cloudflare Cache + needs: [determine-release-type, publish_apt] + if: ${{ needs.publish_apt.result == 'success' && needs.determine-release-type.outputs.release_type != 'stable' }} + uses: ./.github/workflows/stage-3_5_purge_cloudflare_cache.yml + secrets: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} + + # For alpha/beta releases, publish to NPM + publish_to_npm: + needs: [determine-release-type, generate_version, build_package_and_store] + if: ${{ needs.generate_version.result == 'success' && needs.determine-release-type.outputs.release_type != 'stable' }} + uses: ./.github/workflows/reusable-publish-npm.yml + with: + npm_version: ${{ needs.generate_version.outputs.npm_version }} + npm_tag: ${{ needs.determine-release-type.outputs.release_type }} + secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + + # For stable releases, validate prerelease + validate_stable_prerelease: + needs: [determine-release-type, create_stable_prerelease, generate_stable_version] + if: ${{ needs.create_stable_prerelease.result == 'success' && needs.determine-release-type.outputs.release_type == 'stable' }} + uses: ./.github/workflows/reusable-validate-homebridge.yml + with: + validation_type: "prerelease" + release_tag: v${{ needs.generate_stable_version.outputs.version }} + prerelease: true + latest: false diff --git a/.github/workflows/reusable-build-and-release.yml b/.github/workflows/reusable-build-and-release.yml deleted file mode 100644 index 128595f..0000000 --- a/.github/workflows/reusable-build-and-release.yml +++ /dev/null @@ -1,269 +0,0 @@ -name: Reusable - Build and Release Package (All Release Types) - -on: - workflow_call: - inputs: - release_type: - description: 'Release type (alpha, beta, or stable)' - required: true - type: string - event_name: - description: 'Event name for conditional execution' - required: false - type: string - default: 'workflow_call' - pr_merged: - description: 'Whether PR was merged (for pull_request events)' - required: false - type: boolean - default: true - secrets: - GPG_PRIVATE_KEY: - required: true - GPG_PASSPHRASE: - required: true - GPG_KEY_ID: - required: true - AWS_ACCESS_KEY_ID: - required: true - AWS_SECRET_ACCESS_KEY: - required: true - CLOUDFLARE_API_TOKEN: - required: true - CLOUDFLARE_ZONE_ID: - required: true - NPM_TOKEN: - required: true - -permissions: - contents: write - actions: write - id-token: write - -jobs: - check-changes: - if: ${{ inputs.pr_merged == true || inputs.event_name == 'workflow_dispatch' }} - runs-on: ubuntu-latest - outputs: - changes_only: ${{ steps.verify_changes.outputs.changes_only }} - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 2 - - - name: Log event context for debugging - if: ${{ inputs.event_name == 'pull_request' }} - run: | - echo "Event: ${{ inputs.event_name }}" - echo "Merged: ${{ inputs.pr_merged }}" - - - name: Verify ${{ inputs.release_type }}-only changes - id: verify_changes - run: | - if [ "${{ inputs.event_name }}" == "workflow_dispatch" ]; then - echo "Workflow dispatch detected, setting changes_only=true" - echo "changes_only=true" >> $GITHUB_OUTPUT - else - changes_only=true - changed_files=$(git diff --name-only HEAD^ HEAD) - for file in $changed_files; do - if [[ ! $file == ${{ inputs.release_type }}/* ]]; then - echo "Non-${{ inputs.release_type }} changes detected in $file. Setting changes_only=false" - changes_only=false - fi - done - echo "changes_only=$changes_only" >> $GITHUB_OUTPUT - fi - - # For alpha/beta releases, generate version using date-based approach - generate_version: - needs: check-changes - if: ${{ needs.check-changes.outputs.changes_only == 'true' && 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@v4 - - - name: Get base version - uses: reecetech/version-increment@2023.10.1 - 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: - needs: check-changes - if: ${{ needs.check-changes.outputs.changes_only == 'true' && 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@v4 - - - name: Get next stable version - uses: reecetech/version-increment@2023.10.1 - id: release_version - with: - scheme: semver - increment: patch - - build_and_store: - needs: [generate_version, generate_stable_version] - if: always() && (needs.generate_version.result == 'success' || needs.generate_stable_version.result == 'success') - uses: ./.github/workflows/reusable-build-package.yml - with: - release_type: ${{ inputs.release_type }} - release_version: ${{ inputs.release_type == 'stable' && needs.generate_stable_version.outputs.version || needs.generate_version.outputs.version }} - docker_tag_prefix: ${{ inputs.release_type }}-package-build - - # For alpha/beta releases, publish directly to APT - publish_apt: - needs: [generate_version, build_and_store] - if: ${{ needs.generate_version.result == 'success' && inputs.release_type != 'stable' }} - uses: ./.github/workflows/reusable-publish-apt.yml - with: - codename: ${{ inputs.release_type }} - suite: ${{ inputs.release_type }} - release_version: ${{ needs.generate_version.outputs.version }} - secrets: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - # For stable releases, create prerelease instead of publishing to APT - create_stable_prerelease: - needs: [generate_stable_version, build_and_store] - if: ${{ needs.generate_stable_version.result == 'success' && inputs.release_type == 'stable' }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 - 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: Create Stable Pre-Release - uses: docker://ghcr.io/mini-bomba/create-github-release:v1.2.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - tag: v${{ needs.generate_stable_version.outputs.version }} - prerelease: true - draft: false - name: "v${{ needs.generate_stable_version.outputs.version }} - ${{ steps.date.outputs.builddate }}" - files: | - *.deb - *.manifest - body: ${{ steps.release_body.outputs.BODY_FILE }} - clear_attachments: true - fail_on_no_files: true - - - name: update release - uses: tubone24/update_release@v1.3.1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG_NAME: v${{ needs.generate_stable_version.outputs.version }} - with: - body_path: ${{ steps.release_body.outputs.BODY_FILE }} - - # For alpha/beta releases, create GitHub release - publish_github_release: - needs: [check-changes, publish_apt, generate_version] - if: ${{ needs.generate_version.result == 'success' && inputs.release_type != 'stable' }} - runs-on: ubuntu-latest - steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - merge-multiple: true - path: artifacts/ - - - name: Read manifest content - id: read_manifest - run: | - echo "MANIFEST_FILE=$(ls artifacts/*.manifest | head -n 1)" >> $GITHUB_OUTPUT - - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ needs.generate_version.outputs.npm_version }} - name: "${{ inputs.release_type == 'alpha' && 'ALPHA' || 'BETA' }}: Homebridge APT Pkg Release v${{ needs.generate_version.outputs.npm_version }}" - prerelease: true - draft: false - files: | - artifacts/homebridge_v*.deb - artifacts/homebridge_v*.manifest - body_path: ${{ steps.read_manifest.outputs.MANIFEST_FILE }} - body: | - Homebridge Apt Package Manifest - - Release Version: v${{ needs.generate_version.outputs.npm_version }} - Release Type: ${{ inputs.release_type }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - # For alpha/beta releases, purge CloudFlare cache - purge_cloudflare_cache: - name: Purge Cloudflare Cache - needs: [publish_apt] - if: ${{ needs.publish_apt.result == 'success' && inputs.release_type != 'stable' }} - uses: ./.github/workflows/stage-3_5_purge_cloudflare_cache.yml - secrets: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} - - # For alpha/beta releases, publish to NPM - publish_to_npm: - needs: [generate_version, build_and_store] - if: ${{ needs.generate_version.result == 'success' && inputs.release_type != 'stable' }} - uses: ./.github/workflows/reusable-publish-npm.yml - with: - npm_version: ${{ needs.generate_version.outputs.npm_version }} - npm_tag: ${{ inputs.release_type }} - secrets: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - # For stable releases, validate prerelease - validate_stable_prerelease: - needs: [create_stable_prerelease, generate_stable_version] - if: ${{ needs.create_stable_prerelease.result == 'success' && inputs.release_type == 'stable' }} - uses: ./.github/workflows/reusable-validate-homebridge.yml - with: - validation_type: "prerelease" - release_tag: v${{ needs.generate_stable_version.outputs.version }} - prerelease: true - latest: false \ No newline at end of file diff --git a/.github/workflows/reusable-build-package.yml b/.github/workflows/reusable-build-package.yml deleted file mode 100644 index 06806bb..0000000 --- a/.github/workflows/reusable-build-package.yml +++ /dev/null @@ -1,111 +0,0 @@ -name: "Reusable - Build APT Package" - -on: - workflow_call: - inputs: - release_type: - description: 'Release type (stable, beta, alpha)' - required: true - type: string - release_version: - description: 'Release version string' - required: true - type: string - docker_tag_prefix: - description: 'Docker tag prefix for build image' - required: false - type: string - default: 'package-build' - -jobs: - build_package_and_store: - name: Build Packages for (${{ matrix.name }}) v${{ inputs.release_version }} - 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@v4 - - - 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.docker_tag_prefix }}-${{ inputs.release_type }} \ - --platform=linux/${{ matrix.QEMU_ARCH }} \ - --cache-from=${{ inputs.docker_tag_prefix }}-${{ inputs.release_type }}: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="${{ inputs.release_version }}" \ - ${{ inputs.docker_tag_prefix }}-${{ inputs.release_type }} - 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@v4 - with: - name: artifacts-${{ matrix.name }} - retention-days: 7 - path: | - *.deb - *.manifest \ No newline at end of file diff --git a/.github/workflows/reusable-update-dependencies.yml b/.github/workflows/reusable-update-dependencies.yml deleted file mode 100644 index fe2ef70..0000000 --- a/.github/workflows/reusable-update-dependencies.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Reusable - Update Dependencies and Trigger Build - -on: - workflow_call: - inputs: - release_type: - description: 'Release type (alpha or beta)' - required: true - type: string - config_file: - description: 'Configuration file for the homebridge bot' - required: true - type: string - trigger_workflow: - description: 'Workflow file to trigger for Stage 2' - required: true - type: string - cron_schedule: - description: 'Cron schedule for display purposes' - required: false - type: string - default: 'Manual' - -jobs: - homebridge-bot: - name: Run Homebridge ${{ inputs.release_type }} Bot - uses: homebridge/.github/.github/workflows/homebridge-beta-bot.yml@latest - with: - config_file: ${{ inputs.config_file }} - release_stream: ${{ inputs.release_type }} - secrets: inherit - - log-skipped-trigger: - name: Log Skipped ${{ inputs.release_type }} Stage 2 Trigger - needs: homebridge-bot - if: needs.homebridge-bot.outputs.changes_detected != 'true' || needs.homebridge-bot.outputs.auto_merge != 'true' - runs-on: ubuntu-latest - steps: - - name: Log Skipped Trigger - run: | - echo "::warning::${{ inputs.release_type }} Stage 2 not triggered: Changes Detected=${{ needs.homebridge-bot.outputs.changes_detected }}, Auto Merge=${{ needs.homebridge-bot.outputs.auto_merge }}" - - trigger-stage-2: - name: Trigger Build and Release ${{ inputs.release_type }} Package - needs: homebridge-bot - if: needs.homebridge-bot.outputs.changes_detected == 'true' && needs.homebridge-bot.outputs.auto_merge == 'true' - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Trigger ${{ inputs.release_type }} Stage 2 Workflow - run: | - echo "::notice::Triggering ${{ inputs.release_type }} Stage 2 - Build and Release ${{ inputs.release_type }} Package" - gh workflow run ${{ inputs.trigger_workflow }} --ref latest || { echo "::error::Failed to trigger ${{ inputs.release_type }} Stage 2 workflow"; exit 1; } - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 48cfb4653538609da5bf856558adc342ecf3eb20 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Sep 2025 22:12:46 +0000 Subject: [PATCH 14/16] Implement unified release pipeline with consistent prerelease validation workflow Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .../release-stage-2_build_and_release.yml | 196 +++++++++++------- .github/workflows/stable-promotion.yml | 150 -------------- 2 files changed, 120 insertions(+), 226 deletions(-) delete mode 100644 .github/workflows/stable-promotion.yml diff --git a/.github/workflows/release-stage-2_build_and_release.yml b/.github/workflows/release-stage-2_build_and_release.yml index 3dc0772..2bef557 100644 --- a/.github/workflows/release-stage-2_build_and_release.yml +++ b/.github/workflows/release-stage-2_build_and_release.yml @@ -1,4 +1,4 @@ -name: Release Stage 2 - Build and Release Alpha, Beta, and Stable Packages +name: Unified Release Pipeline - Build, Prerelease, Validate, and Publish on: pull_request: @@ -187,7 +187,7 @@ jobs: scheme: semver increment: patch - build_package_and_store: + build_packages: name: Build Packages for (${{ matrix.name }}) v${{ needs.generate_version.outputs.version || needs.generate_stable_version.outputs.version }} needs: [determine-release-type, generate_version, generate_stable_version] if: always() && (needs.generate_version.result == 'success' || needs.generate_stable_version.result == 'success') @@ -281,27 +281,14 @@ jobs: *.deb *.manifest - # For alpha/beta releases, publish directly to APT - publish_apt: - needs: [determine-release-type, generate_version, build_package_and_store] - if: ${{ needs.generate_version.result == 'success' && needs.determine-release-type.outputs.release_type != 'stable' }} - uses: ./.github/workflows/reusable-publish-apt.yml - with: - codename: ${{ needs.determine-release-type.outputs.release_type }} - suite: ${{ needs.determine-release-type.outputs.release_type }} - release_version: ${{ needs.generate_version.outputs.version }} - secrets: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - # For stable releases, create prerelease instead of publishing to APT - create_stable_prerelease: - needs: [determine-release-type, generate_stable_version, build_package_and_store] - if: ${{ needs.generate_stable_version.result == 'success' && needs.determine-release-type.outputs.release_type == 'stable' }} + # 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: [determine-release-type, 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.create_release.outputs.release_tag }} steps: - uses: actions/checkout@v4 @@ -323,14 +310,35 @@ jobs: run: | echo "BODY_FILE=$(ls *.manifest | head -n 1)" >> $GITHUB_OUTPUT - - name: Create Stable Pre-Release + - name: Determine release details + id: release_details + run: | + RELEASE_TYPE="${{ needs.determine-release-type.outputs.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: v${{ needs.generate_stable_version.outputs.version }} + tag: ${{ steps.release_details.outputs.tag_name }} prerelease: true draft: false - name: "v${{ needs.generate_stable_version.outputs.version }} - ${{ steps.date.outputs.builddate }}" + name: ${{ steps.release_details.outputs.release_title }} files: | *.deb *.manifest @@ -338,79 +346,115 @@ jobs: clear_attachments: true fail_on_no_files: true - - name: update release + - name: Set release tag output + run: | + echo "release_tag=${{ steps.release_details.outputs.tag_name }}" >> $GITHUB_OUTPUT + + - name: Update release body uses: tubone24/update_release@v1.3.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG_NAME: v${{ needs.generate_stable_version.outputs.version }} + TAG_NAME: ${{ steps.release_details.outputs.tag_name }} with: body_path: ${{ steps.release_body.outputs.BODY_FILE }} - # For alpha/beta releases, create GitHub release - publish_github_release: - needs: [determine-release-type, publish_apt, generate_version] - if: ${{ needs.generate_version.result == 'success' && needs.determine-release-type.outputs.release_type != 'stable' }} + # Step 2: Validate prerelease artifacts + validate_prerelease: + name: Validate GitHub Prerelease Artifacts + needs: [create_prerelease] + 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, determine-release-type] + if: needs.validate_prerelease.result == 'success' runs-on: ubuntu-latest steps: - - name: Download all artifacts - uses: actions/download-artifact@v4 + - name: Promote prerelease to full release + uses: actions/github-script@v7 with: - pattern: artifacts-* - merge-multiple: true - path: artifacts/ - - - name: Read manifest content - id: read_manifest - run: | - echo "MANIFEST_FILE=$(ls artifacts/*.manifest | head -n 1)" >> $GITHUB_OUTPUT + 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, determine-release-type, generate_version, generate_stable_version] + if: needs.promote_to_release.result == 'success' + uses: ./.github/workflows/reusable-publish-apt.yml + with: + codename: ${{ needs.determine-release-type.outputs.release_type == 'stable' && 'stable' || needs.determine-release-type.outputs.release_type }} + suite: ${{ needs.determine-release-type.outputs.release_type == 'stable' && 'stable' || needs.determine-release-type.outputs.release_type }} + release_version: ${{ needs.create_prerelease.outputs.release_tag }} + download_from_release: true + release_tag: ${{ needs.create_prerelease.outputs.release_tag }} + secrets: + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - name: Create GitHub Release - uses: softprops/action-gh-release@v2 - with: - tag_name: v${{ needs.generate_version.outputs.npm_version }} - name: "${{ needs.determine-release-type.outputs.release_type == 'alpha' && 'ALPHA' || 'BETA' }}: Homebridge APT Pkg Release v${{ needs.generate_version.outputs.npm_version }}" - prerelease: true - draft: false - files: | - artifacts/homebridge_v*.deb - artifacts/homebridge_v*.manifest - body_path: ${{ steps.read_manifest.outputs.MANIFEST_FILE }} - body: | - Homebridge Apt Package Manifest - - Release Version: v${{ needs.generate_version.outputs.npm_version }} - Release Type: ${{ needs.determine-release-type.outputs.release_type }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Step 5: Validate APT installation after publishing + validate_apt: + name: Validate APT Installation + needs: [publish_apt, determine-release-type] + if: needs.publish_apt.result == 'success' + uses: ./.github/workflows/reusable-validate-homebridge.yml + with: + validation_type: "apt" + release_channel: ${{ needs.determine-release-type.outputs.release_type == 'stable' && 'stable' || needs.determine-release-type.outputs.release_type }} - # For alpha/beta releases, purge CloudFlare cache + # Step 6: Purge CloudFlare cache purge_cloudflare_cache: name: Purge Cloudflare Cache - needs: [determine-release-type, publish_apt] - if: ${{ needs.publish_apt.result == 'success' && needs.determine-release-type.outputs.release_type != 'stable' }} + needs: [publish_apt] + if: needs.publish_apt.result == 'success' uses: ./.github/workflows/stage-3_5_purge_cloudflare_cache.yml secrets: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} - # For alpha/beta releases, publish to NPM + # Step 7: Publish to NPM publish_to_npm: - needs: [determine-release-type, generate_version, build_package_and_store] - if: ${{ needs.generate_version.result == 'success' && needs.determine-release-type.outputs.release_type != 'stable' }} + name: Publish to NPM + needs: [validate_apt, create_prerelease, determine-release-type, generate_version, generate_stable_version] + if: needs.validate_apt.result == 'success' uses: ./.github/workflows/reusable-publish-npm.yml with: - npm_version: ${{ needs.generate_version.outputs.npm_version }} - npm_tag: ${{ needs.determine-release-type.outputs.release_type }} + npm_version: ${{ needs.generate_version.outputs.npm_version || needs.generate_stable_version.outputs.npm_version }} + npm_tag: ${{ needs.determine-release-type.outputs.release_type == 'stable' && 'latest' || needs.determine-release-type.outputs.release_type }} secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - # For stable releases, validate prerelease - validate_stable_prerelease: - needs: [determine-release-type, create_stable_prerelease, generate_stable_version] - if: ${{ needs.create_stable_prerelease.result == 'success' && needs.determine-release-type.outputs.release_type == 'stable' }} - uses: ./.github/workflows/reusable-validate-homebridge.yml + # Step 8: Notify Discord (for stable releases only) + notify_discord: + name: Notify Discord + needs: [publish_to_npm, create_prerelease, determine-release-type] + if: needs.publish_to_npm.result == 'success' && needs.determine-release-type.outputs.release_type == 'stable' + uses: homebridge/.github/.github/workflows/discord-webhooks.yml@latest with: - validation_type: "prerelease" - release_tag: v${{ needs.generate_stable_version.outputs.version }} - prerelease: true - latest: false + 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 }} diff --git a/.github/workflows/stable-promotion.yml b/.github/workflows/stable-promotion.yml deleted file mode 100644 index b12117d..0000000 --- a/.github/workflows/stable-promotion.yml +++ /dev/null @@ -1,150 +0,0 @@ -name: "Stable Release Promotion - Promote to APT and Finalize" - -on: - release: - types: [published, released] - workflow_dispatch: - inputs: - release_tag: - description: "The tag of the release to promote to APT stores." - required: true - type: string - -jobs: - determine_release_tag: - name: Determine Release Tag - runs-on: ubuntu-latest - outputs: - release_tag: ${{ steps.set_release_tag.outputs.release_tag }} - steps: - - name: Set Release Tag - id: set_release_tag - run: | - if [ "${{ github.event_name }}" == "release" ]; then - echo "release_tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT" - else - echo "release_tag=${{ inputs.release_tag }}" >> "$GITHUB_OUTPUT" - fi - - release_type: - name: Determine Release Type - runs-on: ubuntu-latest - needs: determine_release_tag - outputs: - release_type: ${{ steps.release_type.outputs.type }} - steps: - - name: Determine Release Type - id: release_type - run: | - if [ "${{ github.event.release.prerelease }}" == "true" ]; then - echo "type=test" >> "$GITHUB_OUTPUT" - else - echo "type=stable" >> "$GITHUB_OUTPUT" - fi - - update_apt_repo: - needs: [determine_release_tag, release_type] - uses: ./.github/workflows/reusable-publish-apt.yml - with: - codename: ${{ needs.release_type.outputs.release_type }} - suite: stable - release_version: ${{ needs.determine_release_tag.outputs.release_tag }} - download_from_release: true - release_tag: ${{ needs.determine_release_tag.outputs.release_tag }} - secrets: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - purge_cloudflare_cache: - name: Purge Cloudflare Cache - needs: update_apt_repo - uses: ./.github/workflows/stage-3_5_purge_cloudflare_cache.yml - secrets: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} - - validate_apt_installation: - needs: [purge_cloudflare_cache] - uses: ./.github/workflows/reusable-validate-homebridge.yml - with: - validation_type: "apt" - release_channel: "stable" - - extract_npm_version: - name: Extract NPM Version - needs: [determine_release_tag] - runs-on: ubuntu-latest - outputs: - npm_version: ${{ steps.extract_version.outputs.npm_version }} - steps: - - name: Extract and validate version from tag - id: extract_version - run: | - RAW_TAG="${{ needs.determine_release_tag.outputs.release_tag }}" - if [[ "$RAW_TAG" =~ ^v([0-9]+\.[0-9]+\.[0-9]+.*)$ ]]; then - CLEAN_VERSION="${BASH_REMATCH[1]}" - echo "npm_version=$CLEAN_VERSION" >> "$GITHUB_OUTPUT" - else - echo "::error::Invalid tag format: '$RAW_TAG'. Expected format 'v1.2.3'" - exit 1 - fi - - publish_to_npm: - needs: [extract_npm_version, update_apt_repo] - uses: ./.github/workflows/reusable-publish-npm.yml - with: - npm_version: ${{ needs.extract_npm_version.outputs.npm_version }} - npm_tag: 'latest' - secrets: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - notify_discord: - name: Notify Discord - needs: [update_apt_repo, purge_cloudflare_cache, determine_release_tag] - uses: homebridge/.github/.github/workflows/discord-webhooks.yml@latest - with: - title: "Homebridge APT Package Released" - description: "Version `${{ needs.determine_release_tag.outputs.release_tag }}` has been released." - url: "https://github.com/homebridge/homebridge-apt-pkg/releases/tag/${{ needs.determine_release_tag.outputs.release_tag }}" - secrets: - DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_URL_LATEST }} - - print_context: - name: Print Debug Context - runs-on: ubuntu-latest - steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJson(github) }} - run: echo "$GITHUB_CONTEXT" - - name: Dump job context - env: - JOB_CONTEXT: ${{ toJson(job) }} - run: echo "$JOB_CONTEXT" - - name: Dump steps context - env: - STEPS_CONTEXT: ${{ toJson(steps) }} - run: echo "$STEPS_CONTEXT" - - name: Dump runner context - env: - RUNNER_CONTEXT: ${{ toJson(runner) }} - run: echo "$RUNNER_CONTEXT" - - name: Dump strategy context - env: - STRATEGY_CONTEXT: ${{ toJson(strategy) }} - run: echo "$STRATEGY_CONTEXT" - - name: Dump matrix context - env: - MATRIX_CONTEXT: ${{ toJson(matrix) }} - run: echo "$MATRIX_CONTEXT" - - - name: Show default environment variables - run: | - echo "The job_id is: $GITHUB_JOB" - echo "The id of this action is: $GITHUB_ACTION" - echo "The run id is: $GITHUB_RUN_ID" - echo "The GitHub Actor's username is: $GITHUB_ACTOR" - echo "GitHub SHA: $GITHUB_SHA" \ No newline at end of file From de1db31ec4c4ade3e2561b8625c931a93128077d Mon Sep 17 00:00:00 2001 From: Northern Man <19808920+NorthernMan54@users.noreply.github.com> Date: Fri, 5 Sep 2025 10:30:15 -0400 Subject: [PATCH 15/16] Update release-stage-1_update_dependencies.yml --- .github/workflows/release-stage-1_update_dependencies.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-stage-1_update_dependencies.yml b/.github/workflows/release-stage-1_update_dependencies.yml index 6536418..b8e78e6 100644 --- a/.github/workflows/release-stage-1_update_dependencies.yml +++ b/.github/workflows/release-stage-1_update_dependencies.yml @@ -50,7 +50,7 @@ jobs: steps: - name: Run Homebridge ${{ matrix.release_type }} Bot id: homebridge-bot - uses: homebridge/.github/.github/workflows/homebridge-beta-bot.yml@latest + uses: homebridge/.github/sharedWorkflows/homebridge-dependency-bot.yml@latest with: config_file: ${{ matrix.config_file }} release_stream: ${{ matrix.release_type }} From de82f27a564d698a241dce96870f5e039b0e9813 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 5 Sep 2025 14:37:59 +0000 Subject: [PATCH 16/16] Consolidate reusable-publish workflows into main release pipeline Co-authored-by: NorthernMan54 <19808920+NorthernMan54@users.noreply.github.com> --- .github/workflows/README.md | 207 +++++------------- .../release-stage-2_build_and_release.yml | 82 +++++-- .github/workflows/reusable-publish-apt.yml | 89 -------- .github/workflows/reusable-publish-npm.yml | 51 ----- 4 files changed, 119 insertions(+), 310 deletions(-) delete mode 100644 .github/workflows/reusable-publish-apt.yml delete mode 100644 .github/workflows/reusable-publish-npm.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 8e68d42..d515032 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -2,163 +2,68 @@ This directory contains the GitHub Actions workflows for the Homebridge APT package repository. As of the consolidation effort (Issue #190), the workflows have been reorganized to reduce duplication and improve maintainability. -## Reusable Workflows +## Workflow Directory Structure -The following reusable workflows contain common functionality that is shared across multiple release streams: +This directory contains GitHub Actions workflows organized by functionality: -### `reusable-build-package.yml` -Builds APT packages for all supported architectures using Docker and QEMU emulation. +### Core Release Workflows (2 files) +- `release-stage-1_update_dependencies.yml` - Bot-managed dependency updates for all release types (stable, beta, alpha) +- `release-stage-2_build_and_release.yml` - Unified build, validation and publishing pipeline for all releases -**Inputs:** -- `release_type`: Release type (stable, beta, alpha) -- `release_version`: Release version string -- `docker_tag_prefix`: Docker tag prefix for build image - -**Outputs:** Artifacts containing .deb and .manifest files for each architecture - -### `reusable-publish-apt.yml` -Publishes packages to the APT repository hosted on S3. - -**Inputs:** -- `codename`: APT repository codename (stable, beta, alpha, test) -- `suite`: APT repository suite -- `release_version`: Release version for display -- `download_from_release`: Whether to download from GitHub release vs artifacts -- `release_tag`: Release tag to download from (when downloading from release) - -**Secrets:** GPG keys and AWS credentials - -### `reusable-create-github-release.yml` -Creates GitHub releases with packages and manifests. - -**Inputs:** -- `tag_name`: Release tag name -- `release_name`: Release name/title -- `release_type`: Release type (stable, beta, alpha) -- `prerelease`: Mark as prerelease (boolean) -- `draft`: Mark as draft (boolean) -- `body_prefix`: Additional content for release body - -### `reusable-publish-npm.yml` -Publishes the package to NPM with specified version and tag. - -**Inputs:** -- `npm_version`: NPM version string -- `npm_tag`: NPM tag (latest, beta, alpha) -- `package_name`: NPM package name - -**Secrets:** NPM token - -### `reusable-debug-context.yml` -Provides comprehensive debug output for GitHub Actions contexts. - -**Usage:** Call this workflow from any workflow that needs debug context information. - -### `reusable-validate-homebridge.yml` -Validates Homebridge package installations from different sources. - -**Inputs:** -- `validation_type`: Type of validation ("prerelease" for GitHub releases, "apt" for APT installations) -- `release_tag`: GitHub release tag (for prerelease validation) -- `release_channel`: APT release channel (for APT validation) -- `prerelease`: Whether to download from prerelease -- `latest`: Whether to download latest release - -**Features:** -- Multi-architecture validation for APT installations -- Package download and installation verification -- Service status validation -- File count verification for prerelease packages - -### `reusable-generate-version.yml` -Generates version strings for beta and alpha releases using date stamps. - -**Inputs:** -- `release_type`: Release type (beta, alpha) -- `increment`: Version increment type - -**Outputs:** -- `version`: APT package version -- `npm_version`: NPM version - -### `reusable-update-dependencies.yml` -Handles dependency updates and triggering of Stage 2 workflows for prerelease streams. - -**Inputs:** -- `release_type`: Release type (alpha, beta) -- `config_file`: Configuration file for homebridge bot -- `trigger_workflow`: Workflow file to trigger for Stage 2 -- `cron_schedule`: Cron schedule for display purposes - -### `reusable-build-and-release-prerelease.yml` -Consolidated build and release logic for alpha and beta packages, including all steps from version generation through NPM publishing. - -**Inputs:** -- `release_type`: Release type (alpha, beta) -- `event_name`: Event name for conditional execution -- `pr_merged`: Whether PR was merged (for pull_request events) - -**Secrets:** All required secrets for GPG, AWS, CloudFlare, and NPM - -## Release Stream Workflows - -### Stable Release (4 stages) -1. **`stage-1_create_a_release_and_store.yml`** - Creates pre-release and builds packages - - Uses: `reusable-build-package.yml` - - Triggered by: Dependabot updates to package.json files - -2. **`stage-2_pre-release_validation.yml`** - Validates pre-release packages - - Downloads and tests AMD64 package installation - -3. **`stage-3_promote_release_to_apt.yml`** - Promotes to APT repository - - Uses: `reusable-publish-apt.yml` - - Triggered by: Release publication - -4. **`Stage-4_post_release_validation.yml`** - Post-release validation - - Tests APT installation from repository - -### Prerelease (Alpha and Beta) Workflows (2 stages) -1. **`prerelease-stage-1_update_dependencies.yml`** - Updates dependencies for both alpha and beta - - Uses: `reusable-update-dependencies.yml` - - Supports both scheduled runs (separate cron schedules for alpha/beta) and manual dispatch - - Managed by homebridge-alpha-bot and homebridge-beta-bot - - Consolidates both alpha and beta dependency updates into a single workflow - -2. **`prerelease-stage-2_build_and_release.yml`** - Builds and releases alpha and beta packages - - Uses: `reusable-build-and-release-prerelease.yml` - - Automatically detects release type based on changed directories in PRs - - Supports manual dispatch with release type selection - - Consolidates both alpha and beta build/release logic into a single workflow - -## Utility Workflows - -- **`stage-3_5_purge_cloudflare_cache.yml`** - Reusable CloudFlare cache purging -- **`stage-5_update_version_on_npm.yml`** - NPM version updates for stable releases -- **`purge.yml`** - Manual cache purging -- **`stale.yml`** - Stale issue management -- **`pr-labeler.yml`** - PR labeling +### Reusable Workflows (1 file) +- `reusable-validate-homebridge.yml` - Package installation validation (used 2+ times) -## Benefits of Consolidation +### Utility Workflows (7 files) +- `beta-backup_and_clean.yml` - Beta repository cleanup +- `pr-labeler.yml` - PR labeling automation +- `purge.yml` - CloudFlare cache purging +- `release_trigger_logger.yml` - Release event logging +- `stage-3_5_purge_cloudflare_cache.yml` - Post-release cache purging +- `stale.yml` - Stale issue management +- `README.md` - This documentation + +## Release Pipeline Architecture + +### Unified Process (All Release Types) +All release streams (stable, beta, alpha) follow the same 8-step pipeline: + +1. **Bot Updates Dependencies** - `homebridge-beta-bot` updates package.json files daily +2. **Build Packages** - Cross-platform package builds for all architectures +3. **Create GitHub Prerelease** - Upload artifacts and create prerelease +4. **Validate Prerelease** - Download and test .deb packages from GitHub +5. **Promote to Release** - Convert prerelease to full release after validation +6. **Publish to APT** - Upload packages to repository +7. **Validate APT Installation** - Test installation from repository +8. **Publish to NPM** - Publish package with appropriate tags -1. **Reduced Duplication**: ~600 lines of duplicate YAML eliminated across all workflow consolidations -2. **Consistency**: All builds use identical Docker setup and architecture matrix -3. **Maintainability**: Common changes only need to be made in reusable workflows -4. **Flexibility**: Reusable workflows are parameterized for different use cases -5. **Reliability**: Shared logic reduces the chance of inconsistencies between release streams -6. **Complete Prerelease Consolidation**: Alpha and beta workflows combined into 2 shared workflows instead of 4 separate ones -7. **Intelligent Release Detection**: Automatic detection of release type based on changed directories -8. **Simplified Management**: Single workflow files handle both alpha and beta with appropriate scheduling +### Configuration Files +- `.github/homebridge-stable-bot.json` - Bot configuration for stable releases +- `.github/homebridge-beta-bot.json` - Bot configuration for beta/alpha releases -## Making Changes +### Workflow Triggers +- **Scheduled**: Daily at 8 AM UTC for dependency updates +- **Push**: Triggered by bot commits to package.json files +- **Manual**: workflow_dispatch for testing and manual releases -When modifying build or publishing logic: +## Development and Maintenance -1. **Package Building**: Edit `reusable-build-package.yml` -2. **APT Publishing**: Edit `reusable-publish-apt.yml` -3. **GitHub Releases**: Edit `reusable-create-github-release.yml` -4. **NPM Publishing**: Edit `reusable-publish-npm.yml` -5. **Version Generation**: Edit `reusable-generate-version.yml` -6. **Prerelease Dependency Updates**: Edit `reusable-update-dependencies.yml` -7. **Prerelease Build and Release**: Edit `reusable-build-and-release-prerelease.yml` +### Modifying Release Logic +Since all release types use unified workflows, changes only need to be made in 2 places: +1. **Dependency Updates**: Edit `release-stage-1_update_dependencies.yml` +2. **Build/Release Pipeline**: Edit `release-stage-2_build_and_release.yml` + +### Adding Validation Steps +Edit the validation logic in `reusable-validate-homebridge.yml` which is used by both prerelease and APT validation steps. + +### Managing Bot Configuration +- **Stable releases**: Update `.github/homebridge-stable-bot.json` +- **Beta/Alpha releases**: Update `.github/homebridge-beta-bot.json` + +## Benefits of Consolidation -The changes will automatically apply to all release streams that use these workflows. \ No newline at end of file +1. **Maximum Workflow Reduction**: 25 → 10 workflows (60% reduction) +2. **Unified Release Management**: All release streams use identical infrastructure +3. **Enhanced Quality Assurance**: Double validation (GitHub + APT) for every release +4. **Simplified Operations**: Single workflow to maintain instead of separate logic +5. **Automated Process**: Eliminates manual promotion steps with automatic validation gates +6. **Future-Proof Architecture**: Easy to add validation steps or extend to new release streams \ No newline at end of file diff --git a/.github/workflows/release-stage-2_build_and_release.yml b/.github/workflows/release-stage-2_build_and_release.yml index 2bef557..bd8ee51 100644 --- a/.github/workflows/release-stage-2_build_and_release.yml +++ b/.github/workflows/release-stage-2_build_and_release.yml @@ -400,19 +400,44 @@ jobs: name: Publish to APT Repository needs: [promote_to_release, create_prerelease, determine-release-type, generate_version, generate_stable_version] if: needs.promote_to_release.result == 'success' - uses: ./.github/workflows/reusable-publish-apt.yml - with: - codename: ${{ needs.determine-release-type.outputs.release_type == 'stable' && 'stable' || needs.determine-release-type.outputs.release_type }} - suite: ${{ needs.determine-release-type.outputs.release_type == 'stable' && 'stable' || needs.determine-release-type.outputs.release_type }} - release_version: ${{ needs.create_prerelease.outputs.release_tag }} - download_from_release: true - release_tag: ${{ needs.create_prerelease.outputs.release_tag }} - secrets: - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} - AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Download from GitHub Release + uses: robinraju/release-downloader@v1.11 + 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=${{ needs.determine-release-type.outputs.release_type == 'stable' && 'stable' || needs.determine-release-type.outputs.release_type }} \ + --suite=${{ needs.determine-release-type.outputs.release_type == 'stable' && 'stable' || needs.determine-release-type.outputs.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: Validate APT installation after publishing validate_apt: @@ -439,12 +464,31 @@ jobs: name: Publish to NPM needs: [validate_apt, create_prerelease, determine-release-type, generate_version, generate_stable_version] if: needs.validate_apt.result == 'success' - uses: ./.github/workflows/reusable-publish-npm.yml - with: - npm_version: ${{ needs.generate_version.outputs.npm_version || needs.generate_stable_version.outputs.npm_version }} - npm_tag: ${{ needs.determine-release-type.outputs.release_type == 'stable' && 'latest' || needs.determine-release-type.outputs.release_type }} - secrets: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + runs-on: ubuntu-latest + steps: + - name: Checkout Repo + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + 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 ${{ needs.determine-release-type.outputs.release_type == 'stable' && 'latest' || needs.determine-release-type.outputs.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 ${{ needs.determine-release-type.outputs.release_type == 'stable' && 'latest' || needs.determine-release-type.outputs.release_type }} tag" # Step 8: Notify Discord (for stable releases only) notify_discord: diff --git a/.github/workflows/reusable-publish-apt.yml b/.github/workflows/reusable-publish-apt.yml deleted file mode 100644 index 4538e56..0000000 --- a/.github/workflows/reusable-publish-apt.yml +++ /dev/null @@ -1,89 +0,0 @@ -name: "Reusable - Publish to APT Repository" - -on: - workflow_call: - inputs: - codename: - description: 'APT repository codename (stable, beta, alpha, test)' - required: true - type: string - suite: - description: 'APT repository suite' - required: false - type: string - default: 'stable' - release_version: - description: 'Release version for display' - required: true - type: string - download_from_release: - description: 'Download files from GitHub release instead of artifacts' - required: false - type: boolean - default: false - release_tag: - description: 'Release tag to download from (when download_from_release is true)' - required: false - type: string - secrets: - GPG_PRIVATE_KEY: - required: true - GPG_PASSPHRASE: - required: true - GPG_KEY_ID: - required: true - AWS_ACCESS_KEY_ID: - required: true - AWS_SECRET_ACCESS_KEY: - required: true - -jobs: - publish_to_apt: - name: APT Repo Publish (${{ inputs.codename }}) ${{ inputs.release_version }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Download from GitHub Release - if: ${{ inputs.download_from_release }} - uses: robinraju/release-downloader@v1.11 - with: - tag: ${{ inputs.release_tag }} - fileName: 'homebridge*.deb' - out-file-path: 'repo/' - - - name: Download artifacts - if: ${{ !inputs.download_from_release }} - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - merge-multiple: true - 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.codename }} \ - --suite=${{ inputs.suite }} \ - --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 \ No newline at end of file diff --git a/.github/workflows/reusable-publish-npm.yml b/.github/workflows/reusable-publish-npm.yml deleted file mode 100644 index e5dcfb5..0000000 --- a/.github/workflows/reusable-publish-npm.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: "Reusable - Publish to NPM" - -on: - workflow_call: - inputs: - npm_version: - description: 'NPM version string' - required: true - type: string - npm_tag: - description: 'NPM tag (latest, beta, alpha)' - required: false - type: string - default: 'latest' - package_name: - description: 'NPM package name' - required: false - type: string - default: '@homebridge/homebridge-apt-pkg' - secrets: - NPM_TOKEN: - required: true - -jobs: - publish_to_npm: - name: NPM Publish ${{ inputs.npm_version }} - runs-on: ubuntu-latest - steps: - - name: Checkout Repo - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: latest - registry-url: 'https://registry.npmjs.org' - - - name: Set package.json version - run: | - NPM_VERSION="${{ inputs.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.npm_tag }} - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Output Success Notice - run: echo "::notice::Published ${{ inputs.package_name }} as version ${{ inputs.npm_version }} with ${{ inputs.npm_tag }} tag" \ No newline at end of file