mirror of
https://github.com/bitwarden/clients.git
synced 2026-06-04 21:04:29 +08:00
457 lines
17 KiB
YAML
457 lines
17 KiB
YAML
name: SDK Update
|
|
run-name: "SDK ${{ inputs.run-mode }} - ${{ inputs.base-branch }} @ ${{ inputs.sdk-version }}"
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
run-mode:
|
|
description: "Run Mode"
|
|
type: choice
|
|
options:
|
|
- Update # opens a PR in this repo updating the SDK
|
|
- Test # validates SDK update without creating PR
|
|
default: Update
|
|
sdk-version:
|
|
description: "SDK Version"
|
|
type: string
|
|
required: true
|
|
base-branch:
|
|
description: "Base branch for PR (branch to merge into)"
|
|
type: string
|
|
default: "main"
|
|
triggering-run-id:
|
|
description: "Run ID of the workflow that triggered this update (e.g. sdk-internal publish run)"
|
|
type: string
|
|
|
|
env:
|
|
_BOT_NAME: "bw-ghapp[bot]"
|
|
_BOT_EMAIL: "178206702+bw-ghapp[bot]@users.noreply.github.com"
|
|
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
|
|
jobs:
|
|
update:
|
|
name: Update and PR
|
|
if: ${{ inputs.run-mode == 'Update' }}
|
|
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@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
|
|
id: app-token
|
|
with:
|
|
app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }}
|
|
private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }}
|
|
permission-pull-requests: write
|
|
permission-actions: read
|
|
permission-contents: write
|
|
|
|
- name: Log inputs to job summary
|
|
env:
|
|
RUN_MODE: ${{ inputs.run-mode }}
|
|
SDK_VERSION: ${{ inputs.sdk-version }}
|
|
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
run: |
|
|
{
|
|
echo "## 📋 Workflow Inputs"
|
|
echo "- Run Mode: $RUN_MODE"
|
|
echo "- SDK Version: $SDK_VERSION"
|
|
echo "- Base Branch: $BASE_BRANCH"
|
|
} >> $GITHUB_STEP_SUMMARY
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
token: ${{ steps.app-token.outputs.token }}
|
|
ref: ${{ inputs.base-branch }}
|
|
fetch-depth: 0
|
|
persist-credentials: true
|
|
|
|
- name: Get Node Version
|
|
id: retrieve-node-version
|
|
run: |
|
|
NODE_NVMRC=$(cat .nvmrc)
|
|
NODE_VERSION=${NODE_NVMRC/v/''}
|
|
echo "node_version=$NODE_VERSION" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Set up Node
|
|
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
|
with:
|
|
cache: 'npm'
|
|
cache-dependency-path: '**/package-lock.json'
|
|
node-version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
|
|
|
- name: Print environment
|
|
run: |
|
|
node --version
|
|
npm --version
|
|
|
|
- name: Get current SDK version from base branch
|
|
id: get-current-sdk
|
|
env:
|
|
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
run: |
|
|
CURRENT_SDK_VERSION=$(git show origin/$BASE_BRANCH:package.json | jq -r '.dependencies."@bitwarden/sdk-internal" // empty')
|
|
CURRENT_COMMERCIAL_VERSION=$(git show origin/$BASE_BRANCH:package.json | jq -r '.dependencies."@bitwarden/commercial-sdk-internal" // empty')
|
|
|
|
if [ -z "$CURRENT_SDK_VERSION" ]; then
|
|
echo "::warning::Could not find current @bitwarden/sdk-internal version"
|
|
CURRENT_SDK_VERSION="unknown"
|
|
fi
|
|
|
|
if [ -z "$CURRENT_COMMERCIAL_VERSION" ]; then
|
|
echo "::warning::Could not find current @bitwarden/commercial-sdk-internal version"
|
|
CURRENT_COMMERCIAL_VERSION="unknown"
|
|
fi
|
|
|
|
echo "current-sdk-version=$CURRENT_SDK_VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "current-commercial-version=$CURRENT_COMMERCIAL_VERSION" >> "$GITHUB_OUTPUT"
|
|
echo "📋 Current SDK version: $CURRENT_SDK_VERSION"
|
|
echo "📋 Current Commercial SDK version: $CURRENT_COMMERCIAL_VERSION"
|
|
|
|
- name: Validate SDK version
|
|
id: validate-version
|
|
env:
|
|
SDK_VERSION: ${{ inputs.sdk-version }}
|
|
CURRENT_VERSION: ${{ steps.get-current-sdk.outputs.current-sdk-version }}
|
|
run: |
|
|
echo "🔍 Validating SDK version: $SDK_VERSION"
|
|
if ! npm view @bitwarden/sdk-internal@$SDK_VERSION version 2>/dev/null; then
|
|
echo "::error::SDK version $SDK_VERSION not found in npm registry"
|
|
exit 1
|
|
fi
|
|
|
|
if ! npm view @bitwarden/commercial-sdk-internal@$SDK_VERSION version 2>/dev/null; then
|
|
echo "::error::Commercial SDK version $SDK_VERSION not found in npm registry"
|
|
exit 1
|
|
fi
|
|
|
|
echo "✅ SDK versions exist in npm registry"
|
|
|
|
if [ "$SDK_VERSION" = "$CURRENT_VERSION" ]; then
|
|
echo "::error::Provided SDK version is the same as current version ($CURRENT_VERSION)"
|
|
exit 1
|
|
fi
|
|
|
|
- name: Check for existing branch
|
|
id: check-branch
|
|
env:
|
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
run: |
|
|
BRANCH_NAME="sdlc/sdk-update"
|
|
echo "branch_name=$BRANCH_NAME" >> "$GITHUB_OUTPUT"
|
|
|
|
if git ls-remote --exit-code --heads origin "$BRANCH_NAME" > /dev/null 2>&1; then
|
|
echo "branch_exists=true" >> "$GITHUB_OUTPUT"
|
|
echo "📋 Branch $BRANCH_NAME exists on remote"
|
|
else
|
|
echo "branch_exists=false" >> "$GITHUB_OUTPUT"
|
|
echo "📋 Branch $BRANCH_NAME does not exist"
|
|
fi
|
|
|
|
- name: Prevent updating branch with manual changes
|
|
if: ${{ steps.check-branch.outputs.branch_exists == 'true' }}
|
|
env:
|
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
_BRANCH_NAME: ${{ steps.check-branch.outputs.branch_name }}
|
|
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
run: |
|
|
git fetch origin "$_BRANCH_NAME"
|
|
LATEST_COMMIT_AUTHOR=$(git log -1 --format='%ae' "origin/$_BRANCH_NAME")
|
|
|
|
echo "Latest commit author in branch ($_BRANCH_NAME): $LATEST_COMMIT_AUTHOR"
|
|
echo "Expected bot email: $_BOT_EMAIL"
|
|
|
|
if [ "$LATEST_COMMIT_AUTHOR" != "$_BOT_EMAIL" ]; then
|
|
echo "::error::Branch $_BRANCH_NAME has a commit not made by the bot." \
|
|
"This indicates manual changes have been made to the branch," \
|
|
"PR has to be merged or closed before running this workflow again."
|
|
|
|
echo "👀 Fetching existing PR..."
|
|
EXISTING_PR=$(gh pr list --head "$_BRANCH_NAME" --base "$BASE_BRANCH" --state open --json number --jq '.[0].number // empty')
|
|
if [ -z "$EXISTING_PR" ]; then
|
|
echo "::error::Couldn't find an existing PR for branch $_BRANCH_NAME."
|
|
exit 1
|
|
fi
|
|
|
|
PR_URL="https://github.com/${{ github.repository }}/pull/$EXISTING_PR"
|
|
echo "## ❌ Merge or close: $PR_URL" >> "$GITHUB_STEP_SUMMARY"
|
|
exit 1
|
|
fi
|
|
|
|
echo "✅ Branch tip commit was made by the bot. Safe to proceed."
|
|
|
|
- name: Check for stop-updates label
|
|
if: ${{ steps.check-branch.outputs.branch_exists == 'true' }}
|
|
env:
|
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
_BRANCH_NAME: ${{ steps.check-branch.outputs.branch_name }}
|
|
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
run: |
|
|
EXISTING_PR=$(gh pr list --head "$_BRANCH_NAME" --base "$BASE_BRANCH" --state open --json number,labels --jq '.[0] // empty')
|
|
|
|
if [ -n "$EXISTING_PR" ]; then
|
|
HAS_STOP_LABEL=$(echo "$EXISTING_PR" | jq -r '.labels | map(select(.name == "stop-updates")) | length > 0')
|
|
|
|
if [ "$HAS_STOP_LABEL" = "true" ]; then
|
|
PR_NUMBER=$(echo "$EXISTING_PR" | jq -r '.number')
|
|
PR_URL="https://github.com/${{ github.repository }}/pull/$PR_NUMBER"
|
|
echo "::error::PR #$PR_NUMBER has the 'stop-updates' label. Remove the label to resume automated updates."
|
|
echo "## ⛔ Blocked by stop-updates label: $PR_URL" >> "$GITHUB_STEP_SUMMARY"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
echo "✅ No stop-updates label found. Proceeding."
|
|
|
|
- name: Configure git identity
|
|
run: |
|
|
git config user.name "$_BOT_NAME"
|
|
git config user.email "$_BOT_EMAIL"
|
|
|
|
- name: Create fresh branch from base
|
|
env:
|
|
_BRANCH_NAME: ${{ steps.check-branch.outputs.branch_name }}
|
|
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
run: |
|
|
# Delete local branch if it exists (from fetch)
|
|
git branch -D "$_BRANCH_NAME" 2>/dev/null || true
|
|
|
|
# Create fresh branch from base
|
|
echo "📝 Creating fresh branch $_BRANCH_NAME from $BASE_BRANCH"
|
|
git switch -c "$_BRANCH_NAME"
|
|
|
|
- name: Update SDK versions
|
|
env:
|
|
SDK_VERSION: ${{ inputs.sdk-version }}
|
|
run: |
|
|
echo "📦 Installing SDK version: $SDK_VERSION"
|
|
npm install --save-exact @bitwarden/sdk-internal@$SDK_VERSION
|
|
npm install --save-exact @bitwarden/commercial-sdk-internal@$SDK_VERSION
|
|
echo "✅ SDK packages updated in package.json"
|
|
|
|
- name: Generate changelog
|
|
id: changelog
|
|
env:
|
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
OLD_VERSION: ${{ steps.get-current-sdk.outputs.current-sdk-version }}
|
|
NEW_VERSION: ${{ inputs.sdk-version }}
|
|
run: |
|
|
# Look up source commits from npm package metadata
|
|
OLD_REF=$(npm view "@bitwarden/sdk-internal@$OLD_VERSION" gitHead 2>/dev/null || echo "")
|
|
NEW_REF=$(npm view "@bitwarden/sdk-internal@$NEW_VERSION" gitHead 2>/dev/null || echo "")
|
|
|
|
if [ -n "$OLD_REF" ] && [ -n "$NEW_REF" ]; then
|
|
echo "Fetching changelog for sdk-internal: $OLD_REF...$NEW_REF"
|
|
CHANGELOG=$(./scripts/get-repo-changelog.sh "bitwarden/sdk-internal" "$OLD_REF" "$NEW_REF" 2>/dev/null || echo "")
|
|
|
|
if [ -n "$CHANGELOG" ]; then
|
|
echo "changelog<<__CHANGELOG_END__" >> "$GITHUB_OUTPUT"
|
|
echo "$CHANGELOG" >> "$GITHUB_OUTPUT"
|
|
echo "__CHANGELOG_END__" >> "$GITHUB_OUTPUT"
|
|
echo "✅ Generated changelog"
|
|
else
|
|
echo "::warning::Could not generate changelog"
|
|
fi
|
|
else
|
|
echo "::warning::Could not resolve source commits from npm for changelog (old: $OLD_VERSION, new: $NEW_VERSION)"
|
|
fi
|
|
|
|
- name: Generate validation summary
|
|
env:
|
|
SDK_VERSION: ${{ inputs.sdk-version }}
|
|
run: |
|
|
{
|
|
echo "## 📊 Validation Summary"
|
|
echo ""
|
|
echo "- SDK Version: $SDK_VERSION"
|
|
echo ""
|
|
echo "### Changed Files"
|
|
echo '```'
|
|
git diff --stat package.json package-lock.json
|
|
echo '```'
|
|
} >> $GITHUB_STEP_SUMMARY
|
|
|
|
- name: Commit changes
|
|
env:
|
|
SDK_VERSION: ${{ inputs.sdk-version }}
|
|
BRANCH_NAME: ${{ steps.check-branch.outputs.branch_name }}
|
|
run: |
|
|
echo "👀 Committing SDK version update..."
|
|
|
|
git add package.json package-lock.json
|
|
|
|
if git diff --cached --quiet; then
|
|
echo "::warning::No changes to commit — branch already contains SDK $SDK_VERSION"
|
|
else
|
|
git commit -m "Update sdk-internal to $SDK_VERSION"
|
|
fi
|
|
|
|
git push --force-with-lease origin "$BRANCH_NAME"
|
|
|
|
- name: Create or Update Pull Request
|
|
env:
|
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
BRANCH_NAME: ${{ steps.check-branch.outputs.branch_name }}
|
|
SDK_VERSION: ${{ inputs.sdk-version }}
|
|
OLD_SDK_VERSION: ${{ steps.get-current-sdk.outputs.current-sdk-version }}
|
|
OLD_COMMERCIAL_VERSION: ${{ steps.get-current-sdk.outputs.current-commercial-version }}
|
|
CHANGELOG: ${{ steps.changelog.outputs.changelog }}
|
|
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
GITHUB_REPOSITORY: ${{ github.repository }}
|
|
GITHUB_SERVER_URL: ${{ github.server_url }}
|
|
GITHUB_RUN_ID: ${{ github.run_id }}
|
|
TRIGGERING_RUN_ID: ${{ inputs.triggering-run-id }}
|
|
run: |
|
|
# Build PR body
|
|
PR_TITLE="Update sdk-internal to $SDK_VERSION"
|
|
{
|
|
echo "## 🤖 Automated SDK Update"
|
|
echo ""
|
|
echo "This PR updates the SDK packages to version \`$SDK_VERSION\`."
|
|
echo ""
|
|
echo "### Changes"
|
|
echo "- \`@bitwarden/sdk-internal\`: \`$OLD_SDK_VERSION\` → \`$SDK_VERSION\`"
|
|
echo "- \`@bitwarden/commercial-sdk-internal\`: \`$OLD_COMMERCIAL_VERSION\` → \`$SDK_VERSION\`"
|
|
if [ -n "$CHANGELOG" ]; then
|
|
echo ""
|
|
echo "### What's Changed in SDK"
|
|
echo "$CHANGELOG"
|
|
fi
|
|
echo ""
|
|
echo "### Links"
|
|
echo "- [Workflow Run]($GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID)"
|
|
if [ -n "$TRIGGERING_RUN_ID" ]; then
|
|
echo "- [Triggering SDK Publish Run](https://github.com/bitwarden/sdk-internal/actions/runs/$TRIGGERING_RUN_ID)"
|
|
fi
|
|
echo "- [SDK Releases](https://github.com/bitwarden/sdk-internal/releases)"
|
|
} > /tmp/pr-body.md
|
|
|
|
# Check for existing PR
|
|
EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --base "$BASE_BRANCH" --state open --json number --jq '.[0].number // empty')
|
|
|
|
if [ -n "$EXISTING_PR" ]; then
|
|
echo "🔄 Updating existing PR #$EXISTING_PR..."
|
|
gh pr edit "$EXISTING_PR" \
|
|
--title "$PR_TITLE" \
|
|
--body-file /tmp/pr-body.md
|
|
PR_URL="https://github.com/$GITHUB_REPOSITORY/pull/$EXISTING_PR"
|
|
echo "## ✅ Updated PR: $PR_URL" >> "$GITHUB_STEP_SUMMARY"
|
|
else
|
|
echo "📝 Creating new PR..."
|
|
LABELS="automated pr"
|
|
|
|
PR_URL=$(gh pr create \
|
|
--title "$PR_TITLE" \
|
|
--body-file /tmp/pr-body.md \
|
|
--base "$BASE_BRANCH" \
|
|
--head "$BRANCH_NAME" \
|
|
--label "$LABELS")
|
|
echo "## 🚀 Created PR: $PR_URL" >> "$GITHUB_STEP_SUMMARY"
|
|
fi
|
|
|
|
test:
|
|
name: Test SDK Update
|
|
if: ${{ inputs.run-mode == 'Test' }}
|
|
runs-on: ubuntu-24.04
|
|
permissions:
|
|
contents: read
|
|
|
|
steps:
|
|
- name: Log inputs to job summary
|
|
env:
|
|
RUN_MODE: ${{ inputs.run-mode }}
|
|
SDK_VERSION: ${{ inputs.sdk-version }}
|
|
BASE_BRANCH: ${{ inputs.base-branch }}
|
|
run: |
|
|
{
|
|
echo "## 📋 Workflow Inputs (Test Mode)"
|
|
echo "- Run Mode: $RUN_MODE"
|
|
echo "- SDK Version: $SDK_VERSION"
|
|
echo "- Base Branch: $BASE_BRANCH"
|
|
} >> $GITHUB_STEP_SUMMARY
|
|
|
|
- name: Checkout repository
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
ref: ${{ inputs.base-branch }}
|
|
persist-credentials: false
|
|
|
|
- name: Get Node Version
|
|
id: retrieve-node-version
|
|
run: |
|
|
NODE_NVMRC=$(cat .nvmrc)
|
|
NODE_VERSION=${NODE_NVMRC/v/''}
|
|
echo "node_version=$NODE_VERSION" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Set up Node
|
|
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
|
with:
|
|
cache: 'npm'
|
|
cache-dependency-path: '**/package-lock.json'
|
|
node-version: ${{ steps.retrieve-node-version.outputs.node_version }}
|
|
|
|
- name: Print environment
|
|
run: |
|
|
node --version
|
|
npm --version
|
|
|
|
- name: Validate SDK version exists
|
|
env:
|
|
SDK_VERSION: ${{ inputs.sdk-version }}
|
|
run: |
|
|
echo "🔍 Validating SDK version: $SDK_VERSION"
|
|
if ! npm view @bitwarden/sdk-internal@$SDK_VERSION version 2>/dev/null; then
|
|
echo "::error::SDK version $SDK_VERSION not found in npm registry"
|
|
exit 1
|
|
fi
|
|
|
|
if ! npm view @bitwarden/commercial-sdk-internal@$SDK_VERSION version 2>/dev/null; then
|
|
echo "::error::Commercial SDK version $SDK_VERSION not found in npm registry"
|
|
exit 1
|
|
fi
|
|
|
|
echo "✅ SDK versions exist in npm registry"
|
|
|
|
- name: Update SDK versions
|
|
env:
|
|
SDK_VERSION: ${{ inputs.sdk-version }}
|
|
run: |
|
|
echo "📦 Installing SDK version: $SDK_VERSION"
|
|
npm install --save-exact @bitwarden/sdk-internal@$SDK_VERSION
|
|
npm install --save-exact @bitwarden/commercial-sdk-internal@$SDK_VERSION
|
|
echo "✅ SDK packages updated in package.json"
|
|
|
|
- name: Test summary
|
|
env:
|
|
SDK_VERSION: ${{ inputs.sdk-version }}
|
|
run: |
|
|
{
|
|
echo "## 📊 Test Results"
|
|
echo ""
|
|
echo "- SDK Version: $SDK_VERSION"
|
|
echo "- ✅ SDK packages updated successfully"
|
|
} >> $GITHUB_STEP_SUMMARY
|