1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-06 11:43:51 +00:00

Merge branch 'main' into feature/i18n-component-template

This commit is contained in:
Shane
2025-07-23 15:12:30 -07:00
6210 changed files with 906266 additions and 233515 deletions

1
.browserslistrc Normal file
View File

@@ -0,0 +1 @@
> 0.5%, last 3 major versions, Firefox ESR, not dead

View File

@@ -7,5 +7,6 @@ checkmarx:
scan:
configs:
sast:
presetName: "BW ASA Premium"
# Exclude spec files, and test specific files
filter: "!*.spec.ts,!**/spec/**,!apps/desktop/native-messaging-test-runner/**"

View File

@@ -1,30 +0,0 @@
**/build
**/dist
**/coverage
.angular
storybook-static
**/node_modules
**/webpack.*.js
**/jest.config.js
**/gulpfile.js
apps/browser/config/config.js
apps/browser/src/auth/scripts/duo.js
apps/desktop/desktop_native
apps/desktop/src/auth/scripts/duo.js
apps/web/config.js
apps/web/scripts/*.js
apps/web/src/theme.js
apps/web/tailwind.config.js
apps/cli/config/config.js
tailwind.config.js
libs/components/tailwind.config.base.js
libs/components/tailwind.config.js
scripts/*.js

View File

@@ -1,242 +0,0 @@
{
"root": true,
"env": {
"browser": true,
"webextensions": true
},
"overrides": [
{
"files": ["*.ts", "*.js"],
"plugins": ["@typescript-eslint", "rxjs", "rxjs-angular", "import"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": ["./tsconfig.eslint.json"],
"sourceType": "module",
"ecmaVersion": 2020
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
"prettier",
"plugin:rxjs/recommended",
"plugin:storybook/recommended"
],
"settings": {
"import/parsers": {
"@typescript-eslint/parser": [".ts"]
},
"import/resolver": {
"typescript": {
"alwaysTryTypes": true
}
}
},
"rules": {
"@typescript-eslint/explicit-member-accessibility": [
"error",
{ "accessibility": "no-public" }
],
"@typescript-eslint/no-explicit-any": "off", // TODO: This should be re-enabled
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": ["error", { "checksVoidReturn": false }],
"@typescript-eslint/no-this-alias": ["error", { "allowedNames": ["self"] }],
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
"no-console": "error",
"import/no-unresolved": "off", // TODO: Look into turning off once each package is an actual package.
"import/order": [
"error",
{
"alphabetize": {
"order": "asc"
},
"newlines-between": "always",
"pathGroups": [
{
"pattern": "@bitwarden/**",
"group": "external",
"position": "after"
},
{
"pattern": "src/**/*",
"group": "parent",
"position": "before"
}
],
"pathGroupsExcludedImportTypes": ["builtin"]
}
],
"rxjs-angular/prefer-takeuntil": "error",
"rxjs/no-exposed-subjects": ["error", { "allowProtected": true }],
"no-restricted-syntax": [
"error",
{
"message": "Calling `svgIcon` directly is not allowed",
"selector": "CallExpression[callee.name='svgIcon']"
},
{
"message": "Accessing FormGroup using `get` is not allowed, use `.value` instead",
"selector": "ChainExpression[expression.object.callee.property.name='get'][expression.property.name='value']"
}
],
"curly": ["error", "all"],
"import/namespace": ["off"], // This doesn't resolve namespace imports correctly, but TS will throw for this anyway
"import/no-restricted-paths": [
"error",
{
"zones": [
{
// avoid specific frameworks or large dependencies in common
"target": "./libs/common/**/*",
"from": [
// Angular
"./libs/angular/**/*",
"./node_modules/@angular*/**/*",
// Node
"./libs/node/**/*",
// Import/export
"./libs/importer/**/*",
"./libs/tools/export/vault-export/vault-export-core/**/*"
]
},
{
// avoid import of unexported state objects
"target": [
"!(libs)/**/*",
"libs/!(common)/**/*",
"libs/common/!(src)/**/*",
"libs/common/src/!(platform)/**/*",
"libs/common/src/platform/!(state)/**/*"
],
"from": ["./libs/common/src/platform/state/**/*"],
// allow module index import
"except": ["**/state/index.ts"]
}
]
}
],
"no-restricted-imports": ["error", { "patterns": ["src/**/*"] }]
}
},
{
"files": ["*.html"],
"parser": "@angular-eslint/template-parser",
"plugins": ["@angular-eslint/template", "tailwindcss"],
"rules": {
"@angular-eslint/template/button-has-type": "error",
"tailwindcss/no-custom-classname": [
"error",
{
// uses negative lookahead to whitelist any class that doesn't start with "tw-"
// in other words: classnames that start with tw- must be valid TailwindCSS classes
"whitelist": ["(?!(tw)\\-).*"]
}
],
"tailwindcss/enforces-negative-arbitrary-values": "error",
"tailwindcss/enforces-shorthand": "error",
"tailwindcss/no-contradicting-classname": "error"
}
},
{
"files": ["libs/admin-console/src/**/*.ts"],
"rules": {
"no-restricted-imports": [
"error",
{ "patterns": ["@bitwarden/admin-console/*", "src/**/*"] }
]
}
},
{
"files": ["libs/angular/src/**/*.ts"],
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/angular/*", "src/**/*"] }]
}
},
{
"files": ["libs/auth/src/**/*.ts"],
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/auth/*", "src/**/*"] }]
}
},
{
"files": ["libs/billing/src/**/*.ts"],
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/billing/*", "src/**/*"] }]
}
},
{
"files": ["libs/common/src/**/*.ts"],
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/common/*", "src/**/*"] }]
}
},
{
"files": ["libs/components/src/**/*.ts"],
"rules": {
"no-restricted-imports": [
"error",
{ "patterns": ["@bitwarden/components/*", "src/**/*", "@bitwarden/angular/*"] }
]
}
},
{
"files": ["libs/tools/export/vault-export/vault-export-core/src/**/*.ts"],
"rules": {
"no-restricted-imports": [
"error",
{ "patterns": ["@bitwarden/vault-export-core/*", "src/**/*"] }
]
}
},
{
"files": ["libs/importer/src/**/*.ts"],
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/importer/*", "src/**/*"] }]
}
},
{
"files": ["libs/node/src/**/*.ts"],
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/node/*", "src/**/*"] }]
}
},
{
"files": ["libs/platform/src/**/*.ts"],
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/platform/*", "src/**/*"] }]
}
},
{
"files": ["libs/vault/src/**/*.ts"],
"rules": {
"no-restricted-imports": ["error", { "patterns": ["@bitwarden/vault/*", "src/**/*"] }]
}
},
{
"files": ["apps/browser/src/**/*.ts", "libs/**/*.ts"],
"excludedFiles": [
"apps/browser/src/autofill/{content,notification}/**/*.ts",
"apps/browser/src/**/background/**/*.ts", // It's okay to have long lived listeners in the background
"apps/browser/src/platform/background.ts"
],
"rules": {
"no-restricted-syntax": [
"error",
{
"message": "Using addListener in the browser popup produces a memory leak in Safari, use `BrowserApi.addListener` instead",
// This selector covers events like chrome.storage.onChange & chrome.runtime.onMessage
"selector": "CallExpression > [object.object.object.name='chrome'][property.name='addListener']"
},
{
"message": "Using addListener in the browser popup produces a memory leak in Safari, use `BrowserApi.addListener` instead",
// This selector covers events like chrome.storage.local.onChange
"selector": "CallExpression > [object.object.object.object.name='chrome'][property.name='addListener']"
}
]
}
}
]
}

143
.github/CODEOWNERS vendored
View File

@@ -4,8 +4,13 @@
#
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
## Secrets Manager team files ##
bitwarden_license/bit-web/src/app/secrets-manager @bitwarden/team-secrets-manager-dev
## Desktop native module ##
apps/desktop/desktop_native @bitwarden/team-platform-dev
apps/desktop/desktop_native/objc/src/native/autofill @bitwarden/team-autofill-dev
apps/desktop/desktop_native/core/src/autofill @bitwarden/team-autofill-dev
## No ownership fo Cargo.lock and Cargo.toml to allow dependency updates
apps/desktop/desktop_native/Cargo.lock
apps/desktop/desktop_native/Cargo.toml
## Auth team files ##
apps/browser/src/auth @bitwarden/team-auth-dev
@@ -13,6 +18,7 @@ apps/cli/src/auth @bitwarden/team-auth-dev
apps/desktop/src/auth @bitwarden/team-auth-dev
apps/web/src/app/auth @bitwarden/team-auth-dev
libs/auth @bitwarden/team-auth-dev
libs/user-core @bitwarden/team-auth-dev
# web connectors used for auth
apps/web/src/connectors @bitwarden/team-auth-dev
bitwarden_license/bit-web/src/app/auth @bitwarden/team-auth-dev
@@ -30,12 +36,18 @@ libs/common/src/tools @bitwarden/team-tools-dev
libs/importer @bitwarden/team-tools-dev
libs/tools @bitwarden/team-tools-dev
## Localization/Crowdin (Tools team)
apps/browser/src/_locales @bitwarden/team-tools-dev
apps/browser/store/locales @bitwarden/team-tools-dev
apps/cli/src/locales @bitwarden/team-tools-dev
apps/desktop/src/locales @bitwarden/team-tools-dev
apps/web/src/locales @bitwarden/team-tools-dev
## Dirt (Data Insights & Reporting) team files ##
apps/web/src/app/dirt @bitwarden/team-data-insights-and-reporting-dev
bitwarden_license/bit-common/src/dirt @bitwarden/team-data-insights-and-reporting-dev
bitwarden_license/bit-web/src/app/dirt @bitwarden/team-data-insights-and-reporting-dev
libs/dirt @bitwarden/team-data-insights-and-reporting-dev
## Localization/Crowdin (Platform and Tools team)
apps/browser/src/_locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev
apps/browser/store/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev
apps/cli/src/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev
apps/desktop/src/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev
apps/web/src/locales @bitwarden/team-tools-dev @bitwarden/team-platform-dev
## Vault team files ##
apps/browser/src/vault @bitwarden/team-vault-dev
@@ -52,55 +64,112 @@ apps/cli/src/admin-console @bitwarden/team-admin-console-dev
apps/desktop/src/admin-console @bitwarden/team-admin-console-dev
apps/web/src/app/admin-console @bitwarden/team-admin-console-dev
bitwarden_license/bit-web/src/app/admin-console @bitwarden/team-admin-console-dev
bitwarden_license/bit-cli/src/admin-console @bitwarden/team-admin-console-dev
libs/angular/src/admin-console @bitwarden/team-admin-console-dev
libs/common/src/admin-console @bitwarden/team-admin-console-dev
libs/admin-console @bitwarden/team-admin-console-dev
## Billing team files ##
apps/browser/src/billing @bitwarden/team-billing-dev
apps/desktop/src/billing @bitwarden/team-billing-dev
apps/web/src/app/billing @bitwarden/team-billing-dev
libs/angular/src/billing @bitwarden/team-billing-dev
libs/common/src/billing @bitwarden/team-billing-dev
libs/billing @bitwarden/team-billing-dev
bitwarden_license/bit-web/src/app/billing @bitwarden/team-billing-dev
## Platform team files ##
apps/browser/src/platform @bitwarden/team-platform-dev
apps/cli/src/platform @bitwarden/team-platform-dev
apps/desktop/macos @bitwarden/team-platform-dev
apps/desktop/scripts @bitwarden/team-platform-dev
apps/desktop/src/platform @bitwarden/team-platform-dev
apps/desktop/resources @bitwarden/team-platform-dev
apps/web/src/app/platform @bitwarden/team-platform-dev
libs/angular/src/platform @bitwarden/team-platform-dev
libs/common/src/platform @bitwarden/team-platform-dev
libs/common/spec @bitwarden/team-platform-dev
libs/common/src/state-migrations @bitwarden/team-platform-dev
libs/platform @bitwarden/team-platform-dev
# Node-specifc platform files
libs/node @bitwarden/team-platform-dev
libs/storage-core @bitwarden/team-platform-dev
libs/logging @bitwarden/team-platform-dev
libs/storage-test-utils @bitwarden/team-platform-dev
libs/messaging @bitwarden/team-platform-dev
libs/messaging-internal @bitwarden/team-platform-dev
# Web utils used across app and connectors
apps/web/src/utils/ @bitwarden/team-platform-dev
# Web core and shared files
apps/web/src/app/core @bitwarden/team-platform-dev
apps/web/src/app/shared @bitwarden/team-platform-dev
apps/web/src/translation-constants.ts @bitwarden/team-platform-dev
# Workflows
.github/workflows/automatic-issue-responses.yml @bitwarden/team-platform-dev
.github/workflows/automatic-pull-request-responses.yml @bitwarden/team-platform-dev
.github/workflows/build-browser-target.yml @bitwarden/team-platform-dev
.github/workflows/build-browser.yml @bitwarden/team-platform-dev
.github/workflows/build-cli-target.yml @bitwarden/team-platform-dev
.github/workflows/build-cli.yml @bitwarden/team-platform-dev
.github/workflows/build-desktop-target.yml @bitwarden/team-platform-dev
.github/workflows/build-desktop.yml @bitwarden/team-platform-dev
.github/workflows/build-web-target.yml @bitwarden/team-platform-dev
.github/workflows/build-web.yml @bitwarden/team-platform-dev
.github/workflows/chromatic.yml @bitwarden/team-platform-dev
.github/workflows/crowdin-pull.yml @bitwarden/team-platform-dev
.github/workflows/enforce-labels.yml @bitwarden/team-platform-dev
.github/workflows/lint.yml @bitwarden/team-platform-dev
.github/workflows/locales-lint.yml @bitwarden/team-platform-dev
.github/workflows/repository-management.yml @bitwarden/team-platform-dev
.github/workflows/scan.yml @bitwarden/team-platform-dev
.github/workflows/stale-bot.yml @bitwarden/team-platform-dev
.github/workflows/test.yml @bitwarden/team-platform-dev
.github/workflows/version-auto-bump.yml @bitwarden/team-platform-dev
# ESLint custom rules
libs/eslint @bitwarden/team-platform-dev
# Typescript tooling
tsconfig.base.json @bitwarden/team-platform-dev
nx.json @bitwarden/team-platform-dev
## Autofill team files ##
apps/browser/src/autofill @bitwarden/team-autofill-dev
apps/desktop/src/autofill @bitwarden/team-autofill-dev
libs/common/src/autofill @bitwarden/team-autofill-dev
apps/desktop/macos/autofill-extension @bitwarden/team-autofill-dev
apps/desktop/src/app/components/fido2placeholder.component.ts @bitwarden/team-autofill-dev
apps/desktop/desktop_native/windows_plugin_authenticator @bitwarden/team-autofill-dev
apps/desktop/desktop_native/autotype @bitwarden/team-autofill-dev
# DuckDuckGo integration
apps/desktop/native-messaging-test-runner @bitwarden/team-autofill-dev
apps/desktop/src/services/duckduckgo-message-handler.service.ts @bitwarden/team-autofill-dev
apps/desktop/src/services/encrypted-message-handler.service.ts @bitwarden/team-autofill-dev
.github/workflows/alert-ddg-files-modified.yml @bitwarden/team-autofill-dev
# SSH Agent
apps/desktop/desktop_native/core/src/ssh_agent @bitwarden/team-autofill-dev @bitwarden/wg-ssh-keys
## Component Library ##
.storybook @bitwarden/team-component-library
libs/components @bitwarden/team-component-library
apps/web/src/app/layouts/header
## UI Foundation ##
.storybook @bitwarden/team-ui-foundation
libs/components @bitwarden/team-ui-foundation
libs/ui @bitwarden/team-ui-foundation
apps/browser/src/platform/popup/layout @bitwarden/team-ui-foundation
apps/browser/src/popup/app-routing.animations.ts @bitwarden/team-ui-foundation
apps/browser/src/popup/components/extension-anon-layout-wrapper @bitwarden/team-ui-foundation
apps/web/src/app/layouts @bitwarden/team-ui-foundation
## Desktop native module ##
apps/desktop/desktop_native @bitwarden/team-platform-dev
## DevOps team files ##
/.github/workflows @bitwarden/dept-devops
## Key management team files ##
apps/desktop/src/key-management @bitwarden/team-key-management-dev
apps/web/src/app/key-management @bitwarden/team-key-management-dev
apps/browser/src/key-management @bitwarden/team-key-management-dev
apps/cli/src/key-management @bitwarden/team-key-management-dev
libs/key-management @bitwarden/team-key-management-dev
libs/key-management-ui @bitwarden/team-key-management-dev
libs/common/src/key-management @bitwarden/team-key-management-dev
# Node-cryptofunction service
libs/node @bitwarden/team-key-management-dev
# DevOps for Docker changes.
**/Dockerfile @bitwarden/dept-devops
**/*.Dockerfile @bitwarden/dept-devops
**/.dockerignore @bitwarden/dept-devops
**/entrypoint.sh @bitwarden/dept-devops
apps/desktop/desktop_native/core/src/biometric/ @bitwarden/team-key-management-dev
apps/desktop/src/services/native-messaging.service.ts @bitwarden/team-key-management-dev
apps/browser/src/background/nativeMessaging.background.ts @bitwarden/team-key-management-dev
apps/desktop/src/services/biometric-message-handler.service.ts @bitwarden/team-key-management-dev
## Locales ##
apps/browser/src/_locales/en/messages.json
@@ -108,3 +177,31 @@ apps/browser/store/locales/en
apps/cli/src/locales/en/messages.json
apps/desktop/src/locales/en/messages.json
apps/web/src/locales/en/messages.json
## BRE team owns these workflows ##
.github/workflows/brew-bump-desktop.yml @bitwarden/dept-bre
.github/workflows/deploy-web.yml @bitwarden/dept-bre
.github/workflows/publish-cli.yml @bitwarden/dept-bre
.github/workflows/publish-desktop.yml @bitwarden/dept-bre
.github/workflows/publish-web.yml @bitwarden/dept-bre
.github/workflows/retrieve-current-desktop-rollout.yml @bitwarden/dept-bre
.github/workflows/staged-rollout-desktop.yml @bitwarden/dept-bre
.github/workflows/release-browser.yml @bitwarden/dept-bre
.github/workflows/release-cli.yml @bitwarden/dept-bre
.github/workflows/release-desktop-beta.yml @bitwarden/dept-bre
.github/workflows/release-desktop.yml @bitwarden/dept-bre
.github/workflows/release-web.yml @bitwarden/dept-bre
## Docker files have shared ownership ##
**/Dockerfile
**/*.Dockerfile
**/.dockerignore
**/entrypoint.sh
## Overrides
# For the time being platform owns tsconfig and jest config
# These overrides will be removed after Nx is implemented
# To track that effort please see https://bitwarden.atlassian.net/browse/PM-21636
**/tsconfig.json @bitwarden/team-platform-dev
**/jest.config.js @bitwarden/team-platform-dev
**/project.jsons @bitwarden/team-platform-dev

View File

