Release Flow Simulation (Current Architecture) #9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release Flow Simulation (Current Architecture) | |
| # | |
| # This workflow is fully synced with release.yml for validating the current flow architecture | |
| # | |
| # Architecture features: | |
| # - build-windows: Single job with platform and arch conditional checks | |
| # - build-macos: Matrix strategy (amd64, aarch64), job-level platform check, step-level arch filtering | |
| # - upload-to-s3: Single job downloading all platform artifacts | |
| # - generate-appcast: Single job generating OTA configuration | |
| # | |
| # Purpose: | |
| # - Quickly validate workflow flow logic (< 5 minutes) | |
| # - Test conditional logic correctness | |
| # - Verify artifact naming and passing | |
| # - No actual build execution, only creates simulated artifacts | |
| # | |
| # Note: This simulation does not include Inno Setup installation or language pack processing. | |
| # The actual release.yml uses .islu (Unicode) language files exclusively. | |
| # | |
| # Supported test scenarios: | |
| # 1. platform=windows, arch=amd64 - Build Windows only | |
| # 2. platform=macos, arch=amd64 - Build macOS Intel only | |
| # 3. platform=macos, arch=aarch64 - Build macOS ARM only | |
| # 4. platform=macos, arch=all - Build all macOS | |
| # 5. platform=all, arch=amd64 - Build all Intel | |
| # 6. platform=all, arch=aarch64 - Build macOS ARM only (Windows doesn't support ARM) | |
| # 7. platform=all, arch=all - Complete build | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| platform: | |
| description: 'Build platform (windows, macos, all)' | |
| required: true | |
| default: 'all' | |
| type: choice | |
| options: | |
| - all | |
| - windows | |
| - macos | |
| arch: | |
| description: 'CPU architecture (all, amd64, aarch64)' | |
| required: true | |
| default: 'all' | |
| type: choice | |
| options: | |
| - all | |
| - amd64 | |
| - aarch64 | |
| ref: | |
| description: 'Git ref to build (not used in simulation)' | |
| required: true | |
| default: 'master' | |
| type: string | |
| upload_artifacts: | |
| description: 'Upload artifacts to GitHub Artifacts (debug only)' | |
| required: false | |
| default: 'false' | |
| type: choice | |
| options: | |
| - 'true' | |
| - 'false' | |
| concurrency: | |
| group: ecan-release-sim-${{ github.ref }}-${{ github.event.inputs.platform }}-${{ github.event.inputs.arch }} | |
| cancel-in-progress: true | |
| jobs: | |
| validate-tag: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag-valid: ${{ steps.out.outputs.valid }} | |
| version: ${{ steps.out.outputs.version }} | |
| steps: | |
| - id: out | |
| run: | | |
| echo "valid=true" >> $GITHUB_OUTPUT | |
| echo "version=0.0.0-sim" >> $GITHUB_OUTPUT | |
| build-windows: | |
| name: Build Windows amd64 | |
| needs: validate-tag | |
| if: | | |
| needs.validate-tag.outputs.tag-valid == 'true' && | |
| (github.event.inputs.platform == 'windows' || | |
| github.event.inputs.platform == 'all') && | |
| (github.event.inputs.arch == 'amd64' || | |
| github.event.inputs.arch == 'all') | |
| runs-on: ubuntu-latest # Use ubuntu for faster simulation | |
| env: | |
| BUILD_ARCH: amd64 | |
| steps: | |
| # Note: Frontend is always built in real workflow (no change detection) | |
| # Simulation doesn't need frontend build steps | |
| - name: Simulate Windows build | |
| run: | | |
| echo "=== Simulating Windows amd64 Build ===" | |
| echo "Platform: Windows" | |
| echo "Architecture: $BUILD_ARCH" | |
| echo "Note: Real workflow always builds frontend (no skip logic)" | |
| VERSION="${{ needs.validate-tag.outputs.version }}" | |
| mkdir -p dist artifacts | |
| echo "fake windows installer" > "dist/eCan-${VERSION}-windows-${BUILD_ARCH}-Setup.exe" | |
| cp "dist/eCan-${VERSION}-windows-${BUILD_ARCH}-Setup.exe" artifacts/ | |
| echo "✅ Windows build simulated" | |
| - name: Upload Windows artifacts for S3 transfer (always, short retention) | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: eCan-windows-amd64-${{ needs.validate-tag.outputs.version }}-s3-transfer | |
| path: artifacts/ | |
| retention-days: 1 | |
| compression-level: 6 | |
| if-no-files-found: error | |
| - name: Upload Windows artifacts for users (optional, long retention) | |
| if: github.event.inputs.upload_artifacts == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: eCan-Windows-${{ needs.validate-tag.outputs.version }} | |
| path: artifacts/ | |
| retention-days: 30 | |
| compression-level: 6 | |
| build-macos: | |
| name: Build macOS | |
| needs: validate-tag | |
| if: | | |
| needs.validate-tag.outputs.tag-valid == 'true' && | |
| (github.event.inputs.platform == 'macos' || | |
| github.event.inputs.platform == 'all') | |
| runs-on: ubuntu-latest # Use ubuntu for faster simulation | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - arch: amd64 | |
| runner: macos-14-large # Would use Intel runner in real workflow (macos-13 deprecated) | |
| target_arch: x86_64 | |
| pyinstaller_arch: x86_64 | |
| - arch: aarch64 | |
| runner: macos-latest # Would use Apple Silicon runner in real workflow | |
| target_arch: arm64 | |
| pyinstaller_arch: arm64 | |
| env: | |
| BUILD_ARCH: ${{ matrix.arch }} | |
| TARGET_ARCH: ${{ matrix.target_arch }} | |
| PYINSTALLER_TARGET_ARCH: ${{ matrix.pyinstaller_arch }} | |
| steps: | |
| - name: Decide if this architecture should be built | |
| id: should_build | |
| run: | | |
| ARCH="${{ matrix.arch }}" | |
| INPUT="${{ github.event.inputs.arch }}" | |
| if [ "$INPUT" = "all" ] || [ -z "$INPUT" ] || [ "$INPUT" = "$ARCH" ]; then | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "should_build=false" >> $GITHUB_OUTPUT | |
| fi | |
| # Note: Frontend is always built in real workflow (no change detection) | |
| # Simulation doesn't need frontend build steps | |
| - name: Simulate macOS build | |
| if: steps.should_build.outputs.should_build == 'true' | |
| run: | | |
| echo "=== Simulating macOS ${{ matrix.arch }} Build ===" | |
| echo "Platform: macOS" | |
| echo "Architecture: ${{ matrix.arch }} (${{ matrix.target_arch }})" | |
| echo "Note: Real workflow always builds frontend (no skip logic)" | |
| VERSION="${{ needs.validate-tag.outputs.version }}" | |
| ARCH="${{ matrix.arch }}" | |
| mkdir -p dist artifacts | |
| echo "fake macos installer" > "dist/eCan-${VERSION}-macos-${ARCH}.pkg" | |
| cp "dist/eCan-${VERSION}-macos-${ARCH}.pkg" artifacts/ | |
| echo "✅ macOS ${{ matrix.arch }} build simulated" | |
| - name: Upload macOS artifacts for S3 transfer (always, short retention) | |
| if: steps.should_build.outputs.should_build == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: eCan-macos-${{ matrix.arch }}-${{ needs.validate-tag.outputs.version }}-s3-transfer | |
| path: artifacts/ | |
| retention-days: 1 | |
| compression-level: 6 | |
| if-no-files-found: error | |
| - name: Upload macOS artifacts for users (optional, long retention) | |
| if: steps.should_build.outputs.should_build == 'true' && github.event.inputs.upload_artifacts == 'true' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: eCan-macos-${{ matrix.arch }}-${{ needs.validate-tag.outputs.version }} | |
| path: artifacts/ | |
| retention-days: 30 | |
| compression-level: 6 | |
| upload-to-s3: | |
| needs: [validate-tag, build-windows, build-macos] | |
| if: | | |
| always() && | |
| needs.validate-tag.outputs.tag-valid == 'true' && | |
| (needs.build-windows.result == 'success' || needs.build-windows.result == 'skipped') && | |
| (needs.build-macos.result == 'success' || needs.build-macos.result == 'skipped') | |
| runs-on: ubuntu-latest | |
| env: | |
| AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID || 'NOT_SET' }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY || 'NOT_SET' }} | |
| AWS_REGION: ${{ secrets.AWS_REGION || 'NOT_SET' }} | |
| S3_BUCKET: ${{ secrets.S3_BUCKET || 'NOT_SET' }} | |
| S3_BASE_PATH: ${{ secrets.S3_BASE_PATH || 'NOT_SET' }} | |
| AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION || 'NOT_SET' }} | |
| WIN_CERT_PFX: ${{ secrets.WIN_CERT_PFX || 'NOT_SET' }} | |
| WIN_CERT_PASSWORD: ${{ secrets.WIN_CERT_PASSWORD || 'NOT_SET' }} | |
| # For appcast generation conditions | |
| # Note: IDE may warn about context access, but this is the correct pattern for conditional steps | |
| ED25519_PRIVATE_KEY: ${{ secrets.ED25519_PRIVATE_KEY || 'NOT_SET' }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.inputs.ref || github.ref }} | |
| fetch-depth: 0 | |
| - name: Check AWS S3 Configuration | |
| run: | | |
| echo "=== Checking AWS S3 Configuration ===" | |
| echo "AWS credentials: ${{ env.AWS_ACCESS_KEY_ID != 'NOT_SET' && 'Available' || 'Not Available' }}" | |
| echo "S3 bucket: ${{ env.S3_BUCKET != 'NOT_SET' && env.S3_BUCKET || 'Not configured' }}" | |
| echo "AWS region: ${{ env.AWS_REGION != 'NOT_SET' && env.AWS_REGION || 'Not configured' }}" | |
| echo "S3 base path: ${{ env.S3_BASE_PATH != 'NOT_SET' && env.S3_BASE_PATH || 'Not configured' }}" | |
| if [ "${{ env.AWS_ACCESS_KEY_ID }}" = "NOT_SET" ] || \ | |
| [ "${{ env.AWS_SECRET_ACCESS_KEY }}" = "NOT_SET" ] || \ | |
| [ "${{ env.S3_BUCKET }}" = "NOT_SET" ]; then | |
| echo "S3 upload: Disabled (missing credentials or bucket configuration)" | |
| echo "Artifacts will only be available as GitHub Artifacts" | |
| else | |
| echo "S3 upload: Enabled" | |
| fi | |
| - name: Compute S3_BASE_URL for appcast generation | |
| run: | | |
| echo "=== Computing S3_BASE_URL ===" | |
| echo "Debug: S3_BUCKET=$S3_BUCKET" | |
| echo "Debug: S3_BASE_PATH=$S3_BASE_PATH" | |
| echo "Debug: AWS_REGION=$AWS_REGION" | |
| if [ "$S3_BUCKET" != "NOT_SET" ] && [ "$S3_BASE_PATH" != "NOT_SET" ] && [ "$AWS_REGION" != "NOT_SET" ]; then | |
| BASE_URL="https://${S3_BUCKET}.s3.${AWS_REGION}.amazonaws.com/${S3_BASE_PATH}" | |
| echo "S3_BASE_URL=$BASE_URL" >> $GITHUB_ENV | |
| echo "Computed S3_BASE_URL=$BASE_URL" | |
| else | |
| echo "S3_BASE_URL=" >> $GITHUB_ENV | |
| echo "S3 configuration incomplete, S3_BASE_URL not set" | |
| echo " S3_BUCKET: $S3_BUCKET" | |
| echo " S3_BASE_PATH: $S3_BASE_PATH" | |
| echo " AWS_REGION: $AWS_REGION" | |
| fi | |
| - name: Verify AWS Identity | |
| if: env.AWS_ACCESS_KEY_ID != 'NOT_SET' && env.AWS_SECRET_ACCESS_KEY != 'NOT_SET' | |
| run: | | |
| echo "=== Verifying AWS Identity ===" | |
| aws sts get-caller-identity | |
| echo "AWS credentials are valid" | |
| - name: Test S3 Access | |
| if: env.AWS_ACCESS_KEY_ID != 'NOT_SET' && env.AWS_SECRET_ACCESS_KEY != 'NOT_SET' && env.S3_BUCKET != 'NOT_SET' | |
| run: | | |
| echo "=== Testing S3 Access ===" | |
| echo "Debug: S3_BUCKET=$S3_BUCKET" | |
| echo "Debug: S3_BASE_PATH=$S3_BASE_PATH" | |
| echo "Debug: AWS_REGION=$AWS_REGION" | |
| echo "Debug: AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION" | |
| # Test the actual path used for uploads (not bucket root) | |
| # This matches the path used in actual upload steps | |
| TEST_PATH="s3://${S3_BUCKET}/${S3_BASE_PATH}/" | |
| echo "Testing S3 path (same as upload): $TEST_PATH" | |
| if aws s3 ls "$TEST_PATH" 2>&1 | tee /tmp/s3_test.log; then | |
| echo "✅ S3 path access: OK" | |
| else | |
| echo "❌ S3 path access: FAILED" | |
| echo "" | |
| echo "Error output:" | |
| cat /tmp/s3_test.log | |
| echo "" | |
| echo "Note: Testing the actual upload path, not bucket root" | |
| echo " Full path: $TEST_PATH" | |
| echo " S3_BUCKET: ${S3_BUCKET}" | |
| echo " S3_BASE_PATH: ${S3_BASE_PATH}" | |
| echo " AWS_REGION: ${AWS_REGION}" | |
| echo "" | |
| echo "If this fails but uploads work, check IAM permissions for the specific path" | |
| exit 1 | |
| fi | |
| - name: Download Windows artifacts | |
| if: needs.build-windows.result == 'success' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: eCan-windows-amd64-${{ needs.validate-tag.outputs.version }}-s3-transfer | |
| path: windows-artifacts | |
| - name: Download macOS amd64 artifacts | |
| if: needs.build-macos.result == 'success' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: eCan-macos-amd64-${{ needs.validate-tag.outputs.version }}-s3-transfer | |
| path: macos-amd64-artifacts | |
| continue-on-error: true | |
| - name: Download macOS aarch64 artifacts | |
| if: needs.build-macos.result == 'success' | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: eCan-macos-aarch64-${{ needs.validate-tag.outputs.version }}-s3-transfer | |
| path: macos-aarch64-artifacts | |
| continue-on-error: true | |
| - name: Merge macOS artifacts | |
| run: | | |
| mkdir -p macos-artifacts | |
| for d in macos-amd64-artifacts macos-aarch64-artifacts; do | |
| if [ -d "$d" ]; then cp "$d"/* macos-artifacts/ 2>/dev/null || true; fi | |
| done | |
| - name: Upload Windows installers to S3 | |
| id: upload_windows | |
| if: env.AWS_ACCESS_KEY_ID != 'NOT_SET' && env.S3_BUCKET != 'NOT_SET' && env.S3_BASE_PATH != 'NOT_SET' | |
| run: | | |
| set -euo pipefail | |
| echo "=== Uploading Windows installers to S3 ===" | |
| VERSION="${{ needs.validate-tag.outputs.version }}" | |
| echo "Version: ${VERSION}" | |
| if [ -d "windows-artifacts" ] && [ "$(ls -A windows-artifacts)" ]; then | |
| echo "Found Windows artifacts:" | |
| ls -lh windows-artifacts/ | |
| # Upload Windows installers to S3 with versioned path | |
| echo "Uploading to: s3://${S3_BUCKET}/${S3_BASE_PATH}/v${VERSION}/windows/" | |
| # Note: --acl removed because bucket does not allow ACLs | |
| # Use bucket policy to make files public instead | |
| aws s3 sync windows-artifacts/ "s3://${S3_BUCKET}/${S3_BASE_PATH}/v${VERSION}/windows/" \ | |
| --exclude "*" \ | |
| --include "*.exe" \ | |
| --include "*.msi" | |
| echo "✅ Windows installers uploaded to S3" | |
| # List uploaded files | |
| UPLOAD_COUNT=0 | |
| for file in windows-artifacts/*.exe windows-artifacts/*.msi; do | |
| if [ -f "$file" ]; then | |
| filename=$(basename "$file") | |
| size=$(du -sh "$file" | cut -f1) | |
| echo " - ${filename} (${size})" | |
| echo " URL: https://${S3_BUCKET}.s3.${AWS_REGION}.amazonaws.com/${S3_BASE_PATH}/v${VERSION}/windows/${filename}" | |
| UPLOAD_COUNT=$((UPLOAD_COUNT + 1)) | |
| fi | |
| done | |
| echo "Total Windows files uploaded: $UPLOAD_COUNT" | |
| echo "upload_count=$UPLOAD_COUNT" >> $GITHUB_OUTPUT | |
| else | |
| echo "⚠️ No Windows artifacts found, skipping upload" | |
| echo "upload_count=0" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Upload macOS installers to S3 | |
| id: upload_macos | |
| if: env.AWS_ACCESS_KEY_ID != 'NOT_SET' && env.S3_BUCKET != 'NOT_SET' && env.S3_BASE_PATH != 'NOT_SET' | |
| run: | | |
| set -euo pipefail | |
| echo "=== Uploading macOS installers to S3 ===" | |
| VERSION="${{ needs.validate-tag.outputs.version }}" | |
| echo "Version: ${VERSION}" | |
| # Merge macOS artifacts from both architectures | |
| mkdir -p macos-artifacts | |
| AMD64_COUNT=0 | |
| AARCH64_COUNT=0 | |
| if [ -d "macos-amd64-artifacts" ] && [ "$(ls -A macos-amd64-artifacts 2>/dev/null)" ]; then | |
| echo "Found macOS amd64 artifacts:" | |
| ls -lh macos-amd64-artifacts/ | |
| cp -v macos-amd64-artifacts/* macos-artifacts/ 2>/dev/null || true | |
| AMD64_COUNT=$(ls -1 macos-amd64-artifacts/ | wc -l) | |
| else | |
| echo "ℹ️ No macOS amd64 artifacts found" | |
| fi | |
| if [ -d "macos-aarch64-artifacts" ] && [ "$(ls -A macos-aarch64-artifacts 2>/dev/null)" ]; then | |
| echo "Found macOS aarch64 artifacts:" | |
| ls -lh macos-aarch64-artifacts/ | |
| cp -v macos-aarch64-artifacts/* macos-artifacts/ 2>/dev/null || true | |
| AARCH64_COUNT=$(ls -1 macos-aarch64-artifacts/ | wc -l) | |
| else | |
| echo "ℹ️ No macOS aarch64 artifacts found" | |
| fi | |
| if [ -d "macos-artifacts" ] && [ "$(ls -A macos-artifacts 2>/dev/null)" ]; then | |
| echo "" | |
| echo "Merged macOS artifacts (amd64: $AMD64_COUNT, aarch64: $AARCH64_COUNT):" | |
| ls -lh macos-artifacts/ | |
| # Upload macOS installers to S3 with versioned path | |
| echo "Uploading to: s3://${S3_BUCKET}/${S3_BASE_PATH}/v${VERSION}/macos/" | |
| # Note: --acl removed because bucket does not allow ACLs | |
| # Use bucket policy to make files public instead | |
| aws s3 sync macos-artifacts/ "s3://${S3_BUCKET}/${S3_BASE_PATH}/v${VERSION}/macos/" \ | |
| --exclude "*" \ | |
| --include "*.pkg" \ | |
| --include "*.zip" | |
| echo "✅ macOS installers uploaded to S3" | |
| # List uploaded files | |
| UPLOAD_COUNT=0 | |
| for file in macos-artifacts/*.pkg macos-artifacts/*.zip; do | |
| if [ -f "$file" ]; then | |
| filename=$(basename "$file") | |
| size=$(du -sh "$file" | cut -f1) | |
| echo " - ${filename} (${size})" | |
| echo " URL: https://${S3_BUCKET}.s3.${AWS_REGION}.amazonaws.com/${S3_BASE_PATH}/v${VERSION}/macos/${filename}" | |
| UPLOAD_COUNT=$((UPLOAD_COUNT + 1)) | |
| fi | |
| done | |
| echo "Total macOS files uploaded: $UPLOAD_COUNT" | |
| echo "upload_count=$UPLOAD_COUNT" >> $GITHUB_OUTPUT | |
| else | |
| echo "⚠️ No macOS artifacts found, skipping upload" | |
| echo "upload_count=0" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Verify S3 uploads | |
| if: env.AWS_ACCESS_KEY_ID != 'NOT_SET' && env.S3_BUCKET != 'NOT_SET' && env.S3_BASE_PATH != 'NOT_SET' | |
| continue-on-error: true | |
| run: | | |
| echo "=== Verifying S3 uploads ===" | |
| VERSION="${{ needs.validate-tag.outputs.version }}" | |
| echo "Version: ${VERSION}" | |
| UPLOAD_FAILED=0 | |
| # Verify Windows artifacts | |
| if [ -d "windows-artifacts" ] && [ "$(ls -A windows-artifacts 2>/dev/null)" ]; then | |
| echo "Verifying Windows artifacts in S3..." | |
| WIN_COUNT=0 | |
| for file in windows-artifacts/*.exe windows-artifacts/*.msi; do | |
| if [ -f "$file" ]; then | |
| filename=$(basename "$file") | |
| if aws s3 ls "s3://${S3_BUCKET}/${S3_BASE_PATH}/v${VERSION}/windows/${filename}" >/dev/null 2>&1; then | |
| echo " ✅ ${filename}" | |
| WIN_COUNT=$((WIN_COUNT + 1)) | |
| else | |
| echo " ⚠️ ${filename} not found in S3" | |
| UPLOAD_FAILED=1 | |
| fi | |
| fi | |
| done | |
| echo "Windows files verified: $WIN_COUNT" | |
| else | |
| echo "ℹ️ No Windows artifacts to verify" | |
| fi | |
| # Verify macOS artifacts | |
| if [ -d "macos-artifacts" ] && [ "$(ls -A macos-artifacts 2>/dev/null)" ]; then | |
| echo "Verifying macOS artifacts in S3..." | |
| MACOS_COUNT=0 | |
| for file in macos-artifacts/*.pkg macos-artifacts/*.zip; do | |
| if [ -f "$file" ]; then | |
| filename=$(basename "$file") | |
| if aws s3 ls "s3://${S3_BUCKET}/${S3_BASE_PATH}/v${VERSION}/macos/${filename}" >/dev/null 2>&1; then | |
| echo " ✅ ${filename}" | |
| MACOS_COUNT=$((MACOS_COUNT + 1)) | |
| else | |
| echo " ⚠️ ${filename} not found in S3" | |
| UPLOAD_FAILED=1 | |
| fi | |
| fi | |
| done | |
| echo "macOS files verified: $MACOS_COUNT" | |
| else | |
| echo "ℹ️ No macOS artifacts to verify" | |
| fi | |
| if [ $UPLOAD_FAILED -eq 1 ]; then | |
| echo "" | |
| echo "⚠️ Some uploads may have failed, but continuing..." | |
| exit 0 # Do not fail the job | |
| else | |
| echo "" | |
| echo "✅ All S3 uploads verified successfully" | |
| fi | |
| - name: Generate simulated appcast files | |
| if: env.S3_BUCKET != 'NOT_SET' && env.S3_BASE_PATH != 'NOT_SET' && env.AWS_REGION != 'NOT_SET' | |
| run: | | |
| set -euo pipefail | |
| echo "=== Generating simulated appcast files ===" | |
| VERSION="${{ needs.validate-tag.outputs.version }}" | |
| mkdir -p dist/appcast | |
| WINDOWS_URL="https://${S3_BUCKET}.s3.${AWS_REGION}.amazonaws.com/${S3_BASE_PATH}/v${VERSION}/windows/eCan-${VERSION}-windows-amd64-Setup.exe" | |
| MACOS_AMD64_URL="https://${S3_BUCKET}.s3.${AWS_REGION}.amazonaws.com/${S3_BASE_PATH}/v${VERSION}/macos/eCan-${VERSION}-macos-amd64.pkg" | |
| MACOS_AARCH64_URL="https://${S3_BUCKET}.s3.${AWS_REGION}.amazonaws.com/${S3_BASE_PATH}/v${VERSION}/macos/eCan-${VERSION}-macos-aarch64.pkg" | |
| cat > dist/appcast/appcast-windows.xml <<EOF | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle"> | |
| <channel> | |
| <title>eCan Windows (simulated)</title> | |
| <item> | |
| <title>Version ${VERSION}</title> | |
| <enclosure url="${WINDOWS_URL}" sparkle:version="${VERSION}" sparkle:os="windows" length="0" sparkle:edSignature="SIMULATED"/> | |
| </item> | |
| </channel> | |
| </rss> | |
| EOF | |
| cat > dist/appcast/appcast-macos.xml <<EOF | |
| <?xml version="1.0" encoding="UTF-8"?> | |
| <rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle"> | |
| <channel> | |
| <title>eCan macOS (simulated)</title> | |
| <item> | |
| <title>Version ${VERSION} (amd64)</title> | |
| <enclosure url="${MACOS_AMD64_URL}" sparkle:version="${VERSION}" sparkle:os="macos" sparkle:arch="amd64" length="0" sparkle:edSignature="SIMULATED"/> | |
| </item> | |
| <item> | |
| <title>Version ${VERSION} (aarch64)</title> | |
| <enclosure url="${MACOS_AARCH64_URL}" sparkle:version="${VERSION}" sparkle:os="macos" sparkle:arch="aarch64" length="0" sparkle:edSignature="SIMULATED"/> | |
| </item> | |
| </channel> | |
| </rss> | |
| EOF | |
| echo "✅ Simulated appcast files generated successfully" | |
| - name: Validate appcast XML files | |
| run: | | |
| echo "=== Validating appcast XML files ===" | |
| VALIDATION_FAILED=0 | |
| for f in dist/appcast/*.xml; do | |
| if [ -f "$f" ]; then | |
| echo "Validating: $(basename $f)" | |
| if python -c "import xml.etree.ElementTree as ET; ET.parse('$f')" 2>/dev/null; then | |
| echo " ✅ Valid XML" | |
| # Check if file has enclosure elements | |
| if grep -q '<enclosure' "$f"; then | |
| echo " ✅ Contains enclosure elements" | |
| else | |
| echo " ⚠️ No enclosure elements found" | |
| fi | |
| else | |
| echo " ❌ Invalid XML format" | |
| VALIDATION_FAILED=1 | |
| fi | |
| fi | |
| done | |
| if [ $VALIDATION_FAILED -eq 1 ]; then | |
| echo "" | |
| echo "⚠️ Some appcast files have validation errors" | |
| exit 1 | |
| fi | |
| - name: Upload simulated appcasts to S3 | |
| if: env.AWS_ACCESS_KEY_ID != 'NOT_SET' && env.AWS_SECRET_ACCESS_KEY != 'NOT_SET' && env.S3_BUCKET != 'NOT_SET' | |
| run: | | |
| echo "=== Uploading simulated appcasts to S3 ===" | |
| if [ -d dist/appcast ]; then | |
| # Note: --acl removed because bucket does not allow ACLs | |
| # Use bucket policy to make files public instead | |
| aws s3 sync dist/appcast/ "s3://${S3_BUCKET}/appcast/" --content-type "application/xml" --exclude "*" --include "*.xml" | |
| echo "✅ Simulated appcasts uploaded to S3" | |
| else | |
| echo "No appcast directory; skipping upload" | |
| fi | |
| - name: Generate S3 download links | |
| if: env.AWS_ACCESS_KEY_ID != 'NOT_SET' && env.S3_BUCKET != 'NOT_SET' && env.S3_BASE_PATH != 'NOT_SET' | |
| env: | |
| VERSION: ${{ needs.validate-tag.outputs.version }} | |
| # Use non-secret variables to avoid masking in output | |
| BUCKET_NAME: ${{ secrets.S3_BUCKET || 'NOT_SET' }} | |
| BUCKET_REGION: ${{ secrets.AWS_REGION || 'NOT_SET' }} | |
| BUCKET_BASE_PATH: ${{ secrets.S3_BASE_PATH || 'NOT_SET' }} | |
| run: | | |
| echo "=== Generating S3 Download Links ===" | |
| echo "" | |
| # Display in GitHub Actions Summary | |
| echo "## 📦 Download Links" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "Version: **${VERSION}**" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| # Windows Installers | |
| echo "### Windows Installers (x86_64)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -d "windows-artifacts" ] && [ "$(ls -A windows-artifacts 2>/dev/null)" ]; then | |
| for file in windows-artifacts/*.exe windows-artifacts/*.msi; do | |
| if [ -f "$file" ]; then | |
| filename=$(basename "$file") | |
| size=$(du -sh "$file" | cut -f1) | |
| # Standard S3 URL - use renamed variables to avoid masking | |
| standard_url="https://${BUCKET_NAME}.s3.${BUCKET_REGION}.amazonaws.com/${BUCKET_BASE_PATH}/windows/${filename}" | |
| # S3 Transfer Acceleration URL | |
| accelerate_url="https://${BUCKET_NAME}.s3-accelerate.amazonaws.com/${BUCKET_BASE_PATH}/windows/${filename}" | |
| echo "#### ${filename}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Size**: ${size}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Standard URL**: [Download](${standard_url})" >> $GITHUB_STEP_SUMMARY | |
| echo " \`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo " ${standard_url}" >> $GITHUB_STEP_SUMMARY | |
| echo " \`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Accelerated URL** (faster for global users): [Download](${accelerate_url})" >> $GITHUB_STEP_SUMMARY | |
| echo " \`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo " ${accelerate_url}" >> $GITHUB_STEP_SUMMARY | |
| echo " \`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| done | |
| else | |
| echo "_No Windows installers available_" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| # macOS Installers | |
| echo "### macOS Installers" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ -d "macos-artifacts" ] && [ "$(ls -A macos-artifacts 2>/dev/null)" ]; then | |
| for file in macos-artifacts/*.pkg macos-artifacts/*.zip; do | |
| if [ -f "$file" ]; then | |
| filename=$(basename "$file") | |
| size=$(du -sh "$file" | cut -f1) | |
| # Determine architecture from filename | |
| if [[ "$filename" == *"amd64"* ]] || [[ "$filename" == *"x86_64"* ]]; then | |
| arch_label="Intel (x86_64)" | |
| elif [[ "$filename" == *"aarch64"* ]] || [[ "$filename" == *"arm64"* ]]; then | |
| arch_label="Apple Silicon (ARM64)" | |
| else | |
| arch_label="Universal" | |
| fi | |
| # Standard S3 URL - use renamed variables to avoid masking | |
| standard_url="https://${BUCKET_NAME}.s3.${BUCKET_REGION}.amazonaws.com/${BUCKET_BASE_PATH}/macos/${filename}" | |
| # S3 Transfer Acceleration URL | |
| accelerate_url="https://${BUCKET_NAME}.s3-accelerate.amazonaws.com/${BUCKET_BASE_PATH}/macos/${filename}" | |
| echo "#### ${filename}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Architecture**: ${arch_label}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Size**: ${size}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Standard URL**: [Download](${standard_url})" >> $GITHUB_STEP_SUMMARY | |
| echo " \`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo " ${standard_url}" >> $GITHUB_STEP_SUMMARY | |
| echo " \`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Accelerated URL** (faster for global users): [Download](${accelerate_url})" >> $GITHUB_STEP_SUMMARY | |
| echo " \`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo " ${accelerate_url}" >> $GITHUB_STEP_SUMMARY | |
| echo " \`\`\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| done | |
| else | |
| echo "_No macOS installers available_" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 📝 Download Information" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "#### URL Types" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Standard URL**: Direct S3 access from the bucket's region" >> $GITHUB_STEP_SUMMARY | |
| echo " - Best for: Users in the same AWS region" >> $GITHUB_STEP_SUMMARY | |
| echo " - Format: \`https://{bucket}.s3.{region}.amazonaws.com/{path}/{file}\`" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Accelerated URL**: Uses AWS S3 Transfer Acceleration" >> $GITHUB_STEP_SUMMARY | |
| echo " - Best for: Global users, faster downloads worldwide" >> $GITHUB_STEP_SUMMARY | |
| echo " - Format: \`https://{bucket}.s3-accelerate.amazonaws.com/{path}/{file}\`" >> $GITHUB_STEP_SUMMARY | |
| echo " - Requires: Transfer Acceleration enabled on S3 bucket" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "#### Platform Support" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Windows**: x86_64 (64-bit) only" >> $GITHUB_STEP_SUMMARY | |
| echo " - \`.exe\`: Inno Setup installer (recommended)" >> $GITHUB_STEP_SUMMARY | |
| echo " - \`.msi\`: Windows Installer package" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **macOS**: Intel and Apple Silicon" >> $GITHUB_STEP_SUMMARY | |
| echo " - \`amd64\`: Intel processors (x86_64)" >> $GITHUB_STEP_SUMMARY | |
| echo " - \`aarch64\`: Apple Silicon (M1/M2/M3)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "#### Important Notes" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- Files are hosted on AWS S3" >> $GITHUB_STEP_SUMMARY | |
| echo "- Bucket Policy must be configured for public access" >> $GITHUB_STEP_SUMMARY | |
| echo "- All files are code-signed (Windows: Authenticode, macOS: Apple Developer ID)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "#### Documentation" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- 📖 [Download Links Documentation](https://github.com/${{ github.repository }}/blob/main/docs/DOWNLOAD_LINKS_EXAMPLE.md)" >> $GITHUB_STEP_SUMMARY | |
| echo "- 🔧 [S3 Configuration Guide](https://github.com/${{ github.repository }}/blob/main/docs/S3_ACL_FIX.md)" >> $GITHUB_STEP_SUMMARY | |
| echo "- 🧪 [Local Testing Guide](https://github.com/${{ github.repository }}/blob/main/docs/S3_LOCAL_TEST_GUIDE.md)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| - name: Generate download links text artifact | |
| if: env.AWS_ACCESS_KEY_ID != 'NOT_SET' && env.S3_BUCKET != 'NOT_SET' && env.S3_BASE_PATH != 'NOT_SET' | |
| env: | |
| VERSION: ${{ needs.validate-tag.outputs.version }} | |
| BUCKET_NAME: ${{ secrets.S3_BUCKET || 'NOT_SET' }} | |
| BUCKET_REGION: ${{ secrets.AWS_REGION || 'NOT_SET' }} | |
| BUCKET_BASE_PATH: ${{ secrets.S3_BASE_PATH || 'NOT_SET' }} | |
| run: | | |
| set -euo pipefail | |
| OUTPUT_FILE="download-links-${VERSION}.txt" | |
| echo "eCan Download Links (Simulation)" > "$OUTPUT_FILE" | |
| echo "" >> "$OUTPUT_FILE" | |
| echo "Version: ${VERSION}" >> "$OUTPUT_FILE" | |
| echo "" >> "$OUTPUT_FILE" | |
| # Windows section | |
| if [ -d "windows-artifacts" ] && [ "$(ls -A windows-artifacts 2>/dev/null)" ]; then | |
| echo "[Windows]" >> "$OUTPUT_FILE" | |
| echo "" >> "$OUTPUT_FILE" | |
| for file in windows-artifacts/*.exe windows-artifacts/*.msi; do | |
| if [ -f "$file" ]; then | |
| filename=$(basename "$file") | |
| size=$(du -sh "$file" | cut -f1) | |
| standard_url="https://${BUCKET_NAME}.s3.${BUCKET_REGION}.amazonaws.com/${BUCKET_BASE_PATH}/v${VERSION}/windows/${filename}" | |
| accelerate_url="https://${BUCKET_NAME}.s3-accelerate.amazonaws.com/${BUCKET_BASE_PATH}/v${VERSION}/windows/${filename}" | |
| echo "${filename}" >> "$OUTPUT_FILE" | |
| echo " Size: ${size}" >> "$OUTPUT_FILE" | |
| echo " Standard: ${standard_url}" >> "$OUTPUT_FILE" | |
| echo " Accelerate: ${accelerate_url}" >> "$OUTPUT_FILE" | |
| echo "" >> "$OUTPUT_FILE" | |
| fi | |
| done | |
| echo "" >> "$OUTPUT_FILE" | |
| else | |
| echo "[Windows]" >> "$OUTPUT_FILE" | |
| echo " No Windows installers available" >> "$OUTPUT_FILE" | |
| echo "" >> "$OUTPUT_FILE" | |
| fi | |
| # macOS section | |
| if [ -d "macos-artifacts" ] && [ "$(ls -A macos-artifacts 2>/dev/null)" ]; then | |
| echo "[macOS]" >> "$OUTPUT_FILE" | |
| echo "" >> "$OUTPUT_FILE" | |
| for file in macos-artifacts/*.pkg macos-artifacts/*.zip; do | |
| if [ -f "$file" ]; then | |
| filename=$(basename "$file") | |
| size=$(du -sh "$file" | cut -f1) | |
| if [[ "$filename" == *"amd64"* ]] || [[ "$filename" == *"x86_64"* ]]; then | |
| arch_label="Intel (x86_64)" | |
| elif [[ "$filename" == *"aarch64"* ]] || [[ "$filename" == *"arm64"* ]]; then | |
| arch_label="Apple Silicon (ARM64)" | |
| else | |
| arch_label="Universal" | |
| fi | |
| standard_url="https://${BUCKET_NAME}.s3.${BUCKET_REGION}.amazonaws.com/${BUCKET_BASE_PATH}/v${VERSION}/macos/${filename}" | |
| accelerate_url="https://${BUCKET_NAME}.s3-accelerate.amazonaws.com/${BUCKET_BASE_PATH}/v${VERSION}/macos/${filename}" | |
| echo "${filename}" >> "$OUTPUT_FILE" | |
| echo " Arch: ${arch_label}" >> "$OUTPUT_FILE" | |
| echo " Size: ${size}" >> "$OUTPUT_FILE" | |
| echo " Standard: ${standard_url}" >> "$OUTPUT_FILE" | |
| echo " Accelerate: ${accelerate_url}" >> "$OUTPUT_FILE" | |
| echo "" >> "$OUTPUT_FILE" | |
| fi | |
| done | |
| echo "" >> "$OUTPUT_FILE" | |
| else | |
| echo "[macOS]" >> "$OUTPUT_FILE" | |
| echo " No macOS installers available" >> "$OUTPUT_FILE" | |
| echo "" >> "$OUTPUT_FILE" | |
| fi | |
| echo "---" >> "$OUTPUT_FILE" | |
| echo "This file contains all download URLs for this simulated build." >> "$OUTPUT_FILE" | |
| echo "Generated by the release simulation workflow at $(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> "$OUTPUT_FILE" | |
| echo "Download links document generated: ${OUTPUT_FILE}" | |
| - name: Upload download links document artifact | |
| if: env.AWS_ACCESS_KEY_ID != 'NOT_SET' && env.S3_BUCKET != 'NOT_SET' && env.S3_BASE_PATH != 'NOT_SET' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: download-links-${{ needs.validate-tag.outputs.version }} | |
| path: download-links-${{ needs.validate-tag.outputs.version }}.txt | |
| if-no-files-found: warn | |
| - name: Generate detailed summary | |
| run: | | |
| echo "## Release Flow Simulation Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Input Configuration" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Platform**: ${{ github.event.inputs.platform }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Architecture**: ${{ github.event.inputs.arch }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Version**: ${{ needs.validate-tag.outputs.version }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Job Execution Results" >> $GITHUB_STEP_SUMMARY | |
| echo "- **validate-tag**: ${{ needs.validate-tag.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **build-windows**: ${{ needs.build-windows.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **build-macos**: ${{ needs.build-macos.result }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **upload-to-s3**: success" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Artifacts Generated" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| if [ "${{ needs.build-windows.result }}" = "success" ]; then | |
| echo "- ✅ Windows: \`eCan-windows-amd64-${{ needs.validate-tag.outputs.version }}-s3-transfer\`" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "- ⏭️ Windows: skipped" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| if [ "${{ needs.build-macos.result }}" = "success" ]; then | |
| echo "- ✅ macOS amd64: \`eCan-macos-amd64-${{ needs.validate-tag.outputs.version }}-s3-transfer\`" >> $GITHUB_STEP_SUMMARY | |
| echo "- ✅ macOS aarch64: \`eCan-macos-aarch64-${{ needs.validate-tag.outputs.version }}-s3-transfer\`" >> $GITHUB_STEP_SUMMARY | |
| echo " - Note: Some matrix items may be skipped based on arch input" >> $GITHUB_STEP_SUMMARY | |
| else | |
| echo "- ⏭️ macOS: skipped" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### Configuration" >> $GITHUB_STEP_SUMMARY | |
| echo "- **S3 Upload**: ${{ env.AWS_ACCESS_KEY_ID != 'NOT_SET' && 'Enabled' || 'Disabled (simulated)' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **Appcast Generation**: ${{ env.ED25519_PRIVATE_KEY != 'NOT_SET' && 'Enabled' || 'Disabled (simulated)' }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "---" >> $GITHUB_STEP_SUMMARY | |
| echo "✅ **Simulation completed successfully**" >> $GITHUB_STEP_SUMMARY | |
| # Console output | |
| echo "" | |
| echo "=== Release Flow Simulation Summary ===" | |
| echo "Version: ${{ needs.validate-tag.outputs.version }}" | |
| echo "Platform: ${{ github.event.inputs.platform }}" | |
| echo "Architecture: ${{ github.event.inputs.arch }}" | |
| echo "" | |
| echo "Job Results:" | |
| echo " - build-windows: ${{ needs.build-windows.result }}" | |
| echo " - build-macos: ${{ needs.build-macos.result }}" | |
| echo "" | |
| echo "✅ Simulation completed successfully" |