Skip to content

Release Flow Simulation (Current Architecture) #23

Release Flow Simulation (Current Architecture)

Release Flow Simulation (Current Architecture) #23

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