Skip to content

Commit ec71cef

Browse files
committed
New template-syncing Action
new file: .github/workflows/create-dependabot-labels.yaml modified: .github/workflows/sync-template-updates.yaml deleted: .github/workflows/template-sync.yml modified: src/main/java/frc/robot/generated/TunerConstants.java
1 parent d588b0f commit ec71cef

File tree

4 files changed

+215
-80
lines changed

4 files changed

+215
-80
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: "Ensure Dependabot Labels Exist"
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- '.github/dependabot.yml'
8+
workflow_dispatch:
9+
10+
jobs:
11+
create-labels:
12+
runs-on: ubuntu-latest
13+
permissions:
14+
issues: write # Needed to create labels via API
15+
steps:
16+
- name: Create labels for Dependabot PRs
17+
uses: actions/github-script@v7
18+
with:
19+
script: |
20+
const labels = [
21+
{ name: "github_actions", color: "BFFFD1", description: "Updates to GitHub Actions workflows" },
22+
{ name: "gradle dependencies", color: "02303A", description: "Gradle dependency updates" },
23+
];
24+
25+
for (const label of labels) {
26+
try {
27+
await github.rest.issues.getLabel({
28+
owner: context.repo.owner,
29+
repo: context.repo.repo,
30+
name: label.name,
31+
});
32+
core.info(`✅ Label '${label.name}' already exists.`);
33+
} catch (error) {
34+
if (error.status === 404) {
35+
await github.rest.issues.createLabel({
36+
owner: context.repo.owner,
37+
repo: context.repo.repo,
38+
name: label.name,
39+
color: label.color,
40+
description: label.description,
41+
});
42+
core.info(`🎨 Created label '${label.name}'.`);
43+
} else {
44+
throw error;
45+
}
46+
}
47+
}
Lines changed: 166 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
name: Sync Template Updates
1+
name: "Sync with Az-RBSI Template"
22

33
on:
4+
# cronjob trigger
45
schedule:
5-
- cron: '0 0 * * *' # Runs daily at midnight UTC
6+
- cron: "30 04 * * 1"
7+
# manual trigger
68
workflow_dispatch:
79

810
permissions:
@@ -12,13 +14,21 @@ permissions:
1214
jobs:
1315
sync-template:
1416
runs-on: ubuntu-latest
17+
env:
18+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1519

1620
steps:
21+
# -------------------------------
22+
# Step 1: Checkout repository
23+
# -------------------------------
1724
- name: Checkout repository
1825
uses: actions/checkout@v5
1926
with:
20-
fetch-depth: 0
27+
fetch-depth: 0 # Full history needed for cherry-picking
2128

29+
# -------------------------------
30+
# Step 2: Read template origin info
31+
# -------------------------------
2232
- name: Read template origin
2333
id: template
2434
run: |
@@ -28,68 +38,178 @@ jobs:
2838
exit 0
2939
fi
3040
31-
template_repo=$(sed -n 's/^Template: //p' TEMPLATE_ORIGIN.txt)
32-
template_branch=$(sed -n 's/^Template Branch: //p' TEMPLATE_ORIGIN.txt)
33-
template_commit=$(sed -n 's/^Template Commit: //p' TEMPLATE_ORIGIN.txt)
41+
template_repo=$(sed -n 's/^Template: //p' TEMPLATE_ORIGIN.txt | tr -d '[:space:]')
42+
last_applied_commit=$(sed -n 's/^Template Commit: //p' TEMPLATE_ORIGIN.txt | tr -d '[:space:]')
3443
35-
echo "Template repo: $template_repo"
36-
echo "Template branch: $template_branch"
37-
echo "Template commit: $template_commit"
38-
39-
# Skip if placeholder values
40-
if [ "$template_repo" = "none" ] || [ "$template_branch" = "none" ] || [ "$template_commit" = "none" ]; then
41-
echo "Repository was not created from a template. Skipping sync."
44+
if [ "$template_repo" = "none" ] || [ "$last_applied_commit" = "none" ]; then
45+
echo "Repository is not from a template. Skipping sync."
4246
echo "skip_sync=true" >> $GITHUB_OUTPUT
4347
exit 0
4448
fi
4549
50+
# Get default branch of template repo dynamically
51+
template_branch=$(gh repo view "$template_repo" --json defaultBranchRef --jq '.defaultBranchRef.name')
4652
echo "skip_sync=false" >> $GITHUB_OUTPUT
4753
echo "template_repo=$template_repo" >> $GITHUB_OUTPUT
4854
echo "template_branch=$template_branch" >> $GITHUB_OUTPUT
49-
echo "template_commit=$template_commit" >> $GITHUB_OUTPUT
55+
echo "last_applied_commit=$last_applied_commit" >> $GITHUB_OUTPUT
5056
57+
# -------------------------------
58+
# Step 3: Install GitHub CLI and jq
59+
# -------------------------------
5160
- name: Install GitHub CLI
5261
if: steps.template.outputs.skip_sync == 'false'
53-
run: sudo apt-get install -y gh jq
62+
run: sudo apt-get update && sudo apt-get install -y gh jq
5463