@@ -1,8 +1,19 @@
labels: ["discussions-new"]
body:
- type: markdown
attributes:
value: |
If you would like to contribute code to the Bitwarden codebase for consideration, please review [https://contributing.bitwarden.com/](https://contributing.bitwarden.com/) before posting. To keep discussion on topic, posts that do not include a proposal for a code contribution you wish to develop will be removed. For feature requests and community discussion, please visit https://community.bitwarden.com/
If you would like to contribute code to the Bitwarden codebase for consideration, please review [https://contributing.bitwarden.com/](https://contributing.bitwarden.com/) before posting. To keep discussion on topic, posts that do not include a proposal for a code contribution you wish to develop will be removed.
- type: dropdown
attributes:
label: Select Topic Area
description: "What would you like to discuss? :warning: For feature requests and product feedback, please visit https://community.bitwarden.com/"
options:
- "✅ Code Contribution Proposal"
- "🚫 Product Feedback"
- "🚫 Feature Request"
validations:
required: true
- type: textarea
attributes:
label: Code Contribution Proposal

View File

@@ -1,8 +1,19 @@
labels: ["discussions-new"]
body:
- type: markdown
attributes:
value: |
If you would like to contribute code to the Bitwarden codebase for consideration, please review [https://contributing.bitwarden.com/](https://contributing.bitwarden.com/) before posting. To keep discussion on topic, posts that do not include a proposal for a code contribution you wish to develop will be removed. For feature requests and community discussion, please visit https://community.bitwarden.com/
If you would like to contribute code to the Bitwarden codebase for consideration, please review [https://contributing.bitwarden.com/](https://contributing.bitwarden.com/) before posting. To keep discussion on topic, posts that do not include a proposal for a code contribution you wish to develop will be removed.
- type: dropdown
attributes:
label: Select Topic Area
description: "What would you like to discuss? :warning: For feature requests and product feedback, please visit https://community.bitwarden.com/"
options:
- "✅ Code Contribution Proposal"
- "🚫 Product Feedback"
- "🚫 Feature Request"
validations:
required: true
- type: textarea
attributes:
label: Code Contribution Proposal

View File

@@ -1,4 +1,4 @@
name: Browser Bug Report
name: Browser Extension Bug Report
description: File a bug report
labels: [bug, browser]
body:
@@ -84,11 +84,11 @@ body:
attributes:
label: Browser Version
description: What version of the browser(s) are you seeing the problem on?
- type: input
- type: textarea
id: version
attributes:
label: Build Version
description: What version of our software are you running? (go to "Settings" → "About" in the extension)
label: Environment Versions
description: Copy from "Settings" → "About" → "About Bitwarden" in the extension. Should include the extension version and server environment.
validations:
required: true
- type: checkboxes

View File

@@ -73,6 +73,7 @@ body:
- Homebrew
- Chocolatey
- Snap
- Flatpak
- Other
validations:
required: true

View File

@@ -1,4 +1,4 @@
name: Web Bug Report
name: Web App Bug Report
description: File a bug report
labels: [bug, web]
body:
@@ -77,6 +77,7 @@ body:
- Opera
- Brave
- Vivaldi
- DuckDuckGo
validations:
required: true
- type: input

View File

@@ -1,33 +1,35 @@
## Type of change
## 🎟️ Tracking
<!-- (mark with an `X`) -->
<!-- Paste the link to the Jira or GitHub issue or otherwise describe / point to where this change is coming from. -->
```
- [ ] Bug fix
- [ ] New feature development
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
- [ ] Build/deploy pipeline (DevOps)
- [ ] Other
```
## 📔 Objective
## Objective
<!-- Describe what the purpose of this PR is, for example what bug you're fixing or new feature you're adding. -->
<!--Describe what the purpose of this PR is. For example: what bug you're fixing or what new feature you're adding-->
## 📸 Screenshots
## Code changes
<!-- Required for any UI changes; delete if not applicable. Use fixed width images for better display. -->
<!--Explain the changes you've made to each file or major component. This should help the reviewer understand your changes-->
<!--Also refer to any related changes or PRs in other repositories-->
## ⏰ Reminders before review
- **file.ext:** Description of what was changed and why
- Contributor guidelines followed
- All formatters and local linters executed and passed
- Written new unit and / or integration tests where applicable
- Protected functional changes with optionality (feature flags)
- Used internationalization (i18n) for all UI strings
- CI builds passed
- Communicated to DevOps any deployment requirements
- Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team
## Screenshots
## 🦮 Reviewer guidelines
<!--Required for any UI changes. Delete if not applicable-->
<!-- Suggested interactions but feel free to use (or not) as you desire! -->
## Before you submit
- Please add **unit tests** where it makes sense to do so (encouraged but not required)
- If this change requires a **documentation update** - notify the documentation team
- If this change has particular **deployment requirements** - notify the DevOps team
- Ensure that all UI additions follow [WCAG AA requirements](https://contributing.bitwarden.com/contributing/accessibility/)
- 👍 (`:+1:`) or similar for great changes
- 📝 (`:memo:`) or (`:information_source:`) for notes or general info
- ❓ (`:question:`) for questions
- 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
- 🎨 (`:art:`) for suggestions / improvements
- ❌ (`:x:`) or ⚠️ (`:warning:`) for more significant problems or concerns needing attention
- 🌱 (`:seedling:`) or ♻️ (`:recycle:`) for future improvements or indications of technical debt
- ⛏ (`:pick:`) for minor or nitpick changes

67
.github/codecov.yml vendored
View File

@@ -1,2 +1,69 @@
ignore:
- "**/*.spec.ts" # Tests
component_management:
default_rules:
statuses:
- type: project
target: auto
individual_components:
- component_id: key-management-biometrics
name: Key Management - Biometrics
paths:
- apps/browser/src/key-management/biometrics/**
- apps/cli/src/key-management/cli-biometrics-service.ts
- apps/desktop/desktop_native/core/src/biometric/**
- apps/desktop/src/key-management/biometrics/**
- apps/desktop/src/services/biometric-message-handler.service.ts
- apps/web/src/app/key-management/web-biometric.service.ts
- libs/key-management/src/biometrics/**
- component_id: key-management-lock
name: Key Management - Lock
paths:
- apps/browser/src/key-management/lock/**
- apps/desktop/src/key-management/lock/**
- apps/web/src/app/key-management/lock/**
- libs/key-management-ui/src/lock/**
- libs/common/src/key-management/device-trust/**
- component_id: key-management-ipc
name: Key Management - IPC
paths:
- apps/browser/src/background/nativeMessaging.background.ts
- apps/desktop/src/services/native-messaging.service.ts
- component_id: key-management-key-rotation
name: Key Management - Key Rotation
paths:
- apps/web/src/app/key-management/key-rotation/**
- apps/web/src/app/key-management/migrate-encryption/**
- libs/key-management/src/user-asymmetric-key-regeneration/**
- component_id: key-management-process-reload
name: Key Management - Process Reload
paths:
- apps/web/src/app/key-management/services/web-process-reload.service.ts
- libs/common/src/key-management/services/default-process-reload.service.ts
- component_id: key-management-keys
name: Key Management - Keys
paths:
- apps/desktop/src/key-management/electron-key.service.ts
- libs/key-management/src/kdf-config.service.ts
- libs/key-management/src/key.service.ts
- libs/common/src/key-management/master-password/**
- libs/common/src/key-management/key-connector/**
- component_id: key-management-crypto
name: Key Management - Crypto
paths:
- libs/common/src/key-management/crypto/**
- component_id: key-management
name: Key Management
paths:
- apps/browser/src/key-management/**
- apps/browser/src/background/nativeMessaging.background.ts
- apps/cli/src/key-management/**
- apps/desktop/desktop_native/core/src/biometric/**
- apps/desktop/src/key-management/**
- apps/desktop/src/services/biometric-message-handler.service.ts
- apps/desktop/src/services/native-messaging.service.ts
- apps/web/src/app/key-management/**
- libs/common/src/key-management/**
- libs/key-management/**
- libs/key-management-ui/**

263
.github/renovate.json vendored
View File

@@ -1,263 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["github>bitwarden/renovate-config"],
"enabledManagers": ["cargo", "github-actions", "npm"],
"packageRules": [
{
"groupName": "gh minor",
"matchManagers": ["github-actions"],
"matchUpdateTypes": ["minor", "patch"]
},
{
"matchManagers": ["github-actions"],
"commitMessagePrefix": "[deps] DevOps:"
},
{
"matchManagers": ["cargo"],
"commitMessagePrefix": "[deps] Platform:"
},
{
"matchPackageNames": ["typescript", "zone.js"],
"matchUpdateTypes": ["major", "minor"],
"description": "Determined by Angular",
"enabled": false
},
{
"matchPackageNames": ["typescript", "zone.js"],
"matchUpdateTypes": "patch"
},
{
"groupName": "jest",
"matchPackageNames": ["@types/jest", "jest", "ts-jest", "jest-preset-angular"],
"matchUpdateTypes": "major"
},
{
"matchPackageNames": [
"@ngtools/webpack",
"base64-loader",
"buffer",
"bufferutil",
"clean-webpack-plugin",
"copy-webpack-plugin",
"core-js",
"css-loader",
"html-loader",
"html-webpack-injector",
"html-webpack-plugin",
"mini-css-extract-plugin",
"ngx-infinite-scroll",
"postcss",
"postcss-loader",
"process",
"sass",
"sass-loader",
"style-loader",
"ts-loader",
"tsconfig-paths-webpack-plugin",
"url",
"util",
"webpack",
"webpack-cli",
"webpack-dev-server",
"webpack-node-externals"
],
"description": "Admin Console owned dependencies",
"commitMessagePrefix": "[deps] AC:",
"reviewers": ["team:team-admin-console-dev"]
},
{
"matchPackageNames": [
"@types/duo_web_sdk",
"@types/node-ipc",
"duo_web_sdk",
"node-ipc",
"qrious",
"regedit"
],
"description": "Auth owned dependencies",
"commitMessagePrefix": "[deps] Auth:",
"reviewers": ["team:team-auth-dev"]
},
{
"matchPackageNames": [
"@webcomponents/custom-elements",
"concurrently",
"cross-env",
"del",
"gulp",
"gulp-filter",
"gulp-if",
"gulp-json-editor",
"gulp-replace",
"gulp-zip",
"nord",
"patch-package",
"prettier",
"prettier-plugin-tailwindcss",
"rimraf",
"tabbable",
"tldts",
"wait-on"
],
"description": "Autofill owned dependencies",
"commitMessagePrefix": "[deps] Autofill:",
"reviewers": ["team:team-autofill-dev"]
},
{
"matchPackageNames": ["braintree-web-drop-in"],
"description": "Billing owned dependencies",
"commitMessagePrefix": "[deps] Billing:",
"reviewers": ["team:team-billing-dev"]
},
{
"matchPackageNames": [
"@angular-devkit/build-angular",
"@angular/animations",
"@angular/cdk",
"@angular/cli",
"@angular/common",
"@angular/compiler",
"@angular/compiler-cli",
"@angular/core",
"@angular/forms",
"@angular/platform",
"@angular/compiler",
"@angular/router",
"@types/argon2-browser",
"@types/chrome",
"@types/firefox-webext-browser",
"@types/jquery",
"@types/node",
"@types/node-forge",
"argon2",
"argon2-browser",
"big-integer",
"bootstrap",
"jquery",
"node-forge",
"popper.js",
"rxjs",
"type-fest",
"typescript",
"zone.js"
],
"description": "Platform owned dependencies",
"commitMessagePrefix": "[deps] Platform:",
"reviewers": ["team:team-platform-dev"]
},
{
"matchPackageNames": [
"@compodoc/compodoc",
"@ng-select/ng-select",
"@storybook/addon-a11y",
"@storybook/addon-actions",
"@storybook/addon-designs",
"@storybook/addon-essentials",
"@storybook/addon-links",
"@storybook/angular",
"@types/react",
"autoprefixer",
"chromatic",
"ngx-toastr",
"react",
"react-dom",
"remark-gfm",
"storybook",
"tailwindcss"
],
"description": "Component library owned dependencies",
"commitMessagePrefix": "[deps] Platform (CL):",
"reviewers": ["team:team-component-library"]
},
{
"matchPackageNames": [
"@angular-eslint/eslint-plugin",
"@angular-eslint/eslint-plugin-template",
"@angular-eslint/template-parser",
"@types/jest",
"@typescript-eslint/eslint-plugin",
"@typescript-eslint/parser",
"eslint",
"eslint-config-prettier",
"eslint-import-resolver-typescript",
"eslint-plugin-import",
"eslint-plugin-rxjs",
"eslint-plugin-rxjs-angular",
"eslint-plugin-storybook",
"eslint-plugin-tailwindcss",
"husky",
"jest-junit",
"jest-mock-extended",
"jest-preset-angular",
"lint-staged",
"ts-jest"
],
"description": "Secrets Manager owned dependencies",
"commitMessagePrefix": "[deps] SM:",
"reviewers": ["team:team-secrets-manager-dev"]
},
{
"matchPackageNames": [
"@electron/notarize",
"@electron/rebuild",
"@microsoft/signalr-protocol-msgpack",
"@microsoft/signalr",
"@types/jsdom",
"@types/papaparse",
"@types/zxcvbn",
"electron-builder",
"electron-log",
"electron-reload",
"electron-store",
"electron-updater",
"electron",
"jsdom",
"jszip",
"oidc-client-ts",
"papaparse",
"utf-8-validate",
"zxcvbn"
],
"description": "Tools owned dependencies",
"commitMessagePrefix": "[deps] Tools:",
"reviewers": ["team:team-tools-dev"]
},
{
"matchPackageNames": [
"@koa/multer",
"@koa/router",
"@types/inquirer",
"@types/koa",
"@types/koa__multer",
"@types/koa__router",
"@types/koa-bodyparser",
"@types/koa-json",
"@types/lowdb",
"@types/lunr",
"@types/node-fetch",
"@types/proper-lockfile",
"@types/retry",
"chalk",
"commander",
"form-data",
"https-proxy-agent",
"inquirer",
"koa",
"koa-bodyparser",
"koa-json",
"lowdb",
"lunr",
"multer",
"node-fetch",
"open",
"pkg",
"proper-lockfile",
"qrcode-parser"
],
"description": "Vault owned dependencies",
"commitMessagePrefix": "[deps] Vault:",
"reviewers": ["team:team-vault-dev"]
}
],
"ignoreDeps": ["@types/koa-bodyparser", "bootstrap", "node-ipc", "node", "npm", "regedit"]
}

420
.github/renovate.json5 vendored Normal file
View File

@@ -0,0 +1,420 @@
{
$schema: "https://docs.renovatebot.com/renovate-schema.json",
extends: ["github>bitwarden/renovate-config"], // Extends our default configuration for pinned dependencies
enabledManagers: ["cargo", "github-actions", "npm"],
packageRules: [
{
// Group all Github Action minor updates together to reduce PR noise.
groupName: "Minor github-actions updates",
matchManagers: ["github-actions"],
matchUpdateTypes: ["minor"],
addLabels: ["hold"],
},
{
// Enable support for Rust toolchain updates.
matchManagers: ["custom.regex"],
matchDepNames: ["rust"],
commitMessageTopic: "Rust",
},
{
// By default, we send patch updates to the Dependency Dashboard and do not generate a PR.
// We want to generate PRs for a select number of dependencies to ensure we stay up to date on these.
matchPackageNames: ["browserslist", "electron", "rxjs", "typescript", "webpack", "zone.js"],
matchUpdateTypes: ["patch"],
dependencyDashboardApproval: false,
},
{
// Disable major and minor updates for TypeScript and Zone.js because they are managed by Angular.
matchPackageNames: ["typescript", "zone.js"],
matchUpdateTypes: ["major", "minor"],
description: "Determined by Angular",
enabled: false,
},
{
// Disable major updates for core Angular dependencies because they are managed through ng update
// when we decide to upgrade.
matchSourceUrls: [
"https://github.com/angular-eslint/angular-eslint",
"https://github.com/angular/angular-cli",
"https://github.com/angular/angular",
"https://github.com/angular/components",
"https://github.com/ng-select/ng-select",
],
matchUpdateTypes: ["major"],
description: "Manually updated using ng update",
enabled: false,
},
{
matchPackageNames: ["buffer", "bufferutil", "core-js", "process", "url", "util"],
description: "Admin Console owned dependencies",
commitMessagePrefix: "[deps] AC:",
reviewers: ["team:team-admin-console-dev"],
},
{
matchPackageNames: ["qrious"],
description: "Auth owned dependencies",
commitMessagePrefix: "[deps] Auth:",
reviewers: ["team:team-auth-dev"],
},
{
matchPackageNames: [
"@angular-eslint/schematics",
"@eslint/compat",
"@typescript-eslint/rule-tester",
"@typescript-eslint/utils",
"angular-eslint",
"eslint-config-prettier",
"eslint-import-resolver-typescript",
"eslint-plugin-import",
"eslint-plugin-rxjs-angular",
"eslint-plugin-rxjs",
"eslint-plugin-storybook",
"eslint-plugin-tailwindcss",
"eslint",
"husky",
"lint-staged",
"typescript-eslint",
],
description: "Architecture owned dependencies",
commitMessagePrefix: "[deps] Architecture:",
reviewers: ["team:dept-architecture"],
},
{
matchPackageNames: [
"@angular-eslint/schematics",
"@eslint/compat",
"@typescript-eslint/rule-tester",
"@typescript-eslint/utils",
"angular-eslint",
"eslint-config-prettier",
"eslint-import-resolver-typescript",
"eslint-plugin-import",
"eslint-plugin-rxjs-angular",
"eslint-plugin-rxjs",
"eslint-plugin-storybook",
"eslint-plugin-tailwindcss",
"eslint",
"husky",
"lint-staged",
"typescript-eslint",
],
groupName: "Minor and patch linting updates",
matchUpdateTypes: ["minor", "patch"],
},
{
matchPackageNames: [
"@emotion/css",
"@webcomponents/custom-elements",
"bitwarden-russh",
"bytes",
"concurrently",
"cross-env",
"del",
"ed25519",
"lit",
"@lit-labs/signals",
"patch-package",
"pkcs8",
"prettier",
"prettier-plugin-tailwindcss",
"rimraf",
"ssh-encoding",
"ssh-key",
"@storybook/web-components-webpack5",
"tabbable",
"tldts",
"wait-on",
],
description: "Autofill owned dependencies",
commitMessagePrefix: "[deps] Autofill:",
reviewers: ["team:team-autofill-dev"],
},
{
matchPackageNames: ["braintree-web-drop-in"],
description: "Billing owned dependencies",
commitMessagePrefix: "[deps] Billing:",
reviewers: ["team:team-billing-dev"],
},
{
matchPackageNames: [
"@babel/core",
"@babel/preset-env",
"@bitwarden/sdk-internal",
"@electron/fuses",
"@electron/notarize",
"@electron/rebuild",
"@ngtools/webpack",
"@nx/devkit",
"@nx/eslint",
"@nx/jest",
"@nx/js",
"@types/chrome",
"@types/firefox-webext-browser",
"@types/glob",
"@types/lowdb",
"@types/node",
"@types/node-forge",
"@types/node-ipc",
"@yao-pkg/pkg",
"anyhow",
"arboard",
"babel-loader",
"base64-loader",
"base64",
"bindgen",
"browserslist",
"byteorder",
"bytes",
"core-foundation",
"copy-webpack-plugin",
"css-loader",
"dirs",
"electron",
"electron-builder",
"electron-log",
"electron-reload",
"electron-store",
"electron-updater",
"embed_plist",
"futures",
"hex",
"homedir",
"html-loader",
"html-webpack-injector",
"html-webpack-plugin",
"interprocess",
"json5",
"keytar",
"libc",
"log",
"lowdb",
"mini-css-extract-plugin",
"napi",
"napi-build",
"napi-derive",
"node-forge",
"node-ipc",
"nx",
"oo7",
"oslog",
"pin-project",
"pkg",
"postcss",
"postcss-loader",
"rand",
"rxjs",
"sass",
"sass-loader",
"scopeguard",
"security-framework",
"security-framework-sys",
"semver",
"serde",
"serde_json",
"simplelog",
"style-loader",
"sysinfo",
"ts-node",
"ts-loader",
"tsconfig-paths-webpack-plugin",
"type-fest",
"typenum",
"typescript",
"typescript-strict-plugin",
"uniffi",
"webpack",
"webpack-cli",
"webpack-dev-server",
"webpack-node-externals",
"widestring",
"windows",
"windows-future",
"windows-registry",
"zbus",
"zbus_polkit",
],
description: "Platform owned dependencies",
commitMessagePrefix: "[deps] Platform:",
reviewers: ["team:team-platform-dev"],
},
{
// We need to group all napi-related packages together to avoid build errors caused by version incompatibilities.
groupName: "napi",
matchPackageNames: ["napi", "napi-build", "napi-derive"],
},
{
// We need to group all macOS/iOS binding-related packages together to avoid build errors caused by version incompatibilities.
groupName: "macOS/iOS bindings",
matchPackageNames: ["core-foundation", "security-framework", "security-framework-sys"],
},
{
// We need to group all zbus-related packages together to avoid build errors caused by version incompatibilities.
groupName: "zbus",
matchPackageNames: ["zbus", "zbus_polkit"],
},
{
// We group all webpack build-related minor and patch updates together to reduce PR noise.
// We include patch updates here because we want PRs for webpack patch updates and it's in this group.
matchPackageNames: [
"@babel/core",
"@babel/preset-env",
"babel-loader",
"base64-loader",
"browserslist",
"copy-webpack-plugin",
"css-loader",
"html-loader",
"html-webpack-injector",
"html-webpack-plugin",
"mini-css-extract-plugin",
"postcss-loader",
"postcss",
"sass-loader",
"sass",
"style-loader",
"ts-loader",
"tsconfig-paths-webpack-plugin",
"webpack-cli",
"webpack-dev-server",
"webpack-node-externals",
"webpack",
],
description: "webpack-related build dependencies",
groupName: "Minor and patch webpack updates",
matchUpdateTypes: ["minor", "patch"],
},
{
matchPackageNames: [
"@angular-devkit/build-angular",
"@angular/animations",
"@angular/cdk",
"@angular/cli",
"@angular/common",
"@angular/compiler-cli",
"@angular/compiler",
"@angular/core",
"@angular/forms",
"@angular/platform-browser-dynamic",
"@angular/platform-browser",
"@angular/platform",
"@angular/router",
"axe-playwright",
"@compodoc/compodoc",
"@ng-select/ng-select",
"@storybook/addon-a11y",
"@storybook/addon-actions",
"@storybook/addon-designs",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
"@storybook/addon-links",
"@storybook/test-runner",
"@storybook/addon-themes",
"@storybook/angular",
"@storybook/manager-api",
"@storybook/theming",
"@types/react",
"autoprefixer",
"bootstrap",
"chromatic",
"ngx-toastr",
"react",
"react-dom",
"remark-gfm",
"storybook",
"tailwindcss",
"zone.js",
],
description: "UI Foundation owned dependencies",
commitMessagePrefix: "[deps] UI Foundation:",
reviewers: ["team:team-ui-foundation"],
},
{
matchPackageNames: [
"@types/jest",
"jest-junit",
"jest-mock-extended",
"jest-preset-angular",
"jest-diff",
"ts-jest",
],
description: "Secrets Manager owned dependencies",
commitMessagePrefix: "[deps] SM:",
reviewers: ["team:team-secrets-manager-dev"],
},
{
// We need to update several Jest-related packages together, for version compatibility.
groupName: "jest",
matchPackageNames: ["@types/jest", "jest", "ts-jest", "jest-preset-angular"],
},
{
matchPackageNames: [
"@microsoft/signalr-protocol-msgpack",
"@microsoft/signalr",
"@types/jsdom",
"@types/papaparse",
"@types/zxcvbn",
"jsdom",
"jszip",
"oidc-client-ts",
"papaparse",
"utf-8-validate",
"zxcvbn",
],
description: "Tools owned dependencies",
commitMessagePrefix: "[deps] Tools:",
reviewers: ["team:team-tools-dev"],
},
{
matchPackageNames: [
"@koa/multer",
"@koa/router",
"@types/inquirer",
"@types/koa",
"@types/koa__multer",
"@types/koa__router",
"@types/koa-bodyparser",
"@types/koa-json",
"@types/lunr",
"@types/node-fetch",
"@types/proper-lockfile",
"@types/retry",
"chalk",
"commander",
"form-data",
"https-proxy-agent",
"inquirer",
"koa",
"koa-bodyparser",
"koa-json",
"lunr",
"multer",
"node-fetch",
"open",
"proper-lockfile",
"qrcode-parser",
],
description: "Vault owned dependencies",
commitMessagePrefix: "[deps] Vault:",
reviewers: ["team:team-vault-dev"],
},
{
matchPackageNames: ["aes", "big-integer", "cbc", "rsa", "russh-cryptovec", "sha2"],
description: "Key Management owned dependencies",
commitMessagePrefix: "[deps] KM:",
reviewers: ["team:team-key-management-dev"],
},
{
// Any versions of lowdb above 1.0.0 are not compatible with CommonJS.
matchPackageNames: ["lowdb"],
allowedVersions: "1.0.0",
description: "Higher versions of lowdb are not compatible with CommonJS",
},
{
// Pin types as well since we are not upgrading past v1 (and also v2+ does not need separate types).
matchPackageNames: ["@types/lowdb"],
allowedVersions: "< 2.0.0",
description: "Higher versions of lowdb do not need separate types",
},
],
ignoreDeps: ["@types/koa-bodyparser", "bootstrap", "node-ipc", "@bitwarden/sdk-internal"],
}

View File

@@ -3,32 +3,12 @@
./apps/browser/src/safari/desktop/Assets.xcassets/AppIcon.appiconset
./apps/browser/src/safari/desktop/Base.lproj
./apps/browser/store/windows/Assets
./bitwarden_license/README.md
./libs/angular/src/directives/cipherListVirtualScroll.directive.ts
./libs/angular/src/scss/webfonts/Open_Sans-italic-700.woff
./libs/angular/src/scss/webfonts/Open_Sans-normal-300.woff
./libs/angular/src/scss/webfonts/Open_Sans-normal-700.woff
./libs/angular/src/scss/webfonts/Open_Sans-italic-300.woff
./libs/angular/src/scss/webfonts/Open_Sans-italic-600.woff
./libs/angular/src/scss/webfonts/Open_Sans-italic-800.woff
./libs/angular/src/scss/webfonts/Open_Sans-italic-400.woff
./libs/angular/src/scss/webfonts/Open_Sans-normal-600.woff
./libs/angular/src/scss/webfonts/Open_Sans-normal-800.woff
./libs/angular/src/scss/webfonts/Open_Sans-normal-400.woff
./libs/admin-console/README.md
./libs/auth/README.md
./libs/billing/README.md
./libs/platform/README.md
./libs/tools/README.md
./libs/tools/export/vault-export/README.md
./libs/vault/README.md
./README.md
./LICENSE_BITWARDEN.txt
./CONTRIBUTING.md
./LICENSE_GPL.txt
./LICENSE.txt
./apps/web/Dockerfile
./apps/web/README.md
./apps/desktop/resources/installerSidebar.bmp
./apps/desktop/resources/appx/SplashScreen.png
./apps/desktop/resources/appx/BadgeLogo.png
@@ -36,16 +16,10 @@
./apps/desktop/resources/appx/StoreLogo.png
./apps/desktop/resources/appx/Wide310x150Logo.png
./apps/desktop/resources/appx/Square44x44Logo.png
./apps/desktop/README.md
./apps/desktop/desktop_native/Cargo.toml
./apps/desktop/desktop_native/Cargo.lock
./apps/cli/stores/chocolatey/tools/VERIFICATION.txt
./apps/cli/README.md
./apps/browser/README.md
./apps/browser/store/windows/AppxManifest.xml
./apps/browser/src/background/nativeMessaging.background.ts
./apps/browser/src/models/browserComponentState.ts
./apps/browser/src/models/browserSendComponentState.ts
./apps/browser/src/models/browserGroupingsComponentState.ts
./apps/browser/src/models/biometricErrors.ts
./apps/browser/src/browser/safariApp.ts
@@ -60,3 +34,4 @@
./apps/browser/src/safari/safari/Info.plist
./apps/browser/src/safari/desktop.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
./SECURITY.md
./libs/nx-plugin/src/generators/files/README.md__tmpl__

View File

@@ -0,0 +1,50 @@
name: DDG File Change Monitor
on:
pull_request:
branches: [ main ]
types: [ opened, synchronize ]
jobs:
check-files:
name: Check files
runs-on: ubuntu-22.04
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
list-files: shell
filters: |
monitored:
- 'apps/desktop/native-messaging-test-runner/**'
- 'apps/desktop/src/services/duckduckgo-message-handler.service.ts'
- 'apps/desktop/src/services/encrypted-message-handler.service.ts'
- name: Comment on PR if monitored files changed
if: steps.changed-files.outputs.monitored == 'true'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const changedFiles = `${{ steps.changed-files.outputs.monitored_files }}`.split(' ').filter(file => file.trim() !== '');
const message = `⚠️🦆 **DuckDuckGo Integration files have been modified in this PR:**
${changedFiles.map(file => `- \`${file}\``).join('\n')}
Please run the DuckDuckGo native messaging test runner from this branch using [these instructions](https://contributing.bitwarden.com/getting-started/clients/desktop/native-messaging-test-runner) and ensure it functions properly.`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: message
});

View File

@@ -1,4 +1,3 @@
---
name: Auto Update Branch
on:
@@ -23,13 +22,15 @@ jobs:
env:
_BOT_EMAIL: 106330231+bitwarden-devops-bot@users.noreply.github.com
_BOT_NAME: bitwarden-devops-bot
permissions:
contents: write
steps:
- name: Setup
id: setup
run: echo "branch=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: 'eu-web-${{ steps.setup.outputs.branch }}'
fetch-depth: 0

View File

@@ -0,0 +1,115 @@
name: Auto-reply to Discussions
on:
discussion:
types: created
jobs:
reply:
name: Auto-reply
runs-on: ubuntu-22.04
permissions:
discussions: write
contents: read
steps:
- name: Get discussion label and template name
id: discussion-label
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
const discussion = context.payload.discussion;
const template_name = discussion.category.slug;
const label = discussion.labels?.[0]?.name ?? '';
console.log('Discussion label:', label);
console.log('Discussion category slug:', template_name);
core.setOutput('label', label);
core.setOutput('template_name', template_name);
- name: Get selected topic
id: get_selected_topic
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
result-encoding: string
script: |
try {
const body = context.payload.discussion.body;
const match = body.match(/### Select Topic Area\n\n(.*?)\n\n/);
console.log('Match:', match);
console.log('Match1:', match[1]);
return match ? match[1].trim() : "";
} catch (error) {
console.error('Error getting selected topic:', error);
return "";
}
- name: Reply or close Discussion
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
env:
TEMPLATE_NAME: ${{ steps.discussion-label.outputs.template_name }}
TOPIC: ${{ steps.get_selected_topic.outputs.result }}
with:
script: |
async function addComment(discussionId, body) {
await github.graphql(`
mutation AddDiscussionComment($discussionId: ID!, $body: String!) {
addDiscussionComment(input: {discussionId: $discussionId, body: $body}) {
comment {
id
}
}
}
`, {
discussionId,
body
});
}
async function closeDiscussion(discussionId) {
await github.graphql(`
mutation {
closeDiscussion(input: {discussionId: "${discussionId}"}) {
discussion {
id
}
}
}
`);
console.log('♻️ Closing discussion');
}
const discussion = context.payload.discussion;
const isFeatureRequest = process.env.TEMPLATE_NAME === 'feature-request';
const isTopicEmpty = !process.env.TOPIC || process.env.TOPIC.trim() === ''; // topic step may have failed
const isCodeTopic = process.env.TOPIC.includes('Code Contribution Proposal');
const shouldClose = isFeatureRequest || (!isTopicEmpty && !isCodeTopic);
console.log('Template name:', process.env.TEMPLATE_NAME);
console.log('Topic:', process.env.TOPIC);
console.log('isTopicEmpty:', isTopicEmpty);
console.log('isCodeTopic:', isCodeTopic);
console.log('shouldClose:', shouldClose);
if (shouldClose) {
const closeMessage =
"Thank you for your contribution! GitHub Discussions is specifically for [proposing code](https://contributing.bitwarden.com/) that you would like to write for the Bitwarden codebase. Since this post does not appear to include a proposal, it will be closed. If you believe this was done in error or have any questions, please feel free to reach out to us!\n\n" +
"- :bulb: For feature requests and general discussions, please visit the [Bitwarden Community Forums](https://community.bitwarden.com/).\n" +
"- :information_source: For questions and support, visit the [Bitwarden Help Center](https://bitwarden.com/help/).\n" +
"- :bug: To report a potential bug, please visit the appropriate repository: [Server](https://github.com/bitwarden/server/issues) | [Clients](https://github.com/bitwarden/clients/issues) | [iOS](https://github.com/bitwarden/ios/issues) | [Android](https://github.com/bitwarden/android/issues).";
await addComment(discussion.node_id, closeMessage);
await closeDiscussion(discussion.node_id);
return;
}
const replyMessage =
":sparkles: Thank you for your code contribution proposal! While the Bitwarden team reviews your submission, we encourage you to check out our [contribution guidelines](https://contributing.bitwarden.com/).\n\n" +
"Please ensure that your code contribution includes a detailed description of what you would like to contribute, along with any relevant screenshots and links to existing feature requests. This information helps us gather feedback from the community and Bitwarden team members before you start writing code.\n\n" +
"To keep discussions focused, posts that do not include a proposal for a code contribution will be removed.\n\n" +
"- :bulb: For feature requests and general discussion, please visit the [Bitwarden Community Forums](https://community.bitwarden.com/).\n" +
"- :information_source: For questions and support, visit the [Bitwarden Help Center](https://bitwarden.com/help/).\n" +
"- :bug: To report a potential bug, please visit the corresponding repo. [Server](https://github.com/bitwarden/server/issues) | [Clients](https://github.com/bitwarden/clients/issues) | [iOS](https://github.com/bitwarden/ios/issues) | [Android](https://github.com/bitwarden/android/issues)\n\n" +
"Thank you for contributing to Bitwarden!";
await addComment(discussion.node_id, replyMessage);

View File

@@ -1,4 +1,3 @@
---
name: Automatic issue responses
on:
issues:

View File

@@ -1,4 +1,3 @@
---
name: Automatic pull request responses
on:
pull_request:

View File

@@ -1,41 +0,0 @@
---
name: Bump CLI Formula
on:
push:
tags:
- cli-v**
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
update-desktop-cask:
name: Update Bitwarden CLI Formula
runs-on: macos-13
steps:
- 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: "brew-bump-workflow-pat"
- name: Update Homebrew formula
uses: dawidd6/action-homebrew-bump-formula@baf2b60c51fc1f8453c884b0c61052668a71bd1d # v3.11.0
with:
# Required, custom GitHub access token with the 'public_repo' and 'workflow' scopes
token: ${{ steps.retrieve-secrets.outputs.brew-bump-workflow-pat }}
org: bitwarden
tap: Homebrew/homebrew-core
formula: bitwarden-cli
tag: ${{ github.ref }}
revision: ${{ github.sha }}
force: true

View File

@@ -1,42 +0,0 @@
---
name: Bump Desktop Cask
on:
push:
tags:
- desktop-v**
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
update-desktop-cask:
name: Update Bitwarden Desktop Cask
runs-on: macos-13
steps:
- 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: "brew-bump-workflow-pat"
- name: Update Homebrew cask
uses: macauley/action-homebrew-bump-cask@445c42390d790569d938f9068d01af39ca030feb # v1.0.0
with:
# Required, custom GitHub access token with the 'public_repo' and 'workflow' scopes
token: ${{ steps.retrieve-secrets.outputs.brew-bump-workflow-pat }}
org: bitwarden
tap: Homebrew/homebrew-cask
cask: bitwarden
tag: ${{ github.ref }}
revision: ${{ github.sha }}
force: true
dryrun: true

View File

@@ -0,0 +1,44 @@
# This workflow is intended to be run when we need to build the client and produce artifacts that require secrets
# when the PR source branch does not have access to secrets (e.g. a fork).
# This workflow will run in the context of the target of the PR and have access to secrets.
# This should only be done after reviewing the PR to ensure that no malicious code has been introduced,
# as it could allow the code on the forked branch to have access to workflow secrets.
name: Build Browser on PR Target
on:
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- main
paths:
- 'apps/browser/**'
- 'libs/**'
- '*'
- '!*.md'
- '!*.txt'
workflow_call:
inputs: {}
defaults:
run:
shell: bash
jobs:
check-run:
name: Check PR run
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
permissions:
contents: read
run-workflow:
name: Build Browser
needs: check-run
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
uses: ./.github/workflows/build-browser.yml
secrets: inherit
permissions:
contents: write
pull-requests: write
id-token: write

View File

@@ -1,8 +1,12 @@
---
# This workflow will run in the context of the source of the PR.
# On a PR from a fork, the workflow will not have access to secrets, and so any parts of the build that require secrets will not run.
# If additional artifacts are needed, the failed "build-browser-target.yml" workflow held up by the check-run should be re-run.
name: Build Browser
on:
pull_request:
types: [opened, synchronize]
branches-ignore:
- 'l10n_master'
- 'cf-pages'
@@ -27,12 +31,19 @@ on:
workflow_call:
inputs: {}
workflow_dispatch:
inputs: {}
inputs:
sdk_branch:
description: "Custom SDK branch"
required: false
type: string
defaults:
run:
shell: bash
permissions:
contents: read
jobs:
setup:
name: Setup
@@ -41,9 +52,12 @@ jobs:
repo_url: ${{ steps.gen_vars.outputs.repo_url }}
adj_build_number: ${{ steps.gen_vars.outputs.adj_build_number }}
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
has_secrets: ${{ steps.check-secrets.outputs.has_secrets }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Get Package Version
id: gen_vars
@@ -62,6 +76,12 @@ jobs:
NODE_VERSION=${NODE_NVMRC/v/''}
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Check secrets
id: check-secrets
run: |
has_secrets=${{ secrets.AZURE_CLIENT_ID != '' }}
echo "has_secrets=$has_secrets" >> $GITHUB_OUTPUT
locales-test:
name: Locales Test
@@ -72,8 +92,10 @@ jobs:
run:
working-directory: apps/browser
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Testing locales - extName length
run: |
@@ -100,8 +122,8 @@ jobs:
fi
build:
name: Build
build-source:
name: Build browser source
runs-on: ubuntu-22.04
needs:
- setup
@@ -110,11 +132,13 @@ jobs:
_BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -138,10 +162,6 @@ jobs:
FILES=$(find . -maxdepth 1 -type f)
for FILE in $FILES; do cp "$FILE" browser-source/; done
# Copy patches to the Browser source directory
mkdir -p browser-source/patches
cp -r patches/* browser-source/patches
# Copy apps/browser to the Browser source directory
mkdir -p browser-source/apps/browser
cp -r apps/browser/* browser-source/apps/browser
@@ -152,101 +172,55 @@ jobs:
zip -r browser-source.zip browser-source
- name: NPM setup
run: npm ci
working-directory: browser-source/
- name: Build
run: npm run dist
working-directory: browser-source/apps/browser
# - name: Build Manifest v3
# run: npm run dist:mv3
# working-directory: browser-source/apps/browser
- name: Gulp
run: gulp ci
working-directory: browser-source/apps/browser
- name: Upload Opera artifact
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: dist-opera-${{ env._BUILD_NUMBER }}.zip
path: browser-source/apps/browser/dist/dist-opera.zip
if-no-files-found: error
# - name: Upload Opera MV3 artifact
# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
# with:
# name: dist-opera-MV3-${{ env._BUILD_NUMBER }}.zip
# path: browser-source/apps/browser/dist/dist-opera-mv3.zip
# if-no-files-found: error
- name: Upload Chrome artifact
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: dist-chrome-${{ env._BUILD_NUMBER }}.zip
path: browser-source/apps/browser/dist/dist-chrome.zip
if-no-files-found: error
# - name: Upload Chrome MV3 artifact
# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
# with:
# name: dist-chrome-MV3-${{ env._BUILD_NUMBER }}.zip
# path: browser-source/apps/browser/dist/dist-chrome-mv3.zip
# if-no-files-found: error
- name: Upload Firefox artifact
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: dist-firefox-${{ env._BUILD_NUMBER }}.zip
path: browser-source/apps/browser/dist/dist-firefox.zip
if-no-files-found: error
- name: Upload Edge artifact
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: dist-edge-${{ env._BUILD_NUMBER }}.zip
path: browser-source/apps/browser/dist/dist-edge.zip
if-no-files-found: error
# - name: Upload Edge MV3 artifact
# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
# with:
# name: dist-edge-MV3-${{ env._BUILD_NUMBER }}.zip
# path: browser-source/apps/browser/dist/dist-edge-mv3.zip
# if-no-files-found: error
- name: Upload browser source
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: browser-source-${{ env._BUILD_NUMBER }}.zip
path: browser-source.zip
if-no-files-found: error
- name: Upload coverage artifact
if: false
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: coverage-${{ env._BUILD_NUMBER }}.zip
path: browser-source/apps/browser/coverage/coverage-${{ env._BUILD_NUMBER }}.zip
if-no-files-found: error
build-safari:
name: Build Safari
runs-on: macos-13
build:
name: Build
runs-on: ubuntu-22.04
needs:
- setup
- locales-test
- build-source
env:
_BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
strategy:
matrix:
include:
- name: "chrome"
npm_command: "dist:chrome"
archive_name: "dist-chrome.zip"
artifact_name: "dist-chrome-MV3"
- name: "edge"
npm_command: "dist:edge"
archive_name: "dist-edge.zip"
artifact_name: "dist-edge-MV3"
- name: "firefox"
npm_command: "dist:firefox"
archive_name: "dist-firefox.zip"
artifact_name: "dist-firefox"
- name: "firefox-mv3"
npm_command: "dist:firefox:mv3"
archive_name: "dist-firefox.zip"
artifact_name: "DO-NOT-USE-FOR-PROD-dist-firefox-MV3"
- name: "opera-mv3"
npm_command: "dist:opera:mv3"
archive_name: "dist-opera.zip"
artifact_name: "dist-opera-MV3"
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -257,10 +231,117 @@ jobs:
node --version
npm --version
- name: Login to Azure
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Download browser source
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
name: browser-source-${{ env._BUILD_NUMBER }}.zip
- name: Unzip browser source artifact
run: |
unzip browser-source.zip
rm browser-source.zip
- name: NPM setup
run: npm ci
working-directory: browser-source/
- name: Download SDK artifacts
if: ${{ inputs.sdk_branch != '' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
workflow: build-wasm-internal.yml
workflow_conclusion: success
branch: ${{ inputs.sdk_branch }}
artifacts: sdk-internal
repo: bitwarden/sdk-internal
path: sdk-internal
if_no_artifact_found: fail
- name: Override SDK
if: ${{ inputs.sdk_branch != '' }}
working-directory: browser-source/
run: npm link ../sdk-internal
- name: Check source file size
if: ${{ startsWith(matrix.name, 'firefox') }}
run: |
# Declare variable as indexed array
declare -a FILES
# Search for source files that are greater than 4M
TARGET_DIR='./browser-source/apps/browser'
while IFS=' ' read -r RESULT; do
FILES+=("$RESULT")
done < <(find $TARGET_DIR -size +4M)
# Validate results and provide messaging
if [[ ${#FILES[@]} -ne 0 ]]; then
echo "File(s) exceeds size limit: 4MB"
for FILE in ${FILES[@]}; do
echo "- $(du --si $FILE)"
done
echo "ERROR Firefox rejects extension uploads that contain files larger than 4MB"
# Invoke failure
exit 1
fi
- name: Build extension
run: npm run ${{ matrix.npm_command }}
working-directory: browser-source/apps/browser
- name: Upload extension artifact
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: ${{ matrix.artifact_name }}-${{ env._BUILD_NUMBER }}.zip
path: browser-source/apps/browser/dist/${{ matrix.archive_name }}
if-no-files-found: error
build-safari:
name: Build Safari
runs-on: macos-13
permissions:
contents: read
id-token: write
needs:
- setup
- locales-test
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
env:
_BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- 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: Print environment
run: |
node --version
npm --version
- 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-clients
secrets: "KEYCHAIN-PASSWORD"
- name: Download Provisioning Profiles secrets
env:
@@ -296,9 +377,12 @@ jobs:
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: ${{ secrets.KEYCHAIN_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ steps.get-kv-secrets.outputs.KEYCHAIN-PASSWORD }}
run: |
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
security default-keychain -s build.keychain
@@ -329,6 +413,25 @@ jobs:
run: npm ci
working-directory: ./
- name: Download SDK Artifacts
if: ${{ inputs.sdk_branch != '' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
github_token: ${{secrets.GITHUB_TOKEN}}
workflow: build-wasm-internal.yml
workflow_conclusion: success
branch: ${{ inputs.sdk_branch }}
artifacts: sdk-internal
repo: bitwarden/sdk-internal
path: ../sdk-internal
if_no_artifact_found: fail
- name: Override SDK
if: ${{ inputs.sdk_branch != '' }}
working-directory: ./
run: |
npm link ../sdk-internal
- name: Build Safari extension
run: npm run dist:safari
working-directory: apps/browser
@@ -341,7 +444,7 @@ jobs:
ls -la
- name: Upload Safari artifact
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: dist-safari-${{ env._BUILD_NUMBER }}.zip
path: apps/browser/dist/dist-safari.zip
@@ -349,19 +452,27 @@ jobs:
crowdin-push:
name: Crowdin Push
if: github.ref == 'refs/heads/main'
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main'
runs-on: ubuntu-22.04
permissions:
contents: write
pull-requests: write
id-token: write
needs:
- build
- build-safari
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Login to Azure
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
ref: ${{ github.event.pull_request.head.sha }}
- 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
@@ -370,8 +481,11 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "crowdin-api-token"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Upload Sources
uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
uses: crowdin/github-action@f214c8723025f41fc55b2ad26e67b60b80b1885d # v2.7.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
@@ -382,26 +496,36 @@ jobs:
upload_sources: true
upload_translations: false
check-failures:
name: Check for failures
if: always()
runs-on: ubuntu-22.04
permissions:
contents: read
id-token: write
needs:
- setup
- locales-test
- build-source
- build
- build-safari
- crowdin-push
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')
if: |
github.event_name != 'pull_request_target'
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-browser')
&& contains(needs.*.result, 'failure')
run: exit 1
- name: Login to Azure - Prod Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Log in to Azure
if: failure()
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
client_id: ${{ secrets.AZURE_CLIENT_ID }}
- name: Retrieve secrets
id: retrieve-secrets
@@ -411,8 +535,12 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "devops-alerts-slack-webhook-url"
- name: Log out from Azure
if: failure()
uses: bitwarden/gh-actions/azure-logout@main
- name: Notify Slack on failure
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}

43
.github/workflows/build-cli-target.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
# This workflow is intended to be run when we need to build the client and produce artifacts that require secrets
# when the PR source branch does not have access to secrets (e.g. a fork).
# This workflow will run in the context of the target of the PR and have access to secrets.
# This should only be done after reviewing the PR to ensure that no malicious code has been introduced,
# as it could allow the code on the forked branch to have access to workflow secrets.
name: Build CLI on PR Target
on:
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- main
paths:
- 'apps/cli/**'
- 'libs/**'
- '*'
- '!*.md'
- '!*.txt'
- '.github/workflows/build-cli.yml'
- 'bitwarden_license/bit-cli/**'
defaults:
run:
shell: bash
jobs:
check-run:
name: Check PR run
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
permissions:
contents: read
run-workflow:
name: Build CLI
needs: check-run
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
uses: ./.github/workflows/build-cli.yml
secrets: inherit
permissions:
contents: read
id-token: write

View File

@@ -1,13 +1,19 @@
---
# This workflow will run in the context of the source of the PR.
# On a PR from a fork, the workflow will not have access to secrets, and so any parts of the build that require secrets will not run.
# If additional artifacts are needed, the failed "build-cli-target.yml" workflow held up by the check-run should be re-run.
name: Build CLI
on:
pull_request:
types: [opened, synchronize]
branches-ignore:
- 'l10n_master'
- 'cf-pages'
paths:
- 'apps/cli/**'
- 'bitwarden_license/bit-cli/**'
- 'bitwarden_license/bit-common/**'
- 'libs/**'
- '*'
- '!*.md'
@@ -20,28 +26,42 @@ on:
- 'hotfix-rc-cli'
paths:
- 'apps/cli/**'
- 'bitwarden_license/bit-cli/**'
- 'bitwarden_license/bit-common/**'
- 'libs/**'
- '*'
- '!*.md'
- '!*.txt'
- '.github/workflows/build-cli.yml'
workflow_dispatch:
workflow_call:
inputs: {}
workflow_dispatch:
inputs:
sdk_branch:
description: "Custom SDK branch"
required: false
type: string
defaults:
run:
working-directory: apps/cli
permissions:
contents: read
jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
outputs:
package_version: ${{ steps.retrieve-package-version.outputs.package_version }}
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
has_secrets: ${{ steps.check-secrets.outputs.has_secrets }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Get Package Version
id: retrieve-package-version
@@ -51,29 +71,50 @@ jobs:
- name: Get Node Version
id: retrieve-node-version
working-directory: ./
run: |
NODE_NVMRC=$(cat .nvmrc)
NODE_VERSION=${NODE_NVMRC/v/''}
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Check secrets
id: check-secrets
run: |
has_secrets=${{ secrets.AZURE_CLIENT_ID != '' }}
echo "has_secrets=$has_secrets" >> $GITHUB_OUTPUT
cli:
name: Build CLI ${{ matrix.os }}
name: CLI ${{ matrix.os.base }}${{ matrix.os.target_suffix }} - ${{ matrix.license_type.readable }}
strategy:
matrix:
os: [ubuntu-22.04, macos-11]
runs-on: ${{ matrix.os }}
needs:
- setup
os:
[
{ base: "linux", distro: "ubuntu-22.04", target_suffix: "" },
{ base: "linux", distro: "ubuntu-22.04-arm", target_suffix: "-arm64" },
{ base: "mac", distro: "macos-13", target_suffix: "" },
{ base: "mac", distro: "macos-14", target_suffix: "-arm64" }
]
license_type:
[
{ build_prefix: "oss", artifact_prefix: "-oss", readable: "open source license" },
{ build_prefix: "bit", artifact_prefix: "", readable: "commercial license" }
]
runs-on: ${{ matrix.os.distro }}
needs: setup
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
_WIN_PKG_FETCH_VERSION: 18.5.0
_WIN_PKG_VERSION: 3.4
_WIN_PKG_FETCH_VERSION: 20.11.1
_WIN_PKG_VERSION: 3.5
permissions:
contents: read
id-token: write
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Setup Unix Vars
run: |
@@ -82,7 +123,7 @@ jobs:
awk '{print tolower($0)}')" >> $GITHUB_ENV
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -92,17 +133,108 @@ jobs:
run: npm ci
working-directory: ./
- name: Download SDK Artifacts
if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
workflow: build-wasm-internal.yml
workflow_conclusion: success
branch: ${{ inputs.sdk_branch }}
artifacts: sdk-internal
repo: bitwarden/sdk-internal
path: ../sdk-internal
if_no_artifact_found: fail
- name: Override SDK
if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }}
working-directory: ./
run: |
ls -l ../
npm link ../sdk-internal
- name: Build & Package Unix
run: npm run dist:${{ env.SHORT_RUNNER_OS }} --quiet
run: npm run dist:${{ matrix.license_type.build_prefix }}:${{ env.SHORT_RUNNER_OS }}${{ matrix.os.target_suffix }} --quiet
- name: Login to Azure
if: ${{ matrix.os.base == 'mac' && needs.setup.outputs.has_secrets == 'true' }}
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 certificates
if: ${{ matrix.os.base == 'mac' && needs.setup.outputs.has_secrets == 'true' }}
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
- name: Get Azure Key Vault secrets
id: get-kv-secrets
if: ${{ matrix.os.base == 'mac' && needs.setup.outputs.has_secrets == 'true' }}
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: gh-clients
secrets: "KEYCHAIN-PASSWORD,APP-STORE-CONNECT-AUTH-KEY,APP-STORE-CONNECT-TEAM-ISSUER"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Set up keychain
if: ${{ matrix.os.base == 'mac' && needs.setup.outputs.has_secrets == 'true' }}
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 set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
- name: Sign binary
if: ${{ matrix.os.base == 'mac' && needs.setup.outputs.has_secrets == 'true' }}
env:
MACOS_CERTIFICATE_NAME: "Developer ID Application: 8bit Solutions LLC"
run: codesign --sign "$MACOS_CERTIFICATE_NAME" --verbose=3 --force --options=runtime --entitlements ./entitlements.plist --timestamp ./dist/${{ matrix.license_type.build_prefix }}/${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}/bw
- name: Zip Unix
run: |
cd ./dist/${{ env.LOWER_RUNNER_OS }}
zip ../bw-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip ./bw
cd ./dist/${{ matrix.license_type.build_prefix }}/${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}
zip ../../bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip ./bw
- name: Set up private auth key
if: ${{ matrix.os.base == 'mac' && needs.setup.outputs.has_secrets == 'true' }}
run: |
mkdir ~/private_keys
cat << EOF > ~/private_keys/AuthKey_6TV9MKN3GP.p8
${{ steps.get-kv-secrets.outputs.APP-STORE-CONNECT-AUTH-KEY }}
EOF
- name: Notarize app
if: ${{ matrix.os.base == 'mac' && needs.setup.outputs.has_secrets == 'true' }}
env:
APP_STORE_CONNECT_TEAM_ISSUER: ${{ steps.get-kv-secrets.outputs.APP-STORE-CONNECT-TEAM-ISSUER }}
APP_STORE_CONNECT_AUTH_KEY: 6TV9MKN3GP
APP_STORE_CONNECT_AUTH_KEY_PATH: ~/private_keys/AuthKey_6TV9MKN3GP.p8
run: |
echo "Create keychain profile"
xcrun notarytool store-credentials "notarytool-profile" --key-id "$APP_STORE_CONNECT_AUTH_KEY" --key "$APP_STORE_CONNECT_AUTH_KEY_PATH" --issuer "$APP_STORE_CONNECT_TEAM_ISSUER"
codesign --sign "Developer ID Application: 8bit Solutions LLC" --verbose=3 --force --options=runtime --timestamp ./dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip
echo "Notarize app"
xcrun notarytool submit ./dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip --keychain-profile "notarytool-profile" --wait
- name: Version Test
run: |
unzip "./dist/bw-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip" -d "./test"
unzip "./dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip" -d "./test"
testVersion=$(./test/bw -v)
echo "version: $_PACKAGE_VERSION"
echo "testVersion: $testVersion"
@@ -111,39 +243,56 @@ jobs:
exit 1
fi
- name: Create checksums Unix
run: |
cd ./dist
shasum -a 256 bw-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip \
| awk '{split($0, a); print a[1]}' > bw-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt
- name: Upload unix zip asset
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: bw-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip
path: apps/cli/dist/bw-${{ env.LOWER_RUNNER_OS }}-${{ env._PACKAGE_VERSION }}.zip
name: bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip
path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-${{ env.LOWER_RUNNER_OS }}${{ matrix.os.target_suffix }}-${{ env._PACKAGE_VERSION }}.zip
if-no-files-found: error
- name: Upload unix checksum asset
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: bw-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt
path: apps/cli/dist/bw-${{ env.LOWER_RUNNER_OS }}-sha256-${{ env._PACKAGE_VERSION }}.txt
if-no-files-found: error
# We want to confirm the CLI is runnable using the dependencies defined in `apps/cli/package.json`.
- name: Remove node_modules (root)
run: rm -rf node_modules
working-directory: ./
- name: Remove package.json (root)
run: rm package.json
working-directory: ./
- name: Install (CLI)
run: npm i
- name: Output help
run: node ./build/bw.js --help
cli-windows:
name: Build CLI Windows
name: Windows - ${{ matrix.license_type.readable }}
strategy:
matrix:
license_type:
[
{ build_prefix: "oss", artifact_prefix: "-oss", readable: "open source license" },
{ build_prefix: "bit", artifact_prefix: "", readable: "commercial license" }
]
runs-on: windows-2022
needs:
- setup
permissions:
contents: read
id-token: write
needs: setup
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
_WIN_PKG_FETCH_VERSION: 18.5.0
_WIN_PKG_VERSION: 3.4
_WIN_PKG_FETCH_VERSION: 20.11.1
_WIN_PKG_VERSION: 3.5
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Install AST
run: dotnet tool install --global AzureSignTool --version 4.0.1
- name: Setup Windows builder
run: |
@@ -152,7 +301,7 @@ jobs:
choco install nasm --no-progress
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -162,7 +311,7 @@ jobs:
shell: pwsh
run: |
cd $HOME
$fetchedUrl = "https://github.com/vercel/pkg-fetch/releases/download/v$env:_WIN_PKG_VERSION/node-v$env:_WIN_PKG_FETCH_VERSION-win-x64"
$fetchedUrl = "https://github.com/yao-pkg/pkg-fetch/releases/download/v$env:_WIN_PKG_VERSION/node-v$env:_WIN_PKG_FETCH_VERSION-win-x64"
New-Item -ItemType directory -Path .\.pkg-cache
New-Item -ItemType directory -Path .\.pkg-cache\v$env:_WIN_PKG_VERSION
Invoke-RestMethod -Uri $fetchedUrl `
@@ -213,67 +362,117 @@ jobs:
ResourceHacker -open version-info.rc -save version-info.res -action compile
ResourceHacker -open %WIN_PKG_BUILT% -save %WIN_PKG_BUILT% -action addoverwrite -resource version-info.res
- name: Log in to Azure
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
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
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
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
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
uses: bitwarden/gh-actions/azure-logout@main
- name: Install
run: npm ci
working-directory: ./
- name: Download SDK Artifacts
if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
workflow: build-wasm-internal.yml
workflow_conclusion: success
branch: ${{ inputs.sdk_branch }}
artifacts: sdk-internal
repo: bitwarden/sdk-internal
path: ../sdk-internal
if_no_artifact_found: fail
- name: Override SDK
if: ${{ inputs.sdk_branch != '' && needs.setup.outputs.has_secrets == 'true' }}
working-directory: ./
run: |
ls -l ../
npm link ../sdk-internal
- name: Build & Package Windows
run: npm run dist:win --quiet
run: npm run dist:${{ matrix.license_type.build_prefix }}:win --quiet
- name: Sign executable
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
shell: pwsh
env:
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 }}
EXE_PATH: dist/${{ matrix.license_type.build_prefix }}/windows/bw.exe
run: . .\scripts\sign-cli.ps1
- name: Package Chocolatey
shell: pwsh
if: ${{ matrix.license_type.build_prefix == 'bit' }}
run: |
Copy-Item -Path stores/chocolatey -Destination dist/chocolatey -Recurse
Copy-Item dist/windows/bw.exe -Destination dist/chocolatey/tools
Copy-Item dist/${{ matrix.license_type.build_prefix }}/windows/bw.exe -Destination dist/chocolatey/tools
Copy-Item ${{ github.workspace }}/LICENSE.txt -Destination dist/chocolatey/tools
choco pack dist/chocolatey/bitwarden-cli.nuspec --version ${{ env._PACKAGE_VERSION }} --out dist/chocolatey
- name: Zip Windows
shell: cmd
run: 7z a ./dist/bw-windows-%_PACKAGE_VERSION%.zip ./dist/windows/bw.exe
run: 7z a ./dist/bw${{ matrix.license_type.artifact_prefix}}-windows-%_PACKAGE_VERSION%.zip ./dist/${{ matrix.license_type.build_prefix }}/windows/bw.exe
- name: Version Test
run: |
dir ./dist/
Expand-Archive -Path "./dist/bw-windows-${env:_PACKAGE_VERSION}.zip" -DestinationPath "./test/windows"
$testVersion = Invoke-Expression '& ./test/windows/bw.exe -v'
Expand-Archive -Path "./dist/bw${{ matrix.license_type.artifact_prefix }}-windows-${env:_PACKAGE_VERSION}.zip" -DestinationPath "./test/${{ matrix.license_type.build_prefix }}/windows"
$testVersion = Invoke-Expression '& ./test/${{ matrix.license_type.build_prefix }}/windows/bw.exe -v'
echo "version: $env:_PACKAGE_VERSION"
echo "testVersion: $testVersion"
if($testVersion -ne $env:_PACKAGE_VERSION) {
Throw "Version test failed."
}
- name: Create checksums Windows
run: |
checksum -f="./dist/bw-windows-${env:_PACKAGE_VERSION}.zip" `
-t sha256 | Out-File -Encoding ASCII ./dist/bw-windows-sha256-${env:_PACKAGE_VERSION}.txt
- name: Upload windows zip asset
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: bw-windows-${{ env._PACKAGE_VERSION }}.zip
path: apps/cli/dist/bw-windows-${{ env._PACKAGE_VERSION }}.zip
if-no-files-found: error
- name: Upload windows checksum asset
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: bw-windows-sha256-${{ env._PACKAGE_VERSION }}.txt
path: apps/cli/dist/bw-windows-sha256-${{ env._PACKAGE_VERSION }}.txt
name: bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip
path: apps/cli/dist/bw${{ matrix.license_type.artifact_prefix }}-windows-${{ env._PACKAGE_VERSION }}.zip
if-no-files-found: error
- name: Upload Chocolatey asset
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: matrix.license_type.build_prefix == 'bit'
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg
path: apps/cli/dist/chocolatey/bitwarden-cli.${{ env._PACKAGE_VERSION }}.nupkg
if-no-files-found: error
- name: Zip NPM Build Artifact
run: Get-ChildItem -Path .\build | Compress-Archive -DestinationPath .\bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip
- name: Upload NPM Build Directory asset
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
if: matrix.license_type.build_prefix == 'bit'
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip
path: apps/cli/build
path: apps/cli/bitwarden-cli-${{ env._PACKAGE_VERSION }}-npm-build.zip
if-no-files-found: error
snap:
@@ -286,8 +485,10 @@ jobs:
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.package_version }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Print environment
run: |
@@ -297,7 +498,7 @@ jobs:
echo "BW Package Version: $_PACKAGE_VERSION"
- name: Get bw linux cli
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: bw-linux-${{ env._PACKAGE_VERSION }}.zip
path: apps/cli/dist/snap
@@ -310,17 +511,10 @@ jobs:
ls -alth
- name: Build snap
uses: snapcore/action-build@2096990827aa966f773676c8a53793c723b6b40f # v1.2.0
uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1.3.0
with:
path: apps/cli/dist/snap
- name: Create checksum
run: |
cd dist/snap
ls -alth
sha256sum bw_${{ env._PACKAGE_VERSION }}_amd64.snap \
| awk '{split($0, a); print a[1]}' > bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt
- name: Install Snap
run: sudo snap install dist/snap/bw*.snap --dangerous
@@ -339,24 +533,20 @@ jobs:
run: sudo snap remove bw
- name: Upload snap asset
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: bw_${{ env._PACKAGE_VERSION }}_amd64.snap
path: apps/cli/dist/snap/bw_${{ env._PACKAGE_VERSION }}_amd64.snap
if-no-files-found: error
- name: Upload snap checksum asset
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt
path: apps/cli/dist/snap/bw-snap-sha256-${{ env._PACKAGE_VERSION }}.txt
if-no-files-found: error
check-failures:
name: Check for failures
if: always()
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
permissions:
contents: read
id-token: write
needs:
- setup
- cli
@@ -365,14 +555,19 @@ jobs:
steps:
- name: Check if any job failed
working-directory: ${{ github.workspace }}
if: (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc') && contains(needs.*.result, 'failure')
if: |
github.event_name != 'pull_request_target'
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-cli')
&& contains(needs.*.result, 'failure')
run: exit 1
- name: Login to Azure - Prod Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Log in to Azure
if: failure()
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
client_id: ${{ secrets.AZURE_CLIENT_ID }}
- name: Retrieve secrets
id: retrieve-secrets
@@ -382,8 +577,12 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "devops-alerts-slack-webhook-url"
- name: Log out from Azure
if: failure()
uses: bitwarden/gh-actions/azure-logout@main
- name: Notify Slack on failure
uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}

View File

@@ -0,0 +1,44 @@
# This workflow is intended to be run when we need to build the client and produce artifacts that require secrets
# when the PR source branch does not have access to secrets (e.g. a fork).
# This workflow will run in the context of the target of the PR and have access to secrets.
# This should only be done after reviewing the PR to ensure that no malicious code has been introduced,
# as it could allow the code on the forked branch to have access to workflow secrets.
name: Build Desktop on PR Target
on:
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- main
paths:
- 'apps/desktop/**'
- 'libs/**'
- '*'
- '!*.md'
- '!*.txt'
- '.github/workflows/build-desktop.yml'
defaults:
run:
shell: bash
jobs:
check-run:
name: Check PR run
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
permissions:
contents: read
run-workflow:
name: Build Desktop
needs: check-run
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
uses: ./.github/workflows/build-desktop.yml
secrets: inherit
permissions:
contents: write
pull-requests: write
id-token: write

File diff suppressed because it is too large Load Diff

44
.github/workflows/build-web-target.yml vendored Normal file
View File

@@ -0,0 +1,44 @@
# This workflow is intended to be run when we need to build the client and produce artifacts that require secrets
# when the PR source branch does not have access to secrets (e.g. a fork).
# This workflow will run in the context of the target of the PR and have access to secrets.
# This should only be done after reviewing the PR to ensure that no malicious code has been introduced,
# as it could allow the code on the forked branch to have access to workflow secrets.
name: Build Web on PR Target
on:
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- main
paths:
- 'apps/web/**'
- 'libs/**'
- '*'
- '!*.md'
- '!*.txt'
- '.github/workflows/build-web.yml'
defaults:
run:
shell: bash
jobs:
check-run:
name: Check PR run
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
permissions:
contents: read
run-workflow:
name: Build Web
needs: check-run
if: ${{ github.event.pull_request.head.repo.full_name != github.repository }}
uses: ./.github/workflows/build-web.yml
secrets: inherit
permissions:
contents: write
pull-requests: write
id-token: write
security-events: write

View File

@@ -1,13 +1,19 @@
---
# This workflow will run in the context of the source of the PR.
# On a PR from a fork, the workflow will not have access to secrets, and so any parts of the build that require secrets will not run.
# If additional artifacts are needed, the failed "build-web-target.yml" workflow held up by the check-run should be re-run.
name: Build Web
on:
pull_request:
types: [opened, synchronize]
branches-ignore:
- 'l10n_master'
- 'cf-pages'
paths:
- 'apps/web/**'
- 'bitwarden_license/bit-common/**'
- 'bitwarden_license/bit-web/**'
- 'libs/**'
- '*'
- '!*.md'
@@ -20,6 +26,8 @@ on:
- 'hotfix-rc-web'
paths:
- 'apps/web/**'
- 'bitwarden_license/bit-common/**'
- 'bitwarden_license/bit-web/**'
- 'libs/**'
- '*'
- '!*.md'
@@ -27,25 +35,38 @@ on:
- '.github/workflows/build-web.yml'
release:
types: [published]
workflow_call:
inputs: {}
workflow_dispatch:
inputs:
custom_tag_extension:
description: "Custom image tag extension"
required: false
sdk_branch:
description: "Custom SDK branch"
required: false
type: string
env:
_AZ_REGISTRY: bitwardenprod.azurecr.io
_GITHUB_PR_REPO_NAME: ${{ github.event.pull_request.head.repo.full_name }}
permissions:
contents: read
jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
outputs:
version: ${{ steps.version.outputs.value }}
node_version: ${{ steps.retrieve-node-version.outputs.node_version }}
has_secrets: ${{ steps.check-secrets.outputs.has_secrets }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Get GitHub sha as version
id: version
@@ -58,106 +79,89 @@ jobs:
NODE_VERSION=${NODE_NVMRC/v/''}
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
build-artifacts:
name: Build artifacts
runs-on: ubuntu-22.04
needs: setup
env:
_VERSION: ${{ needs.setup.outputs.version }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
strategy:
matrix:
include:
- name: "selfhosted-open-source"
npm_command: "dist:oss:selfhost"
- name: "cloud-COMMERCIAL"
npm_command: "dist:bit:cloud"
- name: "selfhosted-COMMERCIAL"
npm_command: "dist:bit:selfhost"
- name: "cloud-QA"
npm_command: "build:bit:qa"
git_metadata: true
- name: "ee"
npm_command: "build:bit:ee"
git_metadata: true
- name: "cloud-euprd"
npm_command: "build:bit:euprd"
- name: "cloud-euqa"
npm_command: "build:bit:euqa"
git_metadata: true
- name: "cloud-usdev"
npm_command: "build:bit:usdev"
git_metadata: true
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: ${{ env._NODE_VERSION }}
- name: Print environment
- name: Check secrets
id: check-secrets
run: |
whoami
node --version
npm --version
gulp --version
docker --version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
- name: Install dependencies
run: npm ci
- name: Add Git metadata to build version
working-directory: apps/web
if: matrix.git_metadata
run: |
VERSION=$( jq -r ".version" package.json)
jq --arg version "$VERSION+${GITHUB_SHA:0:7}" '.version = $version' package.json > package.json.tmp
mv package.json.tmp package.json
- name: Build ${{ matrix.name }}
working-directory: apps/web
run: npm run ${{ matrix.npm_command }}
- name: Package artifact
working-directory: apps/web
run: zip -r web-${{ env._VERSION }}-${{ matrix.name }}.zip build
- name: Upload ${{ matrix.name }} artifact
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
with:
name: web-${{ env._VERSION }}-${{ matrix.name }}.zip
path: apps/web/web-${{ env._VERSION }}-${{ matrix.name }}.zip
if-no-files-found: error
has_secrets=${{ secrets.AZURE_CLIENT_ID != '' }}
echo "has_secrets=$has_secrets" >> $GITHUB_OUTPUT
build-containers:
name: Build Docker images
runs-on: ubuntu-22.04
needs:
- setup
- build-artifacts
name: "Build [${{matrix.artifact_name}}], image tag: [${{matrix.image_name}}]"
runs-on: ubuntu-24.04
permissions:
security-events: write
id-token: write
needs: setup
strategy:
fail-fast: false
matrix:
include:
- artifact_name: cloud-QA
image_name: web-qa-cloud
- artifact_name: ee
image_name: web-ee
- artifact_name: selfhosted-open-source
image_name: web-oss
npm_command: dist:oss:selfhost
- artifact_name: cloud-COMMERCIAL
image_name: web-cloud
npm_command: dist:bit:cloud
- artifact_name: selfhosted-COMMERCIAL
image_name: web
npm_command: dist:bit:selfhost
- artifact_name: cloud-QA
image_name: web-qa-cloud
npm_command: build:bit:qa
git_metadata: true
- artifact_name: ee
image_name: web-ee
npm_command: build:bit:ee
git_metadata: true
- artifact_name: cloud-euprd
image_name: web-euprd
npm_command: build:bit:euprd
- artifact_name: cloud-euqa
image_name: web-euqa
npm_command: build:bit:euqa
git_metadata: true
- artifact_name: cloud-usdev
image_name: web-usdev
npm_command: build:bit:usdev
git_metadata: true
env:
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
_VERSION: ${{ needs.setup.outputs.version }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Get Latest Server Version
id: latest-server-version
uses: bitwarden/gh-actions/get-release-version@main
with:
repository: bitwarden/server
trim: false
- name: Set Server Ref
id: set-server-ref
run: |
SERVER_REF="${{ steps.latest-server-version.outputs.version }}"
echo "Latest server release version: $SERVER_REF"
if [[ "$GITHUB_REF" == "refs/heads/main" ]]; then
SERVER_REF="$GITHUB_REF"
elif [[ "$GITHUB_REF" == "refs/heads/rc" ]]; then
SERVER_REF="$GITHUB_REF"
elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
SERVER_REF="refs/heads/main"
fi
echo "Server ref: $SERVER_REF"
echo "server_ref=$SERVER_REF" >> $GITHUB_OUTPUT
- name: Check out Server repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: server
repository: bitwarden/server
ref: ${{ steps.set-server-ref.outputs.server_ref }}
- name: Check Branch to Publish
env:
@@ -172,43 +176,61 @@ jobs:
echo "is_publish_branch=false" >> $GITHUB_ENV
fi
########## ACRs ##########
- name: Login to Prod Azure
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Add Git metadata to build version
working-directory: apps/web
if: matrix.git_metadata
run: |
VERSION=$( jq -r ".version" package.json)
jq --arg version "$VERSION+${GITHUB_SHA:0:7}" '.version = $version' package.json > package.json.tmp
mv package.json.tmp package.json
########## Set up Docker ##########
- name: Set up Docker
uses: docker/setup-docker-action@b60f85385d03ac8acfca6d9996982511d8620a19 # v4.3.0
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
daemon-config: |
{
"debug": true,
"features": {
"containerd-snapshotter": true
}
}
- name: Set up QEMU emulators
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
########## ACRs ##########
- name: Log in to Azure
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
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: Log into Prod container registry
run: az acr login -n bitwardenprod
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve github PAT secrets
id: retrieve-secret-pat
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Download ${{ matrix.artifact_name }} artifact
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
with:
name: web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip
path: apps/web
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
run: az acr login -n ${_AZ_REGISTRY%.azurecr.io}
########## Generate image tag and build Docker image ##########
- name: Generate Docker image tag
- name: Generate container image tag
id: tag
run: |
if [[ $(grep "pull" <<< "${GITHUB_REF}") ]]; then
IMAGE_TAG=$(echo "${GITHUB_HEAD_REF}" | sed "s#/#-#g")
if [[ "${GITHUB_EVENT_NAME}" == "pull_request" || "${GITHUB_EVENT_NAME}" == "pull_request_target" ]]; then
IMAGE_TAG=$(echo "${GITHUB_HEAD_REF}" | sed "s/[^a-zA-Z0-9]/-/g") # Sanitize branch name to alphanumeric only
else
IMAGE_TAG=$(echo "${GITHUB_REF_NAME}" | sed "s#/#-#g")
fi
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
SANITIZED_REPO_NAME=$(echo "$_GITHUB_PR_REPO_NAME" | sed "s/[^a-zA-Z0-9]/-/g") # Sanitize repo name to alphanumeric only
IMAGE_TAG=$SANITIZED_REPO_NAME-$IMAGE_TAG # Add repo name to the tag
IMAGE_TAG=${IMAGE_TAG:0:128} # Limit to 128 characters, as that's the max length for Docker image tags
fi
if [[ "$IMAGE_TAG" == "main" ]]; then
IMAGE_TAG=dev
fi
@@ -222,10 +244,6 @@ jobs:
echo "image_tag=$IMAGE_TAG" >> $GITHUB_OUTPUT
########## Build Image ##########
- name: Extract artifact
working-directory: apps/web
run: unzip web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip
- name: Generate image full name
id: image-name
env:
@@ -234,33 +252,109 @@ jobs:
run: echo "name=$_AZ_REGISTRY/${PROJECT_NAME}:${IMAGE_TAG}" >> $GITHUB_OUTPUT
- name: Build Docker image
uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
id: build-container
uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d # v6.12.0
with:
context: apps/web
build-args: |
NODE_VERSION=${{ env._NODE_VERSION }}
NPM_COMMAND=${{ matrix.npm_command }}
context: .
file: apps/web/Dockerfile
platforms: linux/amd64
push: true
load: true
platforms: |
linux/amd64,
linux/arm/v7,
linux/arm64
push: false
tags: ${{ steps.image-name.outputs.name }}
secrets: |
"GH_PAT=${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}"
- name: Push images
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
env:
IMAGE_NAME: ${{ steps.image-name.outputs.name }}
run: docker push $IMAGE_NAME
- name: Zip project
working-directory: apps/web
env:
IMAGE_NAME: ${{ steps.image-name.outputs.name }}
run: |
mkdir build
docker run --rm --volume $(pwd)/build:/temp --entrypoint bash \
$IMAGE_NAME -c "cp -r ./ /temp"
zip -r web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip build
- name: Upload ${{ matrix.artifact_name }} artifact
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip
path: apps/web/web-${{ env._VERSION }}-${{ matrix.artifact_name }}.zip
if-no-files-found: error
- name: Install Cosign
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main'
uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
- name: Sign image with Cosign
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main'
env:
DIGEST: ${{ steps.build-container.outputs.digest }}
TAGS: ${{ steps.image-name.outputs.name }}
run: |
IFS="," read -a tags <<< "${TAGS}"
images=""
for tag in "${tags[@]}"; do
images+="${tag}@${DIGEST} "
done
cosign sign --yes ${images}
- name: Scan Docker image
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
id: container-scan
uses: anchore/scan-action@2c901ab7378897c01b8efaa2d0c9bf519cc64b9e # v6.2.0
with:
image: ${{ steps.image-name.outputs.name }}
fail-build: false
output-format: sarif
- name: Upload Grype results to GitHub
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2
with:
sarif_file: ${{ steps.container-scan.outputs.sarif }}
sha: ${{ contains(github.event_name, 'pull_request') && github.event.pull_request.head.sha || github.sha }}
ref: ${{ contains(github.event_name, 'pull_request') && format('refs/pull/{0}/head', github.event.pull_request.number) || github.ref }}
- name: Log out of Docker
run: docker logout
run: docker logout $_AZ_REGISTRY
- name: Log out from Azure
if: ${{ needs.setup.outputs.has_secrets == 'true' }}
uses: bitwarden/gh-actions/azure-logout@main
crowdin-push:
name: Crowdin Push
if: github.ref == 'refs/heads/main'
needs: build-artifacts
runs-on: ubuntu-22.04
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main'
needs: build-containers
permissions:
contents: write
pull-requests: write
id-token: write
runs-on: ubuntu-24.04
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Login to Azure
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
ref: ${{ github.event.pull_request.head.sha }}
- 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
@@ -269,8 +363,11 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "crowdin-api-token"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Upload Sources
uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
uses: crowdin/github-action@f214c8723025f41fc55b2ad26e67b60b80b1885d # v2.7.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
@@ -281,16 +378,21 @@ jobs:
upload_sources: true
upload_translations: false
trigger-web-vault-deploy:
name: Trigger web vault deploy
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-22.04
needs: build-artifacts
if: github.event_name != 'pull_request_target' && github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
needs: build-containers
permissions:
id-token: write
steps:
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Log in to Azure
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
client_id: ${{ secrets.AZURE_CLIENT_ID }}
- name: Retrieve github PAT secrets
id: retrieve-secret-pat
@@ -299,7 +401,10 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Trigger web vault deploy
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Trigger web vault deploy using GitHub Run ID
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
@@ -311,30 +416,37 @@ jobs:
ref: 'main',
inputs: {
'environment': 'USDEV',
'branch-or-tag': 'main'
'build-web-run-id': '${{ github.run_id }}'
}
})
check-failures:
name: Check for failures
if: always()
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
needs:
- setup
- build-artifacts
- build-containers
- crowdin-push
- trigger-web-vault-deploy
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')
if: |
github.event_name != 'pull_request_target'
&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/rc' || github.ref == 'refs/heads/hotfix-rc-web')
&& contains(needs.*.result, 'failure')
run: exit 1
- name: Login to Azure - Prod Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Log in to Azure
uses: bitwarden/gh-actions/azure-login@main
if: failure()
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
client_id: ${{ secrets.AZURE_CLIENT_ID }}
- name: Retrieve secrets
id: retrieve-secrets
@@ -344,8 +456,11 @@ jobs:
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@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0
uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0
if: failure()
env:
SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}

View File

@@ -1,56 +1,107 @@
---
name: Chromatic
on:
push:
branches-ignore:
- 'renovate/**'
paths-ignore:
- '.github/workflows/**'
branches:
- "main"
- "rc"
- "hotfix-rc"
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- "main"
jobs:
check-run:
name: Check PR run
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
permissions:
contents: read
chromatic:
name: Chromatic
runs-on: ubuntu-22.04
needs: check-run
permissions:
contents: read
pull-requests: write
id-token: write
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Get Node Version
- name: Get changed files
id: get-changed-files-for-chromatic
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
with:
filters: |
storyFiles:
- "apps/!(cli)/**"
- "bitwarden_license/bit-web/src/app/**"
- "libs/!(eslint)/**"
- "package.json"
- ".storybook/**"
- name: Get Node version
id: retrieve-node-version
if: steps.get-changed-files-for-chromatic.outputs.storyFiles == 'true'
run: |
NODE_NVMRC=$(cat .nvmrc)
NODE_VERSION=${NODE_NVMRC/v/''}
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: ${{ steps.retrieve-node-version.outputs.node_version }}
if: steps.get-changed-files-for-chromatic.outputs.storyFiles == 'true'
- name: Cache npm
- name: Cache NPM
id: npm-cache
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: "~/.npm"
key: ${{ runner.os }}-npm-chromatic-${{ hashFiles('**/package-lock.json') }}
if: steps.get-changed-files-for-chromatic.outputs.storyFiles == 'true'
- name: Install Node dependencies
if: steps.get-changed-files-for-chromatic.outputs.storyFiles == 'true'
run: npm ci
# Manual build the storybook to resolve a chromatic/storybook bug related to TurboSnap
# Manually build the Storybook to resolve a bug related to TurboSnap
- name: Build Storybook
if: steps.get-changed-files-for-chromatic.outputs.storyFiles == 'true'
run: npm run build-storybook:ci
- 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-clients
secrets: "CHROMATIC-PROJECT-TOKEN"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Publish to Chromatic
uses: chromaui/action@c9067691aca4a28d6fbb40d9eea6e144369fbcae # v10.9.5
uses: chromaui/action@e8cc4c31775280b175a3c440076c00d19a9014d7 # v11.28.2
with:
token: ${{ secrets.GITHUB_TOKEN }}
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
projectToken: ${{ steps.get-kv-secrets.outputs.CHROMATIC-PROJECT-TOKEN }}
storybookBuildDir: ./storybook-static
exitOnceUploaded: true
onlyChanged: true
externals: "[\"libs/components/**/*.scss\", \"libs/components/**/*.css\", \"libs/components/tailwind.config*.js\"]"
# Rather than use an `if` check on the whole publish step, we need to tell Chromatic to skip so that any Chromatic-spawned actions are properly skipped
skip: ${{ steps.get-changed-files-for-chromatic.outputs.storyFiles == 'false' }}

View File

@@ -1,4 +1,3 @@
---
name: Crowdin Sync
on:
@@ -10,7 +9,10 @@ on:
jobs:
crowdin-sync:
name: Autosync
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
permissions:
contents: read
id-token: write
strategy:
fail-fast: false
matrix:
@@ -22,13 +24,19 @@ jobs:
- app_name: web
crowdin_project_id: "308189"
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Login to Azure
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Log in to Azure
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
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-org-bitwarden
secrets: "BW-GHAPP-ID,BW-GHAPP-KEY"
- name: Retrieve secrets
id: retrieve-secrets
@@ -37,10 +45,25 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Generate GH App token
uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3
id: app-token
with:
app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }}
private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }}
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
token: ${{ steps.app-token.outputs.token }}
- name: Download translations
uses: bitwarden/gh-actions/crowdin@main
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
CROWDIN_PROJECT_ID: ${{ matrix.crowdin_project_id }}
with:
@@ -59,4 +82,3 @@ jobs:
working_directory: apps/${{ matrix.app_name }}
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
gpg_passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}

