Skip to content

Commit dcf6f03

Browse files
authored
*: harden update bazel files workflow permissions (#67464)
ref #66999
1 parent 801b1d3 commit dcf6f03

File tree

3 files changed

+218
-38
lines changed

3 files changed

+218
-38
lines changed

.github/actions/tidb_build/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ inputs:
1010
required: true
1111
gcp_sa_key:
1212
description: GCP service account key JSON content
13-
required: true
13+
required: false
1414
runs:
1515
using: composite
1616
steps:
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
name: Generate Bazel Files
2+
on:
3+
pull_request:
4+
branches:
5+
- master
6+
types:
7+
- opened
8+
- synchronize
9+
- reopened
10+
- ready_for_review
11+
12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: generate-bazel-files-${{ github.event.pull_request.number }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
generate:
21+
name: Generate Bazel Files
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v6
25+
with:
26+
repository: ${{ github.event.pull_request.head.repo.full_name }}
27+
ref: ${{ github.event.pull_request.head.sha }}
28+
fetch-depth: 0
29+
persist-credentials: false
30+
- name: Build Prepare
31+
uses: pingcap/tidb/.github/actions/tidb_build@fb17a419f54d51f45fd031d8e5069b7022ecec92
32+
with:
33+
bazelrc: ""
34+
gcp_sa_key: ""
35+
go-version: 1.25
36+
- name: Run Bazel Prepare
37+
shell: bash
38+
run: |
39+
#!/bin/bash
40+
unset CI
41+
sed -i '/bazel-cache/d' DEPS.bzl
42+
sed -i '/ats.apps.svc/d' DEPS.bzl
43+
sed -i '/bazel-cache/d' WORKSPACE
44+
sed -i '/ats.apps.svc/d' WORKSPACE
45+
make bazel_prepare
46+
- name: Restore non-generated files
47+
shell: bash
48+
run: |
49+
git restore --worktree --source=HEAD -- WORKSPACE
50+
- name: Create Bazel Patch
51+
shell: bash
52+
run: |
53+
set -euo pipefail
54+
55+
mapfile -t tracked_changes < <(git diff --name-only --no-renames HEAD -- .)
56+
mapfile -t untracked_changes < <(git ls-files --others --exclude-standard)
57+
changed_files=("${tracked_changes[@]}" "${untracked_changes[@]}")
58+
59+
for path in "${changed_files[@]}"; do
60+
if [[ -z "${path}" ]]; then
61+
continue
62+
fi
63+
64+
case "${path}" in
65+
DEPS.bzl|*.bazel|*.bzl)
66+
;;
67+
*)
68+
echo "Unexpected file changed by bazel_prepare: ${path}" >&2
69+
exit 1
70+
;;
71+
esac
72+
done
73+
74+
if ((${#untracked_changes[@]} > 0)); then
75+
git add -N -- "${untracked_changes[@]}"
76+
fi
77+
78+
mkdir -p bazel-artifact
79+
git diff --binary --full-index --no-ext-diff -- \
80+
DEPS.bzl \
81+
':(glob)**/*.bazel' \
82+
':(glob)**/*.bzl' \
83+
> bazel-artifact/bazel.patch
84+
- name: Upload Bazel Files Artifact
85+
uses: actions/upload-artifact@v4
86+
with:
87+
name: bazel-files
88+
path: bazel-artifact/bazel.patch
89+
if-no-files-found: error
90+
retention-days: 1
Lines changed: 127 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,149 @@
11
name: Update Bazel Files
22
on:
3-
pull_request_target:
4-
branches:
5-
- master
3+
workflow_run:
4+
workflows:
5+
- Generate Bazel Files
66
types:
7-
- opened
8-
- synchronize
9-
- reopened
10-
- ready_for_review
7+
- completed
118

129
permissions:
10+
actions: read
1311
contents: write
14-
pull-requests: read
1512

1613
concurrency:
17-
group: update-bazel-files-${{ github.event.pull_request.number }}
14+
group: update-bazel-files-${{ github.event.workflow_run.id }}
1815
cancel-in-progress: true
1916

2017
jobs:
2118
update:
2219
name: Update Bazel Files
2320
if: >
24-
github.event.pull_request.head.repo.full_name == github.repository ||
25-
github.event.pull_request.maintainer_can_modify
21+
github.event.workflow_run.conclusion == 'success' &&
22+
github.event.workflow_run.event == 'pull_request'
2623
runs-on: ubuntu-latest
2724
steps:
28-
- uses: actions/checkout@v6
25+
- name: Resolve PR metadata
26+
id: pr
27+
shell: bash
28+
env:
29+
GH_TOKEN: ${{ github.token }}
30+
REPOSITORY: ${{ github.repository }}
31+
RUN_ID: ${{ github.event.workflow_run.id }}
32+
run: |
33+
set -euo pipefail
34+
35+
pr_number="$(gh api "repos/${REPOSITORY}/actions/runs/${RUN_ID}" --jq '.pull_requests[0].number')"
36+
head_repo="$(gh api "repos/${REPOSITORY}/pulls/${pr_number}" --jq '.head.repo.full_name')"
37+
head_ref="$(gh api "repos/${REPOSITORY}/pulls/${pr_number}" --jq '.head.ref')"
38+
pr_head_sha="$(gh api "repos/${REPOSITORY}/pulls/${pr_number}" --jq '.head.sha')"
39+
run_head_sha="$(gh api "repos/${REPOSITORY}/actions/runs/${RUN_ID}" --jq '.head_sha')"
40+
41+
printf 'pr_number=%s\n' "${pr_number}" >> "${GITHUB_OUTPUT}"
42+
printf 'head_repo=%s\n' "${head_repo}" >> "${GITHUB_OUTPUT}"
43+
printf 'head_ref=%s\n' "${head_ref}" >> "${GITHUB_OUTPUT}"
44+
45+
if [[ "${head_repo}" != "${REPOSITORY}" ]]; then
46+
echo "same_repo=false" >> "${GITHUB_OUTPUT}"
47+
exit 0
48+
fi
49+
50+
echo "same_repo=true" >> "${GITHUB_OUTPUT}"
51+
52+
if [[ "${pr_head_sha}" != "${run_head_sha}" ]]; then
53+
echo "stale=true" >> "${GITHUB_OUTPUT}"
54+
exit 0
55+
fi
56+
57+
echo "stale=false" >> "${GITHUB_OUTPUT}"
58+
- name: Skip stale workflow run
59+
if: steps.pr.outputs.same_repo == 'true' && steps.pr.outputs.stale == 'true'
60+
shell: bash
61+
run: |
62+
echo "Skip stale bazel update for PR #${{ steps.pr.outputs.pr_number }}."
63+
- name: Checkout PR branch
64+
if: steps.pr.outputs.same_repo == 'true' && steps.pr.outputs.stale != 'true'
65+
uses: actions/checkout@v6
2966
with:
30-
repository: ${{ github.event.pull_request.head.repo.full_name }}
31-
ref: ${{ github.event.pull_request.head.ref }}
67+
repository: ${{ steps.pr.outputs.head_repo }}
68+
ref: ${{ steps.pr.outputs.head_ref }}
69+
path: pr
3270
fetch-depth: 0
33-
token: ${{ secrets.BAZEL_PREPARE_PUSH_TOKEN || github.token }}
34-
- name: Build Prepare
35-
uses: ./.github/actions/tidb_build
71+
token: ${{ github.token }}
72+
persist-credentials: false
73+
- name: Download Bazel Files Artifact
74+
if: steps.pr.outputs.same_repo == 'true' && steps.pr.outputs.stale != 'true'
75+
uses: actions/download-artifact@v4
3676
with:
37-
bazelrc: ${{ secrets.BAZELRC }}
38-
gcp_sa_key: ${{ secrets.GCP_SA_KEY }}
39-
go-version: 1.25
40-
- name: Run Bazel Prepare
77+
name: bazel-files
78+
path: bazel-artifact
79+
repository: ${{ github.repository }}
80+
run-id: ${{ github.event.workflow_run.id }}
81+
github-token: ${{ github.token }}
82+
- name: Apply Bazel Files Artifact
83+
if: steps.pr.outputs.same_repo == 'true' && steps.pr.outputs.stale != 'true'
4184
shell: bash
4285
run: |
43-
#!/bin/bash
44-
unset CI
45-
sed -i '/bazel-cache/d' DEPS.bzl
46-
sed -i '/ats.apps.svc/d' DEPS.bzl
47-
sed -i '/bazel-cache/d' WORKSPACE
48-
sed -i '/ats.apps.svc/d' WORKSPACE
49-
make bazel_prepare
86+
set -euo pipefail
87+
88+
patch_path="${PWD}/bazel-artifact/bazel.patch"
89+
summary="$(git apply --summary "${patch_path}")"
90+
91+
while IFS= read -r line; do
92+
case "${line}" in
93+
" rename "*|" copy "*)
94+
echo "Unsupported patch summary: ${line}" >&2
95+
exit 1
96+
;;
97+
esac
98+
done <<< "${summary}"
99+
100+
declare -A seen_paths=()
101+
102+
while IFS= read -r -d '' record; do
103+
if [[ -z "${record}" ]]; then
104+
continue
105+
fi
106+
107+
IFS=$'\t' read -r _ _ path <<< "${record}"
108+
109+
case "${path}" in
110+
""|/*|..|../*|*/..|*/../*)
111+
echo "Unexpected patch path: ${path}" >&2
112+
exit 1
113+
;;
114+
DEPS.bzl|*.bazel|*.bzl)
115+
;;
116+
*)
117+
echo "Unexpected patch path: ${path}" >&2
118+
exit 1
119+
;;
120+
esac
121+
122+
if [[ -n "${seen_paths[${path}]:-}" ]]; then
123+
echo "Duplicate patch path: ${path}" >&2
124+
exit 1
125+
fi
126+
seen_paths["${path}"]=1
127+
done < <(git apply --numstat -z "${patch_path}")
128+
129+
git -C pr apply --check --index "${patch_path}"
130+
git -C pr apply --index "${patch_path}"
50131
- name: Commit Bazel Files
51-
uses: stefanzweifel/git-auto-commit-action@v5
52-
with:
53-
branch: ${{ github.event.pull_request.head.ref }}
54-
commit_message: "chore: update bazel file"
55-
disable_globbing: true
56-
file_pattern: >-
57-
DEPS.bzl
58-
:(top,glob)**/*.bazel
59-
:(top,glob)**/*.bzl
132+
if: steps.pr.outputs.same_repo == 'true' && steps.pr.outputs.stale != 'true'
133+
shell: bash
134+
env:
135+
HEAD_REF: ${{ steps.pr.outputs.head_ref }}
136+
PUSH_TOKEN: ${{ github.token }}
137+
TARGET_REPOSITORY: ${{ steps.pr.outputs.head_repo }}
138+
run: |
139+
set -euo pipefail
140+
141+
if git -C pr diff --cached --quiet -- DEPS.bzl ':(glob)**/*.bazel' ':(glob)**/*.bzl'; then
142+
echo "No generated Bazel changes to commit."
143+
exit 0
144+
fi
145+
146+
git -C pr config user.name "Ti Chi Robot"
147+
git -C pr config user.email "ti-community-prow-bot@tidb.io"
148+
git -C pr commit -m "chore: update bazel file"
149+
git -C pr push "https://x-access-token:${PUSH_TOKEN}@github.com/${TARGET_REPOSITORY}.git" "HEAD:${HEAD_REF}"

0 commit comments

Comments
 (0)