From c6c6525bba02ca51d22556c4853ba64ba752de3d Mon Sep 17 00:00:00 2001 From: Andy <119136210+AndyMik90@users.noreply.github.com> Date: Thu, 15 Jan 2026 21:01:07 +0100 Subject: [PATCH] fix(ci): add beta manifest renaming and validation (#1002) (#1080) * fix(ci): add beta manifest renaming and validation to beta-release workflow The beta-release workflow was uploading `latest*.yml` manifest files without renaming them to `beta*.yml`. Since electron-updater constructs manifest filenames based on the update channel (beta -> beta-mac.yml on macOS), this caused 404 errors when checking for updates. Changes: - Add step to rename latest*.yml to beta*.yml for all platforms - Add validation to ensure all required manifests exist before release - Update dry-run summary to include manifest validation status This fix ensures beta releases include proper manifest files: - beta-mac.yml (macOS) - beta.yml (Windows) - beta-linux.yml (Linux) Fixes #1002 Co-Authored-By: Claude Opus 4.5 * fix(ci): merge macOS manifests for multi-arch auto-update support Fixes the macOS manifest overwrite bug where Intel and ARM64 builds both produce latest-mac.yml, causing one to overwrite the other during artifact flattening. Changes: - Separate binary artifact flattening from manifest handling - Use yq to merge Intel and ARM64 manifest files arrays - Resulting manifest contains update info for both architectures This fixes auto-update for Apple Silicon Mac users, who were previously receiving incorrect update information. See: https://github.com/electron-userland/electron-builder/issues/5592 Co-Authored-By: Claude Opus 4.5 * fix(ci): apply manifest merge fix to production release workflow Applies the same macOS manifest merge fix to release.yml that was added to beta-release.yml. This ensures production releases also have correct multi-architecture update manifests. Changes to release.yml: - Separate binary artifact flattening from manifest handling - Use yq to merge Intel and ARM64 latest-mac.yml files - Add validation for required manifest files - Resulting manifest contains update info for both architectures This fixes auto-update for Apple Silicon Mac users on production releases, who were previously receiving incorrect update information. See: https://github.com/electron-userland/electron-builder/issues/5592 Co-Authored-By: Claude Opus 4.5 * fix(ci): use yq eval-all to fix multiline YAML shell expansion Fixes the yq shell expansion bug where multiline YAML arrays couldn't be passed through shell variables. Uses yq eval-all with fileIndex selector to properly merge files arrays from both manifests. Changes: - Use yq eval-all pattern instead of shell variable expansion - Add error handling for yq download - Fail fast if no macOS manifests found (instead of warning) - Print yq version for debugging Fixes all 3 critical issues from Auto Claude PR review. Co-Authored-By: Claude Opus 4.5 * refactor(ci): extract macOS manifest merging into reusable composite action - Create .github/actions/merge-macos-manifests composite action - Add YAML validation after yq merge (syntax, file count, required fields) - Pin yq version to v4.44.3 for reproducibility - Replace duplicate ~50-line shell scripts in 3 locations with action calls - Add checkout step to dry-run job for composite action access Addresses PR review findings: - Code duplication (manifest logic repeated 3 times) - Missing YAML validation after merge - Unpinned yq version using /latest/ Co-Authored-By: Claude Opus 4.5 --------- Co-authored-by: Claude Opus 4.5 --- .../actions/merge-macos-manifests/action.yml | 155 ++++++++++++++++++ .github/workflows/beta-release.yml | 142 +++++++++++++++- .github/workflows/release.yml | 41 ++++- 3 files changed, 329 insertions(+), 9 deletions(-) create mode 100644 .github/actions/merge-macos-manifests/action.yml diff --git a/.github/actions/merge-macos-manifests/action.yml b/.github/actions/merge-macos-manifests/action.yml new file mode 100644 index 00000000..628643b4 --- /dev/null +++ b/.github/actions/merge-macos-manifests/action.yml @@ -0,0 +1,155 @@ +name: 'Merge macOS Manifests' +description: 'Merge Intel and ARM64 macOS manifests for electron-updater' + +inputs: + dist-path: + description: 'Path to the dist directory containing build artifacts' + required: false + default: 'dist' + output-path: + description: 'Path to output the merged manifest' + required: false + default: 'release-assets' + copy-other-manifests: + description: 'Whether to copy Windows/Linux manifests as well' + required: false + default: 'true' + yq-version: + description: 'Version of yq to use for YAML merging' + required: false + default: 'v4.44.3' + +outputs: + merged: + description: 'Whether manifests were merged (true) or single architecture used (false)' + value: ${{ steps.merge.outputs.merged }} + file-count: + description: 'Number of files in the merged manifest' + value: ${{ steps.validate.outputs.file_count }} + +runs: + using: 'composite' + steps: + - name: Merge macOS manifests + id: merge + shell: bash + run: | + echo "=== Merging macOS update manifests ===" + + # Find all latest-mac.yml files from different build artifacts + intel_manifest=$(find ${{ inputs.dist-path }} -path "*/macos-intel-builds/latest-mac.yml" -type f 2>/dev/null | head -1) + arm64_manifest=$(find ${{ inputs.dist-path }} -path "*/macos-arm64-builds/latest-mac.yml" -type f 2>/dev/null | head -1) + + echo "Intel manifest: ${intel_manifest:-not found}" + echo "ARM64 manifest: ${arm64_manifest:-not found}" + + mkdir -p ${{ inputs.output-path }} + + if [ -n "$intel_manifest" ] && [ -n "$arm64_manifest" ]; then + echo "Both architectures found - merging manifests..." + echo "merged=true" >> "$GITHUB_OUTPUT" + + # Install yq for YAML merging (pinned version) + YQ_VERSION="${{ inputs.yq-version }}" + if ! sudo wget -qO /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64"; then + echo "::error::Failed to download yq ${YQ_VERSION}" + exit 1 + fi + sudo chmod +x /usr/local/bin/yq + echo "Installed yq version:" + yq --version + + # Merge the files arrays from both manifests using eval-all + # This avoids shell expansion issues with multiline YAML + yq eval-all ' + select(fileIndex == 0) * {"files": ([.[].files] | add)} + ' "$intel_manifest" "$arm64_manifest" > ${{ inputs.output-path }}/latest-mac.yml + + echo "Merged manifest contents:" + cat ${{ inputs.output-path }}/latest-mac.yml + + elif [ -n "$intel_manifest" ]; then + echo "Only Intel manifest found - using as-is" + echo "merged=false" >> "$GITHUB_OUTPUT" + cp "$intel_manifest" ${{ inputs.output-path }}/latest-mac.yml + elif [ -n "$arm64_manifest" ]; then + echo "Only ARM64 manifest found - using as-is" + echo "merged=false" >> "$GITHUB_OUTPUT" + cp "$arm64_manifest" ${{ inputs.output-path }}/latest-mac.yml + else + echo "::error::No macOS manifests found - this will cause auto-update to fail" + exit 1 + fi + + - name: Validate merged manifest + id: validate + shell: bash + run: | + manifest_file="${{ inputs.output-path }}/latest-mac.yml" + + echo "=== Validating merged manifest ===" + + # Check file exists + if [ ! -f "$manifest_file" ]; then + echo "::error::Merged manifest file not found at $manifest_file" + exit 1 + fi + + # Install yq if not already installed (for single-arch case) + if ! command -v yq &> /dev/null; then + YQ_VERSION="${{ inputs.yq-version }}" + sudo wget -qO /usr/local/bin/yq "https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/yq_linux_amd64" + sudo chmod +x /usr/local/bin/yq + fi + + # Validate YAML is parseable + if ! yq eval '.' "$manifest_file" > /dev/null 2>&1; then + echo "::error::Merged manifest is not valid YAML" + cat "$manifest_file" + exit 1 + fi + echo "YAML syntax is valid" + + # Count files in manifest + file_count=$(yq eval '.files | length' "$manifest_file") + echo "file_count=$file_count" >> "$GITHUB_OUTPUT" + echo "Manifest contains $file_count file entries" + + # Validate file count + if [ "$file_count" -eq 0 ]; then + echo "::error::Merged manifest contains no files" + exit 1 + fi + + # If we merged both architectures, expect at least 2 files (one per arch) + if [ "${{ steps.merge.outputs.merged }}" = "true" ] && [ "$file_count" -lt 2 ]; then + echo "::warning::Merged manifest has fewer than 2 files - merge may have failed" + fi + + # Validate required fields exist + if ! yq eval '.version' "$manifest_file" | grep -q .; then + echo "::error::Manifest missing 'version' field" + exit 1 + fi + echo "Version field present: $(yq eval '.version' "$manifest_file")" + + echo "Manifest validation passed" + + - name: Copy other manifests + if: inputs.copy-other-manifests == 'true' + shell: bash + run: | + echo "=== Copying other update manifests ===" + + # Copy other manifests (Windows, Linux) - these don't have the duplicate issue + for manifest in latest.yml latest-linux.yml latest-linux-arm64.yml; do + found=$(find ${{ inputs.dist-path }} -name "$manifest" -type f 2>/dev/null | head -1) + if [ -n "$found" ]; then + echo "Copying $manifest" + cp "$found" ${{ inputs.output-path }}/ + fi + done + + echo "" + echo "=== Manifest files in ${{ inputs.output-path }} ===" + ls -la ${{ inputs.output-path }}/*.yml 2>/dev/null || echo "No manifest files found" diff --git a/.github/workflows/beta-release.yml b/.github/workflows/beta-release.yml index 271ff679..0317676d 100644 --- a/.github/workflows/beta-release.yml +++ b/.github/workflows/beta-release.yml @@ -557,10 +557,12 @@ jobs: with: path: dist - - name: Flatten and validate artifacts + - name: Flatten binary artifacts run: | mkdir -p release-assets - find dist -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.flatpak" -o -name "*.yml" \) -exec cp {} release-assets/ \; + + # Copy binary artifacts (these have unique names per architecture) + find dist -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.flatpak" -o -name "*.blockmap" \) -exec cp {} release-assets/ \; # Validate that at least one artifact was copied artifact_count=$(find release-assets -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.flatpak" \) | wc -l) @@ -569,9 +571,84 @@ jobs: exit 1 fi - echo "Found $artifact_count artifact(s):" + echo "Found $artifact_count binary artifact(s):" ls -la release-assets/ + # Merge macOS manifests from Intel and ARM64 builds + # See: https://github.com/electron-userland/electron-builder/issues/5592 + - name: Merge macOS manifests + uses: ./.github/actions/merge-macos-manifests + with: + dist-path: dist + output-path: release-assets + copy-other-manifests: 'true' + + - name: Rename and validate beta manifests + run: | + cd release-assets + + echo "=== Current manifest files ===" + ls -la *.yml 2>/dev/null || echo "No yml files found yet" + + # electron-builder generates latest*.yml files by default + # For beta channel, electron-updater expects beta*.yml files + # Rename: latest.yml -> beta.yml, latest-mac.yml -> beta-mac.yml, latest-linux.yml -> beta-linux.yml + + # Windows: latest.yml -> beta.yml + if [ -f "latest.yml" ]; then + echo "Renaming latest.yml -> beta.yml (Windows)" + mv latest.yml beta.yml + fi + + # macOS: latest-mac.yml -> beta-mac.yml + if [ -f "latest-mac.yml" ]; then + echo "Renaming latest-mac.yml -> beta-mac.yml (macOS)" + mv latest-mac.yml beta-mac.yml + fi + + # Linux: latest-linux.yml -> beta-linux.yml + if [ -f "latest-linux.yml" ]; then + echo "Renaming latest-linux.yml -> beta-linux.yml (Linux)" + mv latest-linux.yml beta-linux.yml + fi + + # Linux ARM64: latest-linux-arm64.yml -> beta-linux-arm64.yml (if exists) + if [ -f "latest-linux-arm64.yml" ]; then + echo "Renaming latest-linux-arm64.yml -> beta-linux-arm64.yml (Linux ARM64)" + mv latest-linux-arm64.yml beta-linux-arm64.yml + fi + + echo "" + echo "=== Beta manifest files after rename ===" + ls -la *.yml 2>/dev/null || echo "No yml files found" + + # Validate required beta manifests exist + missing_manifests="" + + if [ ! -f "beta-mac.yml" ]; then + missing_manifests="$missing_manifests beta-mac.yml" + fi + + if [ ! -f "beta.yml" ]; then + missing_manifests="$missing_manifests beta.yml" + fi + + if [ ! -f "beta-linux.yml" ]; then + missing_manifests="$missing_manifests beta-linux.yml" + fi + + if [ -n "$missing_manifests" ]; then + echo "::error::Missing required beta manifests:$missing_manifests" + echo "::error::Auto-update will fail on affected platforms without these files!" + exit 1 + fi + + echo "" + echo "All required beta manifests present:" + echo " - beta-mac.yml (macOS)" + echo " - beta.yml (Windows)" + echo " - beta-linux.yml (Linux)" + - name: Generate checksums run: | cd release-assets @@ -610,20 +687,77 @@ jobs: runs-on: ubuntu-latest if: ${{ github.event.inputs.dry_run == 'true' }} steps: + - uses: actions/checkout@v4 + - name: Download all artifacts uses: actions/download-artifact@v4 with: path: dist + - name: Flatten binary artifacts + run: | + mkdir -p release-assets + find dist -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.flatpak" -o -name "*.blockmap" \) -exec cp {} release-assets/ \; + + # Merge macOS manifests (same logic as real release) + - name: Merge macOS manifests + uses: ./.github/actions/merge-macos-manifests + with: + dist-path: dist + output-path: release-assets + copy-other-manifests: 'true' + + - name: Validate and rename beta manifests + run: | + cd release-assets + + # Rename latest*.yml to beta*.yml + [ -f "latest.yml" ] && mv latest.yml beta.yml + [ -f "latest-mac.yml" ] && mv latest-mac.yml beta-mac.yml + [ -f "latest-linux.yml" ] && mv latest-linux.yml beta-linux.yml + [ -f "latest-linux-arm64.yml" ] && mv latest-linux-arm64.yml beta-linux-arm64.yml + + # Validate required manifests + missing="" + [ ! -f "beta-mac.yml" ] && missing="$missing beta-mac.yml" + [ ! -f "beta.yml" ] && missing="$missing beta.yml" + [ ! -f "beta-linux.yml" ] && missing="$missing beta-linux.yml" + + if [ -n "$missing" ]; then + echo "::warning::DRY RUN: Missing required beta manifests:$missing" + echo "MANIFEST_STATUS=FAILED" >> $GITHUB_ENV + else + echo "MANIFEST_STATUS=PASSED" >> $GITHUB_ENV + + # Show merged manifest content for verification + echo "" + echo "=== beta-mac.yml content (should have both architectures) ===" + cat beta-mac.yml + fi + - name: Dry run summary run: | echo "## Beta Release Dry Run Complete" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Version:** ${{ needs.create-tag.outputs.version }}" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "Build artifacts created successfully:" >> $GITHUB_STEP_SUMMARY + + echo "### Build Artifacts" >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY find dist -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.flatpak" \) >> $GITHUB_STEP_SUMMARY echo "\`\`\`" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY + + echo "### Update Manifests (Required for Auto-Update)" >> $GITHUB_STEP_SUMMARY + if [ "$MANIFEST_STATUS" = "PASSED" ]; then + echo "All required beta manifests present:" >> $GITHUB_STEP_SUMMARY + echo "- beta-mac.yml (macOS)" >> $GITHUB_STEP_SUMMARY + echo "- beta.yml (Windows)" >> $GITHUB_STEP_SUMMARY + echo "- beta-linux.yml (Linux)" >> $GITHUB_STEP_SUMMARY + else + echo "**WARNING: Missing required manifests! Auto-update will fail.**" >> $GITHUB_STEP_SUMMARY + echo "Check build logs for details." >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + echo "To create a real release, run this workflow again with dry_run unchecked." >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9afdb20a..1209cd17 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -487,31 +487,62 @@ jobs: with: path: dist - - name: Flatten and validate artifacts + - name: Flatten binary artifacts run: | mkdir -p release-assets - find dist -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.flatpak" -o -name "*.yml" -o -name "*.blockmap" \) -exec cp {} release-assets/ \; - # Validate that installer files exist (not just manifests) + # Copy binary artifacts (these have unique names per architecture) + find dist -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.flatpak" -o -name "*.blockmap" \) -exec cp {} release-assets/ \; + + # Validate that installer files exist installer_count=$(find release-assets -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.flatpak" \) | wc -l) if [ "$installer_count" -eq 0 ]; then echo "::error::No installer artifacts found! Expected .dmg, .zip, .exe, .AppImage, .deb, or .flatpak files." exit 1 fi - echo "Found $installer_count installer(s):" + echo "Found $installer_count binary artifact(s):" find release-assets -type f \( -name "*.dmg" -o -name "*.zip" -o -name "*.exe" -o -name "*.AppImage" -o -name "*.deb" -o -name "*.flatpak" \) -exec basename {} \; + # Merge macOS manifests from Intel and ARM64 builds + # See: https://github.com/electron-userland/electron-builder/issues/5592 + - name: Merge macOS manifests + uses: ./.github/actions/merge-macos-manifests + with: + dist-path: dist + output-path: release-assets + copy-other-manifests: 'true' + + - name: Validate manifests + run: | # Validate that electron-updater manifest files are present (required for auto-updates) yml_count=$(find release-assets -type f -name "*.yml" | wc -l) if [ "$yml_count" -eq 0 ]; then - echo "::error::No update manifest (.yml) files found! Auto-update architecture detection will not work." + echo "::error::No update manifest (.yml) files found! Auto-update will not work." exit 1 fi echo "Found $yml_count manifest file(s):" find release-assets -type f -name "*.yml" -exec basename {} \; + # Validate required manifests exist + missing="" + [ ! -f "release-assets/latest-mac.yml" ] && missing="$missing latest-mac.yml" + [ ! -f "release-assets/latest.yml" ] && missing="$missing latest.yml" + [ ! -f "release-assets/latest-linux.yml" ] && missing="$missing latest-linux.yml" + + if [ -n "$missing" ]; then + echo "::error::Missing required manifests:$missing" + echo "::error::Auto-update will fail on affected platforms!" + exit 1 + fi + + echo "" + echo "All required manifests present:" + echo " - latest-mac.yml (macOS)" + echo " - latest.yml (Windows)" + echo " - latest-linux.yml (Linux)" + echo "" echo "All release assets:" ls -la release-assets/