View File

@@ -1,4 +1,3 @@
---
name: Deploy Web Vault
run-name: Deploy Web Vault to ${{ inputs.environment }} from ${{ inputs.branch-or-tag }}
@@ -7,7 +6,7 @@ on:
inputs:
environment:
description: 'Environment'
default: 'QA'
default: 'USQA'
type: choice
options:
- USQA
@@ -27,6 +26,10 @@ on:
description: "Debug mode"
type: boolean
default: true
build-web-run-id:
description: "Build-web workflow Run ID to use for artifact download"
type: string
required: false
workflow_call:
inputs:
@@ -46,6 +49,10 @@ on:
description: "Debug mode"
type: boolean
default: true
build-web-run-id:
description: "Build-web workflow Run ID to use for artifact download"
type: string
required: false
permissions:
deployments: write
@@ -56,13 +63,15 @@ jobs:
runs-on: ubuntu-22.04
outputs:
environment: ${{ steps.config.outputs.environment }}
environment-url: ${{ steps.config.outputs.environment-url }}
environment-name: ${{ steps.config.outputs.environment-name }}
environment-artifact: ${{ steps.config.outputs.environment-artifact }}
azure-login-creds: ${{ steps.config.outputs.azure-login-creds }}
retrieve-secrets-keyvault: ${{ steps.config.outputs.retrieve-secrets-keyvault }}
sync-utility: ${{ steps.config.outputs.sync-utility }}
sync-delete-destination-files: ${{ steps.config.outputs.sync-delete-destination-files }}
environment_url: ${{ steps.config.outputs.environment_url }}
environment_name: ${{ steps.config.outputs.environment_name }}
environment_artifact: ${{ steps.config.outputs.environment_artifact }}
azure_login_client_key_name: ${{ steps.config.outputs.azure_login_client_key_name }}
azure_login_subscription_id_key_name: ${{ steps.config.outputs.azure_login_subscription_id_key_name }}
retrieve_secrets_keyvault: ${{ steps.config.outputs.retrieve_secrets_keyvault }}
sync_utility: ${{ steps.config.outputs.sync_utility }}
sync_delete_destination_files: ${{ steps.config.outputs.sync_delete_destination_files }}
slack_channel_name: ${{ steps.config.outputs.slack_channel_name }}
steps:
- name: Configure
id: config
@@ -73,104 +82,134 @@ jobs:
case ${{ inputs.environment }} in
"USQA")
echo "azure-login-creds=AZURE_KV_US_QA_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT
echo "retrieve-secrets-keyvault=bw-webvault-rlktusqa-kv" >> $GITHUB_OUTPUT
echo "environment-artifact=web-*-cloud-QA.zip" >> $GITHUB_OUTPUT
echo "environment-name=Web Vault - US QA Cloud" >> $GITHUB_OUTPUT
echo "environment-url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT
echo "azure_login_client_key_name=AZURE_CLIENT_ID_USQA" >> $GITHUB_OUTPUT
echo "azure_login_subscription_id_key_name=AZURE_SUBSCRIPTION_ID_USQA" >> $GITHUB_OUTPUT
echo "retrieve_secrets_keyvault=bw-webvault-rlktusqa-kv" >> $GITHUB_OUTPUT
echo "environment_artifact=web-*-cloud-QA.zip" >> $GITHUB_OUTPUT
echo "environment_name=Web Vault - US QA Cloud" >> $GITHUB_OUTPUT
echo "environment_url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT
echo "slack_channel_name=alerts-deploy-qa" >> $GITHUB_OUTPUT
;;
"EUQA")
echo "azure-login-creds=AZURE_KV_EU_QA_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT
echo "retrieve-secrets-keyvault=webvaulteu-westeurope-qa" >> $GITHUB_OUTPUT
echo "environment-artifact=web-*-cloud-euqa.zip" >> $GITHUB_OUTPUT
echo "environment-name=Web Vault - EU QA Cloud" >> $GITHUB_OUTPUT
echo "environment-url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT
echo "azure_login_client_key_name=AZURE_CLIENT_ID_EUQA" >> $GITHUB_OUTPUT
echo "azure_login_subscription_id_key_name=AZURE_SUBSCRIPTION_ID_EUQA" >> $GITHUB_OUTPUT
echo "retrieve_secrets_keyvault=webvaulteu-westeurope-qa" >> $GITHUB_OUTPUT
echo "environment_artifact=web-*-cloud-euqa.zip" >> $GITHUB_OUTPUT
echo "environment_name=Web Vault - EU QA Cloud" >> $GITHUB_OUTPUT
echo "environment_url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT
echo "slack_channel_name=alerts-deploy-qa" >> $GITHUB_OUTPUT
;;
"USPROD")
echo "azure-login-creds=AZURE_KV_US_PROD_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT
echo "retrieve-secrets-keyvault=bw-webvault-klrt-kv" >> $GITHUB_OUTPUT
echo "environment-artifact=web-*-cloud-COMMERCIAL.zip" >> $GITHUB_OUTPUT
echo "environment-name=Web Vault - US Production Cloud" >> $GITHUB_OUTPUT
echo "environment-url=http://vault.bitwarden.com" >> $GITHUB_OUTPUT
echo "azure_login_client_key_name=AZURE_CLIENT_ID_USPROD" >> $GITHUB_OUTPUT
echo "azure_login_subscription_id_key_name=AZURE_SUBSCRIPTION_ID_USPROD" >> $GITHUB_OUTPUT
echo "retrieve_secrets_keyvault=bw-webvault-klrt-kv" >> $GITHUB_OUTPUT
echo "environment_artifact=web-*-cloud-COMMERCIAL.zip" >> $GITHUB_OUTPUT
echo "environment_name=Web Vault - US Production Cloud" >> $GITHUB_OUTPUT
echo "environment_url=http://vault.bitwarden.com" >> $GITHUB_OUTPUT
echo "slack_channel_name=alerts-deploy-prd" >> $GITHUB_OUTPUT
;;
"EUPROD")
echo "azure-login-creds=AZURE_KV_EU_PRD_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT
echo "retrieve-secrets-keyvault=webvault-westeurope-prod" >> $GITHUB_OUTPUT
echo "environment-artifact=web-*-cloud-euprd.zip" >> $GITHUB_OUTPUT
echo "environment-name=Web Vault - EU Production Cloud" >> $GITHUB_OUTPUT
echo "environment-url=http://vault.bitwarden.eu" >> $GITHUB_OUTPUT
echo "azure_login_client_key_name=AZURE_CLIENT_ID_EUPROD" >> $GITHUB_OUTPUT
echo "azure_login_subscription_id_key_name=AZURE_SUBSCRIPTION_ID_EUPROD" >> $GITHUB_OUTPUT
echo "retrieve_secrets_keyvault=webvault-westeurope-prod" >> $GITHUB_OUTPUT
echo "environment_artifact=web-*-cloud-euprd.zip" >> $GITHUB_OUTPUT
echo "environment_name=Web Vault - EU Production Cloud" >> $GITHUB_OUTPUT
echo "environment_url=http://vault.bitwarden.eu" >> $GITHUB_OUTPUT
echo "slack_channel_name=alerts-deploy-prd" >> $GITHUB_OUTPUT
;;
"USDEV")
echo "azure-login-creds=AZURE_KV_US_DEV_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT
echo "retrieve-secrets-keyvault=webvault-eastus-dev" >> $GITHUB_OUTPUT
echo "environment-artifact=web-*-cloud-usdev.zip" >> $GITHUB_OUTPUT
echo "environment-name=Web Vault - US Development Cloud" >> $GITHUB_OUTPUT
echo "environment-url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT
echo "azure_login_client_key_name=AZURE_CLIENT_ID_USDEV" >> $GITHUB_OUTPUT
echo "azure_login_subscription_id_key_name=AZURE_SUBSCRIPTION_ID_USDEV" >> $GITHUB_OUTPUT
echo "retrieve_secrets_keyvault=webvault-eastus-dev" >> $GITHUB_OUTPUT
echo "environment_artifact=web-*-cloud-usdev.zip" >> $GITHUB_OUTPUT
echo "environment_name=Web Vault - US Development Cloud" >> $GITHUB_OUTPUT
echo "environment_url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT
echo "slack_channel_name=alerts-deploy-dev" >> $GITHUB_OUTPUT
;;
esac
# Set the sync utility to use for deployment to the environment (az-sync or azcopy)
echo "sync-utility=azcopy" >> $GITHUB_OUTPUT
echo "sync_utility=azcopy" >> $GITHUB_OUTPUT
- name: Environment Protection
env:
BUILD_WEB_RUN_ID: ${{ inputs.build-web-run-id }}
GH_TOKEN: ${{ github.token }}
run: |
BRANCH_OR_TAG_LOWER=""
if [[ "$BUILD_WEB_RUN_ID" == "" ]]; then
BRANCH_OR_TAG_LOWER=$(echo ${{ inputs.branch-or-tag }} | awk '{print tolower($0)}')
else
BRANCH_OR_TAG_LOWER=$(gh api /repos/bitwarden/clients/actions/runs/$BUILD_WEB_RUN_ID/artifacts --jq '.artifacts[0].workflow_run.head_branch' | awk '{print tolower($0)}')
fi
echo "Branch/Tag: $BRANCH_OR_TAG_LOWER"
PROD_ENV_PATTERN='USPROD|EUPROD'
PROD_ALLOWED_TAGS_PATTERN='web-v[0-9]+\.[0-9]+\.[0-9]+'
QA_ENV_PATTERN='USQA|EUQA'
QA_ALLOWED_TAGS_PATTERN='.*'
DEV_ENV_PATTERN='USDEV'
DEV_ALLOWED_TAGS_PATTERN='main'
if [[ \
${{ inputs.environment }} =~ \.*($PROD_ENV_PATTERN)\.* && \
! "$BRANCH_OR_TAG_LOWER" =~ ^($PROD_ALLOWED_TAGS_PATTERN).* \
]] || [[ \
${{ inputs.environment }} =~ \.*($QA_ENV_PATTERN)\.* && \
! "$BRANCH_OR_TAG_LOWER" =~ ^($QA_ALLOWED_TAGS_PATTERN).* \
]] || [[ \
${{ inputs.environment }} =~ \.*($DEV_ENV_PATTERN)\.* && \
$BRANCH_OR_TAG_LOWER != $DEV_ALLOWED_TAGS_PATTERN \
]]; then
echo "!Deployment blocked!"
echo "Attempting to deploy a tag that is not allowed in ${{ inputs.environment }} environment"
echo
echo "Environment: ${{ inputs.environment }}"
echo "Tag: $BRANCH_OR_TAG_LOWER"
exit 1
else
echo "The input Branch/Tag: '$BRANCH_OR_TAG_LOWER' is allowed to deploy on ${{ inputs.environment }} environment"
fi
approval:
name: Approval for Deployment to ${{ needs.setup.outputs.environment-name }}
name: Approval for Deployment to ${{ needs.setup.outputs.environment_name }}
needs: setup
runs-on: ubuntu-22.04
environment: ${{ needs.setup.outputs.environment-name }}
environment: ${{ needs.setup.outputs.environment_name }}
steps:
- name: Success Code
run: exit 0
get-branch-or-tag-sha:
name: Get Branch or Tag SHA
runs-on: ubuntu-22.04
outputs:
branch-or-tag-sha: ${{ steps.get-branch-or-tag-sha.outputs.sha }}
steps:
- name: Checkout Branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: ${{ inputs.branch-or-tag }}
fetch-depth: 0
- name: Get Branch or Tag SHA
id: get-branch-or-tag-sha
run: |
echo "sha=$(git rev-parse origin/${{ inputs.branch-or-tag }})" >> $GITHUB_OUTPUT
notify-start:
name: Notify Slack with start message
needs:
- approval
- setup
- get-branch-or-tag-sha
runs-on: ubuntu-22.04
if: ${{ always() && contains( inputs.environment , 'QA' ) }}
outputs:
channel_id: ${{ steps.slack-message.outputs.channel_id }}
ts: ${{ steps.slack-message.outputs.ts }}
steps:
- uses: bitwarden/gh-actions/report-deployment-status-to-slack@main
id: slack-message
with:
project: Clients
environment: ${{ needs.setup.outputs.environment-name }}
tag: ${{ inputs.branch-or-tag }}
slack-channel: team-eng-qa-devops
event: 'start'
commit-sha: ${{ needs.get-branch-or-tag-sha.outputs.branch-or-tag-sha }}
url: https://github.com/bitwarden/clients/actions/runs/${{ github.run_id }}
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
artifact-check:
name: Check if Web artifact is present
runs-on: ubuntu-22.04
needs: setup
permissions:
contents: read
id-token: write
env:
_ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment-artifact }}
_ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment_artifact }}
outputs:
artifact_build_commit: ${{ steps.set-artifact-commit.outputs.commit }}
steps:
- name: 'Download latest cloud asset from branch/tag: ${{ inputs.branch-or-tag }}'
- name: 'Download latest cloud asset using GitHub Run ID: ${{ inputs.build-web-run-id }}'
if: ${{ inputs.build-web-run-id }}
uses: bitwarden/gh-actions/download-artifacts@main
id: download-artifacts
id: download-latest-artifacts-run-id
continue-on-error: true
with:
workflow: build-web.yml
path: apps/web
workflow_conclusion: success
run_id: ${{ inputs.build-web-run-id }}
artifacts: ${{ env._ENVIRONMENT_ARTIFACT }}
- name: 'Download latest cloud asset from branch/tag: ${{ inputs.branch-or-tag }}'
if: ${{ !inputs.build-web-run-id }}
uses: bitwarden/gh-actions/download-artifacts@main
id: download-latest-artifacts
continue-on-error: true
with:
workflow: build-web.yml
@@ -179,23 +218,30 @@ jobs:
branch: ${{ inputs.branch-or-tag }}
artifacts: ${{ env._ENVIRONMENT_ARTIFACT }}
- name: Login to Azure
if: ${{ steps.download-artifacts.outcome == 'failure' }}
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Log in to Azure
if: ${{ steps.download-latest-artifacts.outcome == 'failure' }}
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
client_id: ${{ secrets.AZURE_CLIENT_ID }}
- name: Retrieve secrets for Build trigger
if: ${{ steps.download-artifacts.outcome == 'failure' }}
if: ${{ steps.download-latest-artifacts.outcome == 'failure' }}
id: retrieve-secret
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Log out from Azure
if: ${{ steps.download-latest-artifacts.outcome == 'failure' }}
uses: bitwarden/gh-actions/azure-logout@main
- name: 'Trigger build web for missing branch/tag ${{ inputs.branch-or-tag }}'
if: ${{ steps.download-artifacts.outcome == 'failure' }}
if: ${{ steps.download-latest-artifacts.outcome == 'failure' }}
uses: convictional/trigger-workflow-and-wait@f69fa9eedd3c62a599220f4d5745230e237904be # v1.6.5
id: trigger-build-web
with:
owner: bitwarden
repo: clients
@@ -204,6 +250,65 @@ jobs:
ref: ${{ inputs.branch-or-tag }}
wait_interval: 100
- name: Set artifact build commit
id: set-artifact-commit
env:
GH_TOKEN: ${{ github.token }}
run: |
# If run-id was used, get the commit from the download-latest-artifacts-run-id step
if [ "${{ inputs.build-web-run-id }}" ]; then
echo "commit=${{ steps.download-latest-artifacts-run-id.outputs.artifact-build-commit }}" >> $GITHUB_OUTPUT
elif [ "${{ steps.download-latest-artifacts.outcome }}" == "failure" ]; then
# If the download-latest-artifacts step failed, query the GH API to get the commit SHA of the artifact that was just built with trigger-build-web.
commit=$(gh api /repos/bitwarden/clients/actions/runs/${{ steps.trigger-build-web.outputs.workflow_id }}/artifacts --jq '.artifacts[0].workflow_run.head_sha')
echo "commit=$commit" >> $GITHUB_OUTPUT
else
# Set the commit to the output of step download-latest-artifacts.
echo "commit=${{ steps.download-latest-artifacts.outputs.artifact-build-commit }}" >> $GITHUB_OUTPUT
fi
notify-start:
name: Notify Slack with start message
needs:
- approval
- setup
- artifact-check
runs-on: ubuntu-22.04
if: ${{ always() && ( contains( inputs.environment , 'QA' ) || contains( inputs.environment , 'DEV' ) ) }}
permissions:
id-token: write
outputs:
channel_id: ${{ steps.slack-message.outputs.channel_id }}
ts: ${{ steps.slack-message.outputs.ts }}
steps:
- name: Notify Slack with start message
uses: bitwarden/gh-actions/report-deployment-status-to-slack@main
id: slack-message
with:
project: Clients
environment: ${{ needs.setup.outputs.environment_name }}
tag: ${{ inputs.branch-or-tag }}
slack-channel: ${{ needs.setup.outputs.slack_channel_name }}
event: 'start'
commit-sha: ${{ needs.artifact-check.outputs.artifact_build_commit }}
url: https://github.com/bitwarden/clients/actions/runs/${{ github.run_id }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
update-summary:
name: Display commit
needs: artifact-check
runs-on: ubuntu-22.04
steps:
- name: Display commit SHA
run: |
REPO_URL="https://github.com/bitwarden/clients/commit"
COMMIT_SHA="${{ needs.artifact-check.outputs.artifact_build_commit }}"
echo ":steam_locomotive: View [commit]($REPO_URL/$COMMIT_SHA)" >> $GITHUB_STEP_SUMMARY
azure-deploy:
name: Deploy Web Vault to ${{ inputs.environment }} Storage Account
needs:
@@ -213,9 +318,12 @@ jobs:
runs-on: ubuntu-22.04
env:
_ENVIRONMENT: ${{ needs.setup.outputs.environment }}
_ENVIRONMENT_URL: ${{ needs.setup.outputs.environment-url }}
_ENVIRONMENT_NAME: ${{ needs.setup.outputs.environment-name }}
_ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment-artifact }}
_ENVIRONMENT_URL: ${{ needs.setup.outputs.environment_url }}
_ENVIRONMENT_NAME: ${{ needs.setup.outputs.environment_name }}
_ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment_artifact }}
permissions:
id-token: write
deployments: write
steps:
- name: Create GitHub deployment
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
@@ -227,29 +335,48 @@ jobs:
environment: ${{ env._ENVIRONMENT_NAME }}
task: 'deploy'
description: 'Deployment from branch/tag: ${{ inputs.branch-or-tag }}'
ref: ${{ needs.artifact-check.outputs.artifact_build_commit }}
- name: Login to Azure
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets[needs.setup.outputs.azure-login-creds] }}
subscription_id: ${{ secrets[needs.setup.outputs.azure_login_subscription_id_key_name] }}
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
client_id: ${{ secrets[needs.setup.outputs.azure_login_client_key_name] }}
- name: Retrieve Storage Account connection string for az sync
if: ${{ needs.setup.outputs.sync-utility == 'az-sync' }}
if: ${{ needs.setup.outputs.sync_utility == 'az-sync' }}
id: retrieve-secrets-az-sync
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: ${{ needs.setup.outputs.retrieve-secrets-keyvault }}
keyvault: ${{ needs.setup.outputs.retrieve_secrets_keyvault }}
secrets: "sa-bitwarden-web-vault-dev-key-temp"
- name: Retrieve Storage Account name and SPN credentials for azcopy
if: ${{ needs.setup.outputs.sync-utility == 'azcopy' }}
if: ${{ needs.setup.outputs.sync_utility == 'azcopy' }}
id: retrieve-secrets-azcopy
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: ${{ needs.setup.outputs.retrieve-secrets-keyvault }}
keyvault: ${{ needs.setup.outputs.retrieve_secrets_keyvault }}
secrets: "sa-bitwarden-web-vault-name,sp-bitwarden-web-vault-password,sp-bitwarden-web-vault-appid,sp-bitwarden-web-vault-tenant"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: 'Download latest cloud asset using GitHub Run ID: ${{ inputs.build-web-run-id }}'
if: ${{ inputs.build-web-run-id }}
uses: bitwarden/gh-actions/download-artifacts@main
id: download-latest-artifacts
continue-on-error: true
with:
workflow: build-web.yml
path: apps/web
workflow_conclusion: success
run_id: ${{ inputs.build-web-run-id }}
artifacts: ${{ env._ENVIRONMENT_ARTIFACT }}
- name: 'Download cloud asset from branch/tag: ${{ inputs.branch-or-tag }}'
if: ${{ !inputs.build-web-run-id }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-web.yml
@@ -263,7 +390,7 @@ jobs:
run: unzip ${{ env._ENVIRONMENT_ARTIFACT }}
- name: Sync to Azure Storage Account using az storage blob sync
if: ${{ needs.setup.outputs.sync-utility == 'az-sync' }}
if: ${{ needs.setup.outputs.sync_utility == 'az-sync' }}
working-directory: apps/web
run: |
az storage blob sync \
@@ -273,7 +400,7 @@ jobs:
--delete-destination=${{ inputs.force-delete-destination }}
- name: Sync to Azure Storage Account using azcopy
if: ${{ needs.setup.outputs.sync-utility == 'azcopy' }}
if: ${{ needs.setup.outputs.sync_utility == 'azcopy' }}
working-directory: apps/web
env:
AZCOPY_AUTO_LOGIN_TYPE: SPN
@@ -313,21 +440,26 @@ jobs:
notify:
name: Notify Slack with result
runs-on: ubuntu-22.04
if: ${{ always() && contains( inputs.environment , 'QA' ) }}
if: ${{ always() && ( contains( inputs.environment , 'QA' ) || contains( inputs.environment , 'DEV' ) ) }}
needs:
- setup
- notify-start
- azure-deploy
- setup
- get-branch-or-tag-sha
- artifact-check
permissions:
id-token: write
steps:
- uses: bitwarden/gh-actions/report-deployment-status-to-slack@main
- name: Notify Slack with result
uses: bitwarden/gh-actions/report-deployment-status-to-slack@main
with:
project: Clients
environment: ${{ needs.setup.outputs.environment-name }}
environment: ${{ needs.setup.outputs.environment_name }}
tag: ${{ inputs.branch-or-tag }}
slack-channel: ${{ needs.notify-start.outputs.channel_id }}
event: ${{ needs.azure-deploy.result }}
url: https://github.com/bitwarden/clients/actions/runs/${{ github.run_id }}
commit-sha: ${{ needs.get-branch-or-tag-sha.outputs.branch-or-tag-sha }}
commit-sha: ${{ needs.artifact-check.outputs.artifact_build_commit }}
update-ts: ${{ needs.notify-start.outputs.ts }}
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}

