Skip to content

Commit 00a3488

Browse files
authored
workflow: Update BaseTool extdep weekly
Creates a new recurring workflow that runs weekly. This workflow finds all releases that occurred in the last 7 days, and finds the most recent release for each associated branch (e.g. if three releases happened [v202502000.0.1, dev-v202502000.0.1, v202502000.0.2], we reduce that to only [v202502000.0.2', 'dev-v202502000.0.1'] because two of them target the same release/202502 branch). If we do not find any releases, we abort early. Otherwise for each release, we will create a PR for the specific branch, updating the base tools extdep to the most recent release. If a PR already exists for an older version, we will close it.
1 parent 5c58979 commit 00a3488

File tree

1 file changed

+214
-0
lines changed

1 file changed

+214
-0
lines changed
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
name: Update BaseTools Binaries External Dependency
2+
3+
on:
4+
schedule:
5+
# NOTE: If changing the schedule, CUTOFF_DATE in the `check` job must also be updated to match
6+
- cron: '0 22 * * 6' # Every Saturday at 22:00 UTC
7+
workflow_dispatch: # Manual trigger
8+
9+
env:
10+
REPO: ${{ github.repository }}
11+
12+
jobs:
13+
# This job pulls all releases made in the last 7 days (This job runs weekly), and finds the newest unique tag, which
14+
# is then used to generate a matrix for the next job, responsible for updating the BaseTools binaries for each branch.
15+
#
16+
# By "unique tag", it is meant that the semver versioning scheme is ignored, and only the `dev` and date prefix is used.
17+
# e.g. for `dev-v2025020000.0.2`, it is finding the newest unique `dev-v202502`.
18+
check:
19+
name: Find New Releases
20+
21+
runs-on: ubuntu-latest
22+
23+
outputs:
24+
releases: ${{ steps.releases.outputs.releases }}
25+
26+
steps:
27+
- name: Build PR Creation Matrix
28+
id: releases
29+
env:
30+
REPO: ${{ env.REPO }}
31+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # We can use the normal token here, as we are just reading releases.
32+
shell: bash
33+
run: |
34+
CUTOFF_DATE=$(date -u --date='7 days ago' +%Y-%m-%dT%H:%M:%SZ)
35+
echo "Checking for releases since $CUTOFF_DATE..."
36+
37+
RELEASE_JSON=$(gh release list --repo "$REPO" --limit 100 --json tagName,publishedAt | \
38+
jq -c --arg cutoff "$CUTOFF_DATE" '
39+
[ .[]
40+
| select(.publishedAt >= $cutoff)
41+
| {
42+
tag: .tagName,
43+
publishedAt: .publishedAt,
44+
key: (
45+
.tagName
46+
| sub("^dev-"; "") # strip "dev-" prefix if present
47+
| capture("^v(?<yymmdd>\\d{6})") # extract 6-digit date prefix
48+
| .yymmdd
49+
),
50+
isDev: (.tagName | startswith("dev-"))
51+
}
52+
]
53+
| group_by(.key + "-" + (.isDev | tostring))
54+
| map(max_by(.publishedAt))
55+
| map(.tag)
56+
')
57+
58+
echo "Latest releases found since $CUTOFF_DATE: $RELEASE_JSON"
59+
60+
# Map the list of tags to a list of json objects with a tag name and branch name
61+
MAPPED_RELEASES=$(echo "$RELEASE_JSON" | jq -c '
62+
map({
63+
tagName: .,
64+
branchName: (
65+
if startswith("dev-") then
66+
"dev/" + (. | sub("^dev-v"; "") | capture("^(?<yyyymm>\\d{6})").yyyymm)
67+
else
68+
"release/" + (. | sub("^v"; "") | capture("^(?<yyyymm>\\d{6})").yyyymm)
69+
end
70+
)
71+
})
72+
')
73+
74+
echo "Mapped releases: $MAPPED_RELEASES"
75+
echo "releases=$MAPPED_RELEASES" >> $GITHUB_OUTPUT
76+
77+
update:
78+
name: "[${{ matrix.release.branchName }}] Update BaseTools Binary to ${{ matrix.release.tagName }}"
79+
80+
if: ${{ needs.check.outputs.releases != '[]' }}
81+
82+
needs: check
83+
84+
runs-on: ubuntu-latest
85+
86+
permissions:
87+
contents: write
88+
89+
env:
90+
ARTIFACT_BASE_URL: https://github.com/${{ github.repository }}/releases/download
91+
BASETOOLS_PATH: BaseTools/Bin/basetoolsbin_ext_dep.yaml
92+
TAG_NAME: ${{ matrix.release.tagName }}
93+
BRANCH_NAME: ${{ matrix.release.branchName }}
94+
PR_BRANCH_PREFIX: sync-basetools/${{ matrix.release.branchName }}
95+
PR_BRANCH_NAME: sync-basetools/${{ matrix.release.branchName }}/${{ matrix.release.tagName }}
96+
97+
strategy:
98+
matrix:
99+
release: ${{ fromJson(needs.check.outputs.releases) }}
100+
101+
steps:
102+
# If testing on a local fork with less strict permissions, you can remove this step, and update token usage to
103+
# use ${{ secrets.GITHUB_TOKEN }} instead of the app token. You must also add the permission `pull-requests: write`
104+
# to this job.
105+
- name: Generate Token
106+
id: app-token
107+
uses: actions/create-github-app-token@v2
108+
with:
109+
app-id: ${{ vars.MU_ACCESS_APP_ID }}
110+
private-key: ${{ secrets.MU_ACCESS_APP_PRIVATE_KEY }}
111+
owner: ${{ github.repository_owner }}
112+
113+
- name: Check if PR already exists
114+
id: pr-check
115+
shell: bash
116+
env:
117+
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
118+
PR_BRANCH_NAME: ${{ env.PR_BRANCH_NAME }}
119+
REPO: ${{ env.REPO }}
120+
run: |
121+
echo "Checking if PR already exists for branch: $PR_BRANCH_NAME"
122+
RESULT=$(gh pr list --repo "$REPO" --state open --head "$PR_BRANCH_NAME" --json number --jq '.[0].number')
123+
124+
if [[ -n "$RESULT" ]]; then
125+
echo "PR already exists: Pr Number $RESULT."
126+
echo "exists=true" >> $GITHUB_OUTPUT
127+
else
128+
echo "No existing PR found."
129+
echo "exists=false" >> $GITHUB_OUTPUT
130+
fi
131+
132+
- name: Checkout Code
133+
if: steps.pr-check.outputs.exists != 'true'
134+
uses: actions/checkout@v4
135+
with:
136+
ref: ${{ env.BRANCH_NAME }}
137+
138+
- name: Install Dependencies
139+
if: steps.pr-check.outputs.exists != 'true'
140+
shell: bash
141+
run: sudo apt install curl coreutils moreutils -y
142+
143+
- name: Calculate BaseTools SHA256
144+
if: steps.pr-check.outputs.exists != 'true'
145+
id: sha256
146+
shell: bash
147+
env:
148+
SOURCE_URL: ${{ env.ARTIFACT_BASE_URL }}/${{ env.TAG_NAME }}/basetools-${{ env.TAG_NAME }}.tar.gz
149+
run: |
150+
SHA256=$(curl -sSL "$SOURCE_URL" | sha256sum | awk '{print $1}')
151+
echo "sha256=${SHA256}" >> $GITHUB_OUTPUT
152+
153+
- name: Update BaseTools External Dependency File
154+
if: steps.pr-check.outputs.exists != 'true'
155+
env:
156+
BASETOOLS_PATH: ${{ env.BASETOOLS_PATH }}
157+
SOURCE_URL: ${{ env.ARTIFACT_BASE_URL }}/${{ env.TAG_NAME }}/basetools-${{ env.TAG_NAME }}.tar.gz
158+
VERSION: ${{ env.TAG_NAME }}
159+
SHA256: ${{ steps.sha256.outputs.sha256 }}
160+
uses: mikefarah/yq@v4
161+
with:
162+
cmd: yq -i -P '.source = strenv(SOURCE_URL) | .version = strenv(VERSION) | .sha256 = strenv(SHA256)' ${BASETOOLS_PATH}
163+
164+
- name: Commit and push changes
165+
if: steps.pr-check.outputs.exists != 'true'
166+
env:
167+
BASETOOLS_PATH: ${{ env.BASETOOLS_PATH }}
168+
TAG_NAME: ${{ env.TAG_NAME }}
169+
PR_BRANCH_NAME: ${{ env.PR_BRANCH_NAME }}
170+
shell: bash
171+
run: |
172+
git config user.name "uefibot"
173+
git config user.email "[email protected]"
174+
git add $BASETOOLS_PATH
175+
git commit -m "Update BaseTools external dependency to $TAG_NAME" -m "" -m "Signed-off-by: Project Mu UEFI Bot <[email protected]>"
176+
git push origin HEAD:refs/heads/$PR_BRANCH_NAME
177+
178+
- name: Create Pull Request
179+
if: steps.pr-check.outputs.exists != 'true'
180+
id: pr
181+
env:
182+
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
183+
REPO: ${{ env.REPO }}
184+
HEAD: ${{ env.PR_BRANCH_NAME}}
185+
BASE: ${{ env.BRANCH_NAME }}
186+
run: |
187+
gh pr create \
188+
--repo "$REPO" \
189+
--head "$HEAD" \
190+
--base "$BASE" \
191+
--title "[${{ env.BRANCH_NAME }}] Update BaseTools ext dep to ${{ env.TAG_NAME }}" \
192+
--body "This PR updates the BaseTools external dependency to version ${{ env.TAG_NAME }}."
193+
194+
PR_NUMBER=$(gh pr view "$HEAD" --repo "$REPO" --json number -q '.number')
195+
echo "Pr Created: Pr Number $PR_NUMBER."
196+
echo "pr_number=${PR_NUMBER}" >> $GITHUB_OUTPUT
197+
198+
- name: Close Existing Pull Request
199+
if: steps.pr-check.outputs.exists != 'true'
200+
env:
201+
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
202+
PR_BRANCH_PREFIX: ${{ env.PR_BRANCH_PREFIX}}
203+
KEEP_PR_NUMBER: ${{ steps.pr.outputs.pr_number }}
204+
run: |
205+
OPEN_PRS=$(gh pr list --state open --json number,headRefName \
206+
-q ".[] | select(.headRefName | startswith(\"${PR_BRANCH_PREFIX}\")) | .number")
207+
208+
for PR in $OPEN_PRS; do
209+
if [[ "$PR" != "$KEEP_PR_NUMBER" ]]; then
210+
echo "Closing PR #$PR"
211+
gh pr comment "$PR" --body "Closing in favor of #${KEEP_PR_NUMBER}."
212+
gh pr close "$PR" --delete-branch
213+
fi
214+
done

0 commit comments

Comments
 (0)