Release Flow Simulation (Current Architecture) #23
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) | |
| # ============================================================================ | |
| # Workflow Overview | |
| # ============================================================================ | |
| # This workflow simulates the release.yml flow for quick validation. | |
| # Fully synced with release.yml architecture for testing workflow logic. | |
| # | |
| # Architecture: | |
| # - validate-tag: Validates git ref and computes version | |
| # - build-windows: Simulates Windows amd64 build + code signing check | |
| # - build-macos: Matrix simulation for macOS (amd64 + aarch64) + signing/notarization checks | |
| # - upload-to-s3: Simulates S3 upload and OTA feed generation | |
| # | |
| # ============================================================================ | |
| # Purpose | |
| # ============================================================================ | |
| # - Quickly validate workflow flow logic (< 5 minutes) | |
| # - Test conditional logic correctness | |
| # - Verify artifact naming and passing | |
| # - Simulate code signing and notarization requirements | |
| # - No actual build execution, only creates simulated artifacts | |
| # | |
| # Note: This simulation does not include: | |
| # - Inno Setup installation or language pack processing | |
| # - Actual code signing (certificates not required) | |
| # - Actual Apple notarization (credentials not required) | |
| # - External OTA frameworks (using self-contained OTA system) | |
| # | |
| # The actual release.yml uses .islu (Unicode) language files exclusively | |
| # and ENFORCES code signing and notarization for production/staging builds. | |
| # | |
| # ============================================================================ | |
| # 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 | |
| environment: | |
| description: 'Target environment' | |
| required: false | |
| default: 'production' | |
| type: choice | |
| options: | |
| - production | |
| - staging | |
| - test | |
| - development | |
| channel: | |
| description: 'Release channel' | |
| required: false | |
| default: 'stable' | |
| type: choice | |
| options: | |
| - stable | |
| - beta | |
| 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: | |
| # ============================================================================ | |
| # VALIDATION LAYER: Tag and Version Validation | |
| # ============================================================================ | |
| validate-tag: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag-valid: ${{ steps.out.outputs.valid }} | |
| version: ${{ steps.out.outputs.version }} | |
| environment: ${{ steps.detect-env.outputs.environment }} | |
| channel: ${{ steps.detect-env.outputs.channel }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - id: out | |
| run: | | |
| echo "valid=true" >> $GITHUB_OUTPUT | |
| # Read version from VERSION file | |
| if [ -f VERSION ]; then | |
| VERSION=$(cat VERSION | tr -d '[:space:]') | |
| echo "version=${VERSION}-sim" >> $GITHUB_OUTPUT | |
| echo "Version from VERSION file: ${VERSION}-sim" | |
| else | |
| echo "version=0.0.0-sim" >> $GITHUB_OUTPUT | |
| echo "VERSION file not found, using default: 0.0.0-sim" | |
| fi | |
| - name: Detect environment and channel | |
| id: detect-env | |
| shell: bash | |
| run: | | |
| INPUT_ENV="${{ github.event.inputs.environment }}" | |
| INPUT_CHANNEL="${{ github.event.inputs.channel }}" | |
| # Use manual selection or default | |
| if [[ -n "$INPUT_ENV" ]]; then | |
| ENV="$INPUT_ENV" | |
| else | |
| ENV="production" | |
| fi | |
| if [[ -n "$INPUT_CHANNEL" ]]; then | |
| CHANNEL="$INPUT_CHANNEL" | |
| else | |
| CHANNEL="stable" | |
| fi | |
| echo "environment=$ENV" >> $GITHUB_OUTPUT | |
| echo "channel=$CHANNEL" >> $GITHUB_OUTPUT | |
| echo "Simulation environment: $ENV" | |
| echo "Simulation channel: $CHANNEL" | |
| # ============================================================================ | |
| # BUILD LAYER: Windows amd64 (Simulation) | |
| # ============================================================================ | |
| 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 | |
| # Create fake installer | |
| echo "fake windows installer" > "dist/eCan-${VERSION}-windows-${BUILD_ARCH}-Setup.exe" | |
| cp "dist/eCan-${VERSION}-windows-${BUILD_ARCH}-Setup.exe" artifacts/ | |
| # Create fake signature file | |
| echo "fake_ed25519_signature_for_simulation" > "dist/eCan-${VERSION}-windows-${BUILD_ARCH}-Setup.exe.sig" | |
| cp "dist/eCan-${VERSION}-windows-${BUILD_ARCH}-Setup.exe.sig" artifacts/ | |
| echo "✅ Windows build simulated (with signature)" | |
| # ------------------------------------------------------------------------ | |
| # OTA Signing Simulation | |
| # In real workflow, OTA signing is REQUIRED for test/staging/production | |
| # ------------------------------------------------------------------------ | |
| - name: Simulate OTA signing check (test/staging/production requirement) | |
| if: | | |
| needs.validate-tag.outputs.environment == 'test' || | |
| needs.validate-tag.outputs.environment == 'staging' || | |
| needs.validate-tag.outputs.environment == 'production' | |
| run: | | |
| echo "=== Simulating OTA Signing Requirement Check ===" | |
| echo "Environment: ${{ needs.validate-tag.outputs.environment }}" | |
| echo "In real workflow: Ed25519 private key REQUIRED" | |
| echo " - build_system/certificates/ed25519_private_key.pem" | |
| echo "✅ Simulation: Skipping actual OTA signing check" | |
| - name: Simulate OTA signing execution | |
| run: | | |
| echo "=== Simulating OTA Signing ===" | |
| echo "In real workflow: Ed25519 signature generation" | |
| echo " - Creates .sig files for installers" | |
| echo " - Generates signatures.json" | |
| echo "✅ Simulation: OTA signing skipped" | |
| # ------------------------------------------------------------------------ | |
| # Code Signing Simulation | |
| # In real workflow, Authenticode signing is REQUIRED for production/staging | |
| # ------------------------------------------------------------------------ | |
| - name: Simulate code signing check (production/staging requirement) | |
| if: | | |
| needs.validate-tag.outputs.environment == 'production' || | |
| needs.validate-tag.outputs.environment == 'staging' | |
| run: | | |
| echo "=== Simulating Code Signing Requirement Check ===" | |
| echo "Environment: ${{ needs.validate-tag.outputs.environment }}" | |
| echo "In real workflow: Authenticode certificate REQUIRED" | |
| echo " - WIN_CERT_PFX" | |
| echo " - WIN_CERT_PASSWORD" | |
| echo "✅ Simulation: Skipping actual certificate check" | |
| - name: Simulate code signing execution | |
| run: | | |
| echo "=== Simulating Code Signing ===" | |
| echo "In real workflow: signtool sign /f certificate.pfx /fd SHA256" | |
| echo "✅ Simulation: Code signing skipped" | |
| - 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 LAYER: macOS (Matrix: amd64 + aarch64) (Simulation) | |
| # ============================================================================ | |
| 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-15-intel # Intel x86_64 runner (newer than macos-13) | |
| 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: | |
| # ------------------------------------------------------------------------ | |
| # Step 0: Architecture Filtering (MUST BE FIRST - No dependencies) | |
| # ------------------------------------------------------------------------ | |
| - name: Check if this architecture should be built | |
| id: arch_check | |
| run: | | |
| ARCH="${{ matrix.arch }}" | |
| INPUT="${{ github.event.inputs.arch }}" | |
| echo "Matrix architecture: $ARCH" | |
| echo "Requested architecture: $INPUT" | |
| if [ "$INPUT" = "all" ] || [ -z "$INPUT" ] || [ "$INPUT" = "$ARCH" ]; then | |
| echo "✅ This architecture will be built" | |
| echo "should_build=true" >> $GITHUB_OUTPUT | |
| else | |
| echo "⏭️ Skipping this architecture (not requested)" | |
| echo "should_build=false" >> $GITHUB_OUTPUT | |
| exit 0 | |
| fi | |
| # Early exit if architecture not needed - saves all setup time | |
| - name: Exit early if architecture not needed | |
| if: steps.arch_check.outputs.should_build != 'true' | |
| run: | | |
| echo "Architecture ${{ matrix.arch }} not needed for this build" | |
| echo "Exiting early to save resources" | |
| exit 0 | |
| # Note: Frontend is always built in real workflow (no change detection) | |
| # Simulation doesn't need frontend build steps | |
| - name: Simulate macOS build | |
| if: steps.arch_check.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 | |
| # Create fake installer | |
| echo "fake macos installer" > "dist/eCan-${VERSION}-macos-${ARCH}.pkg" | |
| cp "dist/eCan-${VERSION}-macos-${ARCH}.pkg" artifacts/ | |
| # Create fake signature file | |
| echo "fake_ed25519_signature_for_simulation" > "dist/eCan-${VERSION}-macos-${ARCH}.pkg.sig" | |
| cp "dist/eCan-${VERSION}-macos-${ARCH}.pkg.sig" artifacts/ | |
| echo "✅ macOS ${{ matrix.arch }} build simulated (with signature)" | |
| # ------------------------------------------------------------------------ | |
| # OTA Signing Simulation | |
| # In real workflow, OTA signing is REQUIRED for test/staging/production | |
| # ------------------------------------------------------------------------ | |
| - name: Simulate OTA signing check (test/staging/production requirement) | |
| if: | | |
| steps.arch_check.outputs.should_build == 'true' && | |
| (needs.validate-tag.outputs.environment == 'test' || | |
| needs.validate-tag.outputs.environment == 'staging' || | |
| needs.validate-tag.outputs.environment == 'production') | |
| run: | | |
| echo "=== Simulating OTA Signing Requirement Check ===" | |
| echo "Environment: ${{ needs.validate-tag.outputs.environment }}" | |
| echo "In real workflow: Ed25519 private key REQUIRED" | |
| echo " - build_system/certificates/ed25519_private_key.pem" | |
| echo "✅ Simulation: Skipping actual OTA signing check" | |
| - name: Simulate OTA signing execution | |
| if: steps.arch_check.outputs.should_build == 'true' | |
| run: | | |
| echo "=== Simulating OTA Signing ===" | |
| echo "In real workflow: Ed25519 signature generation" | |
| echo " - Creates .sig files for installers" | |
| echo " - Generates signatures.json" | |
| echo "✅ Simulation: OTA signing skipped" | |
| # ------------------------------------------------------------------------ | |
| # Code Signing and Notarization Simulation | |
| # In real workflow, these are REQUIRED for production/staging | |
| # ------------------------------------------------------------------------ | |
| - name: Simulate code signing check (production/staging requirement) | |
| if: | | |
| steps.arch_check.outputs.should_build == 'true' && | |
| (needs.validate-tag.outputs.environment == 'production' || | |
| needs.validate-tag.outputs.environment == 'staging') | |
| run: | | |
| echo "=== Simulating Code Signing Requirement Check ===" | |
| echo "Environment: ${{ needs.validate-tag.outputs.environment }}" | |
| echo "In real workflow: Code signing certificates REQUIRED" | |
| echo " - MAC_CERT_P12" | |
| echo " - MAC_CERT_PASSWORD" | |
| echo " - MAC_CODESIGN_IDENTITY" | |
| echo "✅ Simulation: Skipping actual certificate check" | |
| - name: Simulate code signing execution | |
| if: steps.arch_check.outputs.should_build == 'true' | |
| run: | | |
| echo "=== Simulating Code Signing ===" | |
| echo "In real workflow: codesign --deep --force --options runtime" | |
| echo "✅ Simulation: Code signing skipped" | |
| - name: Simulate notarization check (production/staging requirement) | |
| if: | | |
| steps.arch_check.outputs.should_build == 'true' && | |
| (needs.validate-tag.outputs.environment == 'production' || | |
| needs.validate-tag.outputs.environment == 'staging') | |
| run: | | |
| echo "=== Simulating Notarization Requirement Check ===" | |
| echo "Environment: ${{ needs.validate-tag.outputs.environment }}" | |
| echo "In real workflow: Notarization REQUIRED" | |
| echo " - APPLE_ID" | |
| echo " - APPLE_APP_SPECIFIC_PASSWORD" | |
| echo " - TEAM_ID" | |
| echo "✅ Simulation: Skipping actual notarization check" | |
| - name: Simulate notarization execution | |
| if: steps.arch_check.outputs.should_build == 'true' | |
| run: | | |
| echo "=== Simulating Notarization ===" | |
| echo "In real workflow: xcrun notarytool submit --wait" | |
| echo "In real workflow: xcrun stapler staple" | |
| echo "✅ Simulation: Notarization skipped" | |
| - name: Upload macOS artifacts for S3 transfer (always, short retention) | |
| if: steps.arch_check.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.arch_check.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 | |
| # ============================================================================ | |
| # DISTRIBUTION LAYER: S3 Upload and OTA Feed Generation (Simulation) | |
| # ============================================================================ | |
| # This layer uses reusable workflows (same as release.yml) in simulation mode: | |
| # 1. upload-to-s3: Simulates S3 upload (shared-s3-upload.yml) | |
| # 2. generate-appcast: Simulates appcast generation (shared-appcast-generation.yml) | |
| # 3. generate-download-links: Simulates download links (shared-download-links.yml) | |
| # ============================================================================ | |
| # ---------------------------------------------------------------------------- | |
| # Step 1: Upload artifacts to S3 (Simulation) | |
| # ---------------------------------------------------------------------------- | |
| upload-to-s3: | |
| name: Upload to S3 (Simulation) | |
| 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') | |
| uses: ./.github/workflows/shared-s3-upload.yml | |
| with: | |
| version: ${{ needs.validate-tag.outputs.version }} | |
| environment: ${{ needs.validate-tag.outputs.environment }} | |
| windows-build-result: ${{ needs.build-windows.result }} | |
| macos-build-result: ${{ needs.build-macos.result }} | |
| secrets: inherit | |
| # ---------------------------------------------------------------------------- | |
| # Step 2: Generate OTA update feeds (Simulation) | |
| # ---------------------------------------------------------------------------- | |
| generate-appcast: | |
| name: Generate Appcast (Simulation) | |
| needs: [validate-tag, upload-to-s3] | |
| if: | | |
| always() && | |
| needs.validate-tag.outputs.tag-valid == 'true' && | |
| needs.upload-to-s3.result == 'success' | |
| uses: ./.github/workflows/shared-appcast-generation.yml | |
| with: | |
| environment: ${{ needs.validate-tag.outputs.environment }} | |
| channel: ${{ needs.validate-tag.outputs.channel }} | |
| secrets: inherit | |
| # ---------------------------------------------------------------------------- | |
| # Step 3: Generate download links (Simulation) | |
| # ---------------------------------------------------------------------------- | |
| generate-download-links: | |
| name: Generate Download Links (Simulation) | |
| needs: [validate-tag, build-windows, build-macos, upload-to-s3] | |
| if: | | |
| always() && | |
| needs.validate-tag.outputs.tag-valid == 'true' && | |
| needs.upload-to-s3.result == 'success' | |
| uses: ./.github/workflows/shared-download-links.yml | |
| with: | |
| version: ${{ needs.validate-tag.outputs.version }} | |
| windows-build-result: ${{ needs.build-windows.result }} | |
| macos-build-result: ${{ needs.build-macos.result }} | |
| macos-built-amd64: ${{ (github.event.inputs.platform == 'macos' || github.event.inputs.platform == 'all') && (github.event.inputs.arch == 'amd64' || github.event.inputs.arch == 'all') }} | |
| macos-built-aarch64: ${{ (github.event.inputs.platform == 'macos' || github.event.inputs.platform == 'all') && (github.event.inputs.arch == 'aarch64' || github.event.inputs.arch == 'all') }} | |
| secrets: inherit | |
| # ---------------------------------------------------------------------------- | |
| # Step 4: Final status summary (Simulation) | |
| # ---------------------------------------------------------------------------- | |
| final-status: | |
| name: Final Status Summary (Simulation) | |
| needs: [validate-tag, build-windows, build-macos, upload-to-s3, generate-appcast, generate-download-links] | |
| if: always() | |
| uses: ./.github/workflows/shared-final-status.yml | |
| with: | |
| version: ${{ needs.validate-tag.outputs.version }} | |
| windows-result: ${{ needs.build-windows.result }} | |
| macos-result: ${{ needs.build-macos.result }} | |
| upload-result: ${{ needs.upload-to-s3.result }} | |
| appcast-result: ${{ needs.generate-appcast.result }} | |
| links-result: ${{ needs.generate-download-links.result }} | |
| is-simulation: true | |
| secrets: inherit |