View File

@@ -1,10 +1,12 @@
---
name: Enforce PR labels
on:
workflow_call:
pull_request:
types: [labeled, unlabeled, opened, edited, synchronize]
permissions:
contents: read
pull-requests: read
jobs:
enforce-label:
name: EnforceLabel

View File

@@ -1,24 +0,0 @@
# Runs creation of Pull Requests
# If the PR destination branch is main, add a needs-qa label unless created by renovate[bot]
---
name: Label Issue Pull Request
on:
pull_request:
types:
- opened # Check when PR is opened
paths-ignore:
- .github/workflows/** # We don't need QA on workflow changes
branches:
- 'main' # We only want to check when PRs target main
jobs:
add-needs-qa-label:
runs-on: ubuntu-latest
if: ${{ github.actor != 'renovate[bot]' }}
steps:
- name: Add label to pull request
uses: andymckay/labeler@e6c4322d0397f3240f0e7e30a33b5c5df2d39e90 # 1.0.4
if: ${{ !github.event.pull_request.head.repo.fork }}
with:
add-labels: "needs-qa"

View File

@@ -0,0 +1,55 @@
name: Lint Crowdin Config
on:
pull_request:
types: [opened, synchronize]
paths:
- '**/crowdin.yml'
jobs:
lint-crowdin-config:
name: Lint Crowdin Config ${{ matrix.app.name }}
runs-on: ubuntu-24.04
permissions:
contents: read
id-token: write
strategy:
matrix:
app: [
{ name: 'web', project_id: '308189', config_path: 'apps/web/crowdin.yml' },
{ name: 'desktop', project_id: '299360', config_path: 'apps/desktop/crowdin.yml' },
{ name: 'browser', project_id: '268134', config_path: 'apps/browser/crowdin.yml' }
]
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 1
- 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: "crowdin-api-token"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Lint ${{ matrix.app.name }} config
uses: crowdin/github-action@f214c8723025f41fc55b2ad26e67b60b80b1885d # v2.7.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ matrix.app.project_id }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
with:
dryrun_action: true
command: 'config lint'
command_args: '--verbose -c ${{ matrix.app.config_path }}'

