|
1 | | -name: Update API Bindings |
| 1 | +name: Update Bindings |
2 | 2 |
|
3 | 3 | on: |
4 | 4 | workflow_dispatch: |
| 5 | + inputs: |
| 6 | + binding_type: |
| 7 | + description: "Which bindings do you want to update?" |
| 8 | + required: true |
| 9 | + type: choice |
| 10 | + options: |
| 11 | + - api |
| 12 | + - identity |
| 13 | + - both |
| 14 | + default: "both" |
5 | 15 | schedule: |
6 | | - - cron: "0 4 * * 1" # Every Monday at 4 AM UTC |
| 16 | + # Runs every Monday at 4 AM UTC |
| 17 | + # Note: schedule triggers don't have inputs, so binding_type will be null and default to 'both' |
| 18 | + - cron: "0 4 * * 1" |
7 | 19 |
|
8 | 20 | permissions: |
9 | 21 | contents: read |
|
14 | 26 |
|
15 | 27 | jobs: |
16 | 28 | download: |
17 | | - name: Update API Bindings |
| 29 | + name: Update Bindings |
18 | 30 | runs-on: ubuntu-24.04 |
19 | 31 | permissions: |
20 | 32 | actions: read |
|
37 | 49 | fetch-depth: 0 |
38 | 50 | persist-credentials: true |
39 | 51 |
|
| 52 | + - name: Capture base commit |
| 53 | + id: base-commit |
| 54 | + run: | |
| 55 | + BASE_SHA=$(git rev-parse HEAD) |
| 56 | + echo "sha=$BASE_SHA" >> $GITHUB_OUTPUT |
| 57 | + echo "📌 Base commit: $BASE_SHA" |
| 58 | +
|
40 | 59 | - name: Log in to Azure |
41 | 60 | uses: bitwarden/gh-actions/azure-login@main |
42 | 61 | with: |
|
62 | 81 | private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} |
63 | 82 | permission-pull-requests: write |
64 | 83 |
|
65 | | - - name: Switch to branch |
66 | | - id: switch-branch |
67 | | - run: | |
68 | | - BRANCH_NAME="sdlc/api-update" |
69 | | - echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT |
70 | | -
|
71 | | - if git switch $BRANCH_NAME; then |
72 | | - echo "✅ Switched to existing branch: $BRANCH_NAME" |
73 | | - echo "updating_existing_branch=true" >> $GITHUB_OUTPUT |
74 | | - else |
75 | | - echo "📝 Creating new branch: $BRANCH_NAME" |
76 | | - git switch -c $BRANCH_NAME |
77 | | - echo "updating_existing_branch=false" >> $GITHUB_OUTPUT |
78 | | - fi |
79 | | -
|
80 | | - - name: Prevent updating the branch when the last committer isn't the bot |
81 | | - if: ${{ steps.switch-branch.outputs.updating_existing_branch == 'true' }} |
82 | | - env: |
83 | | - _BRANCH_NAME: ${{ steps.switch-branch.outputs.branch_name }} |
84 | | - run: | |
85 | | - LATEST_COMMIT_AUTHOR=$(git log -1 --format='%ae' $_BRANCH_NAME) |
86 | | -
|
87 | | - echo "Latest commit author in branch ($_BRANCH_NAME): $LATEST_COMMIT_AUTHOR" |
88 | | - echo "Expected bot email: $_BOT_EMAIL" |
89 | | -
|
90 | | - if [ "$LATEST_COMMIT_AUTHOR" != "$_BOT_EMAIL" ]; then |
91 | | - echo "::error::Branch $_BRANCH_NAME has a commit not made by the bot." \ |
92 | | - "This indicates manual changes have been made to the branch," \ |
93 | | - "PR has to be merged or closed before running this workflow again." |
94 | | - echo "👀 Fetching existing PR..." |
95 | | - gh pr list --head $_BRANCH_NAME --base main --state open --json number --jq '.[0].number // empty' |
96 | | - EXISTING_PR=$(gh pr list --head $_BRANCH_NAME --base main --state open --json number --jq '.[0].number // empty') |
97 | | - if [ -z "$EXISTING_PR" ]; then |
98 | | - echo "::error::Couldn't find an existing PR for branch $_BRANCH_NAME." |
99 | | - exit 1 |
100 | | - fi |
101 | | - PR_URL="https://github.com/${{ github.repository }}/pull/$EXISTING_PR" |
102 | | - echo "## ❌ Merge or close: $PR_URL" >> $GITHUB_STEP_SUMMARY |
103 | | - exit 1 |
104 | | - fi |
105 | | -
|
106 | | - echo "✅ Branch tip commit was made by the bot. Safe to proceed." |
107 | | -
|
108 | | - - name: Download json artifacts |
| 84 | + - name: Download json artifacts from server for updated bindings |
109 | 85 | uses: bitwarden/gh-actions/download-artifacts@main |
110 | 86 | with: |
111 | 87 | github_token: ${{ secrets.GITHUB_TOKEN }} |
@@ -160,57 +136,227 @@ jobs: |
160 | 136 | - name: NPM setup |
161 | 137 | run: npm ci |
162 | 138 |
|
| 139 | + - name: Set Commit Info |
| 140 | + id: commit-info |
| 141 | + run: | |
| 142 | + API_HASH=$(cat ./artifacts/api.json | jq -r '.["x-git-commit"]') |
| 143 | + IDENTITY_HASH=$(cat ./artifacts/identity.json | jq -r '.["x-git-commit"]') |
| 144 | +
|
| 145 | + echo "API commit hash: $API_HASH" |
| 146 | + echo "Identity commit hash: $IDENTITY_HASH" |
| 147 | +
|
| 148 | + if [ "$API_HASH" != "$IDENTITY_HASH" ]; then |
| 149 | + echo "::error::Commit hash mismatch! API: $API_HASH, Identity: $IDENTITY_HASH" |
| 150 | + exit 1 |
| 151 | + fi |
| 152 | +
|
| 153 | + echo "HASH=$API_HASH" >> $GITHUB_OUTPUT |
| 154 | + echo "✅ Using server commit: $API_HASH" |
| 155 | +
|
| 156 | + - name: Configure Git |
| 157 | + run: | |
| 158 | + git config user.name "$_BOT_NAME" |
| 159 | + git config user.email "$_BOT_EMAIL" |
| 160 | +
|
| 161 | + # API Bindings PR |
| 162 | + - name: Switch to API branch |
| 163 | + id: switch-api-branch |
| 164 | + if: ${{ inputs.binding_type == 'api' || inputs.binding_type == 'both' || !inputs.binding_type }} |
| 165 | + env: |
| 166 | + BRANCH_NAME: "api-bindings-update" |
| 167 | + BASE_SHA: ${{ steps.base-commit.outputs.sha }} |
| 168 | + run: | |
| 169 | + echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT |
| 170 | +
|
| 171 | + git fetch origin |
| 172 | + if git switch $BRANCH_NAME 2>/dev/null; then |
| 173 | + echo "✅ Switched to existing branch: $BRANCH_NAME" |
| 174 | + echo "updating_existing_branch=true" >> $GITHUB_OUTPUT |
| 175 | + else |
| 176 | + echo "📝 Creating new branch: $BRANCH_NAME from $BASE_SHA" |
| 177 | + git switch -c $BRANCH_NAME $BASE_SHA |
| 178 | + echo "updating_existing_branch=false" >> $GITHUB_OUTPUT |
| 179 | + fi |
| 180 | +
|
| 181 | + - name: Prevent updating API branch when last committer isn't the bot |
| 182 | + if: ${{ (inputs.binding_type == 'api' || inputs.binding_type == 'both' || !inputs.binding_type) && steps.switch-api-branch.outputs.updating_existing_branch == 'true' }} |
| 183 | + env: |
| 184 | + _BRANCH_NAME: ${{ steps.switch-api-branch.outputs.branch_name }} |
| 185 | + GH_TOKEN: ${{ steps.app-token.outputs.token }} |
| 186 | + run: | |
| 187 | + LATEST_COMMIT_AUTHOR=$(git log -1 --format='%ae' $_BRANCH_NAME) |
| 188 | +
|
| 189 | + echo "Latest commit author in branch ($_BRANCH_NAME): $LATEST_COMMIT_AUTHOR" |
| 190 | + echo "Expected bot email: $_BOT_EMAIL" |
| 191 | +
|
| 192 | + if [ "$LATEST_COMMIT_AUTHOR" != "$_BOT_EMAIL" ]; then |
| 193 | + echo "::error::Branch $_BRANCH_NAME has a commit not made by the bot." \ |
| 194 | + "This indicates manual changes have been made to the branch," \ |
| 195 | + "PR has to be merged or closed before running this workflow again." |
| 196 | + EXISTING_PR=$(gh pr list --head $_BRANCH_NAME --base main --state open --json number --jq '.[0].number // empty') |
| 197 | + if [ -z "$EXISTING_PR" ]; then |
| 198 | + echo "::error::Couldn't find an existing PR for branch $_BRANCH_NAME." |
| 199 | + exit 1 |
| 200 | + fi |
| 201 | + PR_URL="https://github.com/${{ github.repository }}/pull/$EXISTING_PR" |
| 202 | + echo "## ❌ Merge or close: $PR_URL" >> $GITHUB_STEP_SUMMARY |
| 203 | + exit 1 |
| 204 | + fi |
| 205 | +
|
| 206 | + echo "✅ Branch tip commit was made by the bot. Safe to proceed." |
| 207 | +
|
163 | 208 | - name: Generate API bindings |
164 | | - run: ./support/build-api-ci.sh |
| 209 | + if: ${{ inputs.binding_type == 'api' || inputs.binding_type == 'both' || !inputs.binding_type }} |
| 210 | + run: ./support/generate-api-bindings-ci.sh |
165 | 211 |
|
166 | | - - name: Format |
| 212 | + - name: Format API bindings |
| 213 | + if: ${{ inputs.binding_type == 'api' || inputs.binding_type == 'both' || !inputs.binding_type }} |
167 | 214 | env: |
168 | 215 | RUST_NIGHTLY_TOOLCHAIN: ${{ steps.nightly-toolchain.outputs.RUST_NIGHTLY_TOOLCHAIN }} |
169 | | - run: cargo +"${RUST_NIGHTLY_TOOLCHAIN}" fmt |
| 216 | + run: cargo +"${RUST_NIGHTLY_TOOLCHAIN}" fmt -p bitwarden-api-api |
170 | 217 |
|
171 | | - - name: Set Commit Info |
172 | | - id: commit-info |
| 218 | + - name: Commit API bindings |
| 219 | + if: ${{ inputs.binding_type == 'api' || inputs.binding_type == 'both' || !inputs.binding_type }} |
| 220 | + env: |
| 221 | + _HASH: ${{ steps.commit-info.outputs.HASH }} |
| 222 | + _BRANCH_NAME: ${{ steps.switch-api-branch.outputs.branch_name }} |
173 | 223 | run: | |
174 | | - HASH=$(cat ./artifacts/identity.json | jq -r '.["x-git-commit"]') |
175 | | - echo "HASH=$HASH" >> $GITHUB_OUTPUT |
| 224 | + echo "👀 Committing API bindings update..." |
| 225 | + git add crates/bitwarden-api-api |
| 226 | + git commit -m "Update API bindings to $_HASH" --no-verify || echo "No changes to commit for API" |
| 227 | + git push origin $_BRANCH_NAME |
176 | 228 |
|
177 | | - - name: Create branch and commit |
| 229 | + - name: Create or Update API Pull Request |
| 230 | + if: ${{ inputs.binding_type == 'api' || inputs.binding_type == 'both' || !inputs.binding_type }} |
178 | 231 | env: |
| 232 | + GH_TOKEN: ${{ steps.app-token.outputs.token }} |
179 | 233 | _HASH: ${{ steps.commit-info.outputs.HASH }} |
180 | | - _BRANCH_NAME: ${{ steps.switch-branch.outputs.BRANCH_NAME }} |
| 234 | + _BRANCH_NAME: ${{ steps.switch-api-branch.outputs.branch_name }} |
181 | 235 | run: | |
182 | | - echo "👀 Committing SDK version update..." |
| 236 | + PR_TITLE="Update API bindings to $_HASH" |
| 237 | + PR_BODY="Updates the API bindings to \`$_HASH\`" |
183 | 238 |
|
184 | | - git config user.name "$_BOT_NAME" |
185 | | - git config user.email "$_BOT_EMAIL" |
| 239 | + # Set reviewer flag for scheduled runs |
| 240 | + if [ "${{ github.event_name }}" == "schedule" ]; then |
| 241 | + echo "🔔 Assigning team-platform-dev as reviewer for scheduled run" |
| 242 | + REVIEWER_FLAG="--reviewer bitwarden/team-platform-dev" |
| 243 | + else |
| 244 | + REVIEWER_FLAG="" |
| 245 | + fi |
186 | 246 |
|
187 | | - git add crates/bitwarden-api-api crates/bitwarden-api-identity |
188 | | - git commit -m "Update API bindings - $_HASH" --no-verify |
| 247 | + EXISTING_PR=$(gh pr list --head $_BRANCH_NAME --base main --state open --json number --jq '.[0].number // empty') |
| 248 | +
|
| 249 | + if [ -n "$EXISTING_PR" ]; then |
| 250 | + echo "🔄 Updating existing API PR #$EXISTING_PR..." |
| 251 | + echo -e "$PR_BODY" | gh pr edit $EXISTING_PR \ |
| 252 | + --title "$PR_TITLE" \ |
| 253 | + --body-file - |
| 254 | + PR_URL="https://github.com/${{ github.repository }}/pull/$EXISTING_PR" |
| 255 | + echo "## ✅ Updated API PR: $PR_URL" >> $GITHUB_STEP_SUMMARY |
| 256 | + else |
| 257 | + echo "📝 Creating new API PR..." |
| 258 | + PR_URL=$(echo -e "$PR_BODY" | gh pr create \ |
| 259 | + --title "$PR_TITLE" \ |
| 260 | + --body-file - \ |
| 261 | + --base main \ |
| 262 | + --head $_BRANCH_NAME \ |
| 263 | + $REVIEWER_FLAG) |
| 264 | + echo "## 🚀 Created API PR: $PR_URL" >> $GITHUB_STEP_SUMMARY |
| 265 | + fi |
| 266 | +
|
| 267 | + # Identity Bindings PR |
| 268 | + - name: Switch to Identity branch |
| 269 | + id: switch-identity-branch |
| 270 | + if: ${{ inputs.binding_type == 'identity' || inputs.binding_type == 'both' || !inputs.binding_type }} |
| 271 | + env: |
| 272 | + BRANCH_NAME: "identity-bindings-update" |
| 273 | + BASE_SHA: ${{ steps.base-commit.outputs.sha }} |
| 274 | + run: | |
| 275 | + echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT |
| 276 | +
|
| 277 | + git fetch origin |
| 278 | + if git switch $BRANCH_NAME 2>/dev/null; then |
| 279 | + echo "✅ Switched to existing branch: $BRANCH_NAME" |
| 280 | + echo "updating_existing_branch=true" >> $GITHUB_OUTPUT |
| 281 | + else |
| 282 | + echo "📝 Creating new branch: $BRANCH_NAME from $BASE_SHA" |
| 283 | + git switch -c $BRANCH_NAME $BASE_SHA |
| 284 | + echo "updating_existing_branch=false" >> $GITHUB_OUTPUT |
| 285 | + fi |
| 286 | +
|
| 287 | + - name: Prevent updating Identity branch when last committer isn't the bot |
| 288 | + if: ${{ (inputs.binding_type == 'identity' || inputs.binding_type == 'both' || !inputs.binding_type) && steps.switch-identity-branch.outputs.updating_existing_branch == 'true' }} |
| 289 | + env: |
| 290 | + _BRANCH_NAME: ${{ steps.switch-identity-branch.outputs.branch_name }} |
| 291 | + GH_TOKEN: ${{ steps.app-token.outputs.token }} |
| 292 | + run: | |
| 293 | + LATEST_COMMIT_AUTHOR=$(git log -1 --format='%ae' $_BRANCH_NAME) |
| 294 | +
|
| 295 | + echo "Latest commit author in branch ($_BRANCH_NAME): $LATEST_COMMIT_AUTHOR" |
| 296 | + echo "Expected bot email: $_BOT_EMAIL" |
| 297 | +
|
| 298 | + if [ "$LATEST_COMMIT_AUTHOR" != "$_BOT_EMAIL" ]; then |
| 299 | + echo "::error::Branch $_BRANCH_NAME has a commit not made by the bot." \ |
| 300 | + "This indicates manual changes have been made to the branch," \ |
| 301 | + "PR has to be merged or closed before running this workflow again." |
| 302 | + EXISTING_PR=$(gh pr list --head $_BRANCH_NAME --base main --state open --json number --jq '.[0].number // empty') |
| 303 | + if [ -z "$EXISTING_PR" ]; then |
| 304 | + echo "::error::Couldn't find an existing PR for branch $_BRANCH_NAME." |
| 305 | + exit 1 |
| 306 | + fi |
| 307 | + PR_URL="https://github.com/${{ github.repository }}/pull/$EXISTING_PR" |
| 308 | + echo "## ❌ Merge or close: $PR_URL" >> $GITHUB_STEP_SUMMARY |
| 309 | + exit 1 |
| 310 | + fi |
| 311 | +
|
| 312 | + echo "✅ Branch tip commit was made by the bot. Safe to proceed." |
| 313 | +
|
| 314 | + - name: Generate Identity bindings |
| 315 | + if: ${{ inputs.binding_type == 'identity' || inputs.binding_type == 'both' || !inputs.binding_type }} |
| 316 | + run: ./support/generate-identity-bindings-ci.sh |
| 317 | + |
| 318 | + - name: Format Identity bindings |
| 319 | + if: ${{ inputs.binding_type == 'identity' || inputs.binding_type == 'both' || !inputs.binding_type }} |
| 320 | + env: |
| 321 | + RUST_NIGHTLY_TOOLCHAIN: ${{ steps.nightly-toolchain.outputs.RUST_NIGHTLY_TOOLCHAIN }} |
| 322 | + run: cargo +"${RUST_NIGHTLY_TOOLCHAIN}" fmt -p bitwarden-api-identity |
| 323 | + |
| 324 | + - name: Commit Identity bindings |
| 325 | + if: ${{ inputs.binding_type == 'identity' || inputs.binding_type == 'both' || !inputs.binding_type }} |
| 326 | + env: |
| 327 | + _HASH: ${{ steps.commit-info.outputs.HASH }} |
| 328 | + _BRANCH_NAME: ${{ steps.switch-identity-branch.outputs.branch_name }} |
| 329 | + run: | |
| 330 | + echo "👀 Committing Identity bindings update..." |
| 331 | + git add crates/bitwarden-api-identity |
| 332 | + git commit -m "Update Identity bindings to $_HASH" --no-verify || echo "No changes to commit for Identity" |
189 | 333 | git push origin $_BRANCH_NAME |
190 | 334 |
|
191 | | - - name: Create or Update Pull Request |
| 335 | + - name: Create or Update Identity Pull Request |
| 336 | + if: ${{ inputs.binding_type == 'identity' || inputs.binding_type == 'both' || !inputs.binding_type }} |
192 | 337 | env: |
193 | 338 | GH_TOKEN: ${{ steps.app-token.outputs.token }} |
194 | 339 | _HASH: ${{ steps.commit-info.outputs.HASH }} |
195 | | - _BRANCH_NAME: ${{ steps.switch-branch.outputs.BRANCH_NAME }} |
| 340 | + _BRANCH_NAME: ${{ steps.switch-identity-branch.outputs.branch_name }} |
196 | 341 | run: | |
197 | | - PR_BODY="Updates the API bindings to \`$_HASH\`" |
| 342 | + PR_TITLE="Update Identity bindings to $_HASH" |
| 343 | + PR_BODY="Updates the Identity bindings to \`$_HASH\`" |
198 | 344 |
|
199 | 345 | EXISTING_PR=$(gh pr list --head $_BRANCH_NAME --base main --state open --json number --jq '.[0].number // empty') |
200 | 346 |
|
201 | 347 | if [ -n "$EXISTING_PR" ]; then |
202 | | - echo "🔄 Updating existing PR #$EXISTING_PR..." |
| 348 | + echo "🔄 Updating existing Identity PR #$EXISTING_PR..." |
203 | 349 | echo -e "$PR_BODY" | gh pr edit $EXISTING_PR \ |
204 | | - --title "Update API to $_HASH" \ |
| 350 | + --title "$PR_TITLE" \ |
205 | 351 | --body-file - |
206 | 352 | PR_URL="https://github.com/${{ github.repository }}/pull/$EXISTING_PR" |
207 | | - echo "## ✅ Updated PR: $PR_URL" >> $GITHUB_STEP_SUMMARY |
| 353 | + echo "## ✅ Updated Identity PR: $PR_URL" >> $GITHUB_STEP_SUMMARY |
208 | 354 | else |
209 | | - echo "📝 Creating new PR..." |
| 355 | + echo "📝 Creating new Identity PR..." |
210 | 356 | PR_URL=$(echo -e "$PR_BODY" | gh pr create \ |
211 | | - --title "Update API to $_HASH" \ |
| 357 | + --title "$PR_TITLE" \ |
212 | 358 | --body-file - \ |
213 | 359 | --base main \ |
214 | 360 | --head $_BRANCH_NAME) |
215 | | - echo "## 🚀 Created PR: $PR_URL" >> $GITHUB_STEP_SUMMARY |
| 361 | + echo "## 🚀 Created Identity PR: $PR_URL" >> $GITHUB_STEP_SUMMARY |
216 | 362 | fi |
0 commit comments