diff --git a/.github/workflows/sparrow-appinstaller-prod.yaml b/.github/workflows/sparrow-appinstaller-prod.yaml new file mode 100644 index 0000000000..07fd9c8259 --- /dev/null +++ b/.github/workflows/sparrow-appinstaller-prod.yaml @@ -0,0 +1,617 @@ +name: Production Desktop Windows AppInstaller Release + +on: + push: + branches: + - main + workflow_dispatch: + +env: + VITE_API_URL: ${{vars.VITE_API_URL}} + VITE_MIX_PANEL_TOKEN: ${{vars.VITE_MIX_PANEL_TOKEN}} + VITE_ENABLE_MIX_PANEL: ${{vars.VITE_ENABLE_MIX_PANEL}} + VITE_API_TIMEOUT: ${{vars.VITE_API_TIMEOUT}} + VITE_SPARROW_SUPPORT_EMAIL: ${{ vars.VITE_SPARROW_SUPPORT_EMAIL }} + VITE_AUTH_URL: ${{ vars.VITE_AUTH_URL }} + VITE_SPARROW_GITHUB: ${{ vars.VITE_SPARROW_GITHUB }} + VITE_SPARROW_DOWNLOAD_LINK: ${{ vars.VITE_SPARROW_DOWNLOAD_LINK }} + VITE_RELEASE_NOTES_PAT_TOKEN: ${{ secrets.VITE_RELEASE_NOTES_PAT_TOKEN }} + VITE_RELEASE_NOTES_API: ${{ vars.VITE_RELEASE_NOTES_API }} + VITE_AZURE_CDN_URL: ${{ vars.VITE_AZURE_CDN_URL }} + VITE_AZURE_INSIGHTS_CONNECTION_STRING: ${{ vars.VITE_AZURE_INSIGHTS_CONNECTION_STRING }} + VITE_CANNY_API: ${{ vars.VITE_CANNY_API }} + VITE_CANNY_URL: ${{ vars.VITE_CANNY_URL }} + VITE_BASE_URL: ${{ vars.VITE_BASE_URL }} + VITE_SPARROW_LINKEDIN: ${{ vars.VITE_SPARROW_LINKEDIN }} + VITE_WEB_SOCKET_IO_API_URL: ${{ vars.VITE_WEB_SOCKET_IO_API_URL }} + VITE_SPARROW_DOCS: ${{ vars.VITE_SPARROW_DOCS }} + VITE_SPARROW_AI_WEBSOCKET: ${{ vars.VITE_SPARROW_AI_WEBSOCKET }} + VITE_APP_ENVIRONMENT_PATH: ${{ vars.VITE_APP_ENVIRONMENT_PATH }} + VITE_CANNY_FEEDBACK_URL: ${{ vars.VITE_CANNY_FEEDBACK_URL }} + VITE_SPARROW_WEB_APP_URL: ${{ vars.VITE_SPARROW_WEB_APP_URL }} + VITE_MARKETING_URL: ${{ vars.VITE_MARKETING_URL }} + VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }} + VITE_APP_ENVIRONMENT: ${{ vars.VITE_APP_ENVIRONMENT }} + VITE_POSTHOG_CONNECTION_API_KEY: ${{ vars.VITE_POSTHOG_CONNECTION_API_KEY }} + VITE_POSTHOG_API_URL: ${{ vars.VITE_POSTHOG_API_URL }} + VITE_SPARROW_ADMIN_URL: ${{ vars.VITE_SPARROW_ADMIN_URL }} + ACTIONS_ALLOW_UNSECURE_COMMANDS: true + CI: false + +jobs: + release_win: + runs-on: windows-latest + environment: production + + steps: + # --- STAGE 1: INITIAL SETUP AND PREREQUISITE INSTALLATIONS --- + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: "20.8" + + - name: Install Rust 1.82.0 + run: | + Invoke-WebRequest -Uri https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe -OutFile rustup-init.exe + .\rustup-init.exe -y + rustup install 1.82.0 + rustup default 1.82.0 + + # Set up DigiCert certificate for cloud signing + - name: Set up DigiCert certificate + shell: pwsh + run: | + Write-Host "Decoding DigiCert .p12 into D:\Certificate_pkcs12.p12" + $bytes = [Convert]::FromBase64String("${{ secrets.SM_CLIENT_CERT_FILE_B64 }}") + [IO.File]::WriteAllBytes("D:\Certificate_pkcs12.p12", $bytes) + + # Set environment variables for DigiCert + - name: Set variables + id: variables + run: | + echo "SM_HOST=${{ secrets.SM_HOST }}" >> "$GITHUB_ENV" + echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> "$GITHUB_ENV" + echo "SM_CLIENT_CERT_FILE=D:\\Certificate_pkcs12.p12" >> "$GITHUB_ENV" + echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> "$GITHUB_ENV" + shell: bash + + # Setup Android SDK (required by DigiCert action for compatibility) + - name: Setup Android SDK (for DigiCert action compatibility) + run: | + # Create minimal Android SDK structure to satisfy DigiCert action requirements + $androidSdkPath = "C:\Android\android-sdk" + $buildToolsPath = "$androidSdkPath\build-tools\30.0.2" + + Write-Host "Creating Android SDK directory structure..." -ForegroundColor Yellow + New-Item -ItemType Directory -Path $buildToolsPath -Force + + # Create dummy files that the DigiCert action expects to find + Write-Host "Creating required Android build tool files..." -ForegroundColor Yellow + New-Item -ItemType File -Path "$buildToolsPath\apksigner.bat" -Force + New-Item -ItemType File -Path "$buildToolsPath\zipalign.exe" -Force + New-Item -ItemType File -Path "$buildToolsPath\aapt.exe" -Force + New-Item -ItemType File -Path "$buildToolsPath\aapt2.exe" -Force + + # Set Android environment variables + echo "ANDROID_HOME=$androidSdkPath" >> $env:GITHUB_ENV + echo "ANDROID_SDK_ROOT=$androidSdkPath" >> $env:GITHUB_ENV + + Write-Host "Android SDK structure created successfully" -ForegroundColor Green + shell: powershell + + # Install DigiCert Signing Manager Tools + - name: Install DigiCert Signing Manager Tools + uses: digicert/ssm-code-signing@v1.0.0 + + - name: Extract first KeyPair alias + id: extract_alias + shell: pwsh + run: | + $line = (smctl keypair ls | Select-Object -Skip 2 | Select-Object -First 1) + $alias = ($line -split '\s{2,}')[2] + Write-Host "✅ Using alias: $alias" + "KEYPAIR_ALIAS=$alias" | Out-File -FilePath $env:GITHUB_ENV -Encoding ascii + + - name: Sync Certificate into Windows Store + shell: pwsh + run: | + Write-Host "🔄 Syncing cert for alias: $($env:KEYPAIR_ALIAS)" + smctl windows certsync ` + --keypair-alias="$($env:KEYPAIR_ALIAS)" ` + --store=system + + # --- STAGE 2: BUILD APP --- + - name: Disable updater in tauri.conf.json for AppInstaller Build + run: | + $confPath = "apps/@sparrow-desktop/src-tauri/tauri.conf.json" + $json = Get-Content $confPath -Raw | ConvertFrom-Json + if ($null -ne $json.plugins -and $null -ne $json.plugins.updater) { + if (-not ($json.plugins.updater.PSObject.Properties.Name -contains "active")) { + $json.plugins.updater | Add-Member -NotePropertyName "active" -NotePropertyValue $false + } + elseif ($json.plugins.updater.active -ne $false) { + # enforce correct value in case it exists but is true + $json.plugins.updater.active = $false + } + $json | ConvertTo-Json -Depth 20 | Set-Content $confPath -Encoding UTF8 + } + shell: pwsh + + - name: Change bundle.targets to an array that contains only "msi" + run: | + $confPath = "apps/@sparrow-desktop/src-tauri/tauri.conf.json" + $json = Get-Content $confPath -Raw | ConvertFrom-Json + if ($null -ne $json.bundle -and $null -ne $json.bundle.targets) { + $json.bundle.targets = @("msi") + $json | ConvertTo-Json -Depth 20 | Set-Content $confPath -Encoding UTF8 + } + + # Print the updated tauri.conf.json + Write-Host "Updated tauri.conf.json:" + Get-Content $confPath + shell: pwsh + + - name: Increase Yarn network timeout + run: yarn config set network-timeout 600000 + + - name: Update @tauri-apps/* versions in desktop package.json from root + run: | + $rootPkg = Get-Content package.json | ConvertFrom-Json + $desktopPkgPath = "apps/@sparrow-desktop/package.json" + $desktopPkg = Get-Content $desktopPkgPath | ConvertFrom-Json + $tauriPackages = @( + "@tauri-apps/api", + "@tauri-apps/cli", + "@tauri-apps/plugin-deep-link", + "@tauri-apps/plugin-dialog", + "@tauri-apps/plugin-fs", + "@tauri-apps/plugin-os", + "@tauri-apps/plugin-process", + "@tauri-apps/plugin-shell", + "@tauri-apps/plugin-updater" + ) + foreach ($pkg in $tauriPackages) { + if ($rootPkg.dependencies.PSObject.Properties.Name -contains $pkg) { + $desktopPkg.dependencies.$pkg = $rootPkg.dependencies.$pkg + } + } + $desktopPkg | ConvertTo-Json -Depth 20 | Set-Content $desktopPkgPath + shell: pwsh + + - name: Build Tauri App + run: | + yarn cache clean + npm install -g pnpm + yarn install + yarn desktop-build + env: + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + GITHUB_TOKEN: ${{ secrets.PR_GITHUB_TOKEN }} + + # --- STAGE 3: SIGN MSIX --- + - name: Verify preinstalled Windows SDK tools + shell: pwsh + run: | + Write-Host "🔎 Looking for signtool.exe and MakeAppx.exe..." + $signtool = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin' -Recurse -Filter signtool.exe | Where-Object FullName -Match '\\x64\\signtool\.exe$' | Select-Object -First 1 + $makeappx = Get-ChildItem 'C:\Program Files (x86)\Windows Kits\10\bin' -Recurse -Filter MakeAppx.exe | Where-Object FullName -Match '\\x64\\MakeAppx\.exe$' | Select-Object -First 1 + + if (-not $signtool) { throw "❌ signtool.exe not found!" } + if (-not $makeappx) { throw "❌ MakeAppx.exe not found!" } + + Write-Host "✅ signtool.exe found at $($signtool.FullName)" + Write-Host "✅ MakeAppx.exe found at $($makeappx.FullName)" + # Export paths for later steps + echo "SIGNTOOL_PATH=$($signtool.FullName)" >> $env:GITHUB_ENV + echo "MAKEAPPX_PATH=$($makeappx.FullName)" >> $env:GITHUB_ENV + + - name: Find and Sign MSI files + shell: pwsh + run: | + $certThumb = "${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}" + $bundlePath = "D:\a\sparrow-app\sparrow-app\apps\@sparrow-desktop\src-tauri\target\debug\bundle\msi" + + # List all files in the bundle directory for debugging + Write-Host "🔍 Contents of bundle directory:" + Get-ChildItem -Path $bundlePath -Recurse | Format-Table Name, FullName + + # Find all MSI files + $msiFiles = Get-ChildItem -Path $bundlePath -Filter "*.msi" + + if ($msiFiles.Count -eq 0) { + Write-Host "❌ No MSI files found in $bundlePath" + Write-Host "🔍 Checking parent directories..." + Get-ChildItem -Path "D:\a\sparrow-app\sparrow-app\apps\@sparrow-desktop\src-tauri\target" -Recurse -Filter "*.msi" | ForEach-Object { + Write-Host "Found MSI: $($_.FullName)" + } + exit 1 + } + + $signtoolPath = $env:SIGNTOOL_PATH.Trim() + # Sign each MSI file found + foreach ($msiFile in $msiFiles) { + Write-Host "🔐 Signing: $($msiFile.FullName)" + & "$signtoolPath" sign ` + /sm /s My ` + /tr http://timestamp.digicert.com ` + /td sha256 ` + /fd sha256 ` + /sha1 $certThumb ` + "$($msiFile.FullName)" + + if ($LASTEXITCODE -ne 0) { + Write-Host "❌ Failed to sign $($msiFile.Name)" + exit 1 + } + + Write-Host "✅ Successfully signed $($msiFile.Name)" + } + + - name: Verify MSI Signature + shell: pwsh + run: | + $bundlePath = "D:\a\sparrow-app\sparrow-app\apps\@sparrow-desktop\src-tauri\target\debug\bundle\msi" + + $allValid = $true + + $files = Get-ChildItem -Path $bundlePath -Include "*.msi" -Recurse + + foreach ($file in $files) { + $sig = Get-AuthenticodeSignature $file.FullName + + if ($sig.Status -ne 'Valid') { + Write-Host "❌ Invalid signature on $($file.Name): $($sig.Status)" + $allValid = $false + } else { + Write-Host "✅ Valid signature on $($file.Name)" + } + } + + if (-not $allValid) { + throw "One or more MSI files have invalid signatures" + } + + Write-Host "🎉 MSI files successfully signed and verified" + + # --- STAGE 4: CONVERT TO MSIX USING MAKEAPPX --- + - name: Prepare App Folder for MakeAppx + run: | + New-Item -ItemType Directory -Force -Path "app-root/Assets" + $msiPath = Get-ChildItem -Path "apps/@sparrow-desktop/src-tauri/target/debug/bundle/msi" -Filter "Sparrow_*_x64_en-US.msi" | Select-Object -First 1 + $extractPath = "app-root" + Start-Process msiexec.exe -ArgumentList "/a `"$msiPath`" /qn TARGETDIR=`"$extractPath`"" -Wait -NoNewWindow + #Copy app executable (MakeAppx requires it) + $exeSource = Get-ChildItem -Path "apps/@sparrow-desktop/src-tauri/target/debug" -Filter "sparrow-app.exe" -Recurse | Select-Object -First 1 + if ($exeSource) { + Copy-Item $exeSource.FullName -Destination "app-root/Sparrow.exe" + } else { + Write-Host "::error::Sparrow.exe not found! Expected path: apps/@sparrow-desktop/src-tauri/target/debug/sparrow-app.exe" + exit 1 + } + # Copy actual StoreLogo from known path + $logo = "apps/@sparrow-desktop/src-tauri/icons/StoreLogo.png" + Copy-Item $logo -Destination "app-root/Assets/StoreLogo.png" + Copy-Item $logo -Destination "app-root/Assets/Square150x150Logo.png" + Copy-Item $logo -Destination "app-root/Assets/Square44x44Logo.png" + Copy-Item $logo -Destination "app-root/Assets/SplashScreen.png" + shell: powershell + + - name: Generate AppxManifest.xml with version injection + shell: pwsh + run: | + # Debug: Check current working directory + Write-Host "🔍 Current working directory: $(Get-Location)" + + # Ensure app-root directory exists + $appRootPath = "app-root" + if (-not (Test-Path $appRootPath)) { + Write-Host "📁 Creating app-root directory..." + New-Item -ItemType Directory -Path $appRootPath -Force + } + + Write-Host "📁 Contents of app-root directory before manifest generation:" + Get-ChildItem -Path $appRootPath | ForEach-Object { Write-Host " - $($_.Name)" } + # 1. Read version from Cargo.toml + $cargoFile = "apps/@sparrow-desktop/src-tauri/Cargo.toml" + Write-Host "🔍 Reading version from: $cargoFile" + + if (-not (Test-Path $cargoFile)) { + Write-Error "❌ Cargo.toml not found at: $cargoFile" + exit 1 + } + + $versionLine = Select-String -Path $cargoFile -Pattern '^\s*version\s*=\s*"(.*?)"' | Select-Object -First 1 + if (-not $versionLine) { + Write-Error "❌ Version not found in Cargo.toml" + exit 1 + } + + $version = $versionLine.Matches.Groups[1].Value + Write-Host "✅ Found version: $version" + # 2. Lookup our synced cert by thumbprint and get full Subject DN + $thumb = "${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}" + Write-Host "🔍 Looking for certificate with thumbprint: $thumb" + + $cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object Thumbprint -eq $thumb + if (-not $cert) { + Write-Error "❌ Certificate not found with thumbprint: $thumb" + Write-Host "Available certificates:" + Get-ChildItem Cert:\LocalMachine\My | ForEach-Object { + Write-Host " - Thumbprint: $($_.Thumbprint), Subject: $($_.Subject)" + } + exit 1 + } + + $publisherDN = $cert.Subject + Write-Host "✅ Using Publisher DN: $publisherDN" + # 3. Build the AppxManifest.xml with the exact Publisher + $manifest = @" + + + + + Sparrow + sparrowapp + Sparrow + Assets\StoreLogo.png + + + + + + + + + + + + + + + + + + "@ + # 4. Write out the manifest + $manifestPath = Join-Path $appRootPath "AppxManifest.xml" + $manifest | Set-Content -Encoding UTF8 -Path $manifestPath + Write-Host "✅ Generated AppxManifest.xml at: $manifestPath" + + # Verify the file was created + if (Test-Path $manifestPath) { + Write-Host "✅ AppxManifest.xml created successfully" + Write-Host "📄 File size: $((Get-Item $manifestPath).Length) bytes" + } else { + Write-Error "❌ Failed to create AppxManifest.xml" + exit 1 + } + + - name: Create MSIX Package using MakeAppx + run: | + New-Item -ItemType Directory -Force -Path "msix-output" + $msixPath = "C:\a\sparrow-app\sparrow-app\msix-output\sparrow.msix" + $makeappxPath = $env:MAKEAPPX_PATH.Trim() + & "$makeappxPath" pack /d "app-root" /p $msixPath + shell: powershell + + # --- STAGE 5: SIGN AND VERIFY MSIX --- + - name: Find and Sign MSIX files + shell: pwsh + run: | + # 1. Sync certificate into Windows store + smctl windows certsync --keypair-alias="$env:KEYPAIR_ALIAS" + # 2. Sign all MSIX packages + $certThumb = "${{ secrets.SM_CODE_SIGNING_CERT_SHA1_HASH }}" + Get-ChildItem -Path "C:\a\sparrow-app\sparrow-app\msix-output" -Filter "*.msix" | ForEach-Object { + Write-Host "🔐 Signing: $($_.FullName)" + + & "$env:SIGNTOOL_PATH" sign /v /debug /sm /sha1 $certThumb /td SHA256 /fd sha256 /tr http://timestamp.digicert.com "$($_.FullName)" + + if ($LASTEXITCODE -ne 0) { + Write-Host "❌ Failed to sign $($_.Name)" + exit 1 + } + Write-Host "✅ Successfully signed $($_.Name)" + } + + - name: Verify MSIX Signature + run: | + # Verify the MSIX file exists + if (-Not (Test-Path C:\a\sparrow-app\sparrow-app\msix-output\sparrow.msix)) { + Write-Host "MSIX file not found at msix-output/sparrow.msix" + exit 1 + } + Write-Host "Verifying signature for: C:\a\sparrow-app\sparrow-app\msix-output\sparrow.msix" + # Get the Authenticode signature + $sig = Get-AuthenticodeSignature -FilePath C:\a\sparrow-app\sparrow-app\msix-output\sparrow.msix + # Check the signature status + if ($sig.Status -eq 'Valid') { + Write-Host "Valid signature on C:\a\sparrow-app\sparrow-app\msix-output\sparrow.msix" + } else { + Write-Host "Invalid signature on C:\a\sparrow-app\sparrow-app\msix-output\sparrow.msix: $($sig.Status)" + exit 1 + } + shell: powershell + + # --- STAGE 6: GENERATE APPINSTALLER FILE --- + - name: Generate App Installer File + id: generate_appinstaller + shell: pwsh + run: | + # Debug: Check current working directory and list contents + Write-Host "🔍 Current working directory: $(Get-Location)" + Write-Host "📁 Contents of current directory:" + Get-ChildItem -Path "." | ForEach-Object { Write-Host " - $($_.Name)" } + + # Debug: Check if app-root directory exists and list its contents + $appRootPath = "app-root" + if (Test-Path $appRootPath) { + Write-Host "📁 Contents of app-root directory:" + Get-ChildItem -Path $appRootPath | ForEach-Object { Write-Host " - $($_.Name)" } + } else { + Write-Host "❌ app-root directory does not exist!" + Write-Host "🔍 Looking for app-root in absolute path..." + $absoluteAppRoot = "C:\a\sparrow-app\sparrow-app\app-root" + if (Test-Path $absoluteAppRoot) { + Write-Host "✅ Found app-root at absolute path: $absoluteAppRoot" + $appRootPath = $absoluteAppRoot + } else { + Write-Host "❌ app-root not found at absolute path either!" + # Search for AppxManifest.xml anywhere in the workspace + Write-Host "🔍 Searching for AppxManifest.xml files in workspace..." + Get-ChildItem -Path "C:\a\sparrow-app\sparrow-app" -Recurse -Filter "AppxManifest.xml" -ErrorAction SilentlyContinue | ForEach-Object { + Write-Host "Found AppxManifest.xml at: $($_.FullName)" + } + exit 1 + } + } + + $manifestPath = Join-Path $appRootPath "AppxManifest.xml" + Write-Host "🔍 Looking for manifest at: $manifestPath" + + if (-not (Test-Path $manifestPath)) { + Write-Error "❌ AppxManifest.xml not found at: $manifestPath" + Write-Host "🔍 Contents of $appRootPath directory:" + Get-ChildItem -Path $appRootPath -Force | ForEach-Object { Write-Host " - $($_.Name)" } + exit 1 + } + + Write-Host "✅ Found AppxManifest.xml at: $manifestPath" + + # Read and parse the manifest + [xml]$manifest = Get-Content $manifestPath + $identityName = $manifest.Package.Identity.Name + $publisher = $manifest.Package.Identity.Publisher + $version = $manifest.Package.Identity.Version + $architecture = $manifest.Package.Identity.ProcessorArchitecture + Write-Host "📋 Manifest Info:" + Write-Host " - Name: $identityName" + Write-Host " - Publisher: $publisher" + Write-Host " - Version: $version" + Write-Host " - Architecture: $architecture" + # Find the MSIX file + $msixOutputPath = "C:\a\sparrow-app\sparrow-app\msix-output" + Write-Host "🔍 Looking for MSIX files in: $msixOutputPath" + + if (Test-Path $msixOutputPath) { + Write-Host "📁 Contents of msix-output directory:" + Get-ChildItem -Path $msixOutputPath | ForEach-Object { Write-Host " - $($_.Name)" } + } else { + Write-Host "❌ Directory does not exist: $msixOutputPath" + # Try relative path + if (Test-Path "msix-output") { + Write-Host "✅ Found msix-output using relative path" + $msixOutputPath = "msix-output" + } else { + Write-Host "❌ msix-output directory not found anywhere!" + exit 1 + } + } + + $msixFile = Get-ChildItem -Path $msixOutputPath -Filter "*.msix" | Select-Object -First 1 + if (-not $msixFile) { + Write-Error "❌ No MSIX file found in $msixOutputPath folder!" + exit 1 + } + + $msixBaseName = [System.IO.Path]::GetFileName($msixFile.Name) + Write-Host "✅ Found MSIX file: $msixBaseName" + + # Copy MSIX to root for artifact upload and S3 upload + Copy-Item -Path $msixFile.FullName -Destination ".\$msixBaseName" -Force + + # URLs for hosting on S3 + $msixUrl = "https://sparrow-release-assests-prod.s3.us-west-1.amazonaws.com/windows/$msixBaseName" + $appInstallerUrl = "https://sparrow-release-assests-prod.s3.us-west-1.amazonaws.com/windows/Sparrow.appinstaller" + $appInstallerContent = @" + + + + + + + + "@ + + Set-Content -Path "Sparrow.appinstaller" -Value $appInstallerContent -Encoding UTF8 + Write-Host "✅ Generated Sparrow.appinstaller successfully" + echo "MSIX_FILE_NAME=$msixBaseName" >> $env:GITHUB_ENV + + - name: Upload appinstaller and msix to S3 + shell: bash + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: us-east-1 + run: | + aws s3 cp Sparrow.appinstaller s3://sparrow-release-assests-prod/windows/Sparrow.appinstaller --acl public-read + aws s3 cp $MSIX_FILE_NAME s3://sparrow-release-assests-prod/windows/$MSIX_FILE_NAME --acl public-read + + - name: Post Windows App Installer Link to Teams + run: | + $appInstallerUrl = "https://sparrow-release-assests-prod.s3.us-west-1.amazonaws.com/windows/Sparrow.appinstaller" + $buildDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + $body = @{ + "type" = "message" + "attachments" = @( + @{ + "contentType" = "application/vnd.microsoft.card.adaptive" + "content" = @{ + "type" = "AdaptiveCard" + "body" = @( + @{ + "type" = "TextBlock" + "text" = "🚀 New Windows App Installer Available" + "weight" = "bolder" + "size" = "large" + } + @{ + "type" = "TextBlock" + "text" = "Branch: main" + "wrap" = $true + } + @{ + "type" = "TextBlock" + "text" = "Build Date: $buildDate" + "wrap" = $true + } + ) + "actions" = @( + @{ + "type" = "Action.OpenUrl" + "title" = "📥 Install via App Installer" + "url" = $appInstallerUrl + } + ) + "$schema" = "http://adaptivecards.io/schemas/adaptive-card.json" + "version" = "1.2" + } + } + ) + } + $jsonBody = $body | ConvertTo-Json -Depth 10 + Invoke-RestMethod -Uri "${{ secrets.TEAMS_INCOMING_WEBHOOK_URL }}" -Method Post -Body $jsonBody -ContentType 'application/json' + shell: pwsh