View File

@@ -1,13 +1,20 @@
---
name: Lint
on:
push:
pull_request:
types: [opened, synchronize]
branches-ignore:
- 'l10n_master'
- 'cf-pages'
paths-ignore:
- '.github/workflows/**'
push:
branches:
- 'main'
- 'rc'
- 'hotfix-rc-*'
paths-ignore:
- '.github/workflows/**'
workflow_dispatch:
inputs: {}
@@ -15,13 +22,16 @@ defaults:
run:
shell: bash
permissions:
contents: read
jobs:
lint:
name: Lint
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Lint filenames (no capital characters)
run: |
@@ -35,6 +45,10 @@ jobs:
! -path "*/.DS_Store" \
! -path "*/*locales/*" \
! -path "./.github/*" \
! -path "*/README.md" \
! -path "*/Cargo.toml" \
! -path "*/Cargo.lock" \
! -path "./apps/desktop/macos/*" \
> tmp.txt
diff <(sort .github/whitelist-capital-letters.txt) <(sort tmp.txt)
@@ -46,13 +60,45 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
node-version: ${{ steps.retrieve-node-version.outputs.node_version }}
- name: Install Node dependencies
run: npm ci
- name: Lint unowned dependencies
run: npm run lint:dep-ownership
- name: Run linter
run: |
npm ci
npm run lint
run: npm run lint
rust:
name: Run Rust lint on ${{ matrix.os }}
runs-on: ${{ matrix.os || 'ubuntu-24.04' }}
strategy:
matrix:
os:
- ubuntu-24.04
- macos-14
- windows-2022
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Check Rust version
run: rustup --version
- name: Run cargo fmt
working-directory: ./apps/desktop/desktop_native
run: cargo fmt --check
- name: Run Clippy
working-directory: ./apps/desktop/desktop_native
run: cargo clippy --all-features --tests
env:
RUSTFLAGS: "-D warnings"

36
.github/workflows/locales-lint.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
name: Locales lint for Crowdin
on:
pull_request:
branches-ignore:
- 'l10n_master'
- 'cf-pages'
paths:
- '**/messages.json'
permissions:
contents: read
jobs:
lint:
name: Lint
runs-on: ubuntu-22.04
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Checkout base branch repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.base.sha }}
path: base
- name: Install dependencies
run: npm ci
- name: Compare
run: |
npm run test:locales
if [ $? -eq 0 ]; then
echo "Lint check successful."
else
echo "Lint check failed."
exit 1
fi

268
.github/workflows/publish-cli.yml vendored Normal file
View File

