name: Build on: pull_request: {} push: branches: - "main" - "rc" - "hotfix-rc" workflow_dispatch: {} permissions: contents: read jobs: setup: name: Setup runs-on: ubuntu-24.04 permissions: contents: read outputs: package_version: ${{ steps.retrieve-version.outputs.package_version }} node_version: ${{ steps.retrieve-node-version.outputs.node_version }} steps: - name: Checkout repo uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Get Package Version id: retrieve-version run: | PKG_VERSION=$(jq -r .version package.json) echo "package_version=$PKG_VERSION" >> "$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-cli: name: Build Linux CLI runs-on: ubuntu-24.04 needs: setup env: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} permissions: contents: read steps: - name: Checkout repo uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Node uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} - name: Update NPM run: | npm install -g node-gyp node-gyp install "$(node -v)" - name: Keytar run: | keytarVersion=$(cat package.json | jq -r '.dependencies.keytar') keytarTar="keytar-v$keytarVersion-napi-v3-linux-x64.tar" keytarTarGz="$keytarTar.gz" keytarUrl="https://github.com/atom/node-keytar/releases/download/v$keytarVersion/$keytarTarGz" mkdir -p ./keytar/linux wget "$keytarUrl" -O "./keytar/linux/$keytarTarGz" tar -xvf "./keytar/linux/$keytarTarGz" -C ./keytar/linux - name: Install run: npm install - name: Package CLI run: npm run dist:cli:lin - name: Zip run: zip -j "dist-cli/bwdc-linux-$_PACKAGE_VERSION.zip" "dist-cli/linux/bwdc" "keytar/linux/build/Release/keytar.node" - name: Version Test run: | sudo apt-get update sudo apt install libsecret-1-0 dbus-x11 gnome-keyring eval "$(dbus-launch --sh-syntax)" eval "$(echo -n "" | /usr/bin/gnome-keyring-daemon --login)" eval "$(/usr/bin/gnome-keyring-daemon --components=secrets --start)" mkdir -p test/linux unzip "./dist-cli/bwdc-linux-$_PACKAGE_VERSION.zip" -d ./test/linux testVersion=$(./test/linux/bwdc -v) echo "version: $_PACKAGE_VERSION" echo "testVersion: $testVersion" if [ "$testVersion" != "$_PACKAGE_VERSION" ]; then echo "Version test failed." exit 1 fi - name: Upload Linux Zip to GitHub uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bwdc-linux-${{ env._PACKAGE_VERSION }}.zip path: ./dist-cli/bwdc-linux-${{ env._PACKAGE_VERSION }}.zip if-no-files-found: error macos-cli: name: Build Mac CLI runs-on: macos-15-intel needs: setup permissions: contents: read env: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} steps: - name: Checkout repo uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Node uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} - name: Update NPM run: | npm install -g node-gyp node-gyp install "$(node -v)" - name: Keytar run: | keytarVersion=$(cat package.json | jq -r '.dependencies.keytar') keytarTar="keytar-v$keytarVersion-napi-v3-darwin-x64.tar" keytarTarGz="$keytarTar.gz" keytarUrl="https://github.com/atom/node-keytar/releases/download/v$keytarVersion/$keytarTarGz" mkdir -p ./keytar/macos wget "$keytarUrl" -O "./keytar/macos/$keytarTarGz" tar -xvf "./keytar/macos/$keytarTarGz" -C ./keytar/macos - name: Install run: npm install - name: Package CLI run: npm run dist:cli:mac - name: Zip run: zip -j "dist-cli/bwdc-macos-$_PACKAGE_VERSION.zip" "dist-cli/macos/bwdc" "keytar/macos/build/Release/keytar.node" - name: Version Test run: | mkdir -p test/macos unzip "./dist-cli/bwdc-macos-$_PACKAGE_VERSION.zip" -d ./test/macos testVersion=$(./test/macos/bwdc -v) echo "version: $_PACKAGE_VERSION" echo "testVersion: $testVersion" if [ "$testVersion" != "$_PACKAGE_VERSION" ]; then echo "Version test failed." exit 1 fi - name: Upload Mac Zip to GitHub uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bwdc-macos-${{ env._PACKAGE_VERSION }}.zip path: ./dist-cli/bwdc-macos-${{ env._PACKAGE_VERSION }}.zip if-no-files-found: error windows-cli: name: Build Windows CLI runs-on: windows-2022 needs: setup permissions: contents: read env: _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} steps: - name: Checkout repo uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Setup Windows builder run: | choco install checksum --no-progress - name: Set up Node uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} - name: Update NPM run: | npm install -g node-gyp node-gyp install $(node -v) - name: Keytar shell: pwsh run: | $keytarVersion = (Get-Content -Raw -Path ./package.json | ConvertFrom-Json).dependencies.keytar $keytarTar = "keytar-v${keytarVersion}-napi-v3-{0}-x64.tar" $keytarTarGz = "${keytarTar}.gz" $keytarUrl = "https://github.com/atom/node-keytar/releases/download/v${keytarVersion}/${keytarTarGz}" New-Item -ItemType directory -Path ./keytar/windows | Out-Null Invoke-RestMethod -Uri $($keytarUrl -f "win32") -OutFile "./keytar/windows/$($keytarTarGz -f "win32")" 7z e "./keytar/windows/$($keytarTarGz -f "win32")" -o"./keytar/windows" 7z e "./keytar/windows/$($keytarTar -f "win32")" -o"./keytar/windows" - name: Install run: npm install - name: Package CLI run: npm run dist:cli:win - name: Zip shell: cmd run: 7z a .\dist-cli\bwdc-windows-%_PACKAGE_VERSION%.zip .\dist-cli\windows\bwdc.exe .\keytar\windows\keytar.node - name: Version Test shell: pwsh run: | Expand-Archive -Path "dist-cli\bwdc-windows-$env:_PACKAGE_VERSION.zip" -DestinationPath "test\windows" $testVersion = Invoke-Expression '& .\test\windows\bwdc.exe -v' echo "version: ${env:_PACKAGE_VERSION}" echo "testVersion: $testVersion" if ($testVersion -ne ${env:_PACKAGE_VERSION}) { Throw "Version test failed." } - name: Upload Windows Zip to GitHub uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: bwdc-windows-${{ env._PACKAGE_VERSION }}.zip path: ./dist-cli/bwdc-windows-${{ env._PACKAGE_VERSION }}.zip if-no-files-found: error windows-gui: name: Build Windows GUI runs-on: windows-2022 needs: setup permissions: contents: read id-token: write env: NODE_OPTIONS: --max_old_space_size=4096 _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} HUSKY: 0 steps: - name: Checkout repo uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Node uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} - name: Update NPM run: | npm install -g node-gyp node-gyp install $(node -v) - name: Print environment run: | node --version npm --version - name: Install AST run: dotnet tool install --global AzureSignTool --version 4.0.1 - name: Install Node dependencies run: npm install - name: Log in to Azure uses: bitwarden/gh-actions/azure-login@main with: subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} tenant_id: ${{ secrets.AZURE_TENANT_ID }} client_id: ${{ secrets.AZURE_CLIENT_ID }} - 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: Log out from Azure uses: bitwarden/gh-actions/azure-logout@main - name: Build & Sign run: npm run dist:win 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 }} - name: Upload Portable Executable to GitHub uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Connector-Portable-${{ env._PACKAGE_VERSION }}.exe path: ./dist/Bitwarden-Connector-Portable-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload Installer Executable to GitHub uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe path: ./dist/Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe if-no-files-found: error - name: Upload Installer Executable Blockmap to GitHub uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe.blockmap path: ./dist/Bitwarden-Connector-Installer-${{ env._PACKAGE_VERSION }}.exe.blockmap if-no-files-found: error - name: Upload latest auto-update artifact uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: latest.yml path: ./dist/latest.yml if-no-files-found: error linux-gui: name: Build Linux GUI runs-on: ubuntu-24.04 needs: setup permissions: contents: read env: NODE_OPTIONS: --max_old_space_size=4096 _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} HUSKY: 0 steps: - name: Checkout repo uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Node uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} - name: Update NPM run: | npm install -g node-gyp node-gyp install "$(node -v)" - name: Set up environment run: | sudo apt-get update sudo apt-get -y install pkg-config libxss-dev libsecret-1-dev sudo apt-get -y install rpm - name: NPM Install run: npm install - name: NPM Rebuild run: npm run rebuild - name: NPM Package run: npm run dist:lin - name: Upload AppImage uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-x86_64.AppImage path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-x86_64.AppImage if-no-files-found: error - name: Upload latest auto-update artifact uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: latest-linux.yml path: ./dist/latest-linux.yml if-no-files-found: error macos-gui: name: Build MacOS GUI runs-on: macos-15-intel needs: setup permissions: contents: read id-token: write env: NODE_OPTIONS: --max_old_space_size=4096 _PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }} _NODE_VERSION: ${{ needs.setup.outputs.node_version }} HUSKY: 0 steps: - name: Checkout repo uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false - name: Set up Node uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0 with: cache: 'npm' cache-dependency-path: '**/package-lock.json' node-version: ${{ env._NODE_VERSION }} - name: Update NPM run: | npm install -g node-gyp node-gyp install "$(node -v)" - name: Print environment run: | node --version npm --version echo "GitHub ref: $GITHUB_REF" echo "GitHub event: $GITHUB_EVENT" - name: Log in to Azure uses: bitwarden/gh-actions/azure-login@main with: subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} tenant_id: ${{ secrets.AZURE_TENANT_ID }} client_id: ${{ secrets.AZURE_CLIENT_ID }} - name: Get Azure Key Vault secrets id: get-kv-secrets uses: bitwarden/gh-actions/get-keyvault-secrets@main with: keyvault: gh-directory-connector secrets: "KEYCHAIN-PASSWORD,APP-STORE-CONNECT-AUTH-KEY,APP-STORE-CONNECT-TEAM-ISSUER" - name: Get certificates run: | mkdir -p "$HOME/certificates" 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: Log out from Azure uses: bitwarden/gh-actions/azure-logout@main - name: Set up keychain env: KEYCHAIN_PASSWORD: ${{ steps.get-kv-secrets.outputs.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/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/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: Load package version run: | $rootPath = $env:GITHUB_WORKSPACE; $packageVersion = (Get-Content -Raw -Path "$rootPath\package.json" | ConvertFrom-Json).version; Write-Output "Setting package version to $packageVersion"; Write-Output "PACKAGE_VERSION=$packageVersion" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append; shell: pwsh - name: Install Node dependencies run: npm install - name: Set up private auth key env: _APP_STORE_CONNECT_AUTH_KEY: ${{ steps.get-kv-secrets.outputs.APP-STORE-CONNECT-AUTH-KEY }} run: | mkdir ~/private_keys cat << EOF > ~/private_keys/AuthKey_UFD296548T.p8 ${_APP_STORE_CONNECT_AUTH_KEY} EOF - name: Build application run: npm run dist:mac env: APP_STORE_CONNECT_TEAM_ISSUER: ${{ steps.get-kv-secrets.outputs.APP-STORE-CONNECT-TEAM-ISSUER }} APP_STORE_CONNECT_AUTH_KEY: UFD296548T APP_STORE_CONNECT_AUTH_KEY_PATH: ~/private_keys/AuthKey_UFD296548T.p8 CSC_FOR_PULL_REQUEST: true - name: Upload .zip artifact uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-mac.zip path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}-mac.zip if-no-files-found: error - name: Upload .dmg artifact uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg if-no-files-found: error - name: Upload .dmg Blockmap artifact uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg.blockmap path: ./dist/Bitwarden-Connector-${{ env._PACKAGE_VERSION }}.dmg.blockmap if-no-files-found: error - name: Upload latest auto-update artifact uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: latest-mac.yml path: ./dist/latest-mac.yml if-no-files-found: error check-failures: name: Check for failures runs-on: ubuntu-24.04 needs: - setup - linux-cli - macos-cli - windows-cli - windows-gui - linux-gui - macos-gui permissions: id-token: write steps: - name: Check if any job failed if: | (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') && contains(needs.*.result, 'failure') run: exit 1 - name: Log in to Azure if: failure() uses: bitwarden/gh-actions/azure-login@main with: subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} tenant_id: ${{ secrets.AZURE_TENANT_ID }} client_id: ${{ secrets.AZURE_CLIENT_ID }} - name: Retrieve secrets id: retrieve-secrets uses: bitwarden/gh-actions/get-keyvault-secrets@main if: failure() with: keyvault: "bitwarden-ci" secrets: "devops-alerts-slack-webhook-url" - name: Log out from Azure uses: bitwarden/gh-actions/azure-logout@main - name: Notify Slack on failure uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 if: failure() env: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} with: status: ${{ job.status }}