| 1 | name: "Release Siti AI Windows Microsoft Store" |
| 2 | |
| 3 | on: |
| 4 | push: |
| 5 | branches: |
| 6 | - release/windows-microsoft-store |
| 7 | workflow_dispatch: |
| 8 | |
| 9 | # Push to the release branch above to build, sign, and draft-release the |
| 10 | # Windows Microsoft Store artifacts. The signed .msi/.exe are for direct |
| 11 | # download; the MSIX is what gets submitted to Partner Center (the Store |
| 12 | # re-signs it), which is the only form that carries the package identity Store |
| 13 | # services need. |
| 14 | jobs: |
| 15 | publish-tauri: |
| 16 | permissions: |
| 17 | contents: write |
| 18 | runs-on: windows-latest |
| 19 | # Job-level env so the MSIX steps' own `if:` conditions can read these. |
| 20 | # When the MSIX identity secrets are absent the MSIX steps skip and the |
| 21 | # signed .msi/.exe still ship. |
| 22 | env: |
| 23 | MSIX_PUBLISHER: ${{ secrets.MSIX_PUBLISHER }} |
| 24 | MSIX_IDENTITY_NAME: ${{ secrets.MSIX_IDENTITY_NAME }} |
| 25 | steps: |
| 26 | - uses: actions/checkout@v7 |
| 27 | |
| 28 | - name: setup node |
| 29 | uses: actions/setup-node@v7 |
| 30 | with: |
| 31 | node-version: 22 |
| 32 | |
| 33 | # No version pin: reads the exact pnpm from package.json's |
| 34 | # packageManager field (pnpm@11.8.0), matching ci.yml. |
| 35 | - uses: pnpm/action-setup@v6 |
| 36 | |
| 37 | - name: Install Rust stable |
| 38 | uses: dtolnay/rust-toolchain@stable |
| 39 | |
| 40 | # The full dependency tree build is slow cold. Cache the cargo registry |
| 41 | # and target/ keyed on Cargo.lock so a repeat run (a manifest tweak, |
| 42 | # another Partner Center validation round) restores in minutes instead of |
| 43 | # rebuilding from scratch. |
| 44 | - name: Cache Rust build |
| 45 | uses: Swatinem/rust-cache@v2 |
| 46 | with: |
| 47 | workspaces: src-tauri |
| 48 | |
| 49 | # The enabled Windows chat backend pulls in native crypto/C dependencies. |
| 50 | # NASM keeps their x64 assembly path available on the Windows runner. |
| 51 | - name: Install NASM |
| 52 | run: choco install nasm -y --no-progress |
| 53 | shell: pwsh |
| 54 | |
| 55 | # Validate the MSIX manifest before the long Rust build rather than after |
| 56 | # it. makeappx checks the manifest against its schema regardless of what |
| 57 | # the payload files contain, so a stub exe is enough to catch a schema |
| 58 | # error in seconds instead of after a full build. Skipped when the MSIX |
| 59 | # secrets are absent so the .msi/.exe build still ships. |
| 60 | - name: Validate MSIX manifest (fast fail) |
| 61 | if: ${{ env.MSIX_PUBLISHER != '' && env.MSIX_IDENTITY_NAME != '' }} |
| 62 | shell: pwsh |
| 63 | run: ./scripts/build-msix.ps1 -ValidateOnly |
| 64 | |
| 65 | - name: Install frontend dependencies |
| 66 | run: pnpm install --frozen-lockfile |
| 67 | |
| 68 | - name: Patch src-tauri/.cargo/config.toml for native Windows build |
| 69 | shell: pwsh |
| 70 | run: | |
| 71 | # The .cargo/config.toml is authored for macOS hosts: the [env] |
| 72 | # section force-sets CC/CXX to /usr/bin/clang and a macOS deployment |
| 73 | # target, none of which exist on this runner. Strip that section so |
| 74 | # Rust falls back to the runner's native MSVC toolchain. The |
| 75 | # per-Apple-target and Android sections are inert here and left alone. |
| 76 | $path = "src-tauri/.cargo/config.toml" |
| 77 | if (Test-Path $path) { |
| 78 | $lines = Get-Content $path |
| 79 | $out = [System.Collections.Generic.List[string]]::new() |
| 80 | $skipSection = $false |
| 81 | |
| 82 | foreach ($line in $lines) { |
| 83 | if ($line -match '^\[env\]') { |
| 84 | $skipSection = $true |
| 85 | continue |
| 86 | } |
| 87 | if ($skipSection -and $line -match '^\[') { |
| 88 | $skipSection = $false |
| 89 | } |
| 90 | if (-not $skipSection) { |
| 91 | $out.Add($line) |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | $out | Set-Content $path -Encoding UTF8 |
| 96 | Write-Host "Patched src-tauri/.cargo/config.toml — removed macOS-only [env] section" |
| 97 | } else { |
| 98 | Write-Host "No src-tauri/.cargo/config.toml to patch" |
| 99 | } |
| 100 | |
| 101 | - name: Setup MSVC developer environment |
| 102 | uses: ilammy/msvc-dev-cmd@v1 |
| 103 | |
| 104 | # Fail before the long Windows build if SSL.com signing credentials are |
| 105 | # not available to this run. GitHub substitutes absent secrets as empty |
| 106 | # strings, which otherwise surfaces only after the artifacts are built. |
| 107 | - name: Validate code-signing secrets |
| 108 | shell: pwsh |
| 109 | env: |
| 110 | ES_USERNAME: ${{ secrets.ES_USERNAME }} |
| 111 | ES_PASSWORD: ${{ secrets.ES_PASSWORD }} |
| 112 | ES_CREDENTIAL_ID: ${{ secrets.ES_CREDENTIAL_ID }} |
| 113 | ES_TOTP_SECRET: ${{ secrets.ES_TOTP_SECRET }} |
| 114 | run: | |
| 115 | $required = @( |
| 116 | "ES_USERNAME", |
| 117 | "ES_PASSWORD", |
| 118 | "ES_CREDENTIAL_ID", |
| 119 | "ES_TOTP_SECRET" |
| 120 | ) |
| 121 | $missing = $required | Where-Object { |
| 122 | [string]::IsNullOrWhiteSpace([Environment]::GetEnvironmentVariable($_)) |
| 123 | } |
| 124 | if ($missing.Count -gt 0) { |
| 125 | throw "Missing required GitHub Actions secret(s): $($missing -join ', '). Configure them under Settings > Secrets and variables > Actions." |
| 126 | } |
| 127 | |
| 128 | - name: Build App |
| 129 | shell: pwsh |
| 130 | run: | |
| 131 | pnpm tauri build -c src-tauri/tauri.prod-windows-microsoftstore.conf.json |
| 132 | # Move installer artifacts to a dedicated input directory for |
| 133 | # CodeSignTool. The MSIX is packed separately below and deliberately |
| 134 | # left unsigned for Partner Center. |
| 135 | mkdir "${env:GITHUB_WORKSPACE}\src-tauri\target\release\bundle\to-sign" |
| 136 | mkdir "${env:GITHUB_WORKSPACE}\src-tauri\target\release\bundle\signed" |
| 137 | Move-Item "${env:GITHUB_WORKSPACE}\src-tauri\target\release\bundle\msi\*" "${env:GITHUB_WORKSPACE}\src-tauri\target\release\bundle\to-sign\" -Force |
| 138 | Move-Item "${env:GITHUB_WORKSPACE}\src-tauri\target\release\bundle\nsis\*" "${env:GITHUB_WORKSPACE}\src-tauri\target\release\bundle\to-sign\" -Force |
| 139 | |
| 140 | - name: Sign Artifact with CodeSignTool |
| 141 | uses: sslcom/esigner-codesign@v1.3.2 |
| 142 | timeout-minutes: 20 |
| 143 | with: |
| 144 | command: batch_sign |
| 145 | username: ${{ secrets.ES_USERNAME }} |
| 146 | password: ${{ secrets.ES_PASSWORD }} |
| 147 | credential_id: ${{ secrets.ES_CREDENTIAL_ID }} |
| 148 | totp_secret: ${{ secrets.ES_TOTP_SECRET }} |
| 149 | dir_path: ${GITHUB_WORKSPACE}\src-tauri\target\release\bundle\to-sign |
| 150 | output_path: ${GITHUB_WORKSPACE}\src-tauri\target\release\bundle\signed |
| 151 | malware_block: false |
| 152 | clean_logs: true |
| 153 | jvm_max_memory: 1024M |
| 154 | signing_method: v1 |
| 155 | |
| 156 | # The .msi/.exe above are for direct download only. A Store MSI/EXE |
| 157 | # submission is just a hosted installer URL, so it never gets package |
| 158 | # identity and Windows.Services.Store add-ons would fail from it. The MSIX |
| 159 | # below is what goes to Partner Center. It is deliberately left unsigned: |
| 160 | # the Store re-signs with the publisher cert. Skipped when the MSIX |
| 161 | # secrets are absent. |
| 162 | - name: Pack MSIX for Partner Center |
| 163 | if: ${{ env.MSIX_PUBLISHER != '' && env.MSIX_IDENTITY_NAME != '' }} |
| 164 | shell: pwsh |
| 165 | run: ./scripts/build-msix.ps1 |
| 166 | |
| 167 | - name: Extract version from tauri.prod-windows-microsoftstore.conf.json |
| 168 | id: get_version |
| 169 | shell: pwsh |
| 170 | run: | |
| 171 | $json = Get-Content src-tauri/tauri.prod-windows-microsoftstore.conf.json | ConvertFrom-Json |
| 172 | echo "VERSION=$($json.version)" >> $env:GITHUB_ENV |
| 173 | |
| 174 | - name: Release |
| 175 | uses: softprops/action-gh-release@v2 |
| 176 | with: |
| 177 | tag_name: "windows-microsoft-store-siti-ai-v${{ env.VERSION }}" |
| 178 | draft: true |
| 179 | prerelease: false |
| 180 | body: "See the assets to download this version and install." |
| 181 | name: "Siti AI v${{ env.VERSION }} (Windows Microsoft Store)" |
| 182 | files: | |
| 183 | src-tauri/target/release/bundle/signed/*.msi |
| 184 | src-tauri/target/release/bundle/signed/*.exe |
| 185 | src-tauri/target/release/bundle/msix/*.msix |