@@ -0,0 +1,268 @@
name: Publish CLI
run-name: Publish CLI ${{ inputs.publish_type }}
on:
workflow_dispatch:
inputs:
publish_type:
description: 'Publish Options'
required: true
default: 'Initial Publish'
type: choice
options:
- Initial Publish
- Republish
- Dry Run
version:
description: 'Version to publish (default: latest cli release)'
required: true
type: string
default: latest
snap_publish:
description: 'Publish to Snap store'
required: true
default: true
type: boolean
choco_publish:
description: 'Publish to Chocolatey store'
required: true
default: true
type: boolean
npm_publish:
description: 'Publish to npm registry'
required: true
default: true
type: boolean
defaults:
run:
working-directory: apps/cli
jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
outputs:
release_version: ${{ steps.version-output.outputs.version }}
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
defaults:
run:
working-directory: .
permissions:
contents: read
deployments: write
steps:
- name: Branch check
if: ${{ inputs.publish_type != 'Dry Run' }}
run: |
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc-cli" ]]; then
echo "==================================="
echo "[!] Can only publish from the 'rc' or 'hotfix-rc-cli' branches"
echo "==================================="
exit 1
fi
- name: Version output
id: version-output
run: |
if [[ "${{ inputs.version }}" == "latest" || "${{ inputs.version }}" == "" ]]; then
VERSION=$(curl "https://api.github.com/repos/bitwarden/clients/releases" | jq -c '.[] | select(.tag_name | contains("cli")) | .tag_name' | head -1 | grep -ohE '20[0-9]{2}\.([1-9]|1[0-2])\.[0-9]+')
echo "Latest Released Version: $VERSION"
echo "version=$VERSION" >> $GITHUB_OUTPUT
else
echo "Release Version: ${{ inputs.version }}"
echo "version=${{ inputs.version }}" >> $GITHUB_OUTPUT
fi
- name: Create GitHub deployment
if: ${{ inputs.publish_type != 'Dry Run' }}
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
id: deployment
with:
token: '${{ secrets.GITHUB_TOKEN }}'
initial-status: 'in_progress'
environment: 'CLI - Production'
description: 'Deployment ${{ steps.version-output.outputs.version }} from branch ${{ github.ref_name }}'
task: release
snap:
name: Deploy Snap
runs-on: ubuntu-22.04
needs: setup
permissions:
contents: read
packages: read
id-token: write
if: inputs.snap_publish
env:
_PKG_VERSION: ${{ needs.setup.outputs.release_version }}
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- 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: "snapcraft-store-token"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Install Snap
uses: samuelmeuli/action-snapcraft@d33c176a9b784876d966f80fb1b461808edc0641 # v2.1.1
- name: Download artifacts
run: wget https://github.com/bitwarden/clients/releases/download/cli-v${{ env._PKG_VERSION }}/bw_${{ env._PKG_VERSION }}_amd64.snap
- name: Publish Snap & logout
if: ${{ inputs.publish_type != 'Dry Run' }}
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }}
run: |
snapcraft upload bw_${{ env._PKG_VERSION }}_amd64.snap --release stable
snapcraft logout
choco:
name: Deploy Choco
runs-on: windows-2022
needs: setup
permissions:
contents: read
packages: read
id-token: write
if: inputs.choco_publish
env:
_PKG_VERSION: ${{ needs.setup.outputs.release_version }}
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- 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: "cli-choco-api-key"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Setup Chocolatey
run: choco apikey --key $env:CHOCO_API_KEY --source https://push.chocolatey.org/
env:
CHOCO_API_KEY: ${{ steps.retrieve-secrets.outputs.cli-choco-api-key }}
- name: Make dist dir
run: New-Item -ItemType directory -Path ./dist
- name: Download artifacts
run: Invoke-WebRequest -Uri "https://github.com/bitwarden/clients/releases/download/cli-v${{ env._PKG_VERSION }}/bitwarden-cli.${{ env._PKG_VERSION }}.nupkg" -OutFile bitwarden-cli.${{ env._PKG_VERSION }}.nupkg
working-directory: apps/cli/dist
- name: Push to Chocolatey
if: ${{ inputs.publish_type != 'Dry Run' }}
run: choco push --source=https://push.chocolatey.org/
working-directory: apps/cli/dist
npm:
name: Publish NPM
runs-on: ubuntu-22.04
needs: setup
permissions:
contents: read
packages: read
id-token: write
if: inputs.npm_publish
env:
_PKG_VERSION: ${{ needs.setup.outputs.release_version }}
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- 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: "npm-api-key"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Download and set up artifact
run: |
mkdir -p build
wget https://github.com/bitwarden/clients/releases/download/cli-v${{ env._PKG_VERSION }}/bitwarden-cli-${{ env._PKG_VERSION }}-npm-build.zip
unzip bitwarden-cli-${{ env._PKG_VERSION }}-npm-build.zip -d build
- name: Setup NPM
run: |
echo 'registry="https://registry.npmjs.org/"' > ./.npmrc
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ./.npmrc
env:
NPM_TOKEN: ${{ steps.retrieve-secrets.outputs.npm-api-key }}
- name: Install Husky
run: npm install -g husky
- name: Publish NPM
if: ${{ inputs.publish_type != 'Dry Run' }}
run: npm publish --access public --regsitry=https://registry.npmjs.org/ --userconfig=./.npmrc
update-deployment:
name: Update Deployment Status
runs-on: ubuntu-22.04
needs:
- setup
- npm
- snap
- choco
permissions:
contents: read
deployments: write
if: ${{ always() && inputs.publish_type != 'Dry Run' }}
steps:
- name: Check if any job failed
if: contains(needs.*.result, 'failure')
run: exit 1
- name: Update deployment status to Success
if: ${{ inputs.publish_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'success'
deployment-id: ${{ needs.setup.outputs.deployment_id }}
- name: Update deployment status to Failure
if: ${{ inputs.publish_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure'
deployment-id: ${{ needs.setup.outputs.deployment_id }}

326
.github/workflows/publish-desktop.yml vendored Normal file
View File

@@ -0,0 +1,326 @@
name: Publish Desktop
run-name: Publish Desktop ${{ inputs.publish_type }}
on:
workflow_dispatch:
inputs:
publish_type:
description: 'Publish Options'
required: true
default: 'Publish'
type: choice
options:
- Publish
- Dry Run
version:
description: 'Version to publish (default: latest desktop release)'
required: true
type: string
default: latest
electron_rollout_percentage:
description: 'Staged Rollout Percentage for Electron'
required: true
default: '10'
type: string
snap_publish:
description: 'Publish to Snap store'
required: true
default: true
type: boolean
choco_publish:
description: 'Publish to Chocolatey store'
required: true
default: true
type: boolean
jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
outputs:
release_version: ${{ steps.version.outputs.version }}
release_channel: ${{ steps.release_channel.outputs.channel }}
tag_name: ${{ steps.version.outputs.tag_name }}
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
permissions:
contents: read
deployments: write
steps:
- name: Branch check
if: ${{ inputs.publish_type != 'Dry Run' }}
run: |
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc-desktop" ]]; then
echo "==================================="
echo "[!] Can only publish from the 'rc' or 'hotfix-rc-desktop' branches"
echo "==================================="
exit 1
fi
- name: Check Publish Version
id: version
run: |
if [[ "${{ inputs.version }}" == "latest" || "${{ inputs.version }}" == "" ]]; then
TAG_NAME=$(curl "https://api.github.com/repos/bitwarden/clients/releases" | jq -c '.[] | select(.tag_name | contains("desktop")) | .tag_name' | head -1 | cut -d '"' -f 2)
VERSION=$(echo $TAG_NAME | sed "s/desktop-v//")
echo "Latest Released Version: $VERSION"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Tag name: $TAG_NAME"
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
else
echo "Release Version: ${{ inputs.version }}"
echo "version=${{ inputs.version }}"
$TAG_NAME="desktop-v${{ inputs.version }}"
echo "Tag name: $TAG_NAME"
echo "tag_name=$TAG_NAME" >> $GITHUB_OUTPUT
fi
- 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: Create GitHub deployment
if: ${{ inputs.publish_type != 'Dry Run' }}
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
id: deployment
with:
token: '${{ secrets.GITHUB_TOKEN }}'
initial-status: 'in_progress'
environment: 'Desktop - Production'
description: 'Deployment ${{ steps.version.outputs.version }} to channel ${{ steps.release_channel.outputs.channel }} from branch ${{ github.ref_name }}'
task: release
electron-blob:
name: Electron blob publish
runs-on: ubuntu-22.04
needs: setup
permissions:
contents: read
packages: read
id-token: write
deployments: write
env:
_PKG_VERSION: ${{ needs.setup.outputs.release_version }}
_RELEASE_TAG: ${{ needs.setup.outputs.tag_name }}
steps:
- 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: "aws-electron-access-id,
aws-electron-access-key,
aws-electron-bucket-name"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Create artifacts directory
run: mkdir -p apps/desktop/artifacts
- name: Download artifacts
env:
GH_TOKEN: ${{ github.token }}
working-directory: apps/desktop/artifacts
run: gh release download ${{ env._RELEASE_TAG }} -R bitwarden/clients
- name: Set staged rollout percentage
env:
RELEASE_CHANNEL: ${{ needs.setup.outputs.release_channel }}
ROLLOUT_PCT: ${{ inputs.electron_rollout_percentage }}
run: |
echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}.yml
echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}-linux.yml
echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}-mac.yml
- name: Publish artifacts to S3
if: ${{ inputs.publish_type != 'Dry Run' }}
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: ${{ inputs.publish_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'success'
deployment-id: ${{ needs.setup.outputs.deployment_id }}
- name: Update deployment status to Failure
if: ${{ inputs.publish_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure'
deployment-id: ${{ needs.setup.outputs.deployment_id }}
snap:
name: Deploy Snap
runs-on: ubuntu-22.04
needs: setup
permissions:
contents: read
id-token: write
if: inputs.snap_publish
env:
_PKG_VERSION: ${{ needs.setup.outputs.release_version }}
_RELEASE_TAG: ${{ needs.setup.outputs.tag_name }}
steps:
- name: Checkout Repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- 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: "snapcraft-store-token"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Install Snap
uses: samuelmeuli/action-snapcraft@d33c176a9b784876d966f80fb1b461808edc0641 # v2.1.1
- name: Setup
run: mkdir dist
working-directory: apps/desktop
- name: Download artifacts
working-directory: apps/desktop/dist
run: wget https://github.com/bitwarden/clients/releases/download/${{ env._RELEASE_TAG }}/bitwarden_${{ env._PKG_VERSION }}_amd64.snap
- name: Deploy to Snap Store
if: ${{ inputs.publish_type != 'Dry Run' }}
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }}
run: |
snapcraft upload bitwarden_${{ env._PKG_VERSION }}_amd64.snap --release stable
snapcraft logout
working-directory: apps/desktop/dist
choco:
name: Deploy Choco
runs-on: windows-2022
needs: setup
permissions:
contents: read
id-token: write
if: inputs.choco_publish
env:
_PKG_VERSION: ${{ needs.setup.outputs.release_version }}
_RELEASE_TAG: ${{ needs.setup.outputs.tag_name }}
steps:
- name: Checkout Repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Print Environment
run: |
dotnet --version
dotnet nuget --version
- 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: "cli-choco-api-key"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Setup Chocolatey
run: choco apikey --key $env:CHOCO_API_KEY --source https://push.chocolatey.org/
env:
CHOCO_API_KEY: ${{ steps.retrieve-secrets.outputs.cli-choco-api-key }}
- name: Make dist dir
run: New-Item -ItemType directory -Path ./dist
working-directory: apps/desktop
- name: Download artifacts
working-directory: apps/desktop/dist
run: Invoke-WebRequest -Uri "https://github.com/bitwarden/clients/releases/download/${{ env._RELEASE_TAG }}/bitwarden.${{ env._PKG_VERSION }}.nupkg" -OutFile bitwarden.${{ env._PKG_VERSION }}.nupkg
- name: Push to Chocolatey
if: ${{ inputs.publish_type != 'Dry Run' }}
run: choco push --source=https://push.chocolatey.org/
working-directory: apps/desktop/dist
update-deployment:
name: Update Deployment Status
runs-on: ubuntu-22.04
needs:
- setup
- electron-blob
- snap
- choco
permissions:
contents: read
deployments: write
if: ${{ always() && inputs.publish_type != 'Dry Run' }}
steps:
- name: Check if any job failed
if: contains(needs.*.result, 'failure')
run: exit 1
- name: Update deployment status to Success
if: ${{ inputs.publish_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'success'
deployment-id: ${{ needs.setup.outputs.deployment_id }}
- name: Update deployment status to Failure
if: ${{ inputs.publish_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure'
deployment-id: ${{ needs.setup.outputs.deployment_id }}

194
.github/workflows/publish-web.yml vendored Normal file
View File

@@ -0,0 +1,194 @@
name: Publish Web
run-name: Publish Web ${{ inputs.publish_type }}
on:
workflow_dispatch:
inputs:
publish_type:
description: 'Publish Options'
required: true
default: 'Initial Release'
type: choice
options:
- Initial Release
- Redeploy
- Dry Run
env:
_AZ_REGISTRY: bitwardenprod.azurecr.io
jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
outputs:
release_version: ${{ steps.version.outputs.version }}
tag_version: ${{ steps.version.outputs.tag }}
permissions:
contents: read
steps:
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Branch check
if: ${{ inputs.publish_type != 'Dry Run' }}
run: |
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc-web" ]]; then
echo "==================================="
echo "[!] Can only publish from the 'rc' or 'hotfix-rc-web' branches"
echo "==================================="
exit 1
fi
- name: Check Release Version
id: version
uses: bitwarden/gh-actions/release-version-check@main
with:
release-type: ${{ inputs.publish_type }}
project-type: ts
file: apps/web/package.json
monorepo: true
monorepo-project: web
self-host:
name: Release self-host docker
runs-on: ubuntu-22.04
needs: setup
permissions:
id-token: write
contents: read
deployments: write
env:
_BRANCH_NAME: ${{ github.ref_name }}
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
_RELEASE_OPTION: ${{ inputs.publish_type }}
steps:
- name: Print environment
run: |
whoami
docker --version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
echo "Github Release Option: $_RELEASE_OPTION"
- name: Checkout repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
########## ACR ##########
- 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: Login to Azure ACR
run: az acr login -n bitwardenprod
- name: Create GitHub deployment
if: ${{ inputs.publish_type != 'Dry Run' }}
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
id: deployment
with:
token: '${{ secrets.GITHUB_TOKEN }}'
initial-status: 'in_progress'
environment-url: http://vault.bitwarden.com
environment: 'Web Vault - US Production Cloud'
description: 'Deployment ${{ needs.setup.outputs.release_version }} from branch ${{ github.ref_name }}'
task: release
- name: Pull branch image
run: |
if [[ "${{ inputs.publish_type }}" == "Dry Run" ]]; then
docker pull $_AZ_REGISTRY/web:latest
else
docker pull $_AZ_REGISTRY/web:$_BRANCH_NAME
fi
- name: Tag version
run: |
if [[ "${{ inputs.publish_type }}" == "Dry Run" ]]; then
docker tag $_AZ_REGISTRY/web:latest $_AZ_REGISTRY/web:dryrun
docker tag $_AZ_REGISTRY/web:latest $_AZ_REGISTRY/web-sh:dryrun
else
docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web:$_RELEASE_VERSION
docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web-sh:$_RELEASE_VERSION
docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web:latest
docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web-sh:latest
fi
- name: Push version
run: |
if [[ "${{ inputs.publish_type }}" == "Dry Run" ]]; then
docker push $_AZ_REGISTRY/web:dryrun
docker push $_AZ_REGISTRY/web-sh:dryrun
else
docker push $_AZ_REGISTRY/web:$_RELEASE_VERSION
docker push $_AZ_REGISTRY/web-sh:$_RELEASE_VERSION
docker push $_AZ_REGISTRY/web:latest
docker push $_AZ_REGISTRY/web-sh:latest
fi
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Update deployment status to Success
if: ${{ inputs.publish_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
environment-url: http://vault.bitwarden.com
state: 'success'
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
- name: Update deployment status to Failure
if: ${{ inputs.publish_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
environment-url: http://vault.bitwarden.com
state: 'failure'
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
- name: Log out of Docker
run: docker logout
self-host-unified-build:
name: Trigger self-host unified build
runs-on: ubuntu-22.04
needs:
- setup
permissions:
id-token: write
steps:
- 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 GitHub PAT secrets
id: retrieve-secret-pat
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: "bitwarden-ci"
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Trigger self-host build
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
github-token: ${{ steps.retrieve-secret-pat.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
script: |
await github.rest.actions.createWorkflowDispatch({
owner: 'bitwarden',
repo: 'self-host',
workflow_id: 'build-unified.yml',
ref: 'main',
inputs: {
use_latest_core_version: true
}
});

View File

@@ -1,4 +1,3 @@
---
name: Release Browser
run-name: Release Browser ${{ inputs.release_type }}
@@ -23,11 +22,13 @@ jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
permissions:
contents: read
outputs:
release-version: ${{ steps.version.outputs.version }}
release_version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Branch check
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
@@ -54,9 +55,11 @@ jobs:
name: Locales Test
runs-on: ubuntu-22.04
needs: setup
permissions:
contents: read
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Testing locales - extName length
run: |
@@ -90,17 +93,9 @@ jobs:
needs:
- setup
- locales-test
permissions:
contents: write
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: 'Browser - Production'
description: 'Deployment ${{ needs.setup.outputs.release-version }} from branch ${{ github.ref_name }}'
task: release
- name: Download latest Release build artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
@@ -129,7 +124,7 @@ jobs:
- name: Rename build artifacts
env:
PACKAGE_VERSION: ${{ needs.setup.outputs.release-version }}
PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }}
run: |
mv browser-source.zip browser-source-$PACKAGE_VERSION.zip
mv dist-chrome.zip dist-chrome-$PACKAGE_VERSION.zip
@@ -139,32 +134,16 @@ jobs:
- name: Create release
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
uses: ncipollo/release-action@cdcc88a9acf3ca41c16c37bb7d21b9ad48560d87 # v1.15.0
with:
artifacts: 'browser-source-${{ needs.setup.outputs.release-version }}.zip,
dist-chrome-${{ needs.setup.outputs.release-version }}.zip,
dist-opera-${{ needs.setup.outputs.release-version }}.zip,
dist-firefox-${{ needs.setup.outputs.release-version }}.zip,
dist-edge-${{ needs.setup.outputs.release-version }}.zip'
artifacts: 'browser-source-${{ needs.setup.outputs.release_version }}.zip,
dist-chrome-${{ needs.setup.outputs.release_version }}.zip,
dist-opera-${{ needs.setup.outputs.release_version }}.zip,
dist-firefox-${{ needs.setup.outputs.release_version }}.zip,
dist-edge-${{ needs.setup.outputs.release_version }}.zip'
commit: ${{ github.sha }}
tag: "browser-v${{ needs.setup.outputs.release-version }}"
name: "Browser v${{ needs.setup.outputs.release-version }}"
tag: "browser-v${{ needs.setup.outputs.release_version }}"
name: "Browser v${{ needs.setup.outputs.release_version }}"
body: "<insert release notes here>"
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
- 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 }}

View File

@@ -1,4 +1,3 @@
---
name: Release CLI
run-name: Release CLI ${{ inputs.release_type }}
@@ -14,39 +13,26 @@ on:
- Initial Release
- Redeploy
- Dry Run
snap_publish:
description: 'Publish to Snap store'
required: true
default: true
type: boolean
choco_publish:
description: 'Publish to Chocolatey store'
required: true
default: true
type: boolean
npm_publish:
description: 'Publish to npm registry'
required: true
default: true
type: boolean
defaults:
run:
working-directory: apps/cli
permissions:
contents: read
jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
outputs:
release-version: ${{ steps.version.outputs.version }}
release_version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Branch check
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
if: ${{ inputs.release_type != 'Dry Run' }}
run: |
if [[ "$GITHUB_REF" != "refs/heads/rc" ]] && [[ "$GITHUB_REF" != "refs/heads/hotfix-rc-cli" ]]; then
echo "==================================="
@@ -59,25 +45,21 @@ jobs:
id: version
uses: bitwarden/gh-actions/release-version-check@main
with:
release-type: ${{ github.event.inputs.release_type }}
release-type: ${{ inputs.release_type }}
project-type: ts
file: apps/cli/package.json
monorepo: true
monorepo-project: cli
- name: Create GitHub deployment
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
id: deployment
with:
token: '${{ secrets.GITHUB_TOKEN }}'
initial-status: 'in_progress'
environment: 'CLI - Production'
description: 'Deployment ${{ steps.version.outputs.version }} from branch ${{ github.ref_name }}'
task: release
release:
name: Release
runs-on: ubuntu-22.04
needs: setup
permissions:
contents: write
steps:
- name: Download all Release artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
if: ${{ inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-cli.yml
@@ -86,7 +68,7 @@ jobs:
branch: ${{ github.ref_name }}
- name: Dry Run - Download all artifacts
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
if: ${{ inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-cli.yml
@@ -95,209 +77,25 @@ jobs:
branch: main
- name: Create release
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
if: ${{ inputs.release_type != 'Dry Run' }}
uses: ncipollo/release-action@cdcc88a9acf3ca41c16c37bb7d21b9ad48560d87 # v1.15.0
env:
PKG_VERSION: ${{ steps.version.outputs.version }}
PKG_VERSION: ${{ needs.setup.outputs.release_version }}
with:
artifacts: "apps/cli/bw-windows-${{ env.PKG_VERSION }}.zip,
apps/cli/bw-windows-sha256-${{ env.PKG_VERSION }}.txt,
artifacts: "apps/cli/bw-oss-windows-${{ env.PKG_VERSION }}.zip,
apps/cli/bw-windows-${{ env.PKG_VERSION }}.zip,
apps/cli/bw-oss-macos-${{ env.PKG_VERSION }}.zip,
apps/cli/bw-oss-macos-arm64-${{ env.PKG_VERSION }}.zip,
apps/cli/bw-macos-${{ env.PKG_VERSION }}.zip,
apps/cli/bw-macos-sha256-${{ env.PKG_VERSION }}.txt,
apps/cli/bw-macos-arm64-${{ env.PKG_VERSION }}.zip,
apps/cli/bw-oss-linux-${{ env.PKG_VERSION }}.zip,
apps/cli/bw-linux-${{ env.PKG_VERSION }}.zip,
apps/cli/bw-linux-sha256-${{ env.PKG_VERSION }}.txt,
apps/cli/bitwarden-cli.${{ env.PKG_VERSION }}.nupkg,
apps/cli/bw_${{ env.PKG_VERSION }}_amd64.snap,
apps/cli/bw-snap-sha256-${{ env.PKG_VERSION }}.txt"
apps/cli/bitwarden-cli-${{ env.PKG_VERSION }}-npm-build.zip"
commit: ${{ github.sha }}
tag: cli-v${{ env.PKG_VERSION }}
name: CLI v${{ env.PKG_VERSION }}
body: "<insert release notes here>"
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
- name: Update deployment status to Success
if: ${{ github.event.inputs.release_type != 'Dry Run' && 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: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure'
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
snap:
name: Deploy Snap
runs-on: ubuntu-22.04
needs: setup
if: inputs.snap_publish
env:
_PKG_VERSION: ${{ needs.setup.outputs.release-version }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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: "snapcraft-store-token"
- name: Install Snap
uses: samuelmeuli/action-snapcraft@d33c176a9b784876d966f80fb1b461808edc0641 # v2.1.1
- name: Download artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-cli.yml
path: apps/cli
workflow_conclusion: success
branch: ${{ github.ref_name }}
artifacts: bw_${{ env._PKG_VERSION }}_amd64.snap
- name: Dry Run - Download artifacts
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-cli.yml
path: apps/cli
workflow_conclusion: success
branch: main
artifacts: bw_${{ env._PKG_VERSION }}_amd64.snap
- name: Publish Snap & logout
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }}
run: |
snapcraft upload bw_${{ env._PKG_VERSION }}_amd64.snap --release stable
snapcraft logout
choco:
name: Deploy Choco
runs-on: windows-2022
needs: setup
if: inputs.choco_publish
env:
_PKG_VERSION: ${{ needs.setup.outputs.release-version }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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: "cli-choco-api-key"
- name: Setup Chocolatey
run: choco apikey --key $env:CHOCO_API_KEY --source https://push.chocolatey.org/
env:
CHOCO_API_KEY: ${{ steps.retrieve-secrets.outputs.cli-choco-api-key }}
- name: Make dist dir
shell: pwsh
run: New-Item -ItemType directory -Path ./dist
- name: Download artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-cli.yml
path: apps/cli/dist
workflow_conclusion: success
branch: ${{ github.ref_name }}
artifacts: bitwarden-cli.${{ env._PKG_VERSION }}.nupkg
- name: Dry Run - Download artifacts
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-cli.yml
path: apps/cli/dist
workflow_conclusion: success
branch: main
artifacts: bitwarden-cli.${{ env._PKG_VERSION }}.nupkg
- name: Push to Chocolatey
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
shell: pwsh
run: |
cd dist
choco push --source=https://push.chocolatey.org/
npm:
name: Publish NPM
runs-on: ubuntu-22.04
needs: setup
if: inputs.npm_publish
env:
_PKG_VERSION: ${{ needs.setup.outputs.release-version }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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: "npm-api-key"
- name: Download artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-cli.yml
path: apps/cli/build
workflow_conclusion: success
branch: ${{ github.ref_name }}
artifacts: bitwarden-cli-${{ env._PKG_VERSION }}-npm-build.zip
- name: Dry Run - Download artifacts
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-cli.yml
path: apps/cli/build
workflow_conclusion: success
branch: main
artifacts: bitwarden-cli-${{ env._PKG_VERSION }}-npm-build.zip
- name: Setup NPM
run: |
echo 'registry="https://registry.npmjs.org/"' > ./.npmrc
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ./.npmrc
env:
NPM_TOKEN: ${{ steps.retrieve-secrets.outputs.npm-api-key }}
- name: Install Husky
run: npm install -g husky
- name: Publish NPM
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
run: npm publish --access public --regsitry=https://registry.npmjs.org/ --userconfig=./.npmrc

View File

@@ -1,4 +1,3 @@
---
name: Release Desktop Beta
on:
@@ -16,15 +15,17 @@ jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
permissions:
contents: write
outputs:
release-version: ${{ steps.version.outputs.version }}
release-channel: ${{ steps.release-channel.outputs.channel }}
branch-name: ${{ steps.branch.outputs.branch-name }}
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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Branch check
run: |
@@ -64,7 +65,7 @@ jobs:
echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT
- name: Get Version Channel
id: release-channel
id: release_channel
run: |
case "${{ steps.version.outputs.version }}" in
*"alpha"*)
@@ -103,7 +104,7 @@ jobs:
git push -u origin $branch_name
echo "branch-name=$branch_name" >> $GITHUB_OUTPUT
echo "branch_name=$branch_name" >> $GITHUB_OUTPUT
- name: Get Node Version
id: retrieve-node-version
@@ -116,8 +117,10 @@ jobs:
name: Linux Build
runs-on: ubuntu-22.04
needs: setup
permissions:
contents: read
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.release-version }}
_PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
NODE_OPTIONS: --max_old_space_size=4096
defaults:
@@ -125,12 +128,12 @@ jobs:
working-directory: apps/desktop
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ needs.setup.outputs.branch-name }}
ref: ${{ needs.setup.outputs.branch_name }}
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -139,7 +142,7 @@ jobs:
- name: Set up environment
run: |
sudo apt-get update
sudo apt-get -y install pkg-config libxss-dev libsecret-1-dev rpm
sudo apt-get -y install pkg-config libxss-dev rpm
- name: Set up Snap
run: sudo snap install snapcraft --classic
@@ -159,45 +162,45 @@ jobs:
run: npm run dist:lin
- name: Upload .deb artifact
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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
name: ${{ needs.setup.outputs.release_channel }}-linux.yml
path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-linux.yml
if-no-files-found: error
@@ -205,22 +208,25 @@ jobs:
name: Windows Build
runs-on: windows-2022
needs: setup
permissions:
contents: read
id-token: write
defaults:
run:
shell: pwsh
working-directory: apps/desktop
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.release-version }}
_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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ needs.setup.outputs.branch-name }}
ref: ${{ needs.setup.outputs.branch_name }}
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -238,10 +244,12 @@ jobs:
npm --version
choco --version
- name: Login to Azure
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Log in to Azure
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
client_id: ${{ secrets.AZURE_CLIENT_ID }}
- name: Retrieve secrets
id: retrieve-secrets
@@ -254,6 +262,9 @@ jobs:
code-signing-client-secret,
code-signing-cert-name"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Install Node dependencies
run: npm ci
working-directory: ./
@@ -300,94 +311,94 @@ jobs:
-NewName bitwarden-${{ env._PACKAGE_VERSION }}-arm64.nsis.7z
- name: Upload portable exe artifact
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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
name: ${{ needs.setup.outputs.release_channel }}.yml
path: apps/desktop/dist/nsis-web/${{ needs.setup.outputs.release_channel }}.yml
if-no-files-found: error
@@ -395,8 +406,11 @@ jobs:
name: MacOS Build
runs-on: macos-13
needs: setup
permissions:
contents: read
id-token: write
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.release-version }}
_PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
NODE_OPTIONS: --max_old_space_size=4096
defaults:
@@ -404,17 +418,20 @@ jobs:
working-directory: apps/desktop
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ needs.setup.outputs.branch-name }}
ref: ${{ needs.setup.outputs.branch_name }}
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
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
@@ -424,18 +441,32 @@ jobs:
- name: Cache Build
id: build-cache
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
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@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: apps/browser/dist/Safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
- 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-clients
secrets: "KEYCHAIN-PASSWORD"
- name: Download Provisioning Profiles secrets
env:
ACCOUNT_NAME: bitwardenci
@@ -454,7 +485,7 @@ jobs:
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
@@ -470,9 +501,12 @@ jobs:
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: ${{ secrets.KEYCHAIN_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ steps.get-kv-secrets.outputs.KEYCHAIN-PASSWORD }}
run: |
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
security default-keychain -s build.keychain
@@ -526,8 +560,12 @@ jobs:
needs:
- setup
- macos-build
permissions:
contents: read
packages: read
id-token: write
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.release-version }}
_PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
NODE_OPTIONS: --max_old_space_size=4096
defaults:
@@ -535,17 +573,20 @@ jobs:
working-directory: apps/desktop
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ needs.setup.outputs.branch-name }}
ref: ${{ needs.setup.outputs.branch_name }}
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
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
@@ -555,22 +596,31 @@ jobs:
- name: Get Build Cache
id: build-cache
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
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@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
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
- name: Log in to Azure
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
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-clients
secrets: "KEYCHAIN-PASSWORD,APPLE-ID-USERNAME,APPLE-ID-PASSWORD"
- name: Download Provisioning Profiles secrets
env:
@@ -590,7 +640,7 @@ jobs:
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
@@ -606,9 +656,12 @@ jobs:
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: ${{ secrets.KEYCHAIN_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ steps.get-kv-secrets.outputs.KEYCHAIN-PASSWORD }}
run: |
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
security default-keychain -s build.keychain
@@ -659,7 +712,7 @@ jobs:
- name: Download artifact from hotfix-rc
if: github.ref == 'refs/heads/hotfix-rc'
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-browser.yml
workflow_conclusion: success
@@ -668,7 +721,7 @@ jobs:
- name: Download artifact from rc
if: github.ref == 'refs/heads/rc'
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-browser.yml
workflow_conclusion: success
@@ -677,7 +730,7 @@ jobs:
- name: Download artifacts from main
if: ${{ github.ref != 'refs/heads/rc' && github.ref != 'refs/heads/hotfix-rc' }}
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-browser.yml
workflow_conclusion: success
@@ -697,36 +750,36 @@ jobs:
- name: Build application (dist)
env:
APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
APPLE_ID_USERNAME: ${{ steps.get-kv-secrets.outputs.APPLE-ID-USERNAME }}
APPLE_ID_PASSWORD: ${{ steps.get-kv-secrets.outputs.APPLE-ID-PASSWORD }}
run: npm run pack:mac
- name: Upload .zip artifact
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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
name: ${{ needs.setup.outputs.release_channel }}-mac.yml
path: apps/desktop/dist/${{ needs.setup.outputs.release_channel }}-mac.yml
if-no-files-found: error
@@ -736,8 +789,12 @@ jobs:
needs:
- setup
- macos-build
permissions:
contents: read
packages: read
id-token: write
env:
_PACKAGE_VERSION: ${{ needs.setup.outputs.release-version }}
_PACKAGE_VERSION: ${{ needs.setup.outputs.release_version }}
_NODE_VERSION: ${{ needs.setup.outputs.node_version }}
NODE_OPTIONS: --max_old_space_size=4096
defaults:
@@ -745,17 +802,20 @@ jobs:
working-directory: apps/desktop
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ needs.setup.outputs.branch-name }}
ref: ${{ needs.setup.outputs.branch_name }}
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
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
@@ -765,18 +825,32 @@ jobs:
- name: Get Build Cache
id: build-cache
uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
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@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: apps/browser/dist/Safari
key: ${{ runner.os }}-${{ github.run_id }}-safari-extension
- 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-clients
secrets: "KEYCHAIN-PASSWORD,APPLE-ID-USERNAME,APPLE-ID-PASSWORD"
- name: Download Provisioning Profiles secrets
env:
ACCOUNT_NAME: bitwardenci
@@ -795,7 +869,7 @@ jobs:
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
@@ -811,9 +885,12 @@ jobs:
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: ${{ secrets.KEYCHAIN_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ steps.get-kv-secrets.outputs.KEYCHAIN-PASSWORD }}
run: |
security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
security default-keychain -s build.keychain
@@ -864,7 +941,7 @@ jobs:
- name: Download artifact from hotfix-rc
if: github.ref == 'refs/heads/hotfix-rc'
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-browser.yml
workflow_conclusion: success
@@ -873,7 +950,7 @@ jobs:
- name: Download artifact from rc
if: github.ref == 'refs/heads/rc'
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-browser.yml
workflow_conclusion: success
@@ -882,7 +959,7 @@ jobs:
- name: Download artifact from main
if: ${{ github.ref != 'refs/heads/rc' && github.ref != 'refs/heads/hotfix-rc' }}
uses: dawidd6/action-download-artifact@71072fbb1229e1317f1a8de6b04206afb461bd67 # v3.1.2
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-browser.yml
workflow_conclusion: success
@@ -903,11 +980,11 @@ jobs:
- 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 }}
APPLE_ID_USERNAME: ${{ steps.get-kv-secrets.outputs.APPLE-ID-USERNAME }}
APPLE_ID_PASSWORD: ${{ steps.get-kv-secrets.outputs.APPLE-ID-PASSWORD }}
- name: Upload .pkg artifact
uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
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
@@ -923,6 +1000,10 @@ jobs:
- macos-build
- macos-package-github
- macos-package-mas
permissions:
contents: read
id-token: write
deployments: write
steps:
- name: Create GitHub deployment
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
@@ -931,13 +1012,15 @@ jobs:
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 }}'
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
- name: Log in to Azure
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
client_id: ${{ secrets.AZURE_CLIENT_ID }}
- name: Retrieve secrets
id: retrieve-secrets
@@ -946,20 +1029,19 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "aws-electron-access-id,
aws-electron-access-key,
aws-electron-bucket-name,
r2-electron-access-id,
r2-electron-access-key,
r2-electron-bucket-name,
cf-prod-account"
aws-electron-bucket-name"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Download all artifacts
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
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 }}
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
@@ -976,20 +1058,6 @@ jobs:
--recursive \
--quiet
- name: Publish artifacts to R2
env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }}
AWS_DEFAULT_REGION: 'us-east-1'
AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.r2-electron-bucket-name }}
CF_ACCOUNT: ${{ steps.retrieve-secrets.outputs.cf-prod-account }}
working-directory: apps/desktop/artifacts
run: |
aws s3 cp ./ $AWS_S3_BUCKET_NAME/desktop/ \
--recursive \
--quiet \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
- name: Update deployment status to Success
if: ${{ success() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
@@ -1018,9 +1086,11 @@ jobs:
- macos-package-github
- macos-package-mas
- release
permissions:
contents: write
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup git config
run: |
@@ -1030,5 +1100,5 @@ jobs:
git config --global url."https://".insteadOf ssh://
- name: Remove branch
env:
BRANCH: ${{ needs.setup.outputs.branch-name }}
BRANCH: ${{ needs.setup.outputs.branch_name }}
run: git push origin --delete $BRANCH

View File

@@ -1,4 +1,3 @@
---
name: Release Desktop
run-name: Release Desktop ${{ inputs.release_type }}
@@ -6,54 +5,33 @@ on:
workflow_dispatch:
inputs:
release_type:
description: 'Release Options'
description: 'Release Type'
required: true
default: 'Initial Release'
default: 'Release'
type: choice
options:
- Initial Release
- Redeploy
- Release
- Dry Run
rollout_percentage:
description: 'Staged Rollout Percentage'
required: true
default: '10'
type: string
snap_publish:
description: 'Publish to Snap store'
required: true
default: true
type: boolean
choco_publish:
description: 'Publish to Chocolatey store'
required: true
default: true
type: boolean
electron_publish:
description: 'Publish Electron to S3 bucket'
required: true
default: true
type: boolean
github_release:
description: 'Publish GitHub release'
required: true
default: true
type: boolean
defaults:
run:
shell: bash
permissions:
contents: read
jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
permissions:
contents: write
outputs:
release-version: ${{ steps.version.outputs.version }}
release-channel: ${{ steps.release-channel.outputs.channel }}
release_version: ${{ steps.version.outputs.version }}
release_channel: ${{ steps.release_channel.outputs.channel }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Branch check
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
@@ -76,7 +54,7 @@ jobs:
monorepo-project: desktop
- name: Get Version Channel
id: release-channel
id: release_channel
run: |
case "${{ steps.version.outputs.version }}" in
*"alpha"*)
@@ -92,35 +70,6 @@ jobs:
;;
esac
- name: Create GitHub deployment
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
id: deployment
with:
token: '${{ secrets.GITHUB_TOKEN }}'
initial-status: 'in_progress'
environment: 'Desktop - Production'
description: 'Deployment ${{ steps.version.outputs.version }} to channel ${{ steps.release-channel.outputs.channel }} from branch ${{ github.ref_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,
r2-electron-access-id,
r2-electron-access-key,
r2-electron-bucket-name,
cf-prod-account"
- name: Download all artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
@@ -145,57 +94,12 @@ jobs:
working-directory: apps/desktop/artifacts
run: mv Bitwarden-${{ env.PKG_VERSION }}-universal.pkg Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive
- name: Set staged rollout percentage
if: ${{ github.event.inputs.electron_publish == 'true' }}
env:
RELEASE_CHANNEL: ${{ steps.release-channel.outputs.channel }}
ROLLOUT_PCT: ${{ inputs.rollout_percentage }}
run: |
echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}.yml
echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}-linux.yml
echo "stagingPercentage: ${ROLLOUT_PCT}" >> apps/desktop/artifacts/${RELEASE_CHANNEL}-mac.yml
- name: Publish artifacts to S3
if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish == 'true' }}
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: Publish artifacts to R2
if: ${{ github.event.inputs.release_type != 'Dry Run' && github.event.inputs.electron_publish == 'true' }}
env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }}
AWS_DEFAULT_REGION: 'us-east-1'
AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.r2-electron-bucket-name }}
CF_ACCOUNT: ${{ steps.retrieve-secrets.outputs.cf-prod-account }}
working-directory: apps/desktop/artifacts
run: |
aws s3 cp ./ $AWS_S3_BUCKET_NAME/desktop/ \
--recursive \
--quiet \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
- name: Get checksum files
uses: bitwarden/gh-actions/get-checksum@main
with:
packages_dir: "apps/desktop/artifacts"
file_path: "apps/desktop/artifacts/sha256-checksums.txt"
- name: Create Release
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
if: ${{ steps.release-channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' && github.event.inputs.github_release == 'true' }}
uses: ncipollo/release-action@cdcc88a9acf3ca41c16c37bb7d21b9ad48560d87 # v1.15.0
if: ${{ steps.release_channel.outputs.channel == 'latest' && github.event.inputs.release_type != 'Dry Run' }}
env:
PKG_VERSION: ${{ steps.version.outputs.version }}
RELEASE_CHANNEL: ${{ steps.release-channel.outputs.channel }}
RELEASE_CHANNEL: ${{ steps.release_channel.outputs.channel }}
with:
artifacts: "apps/desktop/artifacts/Bitwarden-${{ env.PKG_VERSION }}-amd64.deb,
apps/desktop/artifacts/Bitwarden-${{ env.PKG_VERSION }}-x86_64.rpm,
@@ -220,151 +124,10 @@ jobs:
apps/desktop/artifacts/Bitwarden-${{ env.PKG_VERSION }}-universal.pkg.archive,
apps/desktop/artifacts/${{ env.RELEASE_CHANNEL }}.yml,
apps/desktop/artifacts/${{ env.RELEASE_CHANNEL }}-linux.yml,
apps/desktop/artifacts/${{ env.RELEASE_CHANNEL }}-mac.yml,
apps/desktop/artifacts/sha256-checksums.txt"
apps/desktop/artifacts/${{ env.RELEASE_CHANNEL }}-mac.yml"
commit: ${{ github.sha }}
tag: desktop-v${{ env.PKG_VERSION }}
name: Desktop v${{ env.PKG_VERSION }}
body: "<insert release notes here>"
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
- name: Update deployment status to Success
if: ${{ github.event.inputs.release_type != 'Dry Run' && 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: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
state: 'failure'
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
snap:
name: Deploy Snap
runs-on: ubuntu-22.04
needs: setup
if: ${{ github.event.inputs.snap_publish == 'true' }}
env:
_PKG_VERSION: ${{ needs.setup.outputs.release-version }}
steps:
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- 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: "snapcraft-store-token"
- name: Install Snap
uses: samuelmeuli/action-snapcraft@d33c176a9b784876d966f80fb1b461808edc0641 # v2.1.1
- name: Setup
run: mkdir dist
working-directory: apps/desktop
- name: Download Snap artifact
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-desktop.yml
workflow_conclusion: success
branch: ${{ github.ref_name }}
artifacts: bitwarden_${{ env._PKG_VERSION }}_amd64.snap
path: apps/desktop/dist
- name: Dry Run - Download Snap artifact
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-desktop.yml
workflow_conclusion: success
branch: main
artifacts: bitwarden_${{ env._PKG_VERSION }}_amd64.snap
path: apps/desktop/dist
- name: Deploy to Snap Store
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ steps.retrieve-secrets.outputs.snapcraft-store-token }}
run: |
snapcraft upload bitwarden_${{ env._PKG_VERSION }}_amd64.snap --release stable
snapcraft logout
working-directory: apps/desktop/dist
choco:
name: Deploy Choco
runs-on: windows-2022
needs: setup
if: ${{ github.event.inputs.choco_publish == 'true' }}
env:
_PKG_VERSION: ${{ needs.setup.outputs.release-version }}
steps:
- name: Checkout Repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Print Environment
run: |
dotnet --version
dotnet nuget --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: "cli-choco-api-key"
- name: Setup Chocolatey
shell: pwsh
run: choco apikey --key $env:CHOCO_API_KEY --source https://push.chocolatey.org/
env:
CHOCO_API_KEY: ${{ steps.retrieve-secrets.outputs.cli-choco-api-key }}
- name: Make dist dir
shell: pwsh
run: New-Item -ItemType directory -Path ./dist
working-directory: apps/desktop
- name: Download choco artifact
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-desktop.yml
workflow_conclusion: success
branch: ${{ github.ref_name }}
artifacts: bitwarden.${{ env._PKG_VERSION }}.nupkg
path: apps/desktop/dist
- name: Dry Run - Download choco artifact
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-desktop.yml
workflow_conclusion: success
branch: main
artifacts: bitwarden.${{ env._PKG_VERSION }}.nupkg
path: apps/desktop/dist
- name: Push to Chocolatey
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
shell: pwsh
run: choco push --source=https://push.chocolatey.org/
working-directory: apps/desktop/dist

View File

@@ -1,4 +1,3 @@
---
name: Release Web
run-name: Release Web ${{ inputs.release_type }}
@@ -15,19 +14,18 @@ on:
- Redeploy
- Dry Run
env:
_AZ_REGISTRY: bitwardenprod.azurecr.io
jobs:
setup:
name: Setup
runs-on: ubuntu-22.04
permissions:
contents: read
outputs:
release_version: ${{ steps.version.outputs.version }}
tag_version: ${{ steps.version.outputs.tag }}
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Branch check
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
@@ -49,182 +47,14 @@ jobs:
monorepo: true
monorepo-project: web
self-host:
name: Release self-host docker
runs-on: ubuntu-22.04
needs: setup
env:
_BRANCH_NAME: ${{ github.ref_name }}
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
_RELEASE_OPTION: ${{ github.event.inputs.release_type }}
steps:
- name: Print environment
run: |
whoami
docker --version
echo "GitHub ref: $GITHUB_REF"
echo "GitHub event: $GITHUB_EVENT"
echo "Github Release Option: $_RELEASE_OPTION"
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
########## ACR ##########
- name: Login to Azure - PROD Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}
- name: Login to Azure ACR
run: az acr login -n bitwardenprod
- name: Pull branch image
run: |
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
docker pull $_AZ_REGISTRY/web:latest
else
docker pull $_AZ_REGISTRY/web:$_BRANCH_NAME
fi
- name: Tag version
run: |
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
docker tag $_AZ_REGISTRY/web:latest $_AZ_REGISTRY/web:dryrun
docker tag $_AZ_REGISTRY/web:latest $_AZ_REGISTRY/web-sh:dryrun
else
docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web:$_RELEASE_VERSION
docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web-sh:$_RELEASE_VERSION
docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web:latest
docker tag $_AZ_REGISTRY/web:$_BRANCH_NAME $_AZ_REGISTRY/web-sh:latest
fi
- name: Push version
run: |
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
docker push $_AZ_REGISTRY/web:dryrun
docker push $_AZ_REGISTRY/web-sh:dryrun
else
docker push $_AZ_REGISTRY/web:$_RELEASE_VERSION
docker push $_AZ_REGISTRY/web-sh:$_RELEASE_VERSION
docker push $_AZ_REGISTRY/web:latest
docker push $_AZ_REGISTRY/web-sh:latest
fi
- name: Log out of Docker
run: docker logout
ghpages-deploy:
name: Create Deploy PR for GitHub Pages
runs-on: ubuntu-22.04
needs: setup
env:
_RELEASE_VERSION: ${{ needs.setup.outputs.release_version }}
_TAG_VERSION: ${{ needs.setup.outputs.tag_version }}
_BRANCH: "v${{ needs.setup.outputs.release_version }}-deploy"
steps:
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
- name: Retrieve bot secrets
id: retrieve-bot-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: bitwarden-ci
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
- name: Checkout GH pages repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
repository: bitwarden/web-vault-pages
path: ghpages-deployment
token: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
- name: Download latest cloud asset
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-web.yml
path: assets
workflow_conclusion: success
branch: ${{ github.ref_name }}
artifacts: web-*-cloud-COMMERCIAL.zip
- name: Dry Run - Download latest cloud asset
if: ${{ github.event.inputs.release_type == 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
with:
workflow: build-web.yml
path: assets
workflow_conclusion: success
branch: main
artifacts: web-*-cloud-COMMERCIAL.zip
- name: Unzip build asset
working-directory: assets
run: unzip web-*-cloud-COMMERCIAL.zip
- name: Create new branch
run: |
cd ${{ github.workspace }}/ghpages-deployment
git config user.name = "GitHub Action Bot"
git config user.email = "<>"
git config --global url."https://github.com/".insteadOf ssh://git@github.com/
git config --global url."https://".insteadOf ssh://
git checkout -b ${_BRANCH}
- name: Copy build files
run: |
rm -rf ${{ github.workspace }}/ghpages-deployment/*
cp -Rf ${{ github.workspace }}/assets/build/* ghpages-deployment/
- name: Commit and push changes
working-directory: ghpages-deployment
run: |
git add .
git commit -m "Deploy Web v${_RELEASE_VERSION} to GitHub Pages"
git push --set-upstream origin ${_BRANCH} --force
- name: Create GitHub Pages Deploy PR
working-directory: ghpages-deployment
env:
GITHUB_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
run: |
if [[ "${{ github.event.inputs.release_type }}" == "Dry Run" ]]; then
gh pr create --title "Deploy v${_RELEASE_VERSION} to GitHub Pages" \
--draft \
--body "Deploying v${_RELEASE_VERSION}" \
--base main \
--head "${_BRANCH}"
else
gh pr create --title "Deploy v${_RELEASE_VERSION} to GitHub Pages" \
--body "Deploying v${_RELEASE_VERSION}" \
--base main \
--head "${_BRANCH}"
fi
release:
name: Create GitHub Release
runs-on: ubuntu-22.04
needs:
- setup
- self-host
- ghpages-deploy
permissions:
contents: write
steps:
- name: Create GitHub deployment
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7
id: deployment
with:
token: '${{ secrets.GITHUB_TOKEN }}'
initial-status: 'in_progress'
environment-url: http://vault.bitwarden.com
environment: 'Web Vault - US Production Cloud'
description: 'Deployment ${{ needs.setup.outputs.release_version }} from branch ${{ github.ref_name }}'
task: release
- name: Download latest build artifacts
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: bitwarden/gh-actions/download-artifacts@main
@@ -255,7 +85,7 @@ jobs:
- name: Create release
if: ${{ github.event.inputs.release_type != 'Dry Run' }}
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
uses: ncipollo/release-action@cdcc88a9acf3ca41c16c37bb7d21b9ad48560d87 # v1.15.0
with:
name: "Web v${{ needs.setup.outputs.release_version }}"
commit: ${{ github.sha }}
@@ -265,21 +95,3 @@ jobs:
apps/web/artifacts/web-${{ needs.setup.outputs.release_version }}-selfhosted-open-source.zip"
token: ${{ secrets.GITHUB_TOKEN }}
draft: true
- name: Update deployment status to Success
if: ${{ github.event.inputs.release_type != 'Dry Run' && success() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
environment-url: http://vault.bitwarden.com
state: 'success'
deployment-id: ${{ steps.deployment.outputs.deployment_id }}
- name: Update deployment status to Failure
if: ${{ github.event.inputs.release_type != 'Dry Run' && failure() }}
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3
with:
token: '${{ secrets.GITHUB_TOKEN }}'
environment-url: http://vault.bitwarden.com
state: 'failure'
deployment-id: ${{ steps.deployment.outputs.deployment_id }}

View File

@@ -0,0 +1,482 @@
name: Repository management
on:
workflow_dispatch:
inputs:
task:
default: "Version Bump"
description: "Task to execute"
options:
- "Version Bump"
- "Version Bump and Cut rc"
required: true
type: choice
bump_browser:
description: "Bump Browser version?"
type: boolean
default: false
bump_cli:
description: "Bump CLI version?"
type: boolean
default: false
bump_desktop:
description: "Bump Desktop version?"
type: boolean
default: false
bump_web:
description: "Bump Web version?"
type: boolean
default: false
target_ref:
default: "main"
description: "Branch/Tag to target for cut"
required: true
type: string
version_number_override:
description: "New version override (leave blank for automatic calculation, example: '2024.1.0')"
required: false
type: string
permissions: {}
jobs:
setup:
name: Setup
runs-on: ubuntu-24.04
outputs:
branch: ${{ steps.set-branch.outputs.branch }}
steps:
- name: Set branch
id: set-branch
env:
TASK: ${{ inputs.task }}
run: |
if [[ "$TASK" == "Version Bump" ]]; then
BRANCH="none"
elif [[ "$TASK" == "Version Bump and Cut rc" ]]; then
BRANCH="rc"
fi
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
bump_version:
name: Bump Version
if: ${{ always() }}
runs-on: ubuntu-24.04
needs: setup
outputs:
version_browser: ${{ steps.set-final-version-output.outputs.version_browser }}
version_cli: ${{ steps.set-final-version-output.outputs.version_cli }}
version_desktop: ${{ steps.set-final-version-output.outputs.version_desktop }}
version_web: ${{ steps.set-final-version-output.outputs.version_web }}
permissions:
id-token: write
steps:
- name: Validate version input format
if: ${{ inputs.version_number_override != '' }}
uses: bitwarden/gh-actions/version-check@main
with:
version: ${{ inputs.version_number_override }}
- 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-org-bitwarden
secrets: "BW-GHAPP-ID,BW-GHAPP-KEY"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Generate GH App token
uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3
id: app-token
with:
app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }}
private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }}
- name: Check out branch
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: main
token: ${{ steps.app-token.outputs.token }}
- name: Configure Git
run: |
git config --local user.email "actions@github.com"
git config --local user.name "Github Actions"
########################
# VERSION BUMP SECTION #
########################
### Browser
- name: Get current Browser version
if: ${{ inputs.bump_browser == true }}
id: current-browser-version
run: |
CURRENT_VERSION=$(cat package.json | jq -r '.version')
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
working-directory: apps/browser
- name: Browser - Verify input version
if: ${{ inputs.bump_browser == true && inputs.version_number_override != '' }}
env:
CURRENT_VERSION: ${{ steps.current-browser-version.outputs.version }}
NEW_VERSION: ${{ inputs.version_number_override }}
run: |
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
exit 1
fi
# Check if version is newer.
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
if [ $? -eq 0 ]; then
echo "Version check successful."
else
echo "Version check failed."
exit 1
fi
working-directory: apps/browser
- name: Calculate next Browser release version
if: ${{ inputs.bump_browser == true && inputs.version_number_override == '' }}
id: calculate-next-browser-version
uses: bitwarden/gh-actions/version-next@main
with:
version: ${{ steps.current-browser-version.outputs.version }}
- name: Bump Browser Version - Version Override
if: ${{ inputs.bump_browser == true && inputs.version_number_override != '' }}
id: bump-browser-version-override
env:
VERSION: ${{ inputs.version_number_override }}
run: npm version --workspace=@bitwarden/browser $VERSION
- name: Bump Browser Version - Automatic Calculation
if: ${{ inputs.bump_browser == true && inputs.version_number_override == '' }}
id: bump-browser-version-automatic
env:
VERSION: ${{ steps.calculate-next-browser-version.outputs.version }}
run: npm version --workspace=@bitwarden/browser $VERSION
- name: Bump Browser Version - Manifest - Version Override
if: ${{ inputs.bump_browser == true && inputs.version_number_override != '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "apps/browser/src/manifest.json"
version: ${{ inputs.version_number_override }}
- name: Bump Browser Version - Manifest - Automatic Calculation
if: ${{ inputs.bump_browser == true && inputs.version_number_override == '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "apps/browser/src/manifest.json"
version: ${{ steps.calculate-next-browser-version.outputs.version }}
- name: Bump Browser Version - Manifest v3 - Version Override
if: ${{ inputs.bump_browser == true && inputs.version_number_override != '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "apps/browser/src/manifest.v3.json"
version: ${{ inputs.version_number_override }}
- name: Bump Browser Version - Manifest v3 - Automatic Calculation
if: ${{ inputs.bump_browser == true && inputs.version_number_override == '' }}
uses: bitwarden/gh-actions/version-bump@main
with:
file_path: "apps/browser/src/manifest.v3.json"
version: ${{ steps.calculate-next-browser-version.outputs.version }}
- name: Run Prettier after Browser Version Bump
if: ${{ inputs.bump_browser == true }}
run: |
npm install -g prettier
prettier --write apps/browser/src/manifest.json
prettier --write apps/browser/src/manifest.v3.json
### CLI
- name: Get current CLI version
if: ${{ inputs.bump_cli == true }}
id: current-cli-version
run: |
CURRENT_VERSION=$(cat package.json | jq -r '.version')
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
working-directory: apps/cli
- name: CLI - Verify input version
if: ${{ inputs.bump_cli == true && inputs.version_number_override != '' }}
env:
CURRENT_VERSION: ${{ steps.current-cli-version.outputs.version }}
NEW_VERSION: ${{ inputs.version_number_override }}
run: |
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
exit 1
fi
# Check if version is newer.
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
if [ $? -eq 0 ]; then
echo "Version check successful."
else
echo "Version check failed."
exit 1
fi
working-directory: apps/cli
- name: Calculate next CLI release version
if: ${{ inputs.bump_cli == true && inputs.version_number_override == '' }}
id: calculate-next-cli-version
uses: bitwarden/gh-actions/version-next@main
with:
version: ${{ steps.current-cli-version.outputs.version }}
- name: Bump CLI Version - Version Override
if: ${{ inputs.bump_cli == true && inputs.version_number_override != '' }}
id: bump-cli-version-override
env:
VERSION: ${{ inputs.version_number_override }}
run: npm version --workspace=@bitwarden/cli $VERSION
- name: Bump CLI Version - Automatic Calculation
if: ${{ inputs.bump_cli == true && inputs.version_number_override == '' }}
id: bump-cli-version-automatic
env:
VERSION: ${{ steps.calculate-next-cli-version.outputs.version }}
run: npm version --workspace=@bitwarden/cli $VERSION
### Desktop
- name: Get current Desktop version
if: ${{ inputs.bump_desktop == true }}
id: current-desktop-version
run: |
CURRENT_VERSION=$(cat package.json | jq -r '.version')
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
working-directory: apps/desktop
- name: Desktop - Verify input version
if: ${{ inputs.bump_desktop == true && inputs.version_number_override != '' }}
env:
CURRENT_VERSION: ${{ steps.current-desktop-version.outputs.version }}
NEW_VERSION: ${{ inputs.version_number_override }}
run: |
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
exit 1
fi
# Check if version is newer.
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
if [ $? -eq 0 ]; then
echo "Version check successful."
else
echo "Version check failed."
exit 1
fi
working-directory: apps/desktop
- name: Calculate next Desktop release version
if: ${{ inputs.bump_desktop == true && inputs.version_number_override == '' }}
id: calculate-next-desktop-version
uses: bitwarden/gh-actions/version-next@main
with:
version: ${{ steps.current-desktop-version.outputs.version }}
- name: Bump Desktop Version - Root - Version Override
if: ${{ inputs.bump_desktop == true && inputs.version_number_override != '' }}
id: bump-desktop-version-override
env:
VERSION: ${{ inputs.version_number_override }}
run: npm version --workspace=@bitwarden/desktop $VERSION
- name: Bump Desktop Version - Root - Automatic Calculation
if: ${{ inputs.bump_desktop == true && inputs.version_number_override == '' }}
id: bump-desktop-version-automatic
env:
VERSION: ${{ steps.calculate-next-desktop-version.outputs.version }}
run: npm version --workspace=@bitwarden/desktop $VERSION
- name: Bump Desktop Version - App - Version Override
if: ${{ inputs.bump_desktop == true && inputs.version_number_override != '' }}
env:
VERSION: ${{ inputs.version_number_override }}
run: npm version $VERSION
working-directory: "apps/desktop/src"
- name: Bump Desktop Version - App - Automatic Calculation
if: ${{ inputs.bump_desktop == true && inputs.version_number_override == '' }}
env:
VERSION: ${{ steps.calculate-next-desktop-version.outputs.version }}
run: npm version $VERSION
working-directory: "apps/desktop/src"
### Web
- name: Get current Web version
if: ${{ inputs.bump_web == true }}
id: current-web-version
run: |
CURRENT_VERSION=$(cat package.json | jq -r '.version')
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
working-directory: apps/web
- name: Web - Verify input version
if: ${{ inputs.bump_web == true && inputs.version_number_override != '' }}
env:
CURRENT_VERSION: ${{ steps.current-web-version.outputs.version }}
NEW_VERSION: ${{ inputs.version_number_override }}
run: |
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
exit 1
fi
# Check if version is newer.
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
if [ $? -eq 0 ]; then
echo "Version check successful."
else
echo "Version check failed."
exit 1
fi
working-directory: apps/web
- name: Calculate next Web release version
if: ${{ inputs.bump_web == true && inputs.version_number_override == '' }}
id: calculate-next-web-version
uses: bitwarden/gh-actions/version-next@main
with:
version: ${{ steps.current-web-version.outputs.version }}
- name: Bump Web Version - Version Override
if: ${{ inputs.bump_web == true && inputs.version_number_override != '' }}
id: bump-web-version-override
env:
VERSION: ${{ inputs.version_number_override }}
run: npm version --workspace=@bitwarden/web-vault $VERSION
- name: Bump Web Version - Automatic Calculation
if: ${{ inputs.bump_web == true && inputs.version_number_override == '' }}
id: bump-web-version-automatic
env:
VERSION: ${{ steps.calculate-next-web-version.outputs.version }}
run: npm version --workspace=@bitwarden/web-vault $VERSION
########################
- name: Set final version output
id: set-final-version-output
env:
VERSION: ${{ inputs.version_number_override }}
run: |
if [[ "${{ steps.bump-browser-version-override.outcome }}" = "success" ]]; then
echo "version_browser=$VERSION" >> $GITHUB_OUTPUT
elif [[ "${{ steps.bump-browser-version-automatic.outcome }}" = "success" ]]; then
echo "version_browser=${{ steps.calculate-next-browser-version.outputs.version }}" >> $GITHUB_OUTPUT
fi
if [[ "${{ steps.bump-cli-version-override.outcome }}" = "success" ]]; then
echo "version_cli=$VERSION" >> $GITHUB_OUTPUT
elif [[ "${{ steps.bump-cli-version-automatic.outcome }}" = "success" ]]; then
echo "version_cli=${{ steps.calculate-next-cli-version.outputs.version }}" >> $GITHUB_OUTPUT
fi
if [[ "${{ steps.bump-desktop-version-override.outcome }}" = "success" ]]; then
echo "version_desktop=$VERSION" >> $GITHUB_OUTPUT
elif [[ "${{ steps.bump-desktop-version-automatic.outcome }}" = "success" ]]; then
echo "version_desktop=${{ steps.calculate-next-desktop-version.outputs.version }}" >> $GITHUB_OUTPUT
fi
if [[ "${{ steps.bump-web-version-override.outcome }}" = "success" ]]; then
echo "version_web=$VERSION" >> $GITHUB_OUTPUT
elif [[ "${{ steps.bump-web-version-automatic.outcome }}" = "success" ]]; then
echo "version_web=${{ steps.calculate-next-web-version.outputs.version }}" >> $GITHUB_OUTPUT
fi
- name: Check if version changed
id: version-changed
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "changes_to_commit=TRUE" >> $GITHUB_OUTPUT
else
echo "changes_to_commit=FALSE" >> $GITHUB_OUTPUT
echo "No changes to commit!";
fi
- name: Commit files
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
run: git commit -m "Bumped client version(s)" -a
- name: Push changes
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
run: git push
cut_branch:
name: Cut branch
if: ${{ needs.setup.outputs.branch == 'rc' }}
needs:
- setup
- bump_version
runs-on: ubuntu-24.04
permissions:
id-token: write
steps:
- 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-org-bitwarden
secrets: "BW-GHAPP-ID,BW-GHAPP-KEY"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Generate GH App token
uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3
id: app-token
with:
app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }}
private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }}
- name: Check out target ref
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ inputs.target_ref }}
token: ${{ steps.app-token.outputs.token }}
- name: Check if ${{ needs.setup.outputs.branch }} branch exists
env:
BRANCH_NAME: ${{ needs.setup.outputs.branch }}
run: |
if [[ $(git ls-remote --heads origin $BRANCH_NAME) ]]; then
echo "$BRANCH_NAME already exists! Please delete $BRANCH_NAME before running again." >> $GITHUB_STEP_SUMMARY
exit 1
fi
- name: Cut branch
env:
BRANCH_NAME: ${{ needs.setup.outputs.branch }}
run: |
git switch --quiet --create $BRANCH_NAME
git push --quiet --set-upstream origin $BRANCH_NAME

View File

@@ -0,0 +1,48 @@
name: Retrieve Current Desktop Rollout
on:
workflow_dispatch:
defaults:
run:
shell: bash
jobs:
rollout:
name: Retrieve Rollout Percentage
runs-on: ubuntu-22.04
permissions:
id-token: write
steps:
- 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: "aws-electron-access-id,
aws-electron-access-key,
aws-electron-bucket-name"
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Download channel update info files from 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 }}
run: aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest.yml . --quiet
- name: Get current rollout percentage
run: |
CURRENT_PCT=$(sed -r -n "s/stagingPercentage:\s([0-9]+)/\1/p" latest.yml)
CURRENT_VERSION=$(sed -r -n "s/version:\s(.*)/\1/p" latest.yml)
echo "Desktop ${CURRENT_VERSION} rollout percentage is ${CURRENT_PCT}%" >> $GITHUB_STEP_SUMMARY

53
.github/workflows/scan.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Scan
on:
workflow_dispatch:
push:
branches:
- "main"
- "rc"
- "hotfix-rc"
pull_request:
types: [opened, synchronize, reopened]
branches-ignore:
- "main"
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- "main"
permissions: {}
jobs:
check-run:
name: Check PR run
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
permissions:
contents: read
sast:
name: Checkmarx
uses: bitwarden/gh-actions/.github/workflows/_checkmarx.yml@main
needs: check-run
secrets:
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
permissions:
contents: read
pull-requests: write
security-events: write
id-token: write
quality:
name: Sonar
uses: bitwarden/gh-actions/.github/workflows/_sonar.yml@main
needs: check-run
secrets:
AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
permissions:
contents: read
pull-requests: write
id-token: write

View File

@@ -1,5 +1,5 @@
---
name: Staged Rollout Desktop
run-name: Staged Rollout Desktop - ${{ inputs.rollout_percentage }}%
on:
workflow_dispatch:
@@ -18,11 +18,15 @@ jobs:
rollout:
name: Update Rollout Percentage
runs-on: ubuntu-22.04
permissions:
id-token: write
steps:
- name: Login to Azure
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Log in to Azure
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
client_id: ${{ secrets.AZURE_CLIENT_ID }}
- name: Retrieve secrets
id: retrieve-secrets
@@ -31,29 +35,24 @@ jobs:
keyvault: "bitwarden-ci"
secrets: "aws-electron-access-id,
aws-electron-access-key,
aws-electron-bucket-name,
r2-electron-access-id,
r2-electron-access-key,
r2-electron-bucket-name,
cf-prod-account"
aws-electron-bucket-name"
- name: Download channel update info files from R2
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Download channel update info files from S3
env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }}
AWS_DEFAULT_REGION: 'us-east-1'
AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.r2-electron-bucket-name }}
CF_ACCOUNT: ${{ steps.retrieve-secrets.outputs.cf-prod-account }}
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 }}
run: |
aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest.yml . \
--quiet \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
--quiet
aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest-linux.yml . \
--quiet \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
--quiet
aws s3 cp $AWS_S3_BUCKET_NAME/desktop/latest-mac.yml . \
--quiet \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
--quiet
- name: Check new rollout percentage
env:
@@ -95,20 +94,3 @@ jobs:
aws s3 cp latest-mac.yml $AWS_S3_BUCKET_NAME/desktop/ \
--acl "public-read"
- name: Publish channel update info files to R2
env:
AWS_ACCESS_KEY_ID: ${{ steps.retrieve-secrets.outputs.r2-electron-access-id }}
AWS_SECRET_ACCESS_KEY: ${{ steps.retrieve-secrets.outputs.r2-electron-access-key }}
AWS_DEFAULT_REGION: 'us-east-1'
AWS_S3_BUCKET_NAME: ${{ steps.retrieve-secrets.outputs.r2-electron-bucket-name }}
CF_ACCOUNT: ${{ steps.retrieve-secrets.outputs.cf-prod-account }}
run: |
aws s3 cp latest.yml $AWS_S3_BUCKET_NAME/desktop/ \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
aws s3 cp latest-linux.yml $AWS_S3_BUCKET_NAME/desktop/ \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com
aws s3 cp latest-mac.yml $AWS_S3_BUCKET_NAME/desktop/ \
--endpoint-url https://${CF_ACCOUNT}.r2.cloudflarestorage.com

View File

@@ -1,4 +1,3 @@
---
name: 'Close stale issues and PRs'
on:
workflow_dispatch:
@@ -9,9 +8,14 @@ jobs:
stale:
name: 'Check for stale issues and PRs'
runs-on: ubuntu-22.04
permissions:
actions: write
contents: read
issues: write
pull-requests: write
steps:
- name: 'Run stale action'
uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
stale-issue-label: 'needs-reply'
stale-pr-label: 'needs-changes'

View File

@@ -1,5 +1,4 @@
---
name: Run tests
name: Testing
on:
workflow_dispatch:
@@ -9,18 +8,23 @@ on:
- "rc"
- "hotfix-rc-*"
pull_request:
types: [ opened, synchronize ]
defaults:
run:
shell: bash
permissions: {}
jobs:
test:
testing:
name: Run tests
runs-on: ubuntu-22.04
permissions:
checks: write
contents: read
pull-requests: write
steps:
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Get Node Version
id: retrieve-node-version
@@ -30,7 +34,7 @@ jobs:
echo "node_version=$NODE_VERSION" >> $GITHUB_OUTPUT
- name: Set up Node
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
cache: 'npm'
cache-dependency-path: '**/package-lock.json'
@@ -48,55 +52,63 @@ jobs:
# Tests in apps/ are typechecked when their app is built, so we just do it here for libs/
# See https://bitwarden.atlassian.net/browse/EC-497
- name: Run typechecking
run: npm run test:types --coverage
run: npm run test:types
- name: Run tests
run: npm run test --coverage
# maxWorkers is a workaround for a memory leak that crashes tests in CI:
# https://github.com/facebook/jest/issues/9430#issuecomment-1149882002
run: npm test -- --coverage --maxWorkers=3
- name: Report test results
uses: dorny/test-reporter@eaa763f6ffc21c7a37837f56cd5f9737f27fc6c8 # v1.8.0
if: always()
uses: dorny/test-reporter@6e6a65b7a0bd2c9197df7d0ae36ac5cee784230c # v2.0.0
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && !cancelled() }}
with:
name: Test Results
path: "junit.xml"
reporter: jest-junit
fail-on-error: true
- name: Upload to codecov.io
uses: codecov/codecov-action@54bcd8715eee62d40e33596ef5e8f0f48dbbccab # v4.1.0
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: Upload results to codecov.io
uses: codecov/test-results-action@f2dba722c67b86c6caa034178c6e4d35335f6706 # v1.1.0
- name: Upload test coverage
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: jest-coverage
path: ./coverage/lcov.info
rust:
name: rust - ${{ matrix.os }}
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
name: Run Rust tests on ${{ matrix.os }}
runs-on: ${{ matrix.os || 'ubuntu-22.04' }}
permissions:
contents: read
strategy:
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
- ubuntu-22.04
- macos-14
- windows-2022
steps:
- name: Rust version check
- name: Check Rust version
run: rustup --version
- name: Install gnome-keyring
if: ${{ matrix.os=='ubuntu-latest' }}
if: ${{ matrix.os=='ubuntu-22.04' }}
run: |
sudo apt-get update
sudo apt-get install -y gnome-keyring dbus-x11
- name: Checkout repo
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Build
working-directory: ./apps/desktop/desktop_native
run: cargo build
- name: Test Ubuntu
if: ${{ matrix.os=='ubuntu-latest' }}
if: ${{ matrix.os=='ubuntu-22.04' }}
working-directory: ./apps/desktop/desktop_native
run: |
eval "$(dbus-launch --sh-syntax)"
@@ -106,7 +118,72 @@ jobs:
eval "$(printf '\n' | /usr/bin/gnome-keyring-daemon --start)"
cargo test -- --test-threads=1
- name: Test Windows / macOS
if: ${{ matrix.os!='ubuntu-latest' }}
- name: Test macOS
if: ${{ matrix.os=='macos-14' }}
working-directory: ./apps/desktop/desktop_native
run: cargo test -- --test-threads=1
- name: Test Windows
if: ${{ matrix.os=='windows-2022'}}
working-directory: ./apps/desktop/desktop_native/core
run: cargo test -- --test-threads=1
rust-coverage:
name: Rust Coverage
runs-on: macos-14
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install rust
uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b # stable
with:
toolchain: stable
components: llvm-tools
- name: Cache cargo registry
uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab # v2.7.5
with:
workspaces: "apps/desktop/desktop_native -> target"
- name: Install cargo-llvm-cov
run: cargo install cargo-llvm-cov --version 0.6.16
- name: Generate coverage
working-directory: ./apps/desktop/desktop_native
run: cargo llvm-cov --all-features --lcov --output-path lcov.info --workspace --no-cfg-coverage
- name: Upload test coverage
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: rust-coverage
path: ./apps/desktop/desktop_native/lcov.info
upload-codecov:
name: Upload to Codecov
runs-on: ubuntu-22.04
needs:
- testing
- rust-coverage
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Download jest coverage
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: jest-coverage
path: ./
- name: Download rust coverage
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
with:
name: rust-coverage
path: ./apps/desktop/desktop_native
- name: Upload coverage to codecov.io
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2
with:
files: |
./lcov.info
./apps/desktop/desktop_native/lcov.info

View File

@@ -1,4 +1,3 @@
---
name: Auto Bump Desktop Version
on:
@@ -6,53 +5,78 @@ on:
tags:
- desktop-v**
defaults:
run:
shell: bash
jobs:
bump-version:
name: Bump Desktop Version
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
permissions:
id-token: write
contents: write
steps:
- name: Checkout Branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Calculate bumped version
id: version
env:
RELEASE_TAG: ${{ github.ref }}
run: |
CURR_MAJOR=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/[a-z]*-v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\1/')
CURR_PATCH=$(echo $RELEASE_TAG | sed -r 's/refs\/tags\/[a-z]*-v([0-9]{4}\.[0-9]{1,2})\.([0-9]{1,2})/\2/')
echo "Current Major: $CURR_MAJOR"
echo "Current Patch: $CURR_PATCH"
NEW_PATCH=$((CURR_PATCH+1))
echo "New patch: $NEW_PATCH"
NEW_VER=$CURR_MAJOR.$NEW_PATCH
echo "New Version: $NEW_VER"
echo "New Version: $NEW_VER" >> $GITHUB_STEP_SUMMARY
echo "new_version=$NEW_VER" >> $GITHUB_OUTPUT
- name: Login to Azure - CI Subscription
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0
- name: Log in to Azure
uses: bitwarden/gh-actions/azure-login@main
with:
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}
subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
tenant_id: ${{ secrets.AZURE_TENANT_ID }}
client_id: ${{ secrets.AZURE_CLIENT_ID }}
- name: Retrieve bot secrets
id: retrieve-bot-secrets
- name: Get Azure Key Vault secrets
id: get-kv-secrets
uses: bitwarden/gh-actions/get-keyvault-secrets@main
with:
keyvault: bitwarden-ci
secrets: "github-pat-bitwarden-devops-bot-repo-scope"
keyvault: gh-org-bitwarden
secrets: "BW-GHAPP-ID,BW-GHAPP-KEY"
- name: "Bump version to ${{ steps.version.outputs.new_version }}"
env:
GH_TOKEN: ${{ steps.retrieve-bot-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
- name: Log out from Azure
uses: bitwarden/gh-actions/azure-logout@main
- name: Generate GH App token
uses: actions/create-github-app-token@30bf6253fa41bdc8d1501d202ad15287582246b4 # v2.0.3
id: app-token
with:
app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }}
private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }}
- name: Check out target ref
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: main
token: ${{ steps.app-token.outputs.token }}
- name: Configure Git
run: |
echo '{"cut_rc_branch": "false", "version_number": "${{ steps.version.outputs.new_version }}",
"bump_browser": "false", "bump_cli": "false", "bump_desktop": "true", "bump_web": "false"}' | \
gh workflow run version-bump.yml --json --repo bitwarden/clients
git config --local user.email "actions@github.com"
git config --local user.name "Github Actions"
- name: Get current Desktop version
id: current-desktop-version
run: |
CURRENT_VERSION=$(cat package.json | jq -r '.version')
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
working-directory: apps/desktop
- name: Calculate next Desktop release version
id: calculate-next-desktop-version
uses: bitwarden/gh-actions/version-next@main
with:
version: ${{ steps.current-desktop-version.outputs.version }}
- name: Bump Desktop Version - Root - Automatic Calculation
id: bump-desktop-version-automatic
env:
VERSION: ${{ steps.calculate-next-desktop-version.outputs.version }}
run: npm version --workspace=@bitwarden/desktop $VERSION
- name: Bump Desktop Version - App - Automatic Calculation
env:
VERSION: ${{ steps.calculate-next-desktop-version.outputs.version }}
run: npm version $VERSION
working-directory: "apps/desktop/src"
- name: Commit files
env:
VERSION: ${{ steps.calculate-next-desktop-version.outputs.version }}
run: git commit -m "Bumped Desktop client to $VERSION" -a
- name: Push changes
run: git push

