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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Andy
2026-01-15 21:01:07 +01:00
committed by GitHub
parent 58f4f30b21
commit c6c6525bba
3 changed files with 329 additions and 9 deletions
@@ -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"
+138 -4
View File
@@ -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
+36 -5
View File
@@ -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/