name: Release Desktop Beta

on:
  workflow_dispatch:
    inputs:
      version_number:
        description: "New Beta Version"
        required: true

defaults:
  run:
    shell: bash

jobs:
  setup:
    name: Setup
    runs-on: ubuntu-22.04
    outputs:
      release_version: ${{ steps.version.outputs.version }}
      release_channel: ${{ steps.release_channel.outputs.channel }}
      branch_name: ${{ steps.branch.outputs.branch_name }}
      build_number: ${{ steps.increment-version.outputs.build_number }}
      node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Branch check
        run: |
          if [[ "$GITHUB_REF" != "refs/heads/main" ]] && [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc" ]]; then
            echo "==================================="
            echo "[!] Can only release from the 'main', 'rc' or 'hotfix-rc' branches"
            echo "==================================="
            exit 1
          fi

      - name: Bump Desktop Version - Root
        env:
          VERSION: ${{ github.event.inputs.version_number }}
        run: npm version --workspace=@bitwarden/desktop ${VERSION}-beta

      - name: Bump Desktop Version - App
        env:
          VERSION: ${{ github.event.inputs.version_number }}
        run: npm version ${VERSION}-beta
        working-directory: "apps/desktop/src"

      - name: Check Release Version
        id: version
        uses: bitwarden/gh-actions/release-version-check@main
        with:
          release-type: 'Initial Release'
          project-type: ts
          file: apps/desktop/src/package.json
          monorepo: true
          monorepo-project: desktop

      - name: Increment Version
        id: increment-version
        run: |
          BUILD_NUMBER=$(expr 3000 + $GITHUB_RUN_NUMBER)
          echo "Setting build number to $BUILD_NUMBER"
          echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT

      - name: Get Version Channel
        id: release_channel
        run: |
          case "${{ steps.version.outputs.version }}" in
            *"alpha"*)
              echo "channel=alpha" >> $GITHUB_OUTPUT
              echo "[!] We do not yet support 'alpha'"
              exit 1
              ;;
            *"beta"*)
              echo "channel=beta" >> $GITHUB_OUTPUT
              ;;
            *)
              echo "channel=latest" >> $GITHUB_OUTPUT
              ;;
          esac

      - name: Setup git config
        run: |
          git config --global user.name "GitHub Action Bot"
          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git config --global url."https://github.com/".insteadOf ssh://git@github.com/
          git config --global url."https://".insteadOf ssh://

      - name: Create desktop-beta-release branch
        id: branch
        env:
          VERSION: ${{ github.event.inputs.version_number }}
        run: |
          find="."
          replace="_"
          ver=${VERSION//$find/$replace}
          branch_name=desktop-beta-release-$ver-beta

          git switch -c $branch_name
          git add .
          git commit -m "Bump desktop version to $VERSION-beta"

          git push -u origin $branch_name

          echo "branch_name=$branch_name" >> $GITHUB_OUTPUT

      - name: Get Node Version
        id: retrieve-node-version
        run: |
          NODE_NVMRC=$(cat .nvmrc)
          NODE_VERSION=${NODE_NVMRC/v/''}
          echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT

  linux:
    name: Linux Build
    runs-on: ubuntu-22.04
    needs: setup
    env:
      _PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }}
      _NODE_VERSION: ${{ needs.setup.outputs.node_version }}
      NODE_OPTIONS: --max_old_space_size=4096
    defaults:
      run:
        working-directory: apps/desktop
    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          ref: ${{ needs.setup.outputs.branch_name }}

      - name: Set up Node
        uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
        with:
          cache: 'npm'
          cache-dependency-path: '**/package-lock.json'
          node-version: ${{ env._NODE_VERSION }}

      - name: Set up environment
        run: |
          sudo apt-get update
          sudo apt-get -y install pkg-config libxss-dev rpm

      - name: Set up Snap
        run: sudo snap install snapcraft --classic

      - name: Print environment
        run: |
          node --version
          npm --version
          snap --version
          snapcraft --version || echo 'snapcraft unavailable'

      - name: Install Node dependencies
        run: npm ci
        working-directory: ./

      - name: Build application
        run: npm run dist:lin

      - name: Upload .deb artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-amd64.deb
          if-no-files-found: error

      - name: Upload .rpm artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.rpm
          if-no-files-found: error

      - name: Upload .freebsd artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.freebsd
          if-no-files-found: error

      - name: Upload .snap artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap
          path: apps/desktop/dist/bitwarden_${{ env._PACKAGE_VERSION }}_amd64.snap
          if-no-files-found: error

      - name: Upload .AppImage artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x86_64.AppImage
          if-no-files-found: error

      - name: Upload auto-update artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: ${{ needs.setup.outputs.release_channel }}-linux.yml
          path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-linux.yml
          if-no-files-found: error


  windows:
    name: Windows Build
    runs-on: windows-2022
    needs: setup
    defaults:
      run:
        shell: pwsh
        working-directory: apps/desktop
    env:
      _PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }}
      _NODE_VERSION: ${{ needs.setup.outputs.node_version }}
      NODE_OPTIONS: --max_old_space_size=4096
    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          ref: ${{ needs.setup.outputs.branch_name }}

      - name: Set up Node
        uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
        with:
          cache: 'npm'
          cache-dependency-path: '**/package-lock.json'
          node-version: ${{ env._NODE_VERSION }}

      - name: Install AST
        run: dotnet tool install --global AzureSignTool --version 4.0.1

      - name: Set up environment
        run: choco install checksum --no-progress

      - name: Print environment
        run: |
          node --version
          npm --version
          choco --version

      - name: Login to Azure
        uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
        with:
          creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}

      - name: Retrieve secrets
        id: retrieve-secrets
        uses: bitwarden/gh-actions/get-keyvault-secrets@main
        with:
          keyvault: "bitwarden-ci"
          secrets: "code-signing-vault-url,
            code-signing-client-id,
            code-signing-tenant-id,
            code-signing-client-secret,
            code-signing-cert-name"

      - name: Install Node dependencies
        run: npm ci
        working-directory: ./

      - name: Build & Sign (dev)
        env:
          ELECTRON_BUILDER_SIGN: 1
          SIGNING_VAULT_URL: ${{ steps.retrieve-secrets.outputs.code-signing-vault-url }}
          SIGNING_CLIENT_ID: ${{ steps.retrieve-secrets.outputs.code-signing-client-id }}
          SIGNING_TENANT_ID: ${{ steps.retrieve-secrets.outputs.code-signing-tenant-id }}
          SIGNING_CLIENT_SECRET: ${{ steps.retrieve-secrets.outputs.code-signing-client-secret }}
          SIGNING_CERT_NAME: ${{ steps.retrieve-secrets.outputs.code-signing-cert-name }}
        run: |
          npm run build
          npm run pack:win

      - name: Rename appx files for store
        run: |
          Copy-Item "./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx" `
            -Destination "./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx"
          Copy-Item "./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx" `
            -Destination "./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx"
          Copy-Item "./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx" `
            -Destination "./dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx"

      - name: Package for Chocolatey
        run: |
          Copy-Item -Path ./stores/chocolatey -Destination ./dist/chocolatey -Recurse
          Copy-Item -Path ./dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe `
          -Destination ./dist/chocolatey

          $checksum = checksum -t sha256 ./dist/chocolatey/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe
          $chocoInstall = "./dist/chocolatey/tools/chocolateyinstall.ps1"
          (Get-Content $chocoInstall).replace('__version__', "$env:_PACKAGE_VERSION").replace('__checksum__', $checksum) | Set-Content $chocoInstall
          choco pack ./dist/chocolatey/bitwarden.nuspec --version "$env:_PACKAGE_VERSION" --out ./dist/chocolatey

      - name: Fix NSIS artifact names for auto-updater
        run: |
          Rename-Item -Path .\dist\nsis-web\Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z `
            -NewName bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z
          Rename-Item -Path .\dist\nsis-web\Bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z `
            -NewName bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z
          Rename-Item -Path .\dist\nsis-web\Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z `
            -NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z

      - name: Upload portable exe artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe
          path: apps/desktop/dist/Bitwarden-Portable-${{ env._PACKAGE_VERSION }}.exe
          if-no-files-found: error

      - name: Upload installer exe artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe
          path: apps/desktop/dist/nsis-web/Bitwarden-Installer-${{ env._PACKAGE_VERSION }}.exe
          if-no-files-found: error

      - name: Upload appx ia32 artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32.appx
          if-no-files-found: error

      - name: Upload store appx ia32 artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-ia32-store.appx
          if-no-files-found: error

      - name: Upload NSIS ia32 artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z
          path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-ia32.nsis.7z
          if-no-files-found: error

      - name: Upload appx x64 artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64.appx
          if-no-files-found: error

      - name: Upload store appx x64 artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-x64-store.appx
          if-no-files-found: error

      - name: Upload NSIS x64 artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z
          path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-x64.nsis.7z
          if-no-files-found: error

      - name: Upload appx ARM64 artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64.appx
          if-no-files-found: error

      - name: Upload store appx ARM64 artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-arm64-store.appx
          if-no-files-found: error

      - name: Upload NSIS ARM64 artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z
          path: apps/desktop/dist/nsis-web/bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z
          if-no-files-found: error

      - name: Upload nupkg artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: bitwarden.${{ env._PACKAGE_VERSION }}.nupkg
          path: apps/desktop/dist/chocolatey/bitwarden.${{ env._PACKAGE_VERSION }}.nupkg
          if-no-files-found: error

      - name: Upload auto-update artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: ${{ needs.setup.outputs.release_channel }}.yml
          path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml
          if-no-files-found: error


  macos-build:
    name: MacOS Build
    runs-on: macos-13
    needs: setup
    env:
      _PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }}
      _NODE_VERSION: ${{ needs.setup.outputs.node_version }}
      NODE_OPTIONS: --max_old_space_size=4096
    defaults:
      run:
        working-directory: apps/desktop
    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          ref: ${{ needs.setup.outputs.branch_name }}

      - name: Set up Node
        uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
        with:
          cache: 'npm'
          cache-dependency-path: '**/package-lock.json'
          node-version: ${{ env._NODE_VERSION }}

      - name: Set up Node-gyp
        run: python3 -m pip install setuptools

      - name: Print environment
        run: |
          node --version
          npm --version
          echo "GitHub ref: $GITHUB_REF"
          echo "GitHub event: $GITHUB_EVENT"

      - name: Cache Build
        id: build-cache
        uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
        with:
          path: apps/desktop/build
          key: ${{ runner.os }}-${{ github.run_id }}-build

      - name: Cache Safari
        id: safari-cache
        uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
        with:
          path: apps/browser/dist/Safari
          key: ${{ runner.os }}-${{ github.run_id }}-safari-extension

      - name: Download Provisioning Profiles secrets
        env:
          ACCOUNT_NAME: bitwardenci
          CONTAINER_NAME: profiles
        run: |
          mkdir -p $HOME/secrets

          az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
            --name bitwarden_desktop_appstore.provisionprofile \
            --file $HOME/secrets/bitwarden_desktop_appstore.provisionprofile \
            --output none

      - name: Get certificates
        run: |
          mkdir -p $HOME/certificates

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/bitwarden-desktop-key |
            jq -r .value | base64 -d > $HOME/certificates/bitwarden-desktop-key.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/appstore-app-cert |
            jq -r .value | base64 -d > $HOME/certificates/appstore-app-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/appstore-installer-cert |
            jq -r .value | base64 -d > $HOME/certificates/appstore-installer-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/devid-app-cert |
            jq -r .value | base64 -d > $HOME/certificates/devid-app-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/devid-installer-cert |
            jq -r .value | base64 -d > $HOME/certificates/devid-installer-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/macdev-cert |
            jq -r .value | base64 -d > $HOME/certificates/macdev-cert.p12

      - name: Set up keychain
        env:
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: |
          security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
          security set-keychain-settings -lut 1200 build.keychain
          security import "$HOME/certificates/bitwarden-desktop-key.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/devid-app-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/devid-installer-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/appstore-app-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/appstore-installer-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/macdev-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain

      - name: Set up provisioning profiles
        run: |
          cp $HOME/secrets/bitwarden_desktop_appstore.provisionprofile \
            $GITHUB_WORKSPACE/apps/desktop/bitwarden_desktop_appstore.provisionprofile

      - name: Increment version
        shell: pwsh
        env:
          BUILD_NUMBER: ${{ needs.setup.outputs.build_number }}
        run: |
          $package = Get-Content -Raw -Path electron-builder.json | ConvertFrom-Json
          $package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"
          $package | ConvertTo-Json -Depth 32 | Set-Content -Path electron-builder.json

      - name: Install Node dependencies
        run: npm ci
        working-directory: ./

      - name: Build application (dev)
        run: npm run build


  macos-package-github:
    name: MacOS Package GitHub Release Assets
    runs-on: macos-13
    needs:
      - setup
      - macos-build
    env:
      _PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }}
      _NODE_VERSION: ${{ needs.setup.outputs.node_version }}
      NODE_OPTIONS: --max_old_space_size=4096
    defaults:
      run:
        working-directory: apps/desktop
    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          ref: ${{ needs.setup.outputs.branch_name }}

      - name: Set up Node
        uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
        with:
          cache: 'npm'
          cache-dependency-path: '**/package-lock.json'
          node-version: ${{ env._NODE_VERSION }}

      - name: Set up Node-gyp
        run: python3 -m pip install setuptools

      - name: Print environment
        run: |
          node --version
          npm --version
          echo "GitHub ref: $GITHUB_REF"
          echo "GitHub event: $GITHUB_EVENT"

      - name: Get Build Cache
        id: build-cache
        uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
        with:
          path: apps/desktop/build
          key: ${{ runner.os }}-${{ github.run_id }}-build

      - name: Setup Safari Cache
        id: safari-cache
        uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
        with:
          path: apps/browser/dist/Safari
          key: ${{ runner.os }}-${{ github.run_id }}-safari-extension

      - name: Login to Azure
        uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
        with:
          creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}

      - name: Download Provisioning Profiles secrets
        env:
          ACCOUNT_NAME: bitwardenci
          CONTAINER_NAME: profiles
        run: |
          mkdir -p $HOME/secrets

          az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
            --name bitwarden_desktop_appstore.provisionprofile \
            --file $HOME/secrets/bitwarden_desktop_appstore.provisionprofile \
            --output none

      - name: Get certificates
        run: |
          mkdir -p $HOME/certificates

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/bitwarden-desktop-key |
            jq -r .value | base64 -d > $HOME/certificates/bitwarden-desktop-key.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/appstore-app-cert |
            jq -r .value | base64 -d > $HOME/certificates/appstore-app-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/appstore-installer-cert |
            jq -r .value | base64 -d > $HOME/certificates/appstore-installer-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/devid-app-cert |
            jq -r .value | base64 -d > $HOME/certificates/devid-app-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/devid-installer-cert |
            jq -r .value | base64 -d > $HOME/certificates/devid-installer-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/macdev-cert |
            jq -r .value | base64 -d > $HOME/certificates/macdev-cert.p12

      - name: Set up keychain
        env:
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: |
          security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
          security set-keychain-settings -lut 1200 build.keychain

          security import "$HOME/certificates/bitwarden-desktop-key.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/devid-app-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/devid-installer-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/appstore-app-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/appstore-installer-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/macdev-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain

      - name: Set up provisioning profiles
        run: |
          cp $HOME/secrets/bitwarden_desktop_appstore.provisionprofile \
            $GITHUB_WORKSPACE/apps/desktop/bitwarden_desktop_appstore.provisionprofile

      - name: Increment version
        shell: pwsh
        env:
          BUILD_NUMBER: ${{ needs.setup.outputs.build_number }}
        run: |
          $package = Get-Content -Raw -Path electron-builder.json | ConvertFrom-Json
          $package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"
          $package | ConvertTo-Json -Depth 32 | Set-Content -Path electron-builder.json

      - name: Install Node dependencies
        run: npm ci
        working-directory: ./

      - name: Build
        if: steps.build-cache.outputs.cache-hit != 'true'
        run: npm run build

      - name: Download artifact from hotfix-rc
        if: github.ref == 'refs/heads/hotfix-rc'
        uses: bitwarden/gh-actions/download-artifacts@main
        with:
          workflow: build-browser.yml
          workflow_conclusion: success
          branch: hotfix-rc
          path: ${{ github.workspace }}/browser-build-artifacts

      - name: Download artifact from rc
        if: github.ref == 'refs/heads/rc'
        uses: bitwarden/gh-actions/download-artifacts@main
        with:
          workflow: build-browser.yml
          workflow_conclusion: success
          branch: rc
          path: ${{ github.workspace }}/browser-build-artifacts

      - name: Download artifacts from main
        if: ${{ github.ref != 'refs/heads/rc' && github.ref != 'refs/heads/hotfix-rc' }}
        uses: bitwarden/gh-actions/download-artifacts@main
        with:
          workflow: build-browser.yml
          workflow_conclusion: success
          branch: main
          path: ${{ github.workspace }}/browser-build-artifacts

      - name: Unzip Safari artifact
        run: |
          SAFARI_DIR=$(find $GITHUB_WORKSPACE/browser-build-artifacts -name 'dist-safari-*.zip')
          echo $SAFARI_DIR
          unzip $SAFARI_DIR/dist-safari.zip -d $GITHUB_WORKSPACE/browser-build-artifacts

      - name: Load Safari extension for .dmg
        run: |
          mkdir PlugIns
          cp -r $GITHUB_WORKSPACE/browser-build-artifacts/Safari/dmg/build/Release/safari.appex PlugIns/safari.appex

      - name: Build application (dist)
        env:
          APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
          APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
        run: npm run pack:mac

      - name: Upload .zip artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal-mac.zip
          if-no-files-found: error

      - name: Upload .dmg artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg
          if-no-files-found: error

      - name: Upload .dmg blockmap artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap
          path: apps/desktop/dist/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.dmg.blockmap
          if-no-files-found: error

      - name: Upload auto-update artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: ${{ needs.setup.outputs.release_channel }}-mac.yml
          path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-mac.yml
          if-no-files-found: error


  macos-package-mas:
    name: MacOS Package Prod Release Asset
    runs-on: macos-13
    needs:
      - setup
      - macos-build
    env:
      _PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }}
      _NODE_VERSION: ${{ needs.setup.outputs.node_version }}
      NODE_OPTIONS: --max_old_space_size=4096
    defaults:
      run:
        working-directory: apps/desktop
    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          ref: ${{ needs.setup.outputs.branch_name }}

      - name: Set up Node
        uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
        with:
          cache: 'npm'
          cache-dependency-path: '**/package-lock.json'
          node-version: ${{ env._NODE_VERSION }}

      - name: Set up Node-gyp
        run: python3 -m pip install setuptools

      - name: Print environment
        run: |
          node --version
          npm --version
          echo "GitHub ref: $GITHUB_REF"
          echo "GitHub event: $GITHUB_EVENT"

      - name: Get Build Cache
        id: build-cache
        uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
        with:
          path: apps/desktop/build
          key: ${{ runner.os }}-${{ github.run_id }}-build

      - name: Setup Safari Cache
        id: safari-cache
        uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
        with:
          path: apps/browser/dist/Safari
          key: ${{ runner.os }}-${{ github.run_id }}-safari-extension

      - name: Download Provisioning Profiles secrets
        env:
          ACCOUNT_NAME: bitwardenci
          CONTAINER_NAME: profiles
        run: |
          mkdir -p $HOME/secrets

          az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
            --name bitwarden_desktop_appstore.provisionprofile \
            --file $HOME/secrets/bitwarden_desktop_appstore.provisionprofile \
            --output none

      - name: Get certificates
        run: |
          mkdir -p $HOME/certificates

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/bitwarden-desktop-key |
            jq -r .value | base64 -d > $HOME/certificates/bitwarden-desktop-key.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/appstore-app-cert |
            jq -r .value | base64 -d > $HOME/certificates/appstore-app-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/appstore-installer-cert |
            jq -r .value | base64 -d > $HOME/certificates/appstore-installer-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/devid-app-cert |
            jq -r .value | base64 -d > $HOME/certificates/devid-app-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/devid-installer-cert |
            jq -r .value | base64 -d > $HOME/certificates/devid-installer-cert.p12

          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/macdev-cert |
            jq -r .value | base64 -d > $HOME/certificates/macdev-cert.p12

      - name: Set up keychain
        env:
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: |
          security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
          security set-keychain-settings -lut 1200 build.keychain

          security import "$HOME/certificates/bitwarden-desktop-key.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/devid-app-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/devid-installer-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/appstore-app-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/appstore-installer-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security import "$HOME/certificates/macdev-cert.p12" -k build.keychain -P "" \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild

          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain

      - name: Set up provisioning profiles
        run: |
          cp $HOME/secrets/bitwarden_desktop_appstore.provisionprofile \
            $GITHUB_WORKSPACE/apps/desktop/bitwarden_desktop_appstore.provisionprofile

      - name: Increment version
        shell: pwsh
        env:
          BUILD_NUMBER: ${{ needs.setup.outputs.build_number }}
        run: |
          $package = Get-Content -Raw -Path electron-builder.json | ConvertFrom-Json
          $package | Add-Member -MemberType NoteProperty -Name buildVersion -Value "$env:BUILD_NUMBER"
          $package | ConvertTo-Json -Depth 32 | Set-Content -Path electron-builder.json

      - name: Install Node dependencies
        run: npm ci
        working-directory: ./

      - name: Build
        if: steps.build-cache.outputs.cache-hit != 'true'
        run: npm run build

      - name: Download artifact from hotfix-rc
        if: github.ref == 'refs/heads/hotfix-rc'
        uses: bitwarden/gh-actions/download-artifacts@main
        with:
          workflow: build-browser.yml
          workflow_conclusion: success
          branch: hotfix-rc
          path: ${{ github.workspace }}/browser-build-artifacts

      - name: Download artifact from rc
        if: github.ref == 'refs/heads/rc'
        uses: bitwarden/gh-actions/download-artifacts@main
        with:
          workflow: build-browser.yml
          workflow_conclusion: success
          branch: rc
          path: ${{ github.workspace }}/browser-build-artifacts

      - name: Download artifact from main
        if: ${{ github.ref != 'refs/heads/rc' && github.ref != 'refs/heads/hotfix-rc' }}
        uses: bitwarden/gh-actions/download-artifacts@main
        with:
          workflow: build-browser.yml
          workflow_conclusion: success
          branch: main
          path: ${{ github.workspace }}/browser-build-artifacts

      - name: Unzip Safari artifact
        run: |
          SAFARI_DIR=$(find $GITHUB_WORKSPACE/browser-build-artifacts -name 'dist-safari-*.zip')
          echo $SAFARI_DIR
          unzip $SAFARI_DIR/dist-safari.zip -d $GITHUB_WORKSPACE/browser-build-artifacts

      - name: Load Safari extension for App Store
        run: |
          mkdir PlugIns
          cp -r $GITHUB_WORKSPACE/browser-build-artifacts/Safari/mas/build/Release/safari.appex PlugIns/safari.appex

      - name: Build application for App Store
        run: npm run pack:mac:mas
        env:
          APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
          APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}

      - name: Upload .pkg artifact
        uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
        with:
          name: Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
          path: apps/desktop/dist/mas-universal/Bitwarden-${{ env._PACKAGE_VERSION }}-universal.pkg
          if-no-files-found: error

  release:
    name: Release beta channel to S3
    runs-on: ubuntu-22.04
    needs:
      - setup
      - linux
      - windows
      - macos-build
      - macos-package-github
      - macos-package-mas
    steps:
      - name: Create GitHub deployment
        uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
        id: deployment
        with:
          token: '${{ secrets.GITHUB_TOKEN }}'
          initial-status: 'in_progress'
          environment: 'Desktop - Beta'
          description: 'Deployment ${{ needs.setup.outputs.release_version }} to channel ${{ needs.setup.outputs.release_channel }} from branch ${{ needs.setup.outputs.branch_name }}'
          task: release

      - name: Login to Azure
        uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
        with:
          creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}

      - name: Retrieve secrets
        id: retrieve-secrets
        uses: bitwarden/gh-actions/get-keyvault-secrets@main
        with:
          keyvault: "bitwarden-ci"
          secrets: "aws-electron-access-id,
            aws-electron-access-key,
            aws-electron-bucket-name"

      - name: Download all artifacts
        uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
        with:
          path: apps/desktop/artifacts

      - name: Rename .pkg to .pkg.archive
        env:
          PKG_VERSION: ${{ needs.setup.outputs.release_version }}
        working-directory: apps/desktop/artifacts
        run: mv Bitwarden-${{ env.PKG_VERSION }}-universal.pkg Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive

      - name: Publish artifacts to S3
        env:
          AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.aws-electron-access-id }}
          AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.aws-electron-access-key }}
          AWS_DEFAULT_REGION: 'us-west-2'
          AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.aws-electron-bucket-name }}
        working-directory: apps/desktop/artifacts
        run: |
          aws s3 cp ./ $AWS_S3_BUCKET_NAME/desktop/ \
          --acl "public-read" \
          --recursive \
          --quiet

      - name: Update deployment status to Success
        if: ${{ success() }}
        uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
        with:
          token: '${{ secrets.GITHUB_TOKEN }}'
          state: 'success'
          deployment-id: ${{ steps.deployment.outputs.deployment_id }}

      - name: Update deployment status to Failure
        if: ${{ failure() }}
        uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
        with:
          token: '${{ secrets.GITHUB_TOKEN }}'
          state: 'failure'
          deployment-id: ${{ steps.deployment.outputs.deployment_id }}

  remove-branch:
    name: Remove branch
    runs-on: ubuntu-22.04
    if: always()
    needs:
      - setup
      - linux
      - windows
      - macos-build
      - macos-package-github
      - macos-package-mas
      - release
    steps:
      - name: Checkout repo
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Setup git config
        run: |
          git config --global user.name "GitHub Action Bot"
          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git config --global url."https://github.com/".insteadOf ssh://git@github.com/
          git config --global url."https://".insteadOf ssh://
      - name: Remove branch
        env:
          BRANCH: ${{ needs.setup.outputs.branch_name }}
        run: git push origin --delete $BRANCH