View File

@@ -1,394 +0,0 @@
---
name: Version Bump
run-name: Version Bump - v${{ inputs.version_number }}
on:
workflow_dispatch:
inputs:
bump_browser:
description: "Bump Browser?"
type: boolean
default: false
bump_cli:
description: "Bump CLI?"
type: boolean
default: false
bump_desktop:
description: "Bump Desktop?"
type: boolean
default: false
bump_web:
description: "Bump Web?"
type: boolean
default: false
version_number:
description: "New version (example: '2024.1.0')"
required: true
cut_rc_branch:
description: "Cut RC branch?"
default: true
type: boolean
jobs:
bump_version:
name: "Bump Version to v${{ inputs.version_number }}"
runs-on: ubuntu-22.04
steps:
- name: Login to Azure - Prod Subscription
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: "github-gpg-private-key,
github-gpg-private-key-passphrase,
github-pat-bitwarden-devops-bot-repo-scope"
- name: Checkout Branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: main
- name: Check if RC branch exists
if: ${{ inputs.cut_rc_branch == true }}
run: |
remote_rc_branch_check=$(git ls-remote --heads origin rc | wc -l)
if [[ "${remote_rc_branch_check}" -gt 0 ]]; then
echo "Remote RC branch exists."
echo "Please delete current RC branch before running again."
exit 1
fi
- name: Import GPG key
uses: crazy-max/ghaction-import-gpg@01dd5d3ca463c7f10f7f4f7b4f177225ac661ee4 # v6.1.0
with:
gpg_private_key: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key }}
passphrase: ${{ steps.retrieve-secrets.outputs.github-gpg-private-key-passphrase }}
git_user_signingkey: true
git_commit_gpgsign: true
- name: Create Version Branch
id: create-branch
run: |
CLIENTS=()
if [[ ${{ inputs.bump_browser }} == true ]]; then
CLIENTS+=("browser")
fi
if [[ ${{ inputs.bump_cli }} == true ]]; then
CLIENTS+=("cli")
fi
if [[ ${{ inputs.bump_desktop }} == true ]]; then
CLIENTS+=("desktop")
fi
if [[ ${{ inputs.bump_web }} == true ]]; then
CLIENTS+=("web")
fi
printf -v joined '%s,' "${CLIENTS[@]}"
echo "client=${joined%,}" >> $GITHUB_OUTPUT
NAME=version_bump_${{ github.ref_name }}_${{ inputs.version_number }}
git switch -c $NAME
echo "name=$NAME" >> $GITHUB_OUTPUT
########################
# VERSION BUMP SECTION #
########################
### Browser
- name: Browser - Verify input version
if: ${{ inputs.bump_browser == true }}
env:
NEW_VERSION: ${{ inputs.version_number }}
run: |
CURRENT_VERSION=$(cat package.json | jq -r '.version')
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
exit 1
fi
# Check if version is newer.
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
if [ $? -eq 0 ]; then
echo "Version check successful."
else
echo "Version check failed."
exit 1
fi
working-directory: apps/browser
- name: Bump Browser Version
if: ${{ inputs.bump_browser == true }}
run: npm version --workspace=@bitwarden/browser ${{ inputs.version_number }}
- name: Bump Browser Version - Manifest
if: ${{ inputs.bump_browser == true }}
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "apps/browser/src/manifest.json"
- name: Bump Browser Version - Manifest v3
if: ${{ inputs.bump_browser == true }}
uses: bitwarden/gh-actions/version-bump@main
with:
version: ${{ inputs.version_number }}
file_path: "apps/browser/src/manifest.v3.json"
- name: Run Prettier after Browser Version Bump
if: ${{ inputs.bump_browser == true }}
run: |
npm install -g prettier
prettier --write apps/browser/src/manifest.json
prettier --write apps/browser/src/manifest.v3.json
### CLI
- name: CLI - Verify input version
if: ${{ inputs.bump_cli == true }}
env:
NEW_VERSION: ${{ inputs.version_number }}
run: |
CURRENT_VERSION=$(cat package.json | jq -r '.version')
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
exit 1
fi
# Check if version is newer.
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
if [ $? -eq 0 ]; then
echo "Version check successful."
else
echo "Version check failed."
exit 1
fi
working-directory: apps/cli
- name: Bump CLI Version
if: ${{ inputs.bump_cli == true }}
run: npm version --workspace=@bitwarden/cli ${{ inputs.version_number }}
### Desktop
- name: Desktop - Verify input version
if: ${{ inputs.bump_desktop == true }}
env:
NEW_VERSION: ${{ inputs.version_number }}
run: |
CURRENT_VERSION=$(cat package.json | jq -r '.version')
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
exit 1
fi
# Check if version is newer.
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
if [ $? -eq 0 ]; then
echo "Version check successful."
else
echo "Version check failed."
exit 1
fi
working-directory: apps/desktop
- name: Bump Desktop Version - Root
if: ${{ inputs.bump_desktop == true }}
run: npm version --workspace=@bitwarden/desktop ${{ inputs.version_number }}
- name: Bump Desktop Version - App
if: ${{ inputs.bump_desktop == true }}
run: npm version ${{ inputs.version_number }}
working-directory: "apps/desktop/src"
### Web
- name: Web - Verify input version
if: ${{ inputs.bump_web == true }}
env:
NEW_VERSION: ${{ inputs.version_number }}
run: |
CURRENT_VERSION=$(cat package.json | jq -r '.version')
# Error if version has not changed.
if [[ "$NEW_VERSION" == "$CURRENT_VERSION" ]]; then
echo "Version has not changed."
exit 1
fi
# Check if version is newer.
printf '%s\n' "${CURRENT_VERSION}" "${NEW_VERSION}" | sort -C -V
if [ $? -eq 0 ]; then
echo "Version check successful."
else
echo "Version check failed."
exit 1
fi
working-directory: apps/web
- name: Bump Web Version
if: ${{ inputs.bump_web == true }}
run: npm version --workspace=@bitwarden/web-vault ${{ inputs.version_number }}
########################
- name: Setup git
run: |
git config --local user.email "106330231+bitwarden-devops-bot@users.noreply.github.com"
git config --local user.name "bitwarden-devops-bot"
- name: Check if version changed
id: version-changed
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "changes_to_commit=TRUE" >> $GITHUB_OUTPUT
else
echo "changes_to_commit=FALSE" >> $GITHUB_OUTPUT
echo "No changes to commit!";
fi
- name: Commit files
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
env:
CLIENT: ${{ steps.create-branch.outputs.client }}
VERSION: ${{ inputs.version_number }}
run: git commit -m "Bumped ${CLIENT} version to ${VERSION}" -a
- name: Push changes
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
env:
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
run: git push -u origin $PR_BRANCH
- name: Create Version PR
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
id: create-pr
env:
GH_TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
PR_BRANCH: ${{ steps.create-branch.outputs.name }}
TITLE: "Bump ${{ steps.create-branch.outputs.client }} version to ${{ inputs.version_number }}"
run: |
PR_URL=$(gh pr create --title "$TITLE" \
--base "main" \
--head "$PR_BRANCH" \
--label "version update" \
--label "automated pr" \
--body "
## Type of change
- [ ] Bug fix
- [ ] New feature development
- [ ] Tech debt (refactoring, code cleanup, dependency upgrades, etc)
- [ ] Build/deploy pipeline (DevOps)
- [X] Other
## Objective
Automated ${{ steps.create-branch.outputs.client }} version bump to ${{ inputs.version_number }}")
echo "pr_number=${PR_URL##*/}" >> $GITHUB_OUTPUT
- name: Approve PR
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
run: gh pr review $PR_NUMBER --approve
- name: Merge PR
if: ${{ steps.version-changed.outputs.changes_to_commit == 'TRUE' }}
env:
GH_TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
PR_NUMBER: ${{ steps.create-pr.outputs.pr_number }}
run: gh pr merge $PR_NUMBER --squash --auto --delete-branch
cut_rc:
name: Cut RC branch
needs: bump_version
if: ${{ inputs.cut_rc_branch == true }}
runs-on: ubuntu-22.04
steps:
- name: Checkout Branch
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: main
### Browser
- name: Browser - Verify version has been updated
if: ${{ inputs.bump_browser == true }}
env:
NEW_VERSION: ${{ inputs.version_number }}
run: |
# Wait for version to change.
while : ; do
echo "Waiting for version to be updated..."
git pull --force
CURRENT_VERSION=$(cat package.json | jq -r '.version')
# If the versions don't match we continue the loop, otherwise we break out of the loop.
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
sleep 10
done
working-directory: apps/browser
### CLI
- name: CLI - Verify version has been updated
if: ${{ inputs.bump_cli == true }}
env:
NEW_VERSION: ${{ inputs.version_number }}
run: |
# Wait for version to change.
while : ; do
echo "Waiting for version to be updated..."
git pull --force
CURRENT_VERSION=$(cat package.json | jq -r '.version')
# If the versions don't match we continue the loop, otherwise we break out of the loop.
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
sleep 10
done
working-directory: apps/cli
### Desktop
- name: Desktop - Verify version has been updated
if: ${{ inputs.bump_desktop == true }}
env:
NEW_VERSION: ${{ inputs.version_number }}
run: |
# Wait for version to change.
while : ; do
echo "Waiting for version to be updated..."
git pull --force
CURRENT_VERSION=$(cat package.json | jq -r '.version')
# If the versions don't match we continue the loop, otherwise we break out of the loop.
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
sleep 10
done
working-directory: apps/desktop
### Web
- name: Web - Verify version has been updated
if: ${{ inputs.bump_web == true }}
env:
NEW_VERSION: ${{ inputs.version_number }}
run: |
# Wait for version to change.
while : ; do
echo "Waiting for version to be updated..."
git pull --force
CURRENT_VERSION=$(cat package.json | jq -r '.version')
# If the versions don't match we continue the loop, otherwise we break out of the loop.
[[ "$NEW_VERSION" != "$CURRENT_VERSION" ]] || break
sleep 10
done
working-directory: apps/web
- name: Cut RC branch
run: |
git switch --quiet --create rc
git push --quiet --set-upstream origin rc

9
.gitignore vendored
View File

@@ -27,10 +27,16 @@ npm-debug.log
dist
build
.angular/cache
.flatpak
.flatpak-repo
.flatpak-builder
# Testing
coverage
junit.xml
## The "base" root level folder is expected for some local tests that do
## comparisons between the current branch and a base branch (usually main)
base/
# Misc
*.crx
@@ -45,3 +51,6 @@ storybook-static
# Local app configuration
apps/**/config/local.json
# Nx
.nx

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
save-exact=true

2
.nvmrc
View File

@@ -1 +1 @@
v18
v22

View File

@@ -0,0 +1,33 @@
import { argsToTemplate, StoryObj } from "@storybook/angular";
type RenderArgType<T> = StoryObj<T>["args"];
export const formatArgsForCodeSnippet = <ComponentType extends Record<string, any>>(
args: RenderArgType<ComponentType>,
) => {
const nonNullArgs = Object.entries(args as ComponentType).filter(
([_, value]) => value !== null && value !== undefined,
);
const functionArgs = nonNullArgs.filter(([_, value]) => typeof value === "function");
const argsToFormat = nonNullArgs.filter(([_, value]) => typeof value !== "function");
const argsToTemplateIncludeKeys = [...functionArgs].map(
([key, _]) => key as keyof RenderArgType<ComponentType>,
);
const formattedNonFunctionArgs = argsToFormat
.map(([key, value]) => {
if (typeof value === "boolean") {
return `[${key}]="${value}"`;
}
if (Array.isArray(value)) {
const formattedArray = value.map((v) => `'${v}'`).join(", ");
return `[${key}]="[${formattedArray}]"`;
}
return `${key}="${value}"`;
})
.join(" ");
return `${formattedNonFunctionArgs} ${argsToTemplate(args as ComponentType, { include: argsToTemplateIncludeKeys })}`;
};

View File

@@ -1,24 +1,39 @@
import { dirname, join } from "path";
import { StorybookConfig } from "@storybook/angular";
import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
import remarkGfm from "remark-gfm";
import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin";
const config: StorybookConfig = {
stories: [
"../libs/auth/src/**/*.mdx",
"../libs/auth/src/**/*.stories.@(js|jsx|ts|tsx)",
"../libs/dirt/card/src/**/*.mdx",
"../libs/dirt/card/src/**/*.stories.@(js|jsx|ts|tsx)",
"../libs/tools/send/send-ui/src/**/*.mdx",
"../libs/tools/send/send-ui/src/**/*.stories.@(js|jsx|ts|tsx)",
"../libs/vault/src/**/*.mdx",
"../libs/vault/src/**/*.stories.@(js|jsx|ts|tsx)",
"../libs/components/src/**/*.mdx",
"../libs/components/src/**/*.stories.@(js|jsx|ts|tsx)",
"../apps/web/src/**/*.mdx",
"../apps/web/src/**/*.stories.@(js|jsx|ts|tsx)",
"../apps/browser/src/**/*.mdx",
"../apps/browser/src/**/*.stories.@(js|jsx|ts|tsx)",
"../bitwarden_license/bit-web/src/**/*.mdx",
"../bitwarden_license/bit-web/src/**/*.stories.@(js|jsx|ts|tsx)",
"../libs/angular/src/**/*.stories.@(js|jsx|ts|tsx)",
],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-a11y",
"@storybook/addon-designs",
"@storybook/addon-interactions",
getAbsolutePath("@storybook/addon-links"),
getAbsolutePath("@storybook/addon-essentials"),
getAbsolutePath("@storybook/addon-a11y"),
getAbsolutePath("@storybook/addon-designs"),
getAbsolutePath("@storybook/addon-interactions"),
getAbsolutePath("@storybook/addon-themes"),
{
// @storybook/addon-docs is part of @storybook/addon-essentials
// eslint-disable-next-line storybook/no-uninstalled-addons
name: "@storybook/addon-docs",
options: {
mdxPluginOptions: {
@@ -30,7 +45,7 @@ const config: StorybookConfig = {
},
],
framework: {
name: "@storybook/angular",
name: getAbsolutePath("@storybook/angular"),
options: {},
},
core: {
@@ -38,9 +53,7 @@ const config: StorybookConfig = {
},
env: (config) => ({
...config,
FLAGS: JSON.stringify({
secretsManager: true,
}),
FLAGS: JSON.stringify({}),
}),
webpackFinal: async (config, { configType }) => {
if (config.resolve) {
@@ -48,9 +61,13 @@ const config: StorybookConfig = {
}
return config;
},
docs: {
autodocs: true,
},
docs: {},
staticDirs: ["../apps/web/src/images"],
};
export default config;
// Recommended for mono-repositories
function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, "package.json")));
}

View File

@@ -1,4 +1,4 @@
import { addons } from "@storybook/addons";
import { addons } from "@storybook/manager-api";
import { create } from "@storybook/theming/create";
const lightTheme = create({
@@ -50,10 +50,14 @@ const darkTheme = create({
});
export const getPreferredColorScheme = () => {
if (!globalThis || !globalThis.matchMedia) return "light";
if (!globalThis || !globalThis.matchMedia) {
return "light";
}
const isDarkThemePreferred = globalThis.matchMedia("(prefers-color-scheme: dark)").matches;
if (isDarkThemePreferred) return "dark";
if (isDarkThemePreferred) {
return "dark";
}
return "light";
};

View File

@@ -1,98 +1,34 @@
import { setCompodocJson } from "@storybook/addon-docs/angular";
import { withThemeByClassName } from "@storybook/addon-themes";
import { componentWrapperDecorator } from "@storybook/angular";
import type { Preview } from "@storybook/angular";
import docJson from "../documentation.json";
setCompodocJson(docJson);
const decorator = componentWrapperDecorator(
(story) => {
return `
<ng-template #lightPreview>
<div
class="theme_light tw-border-2 tw-border-solid tw-border-secondary-300 tw-bg-[#ffffff] tw-px-5 tw-py-10 tw-mb-5"
*ngIf="theme == 'both' || theme == 'light'"
>
${story}
</div>
</ng-template>
<ng-template #darkPreview>
<div
class="theme_dark tw-border-2 tw-border-solid tw-bg-[#1f242e] tw-px-5 tw-py-10"
*ngIf="theme == 'both' || theme == 'dark'"
>
${story}
</div>
</ng-template>
<ng-template #nordPreview>
<div
class="theme_nord tw-border-2 tw-border-solid tw-bg-[#434C5E] tw-px-5 tw-py-10"
*ngIf="theme == 'nord'">
${story}
</div>
</ng-template>
<ng-template #solarizedPreview>
<div
class="theme_solarized tw-border-2 tw-border-solid tw-bg-[#002b36] tw-px-5 tw-py-10"
*ngIf="theme == 'solarized'"
>
${story}
</div>
</ng-template>
<ng-container *ngTemplateOutlet="lightPreview"></ng-container>
<ng-container *ngTemplateOutlet="darkPreview"></ng-container>
<ng-container *ngTemplateOutlet="nordPreview"></ng-container>
<ng-container *ngTemplateOutlet="solarizedPreview"></ng-container>
const wrapperDecorator = componentWrapperDecorator((story) => {
return /*html*/ `
<div class="tw-bg-background tw-px-5 tw-py-10">
${story}
</div>
`;
},
({ globals }) => {
return { theme: `${globals["theme"]}` };
},
);
});
const preview: Preview = {
decorators: [decorator],
globalTypes: {
theme: {
description: "Global theme for components",
defaultValue: "both",
toolbar: {
title: "Theme",
icon: "circlehollow",
items: [
{
title: "Light & Dark",
value: "both",
icon: "sidebyside",
},
{
title: "Light",
value: "light",
icon: "sun",
},
{
title: "Dark",
value: "dark",
icon: "moon",
},
{
title: "Nord",
value: "nord",
left: "⛰",
},
{
title: "Solarized",
value: "solarized",
left: "☯",
},
],
dynamicTitle: true,
decorators: [
withThemeByClassName({
themes: {
light: "theme_light",
dark: "theme_dark",
},
},
},
defaultTheme: "light",
}),
wrapperDecorator,
],
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
a11y: {
element: "#storybook-root",
},
controls: {
matchers: {
color: /(background|color)$/i,
@@ -105,8 +41,17 @@ const preview: Preview = {
order: ["Documentation", ["Introduction", "Colors", "Icons"], "Component Library"],
},
},
docs: { source: { type: "dynamic", excludeDecorators: true } },
docs: {
source: {
type: "dynamic",
excludeDecorators: true,
},
},
backgrounds: {
disable: true,
},
},
tags: ["autodocs"],
};
export default preview;

47
.storybook/test-runner.ts Normal file
View File

@@ -0,0 +1,47 @@
import { type TestRunnerConfig } from "@storybook/test-runner";
import { injectAxe, checkA11y } from "axe-playwright";
const testRunnerConfig: TestRunnerConfig = {
setup() {},
async preVisit(page, context) {
return await injectAxe(page);
},
async postVisit(page, context) {
await page.waitForSelector("#storybook-root");
// https://github.com/abhinaba-ghosh/axe-playwright#parameters-on-checka11y-axerun
await checkA11y(
// Playwright page instance.
page,
// context
"#storybook-root",
// axeOptions, see https://www.deque.com/axe/core-documentation/api-documentation/#parameters-axerun
{
detailedReport: true,
detailedReportOptions: {
// Includes the full html for invalid nodes
html: true,
},
verbose: false,
},
// skipFailures
false,
// reporter "v2" is terminal reporter, "html" writes results to file
"v2",
// axeHtmlReporterOptions
// NOTE: set reporter param (above) to "html" to activate these options
{
outputDir: "reports/a11y",
reportFileName: `${context.id}.html`,
},
);
},
};
export default testRunnerConfig;

View File

@@ -1,12 +1,10 @@
{
"extends": "../tsconfig",
"compilerOptions": {
"types": ["node", "jest", "chrome"],
"allowSyntheticDefaultImports": true
},
"exclude": ["../src/test.setup.ts", "../apps/src/**/*.spec.ts", "../libs/**/*.spec.ts"],
"exclude": ["../src/test.setup.ts", "../apps/**/*.spec.ts", "../libs/**/*.spec.ts"],
"files": [
"./typings.d.ts",
"./preview.tsx",
"../libs/components/src/main.ts",
"../libs/components/src/polyfills.ts"

View File

@@ -1,4 +0,0 @@
declare module "*.md" {
const content: string;
export default content;
}

View File

@@ -1,9 +1,12 @@
{
"cSpell.words": ["Csprng", "decryptable", "Popout", "Reprompt", "takeuntil"],
"cSpell.words": ["Csprng", "Decapsulation", "decryptable", "Popout", "Reprompt", "takeuntil"],
"search.exclude": {
"**/locales/[^e]*/messages.json": true,
"**/locales/*[^n]/messages.json": true,
"**/_locales/[^e]*/messages.json": true,
"**/_locales/*[^n]/messages.json": true
}
},
"rust-analyzer.linkedProjects": ["apps/desktop/desktop_native/Cargo.toml"],
"typescript.tsdk": "node_modules/typescript/lib",
"eslint.useFlatConfig": true
}

View File

@@ -5,13 +5,13 @@ specifies another license. Bitwarden Licensed code is found only in the
/bitwarden_license directory.
GPL v3.0:
https://github.com/bitwarden/web/blob/master/LICENSE_GPL.txt
https://github.com/bitwarden/clients/blob/main/LICENSE_GPL.txt
Bitwarden License v1.0:
https://github.com/bitwarden/web/blob/master/LICENSE_BITWARDEN.txt
https://github.com/bitwarden/clients/blob/main/LICENSE_BITWARDEN.txt
No grant of any rights in the trademarks, service marks, or logos of Bitwarden is
made (except as may be necessary to comply with the notice requirements as
applicable), and use of any Bitwarden trademarks must comply with Bitwarden
Trademark Guidelines
<https://github.com/bitwarden/server/blob/master/TRADEMARK_GUIDELINES.md>.
<https://github.com/bitwarden/server/blob/main/TRADEMARK_GUIDELINES.md>.

View File

@@ -56,7 +56,7 @@ such Open Source Software only.
logos of any Contributor (except as may be necessary to comply with the notice
requirements in Section 2.3), and use of any Bitwarden trademarks must comply with
Bitwarden Trademark Guidelines
<https://github.com/bitwarden/server/blob/master/TRADEMARK_GUIDELINES.md>.
<https://github.com/bitwarden/server/blob/main/TRADEMARK_GUIDELINES.md>.
3. TERMINATION

View File

@@ -13,14 +13,15 @@
# Bitwarden Client Applications
This repository houses all Bitwarden client applications except the [Mobile application](https://github.com/bitwarden/mobile).
This repository houses all Bitwarden client applications except the mobile applications ([iOS](https://github.com/bitwarden/ios) | [android](https://github.com/bitwarden/android)).
Please refer to the [Clients section](https://contributing.bitwarden.com/getting-started/clients/) of the [Contributing Documentation](https://contributing.bitwarden.com/) for build instructions, recommended tooling, code style tips, and lots of other great information to get you started.
## Related projects:
- [bitwarden/server](https://github.com/bitwarden/server): The core infrastructure backend (API, database, Docker, etc).
- [bitwarden/mobile](https://github.com/bitwarden/mobile): The mobile app vault (iOS and Android).
- [bitwarden/ios](https://github.com/bitwarden/ios): Bitwarden iOS Password Manager & Authenticator apps.
- [bitwarden/android](https://github.com/bitwarden/android): Bitwarden Android Password Manager & Authenticator apps.
- [bitwarden/directory-connector](https://github.com/bitwarden/directory-connector): A tool for syncing a directory (AD, LDAP, Azure, G Suite, Okta) to an organization.
# We're Hiring!

View File

@@ -6,6 +6,32 @@
"analytics": false
},
"projects": {
"bit-web": {
"projectType": "application",
"schematics": {
"@schematics/angular:application": {
"strict": true
}
},
"root": "bitwarden_license/bit-web",
"sourceRoot": "bitwarden_license/bit-web/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/web",
"index": "apps/web/src/index.html",
"main": "bitwarden_license/bit-web/src/app/main.ts",
"polyfills": "apps/web/src/polyfills.ts",
"tsConfig": "bitwarden_license/bit-web/tsconfig.json",
"assets": ["apps/web/src/favicon.ico"],
"styles": [],
"scripts": []
}
}
}
},
"web": {
"projectType": "application",
"schematics": {
@@ -22,8 +48,8 @@
"options": {
"outputPath": "dist/web",
"index": "apps/web/src/index.html",
"main": "apps/web/src/app/main.ts",
"polyfills": "apps/web/src/app/polyfills.ts",
"main": "apps/web/src/main.ts",
"polyfills": "apps/web/src/polyfills.ts",
"tsConfig": "apps/web/tsconfig.json",
"assets": ["apps/web/src/favicon.ico"],
"styles": [],
@@ -128,10 +154,10 @@
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "test-storybook:build:production"
"buildTarget": "test-storybook:build:production"
},
"development": {
"browserTarget": "test-storybook:build:development"
"buildTarget": "test-storybook:build:development"
}
},
"defaultConfiguration": "development"
@@ -142,7 +168,19 @@
"configDir": ".storybook",
"browserTarget": "components:build",
"compodoc": true,
"compodocArgs": ["-p", "./tsconfig.json", "-e", "json", "-d", "."],
"compodocArgs": [
"-p",
"./tsconfig.json",
"-e",
"json",
"--disableInternal",
"--disableLifeCycleHooks",
"--disablePrivate",
"--disableProtected",
"-d",
".",
"--disableRoutesGraph"
],
"port": 6006
}
},
@@ -152,7 +190,19 @@
"configDir": ".storybook",
"browserTarget": "components:build",
"compodoc": true,
"compodocArgs": ["-e", "json", "-d", "."],
"compodocArgs": [
"-p",
"./tsconfig.json",
"-e",
"json",
"--disableInternal",
"--disableLifeCycleHooks",
"--disablePrivate",
"--disableProtected",
"-d",
".",
"--disableRoutesGraph"
],
"outputDir": "storybook-static"
}
}

View File

@@ -1,4 +1,4 @@
[![Github Workflow build browser on master](https://github.com/bitwarden/clients/actions/workflows/build-browser.yml/badge.svg?branch=master)](https://github.com/bitwarden/clients/actions/workflows/build-browser.yml?query=branch:master)
[![Github Workflow build browser on main](https://github.com/bitwarden/clients/actions/workflows/build-browser.yml/badge.svg?branch=main)](https://github.com/bitwarden/clients/actions/workflows/build-browser.yml?query=branch:main)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/bitwarden-browser/localized.svg)](https://crowdin.com/project/bitwarden-browser)
[![Join the chat at https://gitter.im/bitwarden/Lobby](https://badges.gitter.im/bitwarden/Lobby.svg)](https://gitter.im/bitwarden/Lobby)
@@ -15,7 +15,7 @@
The Bitwarden browser extension is written using the Web Extension API and Angular.
![My Vault](https://raw.githubusercontent.com/bitwarden/brand/master/screenshots/browser-chrome.png)
![My Vault](https://raw.githubusercontent.com/bitwarden/brand/main/screenshots/web-browser-extension-generator.png)
## Documentation

View File

@@ -1,8 +1,7 @@
{
"dev_flags": {},
"devFlags": {},
"flags": {
"showPasswordless": true,
"enableCipherKeyEncryption": false,
"accountSwitching": false
"accountSwitching": false,
"sdk": true
}
}

View File

@@ -1,13 +1,11 @@
{
"devFlags": {
"storeSessionDecrypted": false,
"managedEnvironment": {
"base": "https://localhost:8080"
}
},
"skipWelcomeOnInstall": true
},
"flags": {
"showPasswordless": true,
"enableCipherKeyEncryption": false,
"accountSwitching": true
}
}

View File

@@ -1,6 +1,5 @@
{
"flags": {
"enableCipherKeyEncryption": false,
"accountSwitching": true
}
}

View File

@@ -1,248 +0,0 @@
const child = require("child_process");
const fs = require("fs");
const { rimraf } = require("rimraf");
const gulp = require("gulp");
const gulpif = require("gulp-if");
const jeditor = require("gulp-json-editor");
const replace = require("gulp-replace");
const manifest = require("./src/manifest.json");
const paths = {
build: "./build/",
dist: "./dist/",
coverage: "./coverage/",
node_modules: "./node_modules/",
popupDir: "./src/popup/",
cssDir: "./src/popup/css/",
safari: "./src/safari/",
};
const filters = {
fonts: [
"!build/popup/fonts/*",
"build/popup/fonts/Open_Sans*.woff",
"build/popup/fonts/bwi-font.woff2",
"build/popup/fonts/bwi-font.woff",
"build/popup/fonts/bwi-font.ttf",
],
safari: ["!build/safari/**/*"],
};
function buildString() {
var build = "";
if (process.env.MANIFEST_VERSION) {
build = `-mv${process.env.MANIFEST_VERSION}`;
}
if (process.env.BUILD_NUMBER && process.env.BUILD_NUMBER !== "") {
build = `-${process.env.BUILD_NUMBER}`;
}
return build;
}
function distFileName(browserName, ext) {
return `dist-${browserName}${buildString()}.${ext}`;
}
async function dist(browserName, manifest) {
const { default: zip } = await import("gulp-zip");
const { default: filter } = await import("gulp-filter");
return gulp
.src(paths.build + "**/*")
.pipe(filter(["**"].concat(filters.fonts).concat(filters.safari)))
.pipe(gulpif("popup/index.html", replace("__BROWSER__", "browser_" + browserName)))
.pipe(gulpif("manifest.json", jeditor(manifest)))
.pipe(zip(distFileName(browserName, "zip")))
.pipe(gulp.dest(paths.dist));
}
function distFirefox() {
return dist("firefox", (manifest) => {
delete manifest.storage;
delete manifest.sandbox;
manifest.optional_permissions = manifest.optional_permissions.filter(
(permission) => permission !== "privacy",
);
return manifest;
});
}
function distOpera() {
return dist("opera", (manifest) => {
delete manifest.applications;
return manifest;
});
}
function distChrome() {
return dist("chrome", (manifest) => {
delete manifest.applications;
delete manifest.sidebar_action;
delete manifest.commands._execute_sidebar_action;
return manifest;
});
}
function distEdge() {
return dist("edge", (manifest) => {
delete manifest.applications;
delete manifest.sidebar_action;
delete manifest.commands._execute_sidebar_action;
return manifest;
});
}
function distSafariMas(cb) {
return distSafariApp(cb, "mas");
}
function distSafariMasDev(cb) {
return distSafariApp(cb, "masdev");
}
function distSafariDmg(cb) {
return distSafariApp(cb, "dmg");
}
function distSafariApp(cb, subBuildPath) {
const buildPath = paths.dist + "Safari/" + subBuildPath + "/";
const builtAppexPath = buildPath + "build/Release/safari.appex";
const builtAppexFrameworkPath = buildPath + "build/Release/safari.appex/Contents/Frameworks/";
const entitlementsPath = paths.safari + "safari/safari.entitlements";
var args = [
"--verbose",
"--force",
"-o",
"runtime",
"--sign",
"Developer ID Application: 8bit Solutions LLC",
"--entitlements",
entitlementsPath,
];
if (subBuildPath !== "dmg") {
args = [
"--verbose",
"--force",
"--sign",
subBuildPath === "mas"
? "3rd Party Mac Developer Application: Bitwarden Inc"
: "E7C9978F6FBCE0553429185C405E61F5380BE8EB",
"--entitlements",
entitlementsPath,
];
}
return rimraf([buildPath + "**/*"], { glob: true })
.then(() => safariCopyAssets(paths.safari + "**/*", buildPath))
.then(() => safariCopyBuild(paths.build + "**/*", buildPath + "safari/app"))
.then(() => {
const proc = child.spawn("xcodebuild", [
"-project",
buildPath + "desktop.xcodeproj",
"-alltargets",
"-configuration",
"Release",
]);
stdOutProc(proc);
return new Promise((resolve) => proc.on("close", resolve));
})
.then(async () => {
const { default: filter } = await import("gulp-filter");
const libs = fs
.readdirSync(builtAppexFrameworkPath)
.filter((p) => p.endsWith(".dylib"))
.map((p) => builtAppexFrameworkPath + p);
const libPromises = [];
libs.forEach((i) => {
const proc = child.spawn("codesign", args.concat([i]));
stdOutProc(proc);
libPromises.push(new Promise((resolve) => proc.on("close", resolve)));
});
return Promise.all(libPromises);
})
.then(() => {
const proc = child.spawn("codesign", args.concat([builtAppexPath]));
stdOutProc(proc);
return new Promise((resolve) => proc.on("close", resolve));
})
.then(
() => {
return cb;
},
() => {
return cb;
},
);
}
function safariCopyAssets(source, dest) {
return new Promise((resolve, reject) => {
gulp
.src(source)
.on("error", reject)
.pipe(gulpif("safari/Info.plist", replace("0.0.1", manifest.version)))
.pipe(
gulpif("safari/Info.plist", replace("0.0.2", process.env.BUILD_NUMBER || manifest.version)),
)
.pipe(gulpif("desktop.xcodeproj/project.pbxproj", replace("../../../build", "../safari/app")))
.pipe(gulp.dest(dest))
.on("end", resolve);
});
}
async function safariCopyBuild(source, dest) {
const { default: filter } = await import("gulp-filter");
return new Promise((resolve, reject) => {
gulp
.src(source)
.on("error", reject)
.pipe(filter(["**"].concat(filters.fonts)))
.pipe(gulpif("popup/index.html", replace("__BROWSER__", "browser_safari")))
.pipe(
gulpif(
"manifest.json",
jeditor((manifest) => {
delete manifest.sidebar_action;
delete manifest.commands._execute_sidebar_action;
delete manifest.optional_permissions;
manifest.permissions.push("nativeMessaging");
return manifest;
}),
),
)
.pipe(gulp.dest(dest))
.on("end", resolve);
});
}
function stdOutProc(proc) {
proc.stdout.on("data", (data) => console.log(data.toString()));
proc.stderr.on("data", (data) => console.error(data.toString()));
}
async function ciCoverage(cb) {
const { default: zip } = await import("gulp-zip");
const { default: filter } = await import("gulp-filter");
return gulp
.src(paths.coverage + "**/*")
.pipe(filter(["**", "!coverage/coverage*.zip"]))
.pipe(zip(`coverage${buildString()}.zip`))
.pipe(gulp.dest(paths.coverage));
}
exports["dist:firefox"] = distFirefox;
exports["dist:chrome"] = distChrome;
exports["dist:opera"] = distOpera;
exports["dist:edge"] = distEdge;
exports["dist:safari"] = gulp.parallel(distSafariMas, distSafariMasDev, distSafariDmg);
exports["dist:safari:mas"] = distSafariMas;
exports["dist:safari:masdev"] = distSafariMasDev;
exports["dist:safari:dmg"] = distSafariDmg;
exports.dist = gulp.parallel(distFirefox, distChrome, distOpera, distEdge);
exports["ci:coverage"] = ciCoverage;
exports.ci = ciCoverage;

View File

@@ -1,18 +1,17 @@
const { pathsToModuleNameMapper } = require("ts-jest");
const { compilerOptions } = require("./tsconfig");
const { compilerOptions } = require("../../tsconfig.base");
const sharedConfig = require("../../libs/shared/jest.config.angular");
/** @type {import('jest').Config} */
module.exports = {
...sharedConfig,
preset: "jest-preset-angular",
setupFilesAfterEnv: ["<rootDir>/test.setup.ts"],
moduleNameMapper: pathsToModuleNameMapper(
{ "@bitwarden/common/spec": ["../../libs/common/spec"], ...(compilerOptions?.paths ?? {}) },
{ "@bitwarden/common/spec": ["libs/common/spec"], ...(compilerOptions?.paths ?? {}) },
{
prefix: "<rootDir>/",
prefix: "<rootDir>/../../",
},
),
};

View File

@@ -1,25 +1,35 @@
{
"name": "@bitwarden/browser",
"version": "2024.2.1",
"version": "2025.7.0",
"scripts": {
"build": "webpack",
"build:mv3": "cross-env MANIFEST_VERSION=3 webpack",
"build:watch": "webpack --watch",
"build:watch:mv3": "cross-env MANIFEST_VERSION=3 webpack --watch",
"build:prod": "cross-env NODE_ENV=production webpack",
"build:prod:watch": "cross-env NODE_ENV=production webpack --watch",
"dist": "npm run build:prod && gulp dist",
"dist:mv3": "cross-env MANIFEST_VERSION=3 npm run build:prod && cross-env MANIFEST_VERSION=3 gulp dist",
"dist:chrome": "npm run build:prod && gulp dist:chrome",
"dist:firefox": "npm run build:prod && gulp dist:firefox",
"dist:opera": "npm run build:prod && gulp dist:opera",
"dist:safari": "npm run build:prod && gulp dist:safari",
"dist:safari:mas": "npm run build:prod && gulp dist:safari:mas",
"dist:safari:masdev": "npm run build:prod && gulp dist:safari:masdev",
"dist:safari:dmg": "npm run build:prod && gulp dist:safari:dmg",
"build": "npm run build:chrome",
"build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack",
"build:edge": "cross-env BROWSER=edge MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack",
"build:firefox": "cross-env BROWSER=firefox NODE_OPTIONS=\"--max-old-space-size=8192\" webpack",
"build:opera": "cross-env BROWSER=opera MANIFEST_VERSION=3 NODE_OPTIONS=\"--max-old-space-size=8192\" webpack",
"build:safari": "cross-env BROWSER=safari NODE_OPTIONS=\"--max-old-space-size=8192\" webpack",
"build:watch": "npm run build:watch:chrome",
"build:watch:chrome": "npm run build:chrome -- --watch",
"build:watch:edge": "npm run build:edge -- --watch",
"build:watch:firefox": "npm run build:firefox -- --watch",
"build:watch:opera": "npm run build:opera -- --watch",
"build:watch:safari": "npm run build:safari -- --watch",
"build:prod:chrome": "cross-env NODE_ENV=production npm run build:chrome",
"build:prod:edge": "cross-env NODE_ENV=production npm run build:edge",
"build:prod:firefox": "cross-env NODE_ENV=production npm run build:firefox",
"build:prod:opera": "cross-env NODE_ENV=production npm run build:opera",
"build:prod:safari": "cross-env NODE_ENV=production npm run build:safari",
"dist:chrome": "npm run build:prod:chrome && mkdir -p dist && ./scripts/compress.sh dist-chrome.zip",
"dist:edge": "npm run build:prod:edge && mkdir -p dist && ./scripts/compress.sh dist-edge.zip",
"dist:firefox": "npm run build:prod:firefox && mkdir -p dist && ./scripts/compress.sh dist-firefox.zip",
"dist:opera": "npm run build:prod:opera && mkdir -p dist && ./scripts/compress.sh dist-opera.zip",
"dist:safari": "npm run build:prod:safari && ./scripts/package-safari.ps1",
"dist:firefox:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:firefox",
"dist:opera:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:opera",
"dist:safari:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:safari",
"test": "jest",
"test:coverage": "jest --coverage --coverageDirectory=coverage",
"test:watch": "jest --watch",
"test:watch:all": "jest --watchAll"
"test:watch:all": "jest --watchAll",
"test:clearCache": "jest --clear-cache"
}
}

View File

@@ -1,4 +1,9 @@
/* eslint-disable no-undef */
/* eslint-disable @typescript-eslint/no-require-imports */
module.exports = {
plugins: [require("tailwindcss"), require("autoprefixer"), require("postcss-nested")],
plugins: [
require("postcss-import"),
require("postcss-nested"),
require("tailwindcss"),
require("autoprefixer"),
],
};

View File

@@ -0,0 +1,30 @@
#!/usr/bin/env pwsh
####
# Compress the build directory into a zip file.
####
param (
[Parameter(Mandatory = $true)]
[String] $fileName
)
$buildDir = Join-Path $PSScriptRoot "../build"
$distDir = Join-Path $PSScriptRoot "../dist"
# Create dist directory if it doesn't exist
if (-not (Test-Path $distDir)) {
New-Item -ItemType Directory -Path $distDir
}
$distPath = Join-Path -Path $distDir -ChildPath $fileName
if (Test-Path $distPath) {
Remove-Item $distPath
}
# Compress build directory
if (Test-Path $buildDir) {
Compress-Archive -Path (Join-Path $buildDir "*") -DestinationPath $distPath
Write-Output "Zipped $buildDir into $distPath"
}

View File

@@ -0,0 +1,32 @@
#!/usr/bin/env bash
####
# Compress the build directory into a zip file.
####
set -e
set -u
set -x
set -o pipefail
FILENAME=$1
SCRIPT_ROOT="$(dirname "$0")"
BUILD_DIR="$SCRIPT_ROOT/../build"
# Check if build directory exists
if [ -d "$BUILD_DIR" ]; then
cd $BUILD_DIR
# Create dist directory if it doesn't exist
DIST_DIR="../dist"
mkdir -p $DIST_DIR
# Remove existing dist zip file
DIST_PATH="$DIST_DIR/$FILENAME"
rm -f $DIST_PATH
# Compress build directory
zip -r $DIST_PATH ./
echo "Zipped $BUILD_DIR into $DIST_PATH"
fi

View File

@@ -0,0 +1,112 @@
#!/usr/bin/env pwsh
####
# Builds the safari appex.
####
$buildDir = Join-Path $PSScriptRoot "../build"
$distDir = Join-Path $PSScriptRoot "../dist"
Write-Output $PSScriptRoot
if (-not (Test-Path $buildDir)) {
Write-Output "No build directory found. Exiting..."
exit
}
# Create dist directory if it doesn't exist
if (-not (Test-Path $distDir)) {
New-Item -ItemType Directory -Path $distDir
}
$subBuildPaths = @("mas", "masdev", "dmg")
$safariSrc = Join-Path $PSScriptRoot "../src/safari"
$safariDistPath = Join-Path -Path $distDir -ChildPath "Safari"
if (-not (Test-Path $safariDistPath)) {
New-Item -ItemType Directory -Path $safariDistPath
}
# Delete old safari dists
Remove-Item -LiteralPath $safariDistPath -Force -Recurse
foreach ($subBuildPath in $subBuildPaths) {
$safariBuildPath = Join-Path -Path $safariDistPath -ChildPath $subBuildPath
$builtAppexPath = Join-Path -Path $safariBuildPath -ChildPath "build/Release/safari.appex"
$builtAppexFrameworkPath = Join-Path -Path $safariBuildPath -ChildPath "build/Release/safari.appex/Contents/Frameworks/"
$entitlementsPath = Join-Path -Path $safariSrc -ChildPath "safari/safari.entitlements"
switch ($subBuildPath) {
"mas" {
$codesignArgs = @(
"--verbose",
"--force",
"--sign",
'"3rd Party Mac Developer Application: Bitwarden Inc"',
"--entitlements",
$entitlementsPath
)
}
"masdev" {
$codesignArgs = @(
"--verbose",
"--force",
"--sign",
"588E3F1724AE018EBA762E42279DAE85B313E3ED",
"--entitlements",
$entitlementsPath
)
}
"dmg" {
$codesignArgs = @(
"--verbose",
"--force",
"-o",
"runtime",
"--sign",
'"Developer ID Application: 8bit Solutions LLC"',
"--entitlements",
$entitlementsPath
)
}
}
# Copy safari src
Copy-Item -Path $safariSrc -Destination $safariBuildPath -Recurse
# Copy build
$target = Join-Path -Path $safariBuildPath -ChildPath "safari/app"
Copy-Item -Path $buildDir -Destination $target -Recurse
# Update versions
$jsonFilePath = Join-Path $buildDir "manifest.json"
$jsonContent = Get-Content -Path $jsonFilePath -Raw
$jsonObject = $jsonContent | ConvertFrom-Json
$infoFile = Join-Path -Path $safariBuildPath -ChildPath "safari/Info.plist"
(Get-Content $infoFile).Replace('0.0.1', $jsonObject.version).Replace('0.0.2', $jsonObject.version) | Set-Content $infoFile
$projectFile = Join-Path -Path $safariBuildPath -ChildPath "desktop.xcodeproj/project.pbxproj"
(Get-Content $projectFile).Replace('../../../build', "../safari/app") | Set-Content $projectFile
# Build using xcode
$xcodeBuildArgs = @(
"-project",
(Join-Path $safariBuildPath "desktop.xcodeproj"),
"-alltargets",
"-configuration",
"Release"
)
$proc = Start-Process "xcodebuild" -ArgumentList $xcodeBuildArgs -NoNewWindow -PassThru
$proc.WaitForExit()
# Codesign
$libs = Get-ChildItem -Path $builtAppexFrameworkPath -Filter "*.dylib"
foreach ($lib in $libs) {
$proc = Start-Process "codesign" -ArgumentList ($codesignArgs + $lib.FullName) -NoNewWindow -PassThru
$proc.WaitForExit()
}
$proc = Start-Process "codesign" -ArgumentList ($codesignArgs + $builtAppexPath) -NoNewWindow -PassThru
$proc.WaitForExit()
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More