55-
- name: Fetch commits from template since last sync
64+
# -------------------------------
65+
# Step 4: Add template repo as remote and fetch commits
66+
# -------------------------------
67+
- name: Add template repo as remote and fetch
5668
if: steps.template.outputs.skip_sync == 'false'
57-
id: commits
5869
run: |
59-
# Get the list of commits from template repo that are not yet in this repo
60-
commits=$(gh api repos/${{ steps.template.outputs.template_repo }}/commits --jq '.[] | .sha' | tac)
61-
echo "$commits" > template_commits.txt
62-
echo "Commits to consider:"
63-
cat template_commits.txt
64-
echo "commits=$(cat template_commits.txt)" >> $GITHUB_OUTPUT
65-
66-
- name: Cherry-pick commits and create PR
70+
git remote add template_repo https://github.com/${{ steps.template.outputs.template_repo }}.git
71+
git fetch template_repo ${{ steps.template.outputs.template_branch }}
72+
73+
# -------------------------------
74+
# Step 5: Get new commits from template (only non-merge)
75+
# -------------------------------
76+
- name: Get new commits from template
6777
if: steps.template.outputs.skip_sync == 'false'
78+
id: commits
79+
run: |
80+
last_commit=${{ steps.template.outputs.last_applied_commit }}
81+
branch=${{ steps.template.outputs.template_branch }}
82+
83+
# List commits after last applied commit, oldest first
84+
all_commits=$(git rev-list --reverse ${last_commit}..template_repo/${branch} || true)
85+
all_commits=$(echo "$all_commits" | grep -v "^$last_commit$" || true)
86+
87+
# Filter out merge commits (those with >1 parent)
88+
non_merge_commits=""
89+
for sha in $all_commits; do
90+
parent_count=$(git rev-list --parents -n 1 "$sha" | wc -w)
91+
if [ "$parent_count" -le 2 ]; then
92+
non_merge_commits+="$sha"$'\n'
93+
fi
94+
done
95+
96+
non_merge_commits=$(echo "$non_merge_commits" | grep -v '^$' || true)
97+
commit_count=$(echo "$non_merge_commits" | grep -c . || true)
98+
99+
echo "commit_count=$commit_count" >> $GITHUB_OUTPUT
100+
101+
if [ -z "$non_merge_commits" ]; then
102+
echo "No new template commits to cherry-pick."
103+
else
104+
echo "Non-merge commits to cherry-pick:"
105+
echo "$non_merge_commits"
106+
fi
107+
108+
echo "new_commits<<EOF" >> $GITHUB_OUTPUT
109+
echo "$non_merge_commits" >> $GITHUB_OUTPUT
110+
echo "EOF" >> $GITHUB_OUTPUT
111+
112+
# -------------------------------
113+
# Step 6: Cherry-pick commits, squash, and create/update PR
114+
# -------------------------------
115+
- name: Cherry-pick commits, squash, and create/update PR
116+
if: steps.template.outputs.skip_sync == 'false' && steps.commits.outputs.new_commits != ''
68117
env:
69-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
118+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70119
run: |
120+
set -e
71121
git config user.name "github-actions[bot]"
72122
git config user.email "github-actions[bot]@users.noreply.github.com"
73-
74-
branch_name="template-sync-$(date +%Y%m%d%H%M%S)"
75-
git checkout -b "$branch_name"
76123
77-
pr_message="Template Sync Commit Summary:\n\n"
124+
branch_name="template-sync"
125+
target_branch=$(gh repo view $GITHUB_REPOSITORY --json defaultBranchRef --jq '.defaultBranchRef.name')
126+
sync_date=$(date -u +"%Y-%m-%d")
127+
128+
# Ensure branch exists or create new
129+
if git ls-remote --exit-code --heads origin "$branch_name"; then
130+
git fetch origin "$branch_name"
131+
git checkout "$branch_name"
132+
git pull origin "$branch_name"
133+
else
134+
git checkout -b "$branch_name" origin/"$target_branch" || git checkout -b "$branch_name"
135+
fi
136+
137+
# Reset to target branch to avoid conflicts
138+
git fetch origin "$target_branch"
139+
git reset --hard origin/"$target_branch"
140+
141+
# Initialize commit summary
142+
commit_summary=""
143+
newest_commit=""
144+
commit_list=$(echo "${{ steps.commits.outputs.new_commits }}" | tr -d '\r')
78145
79-
while read commit_sha; do
80-
# Skip empty lines
146+
# Cherry-pick each commit safely
147+
while IFS= read -r commit_sha; do
81148
[ -z "$commit_sha" ] && continue
82-
echo "Cherry-picking $commit_sha"
83-
git cherry-pick "$commit_sha" || git cherry-pick --abort
84-
pr_message="$pr_message- $commit_sha\n"
85-
done < template_commits.txt
86-
87-
git push origin "$branch_name"
88-
89-
# Create PR
90-
gh pr create \
91-
--title "Sync Template Updates" \
92-
--body "$pr_message" \
93-
--base main \
94-
--head "$branch_name" \
95-
--label "template-sync"
149+
150+
short_sha=$(git rev-parse --short=7 "$commit_sha")
151+
commit_msg=$(git log -1 --pretty=%B "$commit_sha")
152+
first_line=${commit_msg%%$'\n'*}
153+
154+
# Add only once to the summary
155+
commit_summary+="- $short_sha: $first_line"$'\n'
156+
157+
# Cherry-pick without committing yet
158+
git cherry-pick --no-commit "$commit_sha"
159+
newest_commit="$commit_sha"
160+
done <<< "$commit_list"
161+
162+
# Squash all cherry-picked commits into a single commit
163+
git commit -m "Template Sync Updates"$'\n\n'"$commit_summary"
164+
165+
# Update TEMPLATE_ORIGIN.txt
166+
if [ -n "$newest_commit" ]; then
167+
echo -e "Template: ${{ steps.template.outputs.template_repo }}\nTemplate Branch: ${{ steps.template.outputs.template_branch }}\nTemplate Commit: $newest_commit" > TEMPLATE_ORIGIN.txt
168+
git add TEMPLATE_ORIGIN.txt
169+
git commit --amend --no-edit
170+
fi
171+
172+
# Push changes
173+
git push origin "$branch_name" -f
174+
175+
# Ensure template-sync label exists
176+
if ! gh label list | grep -q "^template-sync"; then
177+
gh label create template-sync --color BC8F8F --description "Updates synced from template repository"
178+
fi
179+
180+
# Build PR title with date and non-merge commit count
181+
pr_count="${{ steps.commits.outputs.commit_count }}"
182+
[ -z "$pr_count" ] && pr_count=0
183+
plural="s"
184+
[ "$pr_count" -eq 1 ] && plural=""
185+
pr_title="Sync Template Updates ($pr_count commit$plural, $sync_date)"
186+
187+
# Build PR body with summary + source info
188+
pr_body=$(printf "Template Sync Commit Summary:\n\n%s\n_Synced from:_ [%s](https://github.com/%s/tree/%s) at commit \`%s\`" \
189+
"$commit_summary" \
190+
"${{ steps.template.outputs.template_repo }}" \
191+
"${{ steps.template.outputs.template_repo }}" \
192+
"${{ steps.template.outputs.template_branch }}" \
193+
"$newest_commit")
194+
195+
existing_pr=$(gh pr list --head "$branch_name" --state open --json number --jq '.[0].number')
196+
197+
if [ -n "$existing_pr" ]; then
198+
echo "Updating existing PR #$existing_pr"
199+
printf "%s" "$pr_body" | gh pr edit "$existing_pr" --title "$pr_title" --body-file - --add-label "template-sync"
200+
else
201+
echo "Creating a new PR"
202+
printf "%s" "$pr_body" | gh pr create \
203+
--title "$pr_title" \
204+
--body-file - \
205+
--base "$target_branch" \
206+
--head "$branch_name" \
207+
--label "template-sync"
208+
fi
209+
210+
# -------------------------------
211+
# Step 7: No new commits
212+
# -------------------------------
213+
- name: No new commits
214+
if: steps.template.outputs.skip_sync == 'false' && steps.commits.outputs.new_commits == ''
215+
run: echo "No new template commits to sync. Exiting."

.github/workflows/template-sync.yml

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/main/java/frc/robot/generated/TunerConstants.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,9 @@ public TunerSwerveDrivetrain(
291291
* @param odometryUpdateFrequency The frequency to run the odometry loop. If unspecified or set
292292
* to 0 Hz, this is 250 Hz on CAN FD, and 100 Hz on CAN 2.0.
293293
* @param odometryStandardDeviation The standard deviation for odometry calculation in the form
294-
* [x, y, theta], with units in meters and radians
294+
* [x, y, theta]T, with units in meters and radians
295295
* @param visionStandardDeviation The standard deviation for vision calculation in the form [x,
296-
* y, theta], with units in meters and radians
296+
* y, theta]T, with units in meters and radians
297297
* @param modules Constants for each specific module
298298
*/
299299
public TunerSwerveDrivetrain(

0 commit comments

Comments
 (0)