From d588b0f10c1b0d90a7edc5393862ea0cf659182e Mon Sep 17 00:00:00 2001 From: "Timothy P. Ellsworth Bowers" Date: Mon, 10 Nov 2025 13:04:01 -0700 Subject: [PATCH 1/9] Update vendordeps modified: vendordeps/maple-sim.json --- vendordeps/maple-sim.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendordeps/maple-sim.json b/vendordeps/maple-sim.json index 0dcb2a0..65f4413 100644 --- a/vendordeps/maple-sim.json +++ b/vendordeps/maple-sim.json @@ -1,7 +1,7 @@ { "fileName": "maple-sim.json", "name": "maplesim", - "version": "0.3.13", + "version": "0.3.14", "frcYear": "2025", "uuid": "c39481e8-4a63-4a4c-9df6-48d91e4da37b", "mavenUrls": [ @@ -13,7 +13,7 @@ { "groupId": "org.ironmaple", "artifactId": "maplesim-java", - "version": "0.3.13" + "version": "0.3.14" }, { "groupId": "org.dyn4j", From eb86b3ffc2ea422f71f8878c5c363e189e5910b9 Mon Sep 17 00:00:00 2001 From: "Timothy P. Ellsworth Bowers" Date: Mon, 10 Nov 2025 16:51:52 -0700 Subject: [PATCH 2/9] New template-syncing Action Add the time to the TEMPLATE_ORIGIN.txt file new file: .github/workflows/create-dependabot-labels.yaml renamed: .github/workflows/record_template_origin.yaml -> .github/workflows/record-template-origin.yaml modified: .github/workflows/sync-template-updates.yaml deleted: .github/workflows/template-sync.yml modified: src/main/java/frc/robot/generated/TunerConstants.java --- .../workflows/create-dependabot-labels.yaml | 47 ++++ ...rigin.yaml => record-template-origin.yaml} | 11 +- .github/workflows/sync-template-updates.yaml | 220 ++++++++++++++---- .github/workflows/template-sync.yml | 32 --- .../frc/robot/generated/TunerConstants.java | 4 +- 5 files changed, 231 insertions(+), 83 deletions(-) create mode 100644 .github/workflows/create-dependabot-labels.yaml rename .github/workflows/{record_template_origin.yaml => record-template-origin.yaml} (87%) delete mode 100644 .github/workflows/template-sync.yml diff --git a/.github/workflows/create-dependabot-labels.yaml b/.github/workflows/create-dependabot-labels.yaml new file mode 100644 index 0000000..b8825fb --- /dev/null +++ b/.github/workflows/create-dependabot-labels.yaml @@ -0,0 +1,47 @@ +name: "Ensure Dependabot Labels Exist" + +on: + push: + branches: [main] + paths: + - '.github/dependabot.yml' + workflow_dispatch: + +jobs: + create-labels: + runs-on: ubuntu-latest + permissions: + issues: write # Needed to create labels via API + steps: + - name: Create labels for Dependabot PRs + uses: actions/github-script@v7 + with: + script: | + const labels = [ + { name: "github_actions", color: "BFFFD1", description: "Updates to GitHub Actions workflows" }, + { name: "gradle dependencies", color: "02303A", description: "Gradle dependency updates" }, + ]; + + for (const label of labels) { + try { + await github.rest.issues.getLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + name: label.name, + }); + core.info(`✅ Label '${label.name}' already exists.`); + } catch (error) { + if (error.status === 404) { + await github.rest.issues.createLabel({ + owner: context.repo.owner, + repo: context.repo.repo, + name: label.name, + color: label.color, + description: label.description, + }); + core.info(`🎨 Created label '${label.name}'.`); + } else { + throw error; + } + } + } diff --git a/.github/workflows/record_template_origin.yaml b/.github/workflows/record-template-origin.yaml similarity index 87% rename from .github/workflows/record_template_origin.yaml rename to .github/workflows/record-template-origin.yaml index 4fdbd01..dedb8e8 100644 --- a/.github/workflows/record_template_origin.yaml +++ b/.github/workflows/record-template-origin.yaml @@ -64,9 +64,14 @@ jobs: - name: Record template origin file run: | - echo "Template: ${{ steps.template.outputs.template_repo }}" > TEMPLATE_ORIGIN.txt - echo "Template Branch: ${{ steps.template.outputs.template_branch }}" >> TEMPLATE_ORIGIN.txt - echo "Template Commit: ${{ steps.template.outputs.template_commit }}" >> TEMPLATE_ORIGIN.txt + timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + { + echo "Template: ${{ steps.template.outputs.template_repo }}" + echo "Template Branch: ${{ steps.template.outputs.template_branch }}" + echo "Template Commit: ${{ steps.template.outputs.template_commit }}" + echo "Recorded At (UTC): $timestamp" + } > TEMPLATE_ORIGIN.txt + echo "Recorded template origin:" cat TEMPLATE_ORIGIN.txt diff --git a/.github/workflows/sync-template-updates.yaml b/.github/workflows/sync-template-updates.yaml index 93db06e..2edfb40 100644 --- a/.github/workflows/sync-template-updates.yaml +++ b/.github/workflows/sync-template-updates.yaml @@ -1,8 +1,11 @@ -name: Sync Template Updates +name: "Sync with Az-RBSI Template" on: + # cronjob trigger schedule: - - cron: '0 0 * * *' # Runs daily at midnight UTC + # Every Monday at 4:30AM + - cron: "30 04 * * 1" + # manual trigger workflow_dispatch: permissions: @@ -12,13 +15,21 @@ permissions: jobs: sync-template: runs-on: ubuntu-latest + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: + # ------------------------------- + # Step 1: Checkout repository + # ------------------------------- - name: Checkout repository uses: actions/checkout@v5 with: - fetch-depth: 0 + fetch-depth: 0 # Full history needed for cherry-picking + # ------------------------------- + # Step 2: Read template origin info + # ------------------------------- - name: Read template origin id: template run: | @@ -28,68 +39,185 @@ jobs: exit 0 fi - template_repo=$(sed -n 's/^Template: //p' TEMPLATE_ORIGIN.txt) - template_branch=$(sed -n 's/^Template Branch: //p' TEMPLATE_ORIGIN.txt) - template_commit=$(sed -n 's/^Template Commit: //p' TEMPLATE_ORIGIN.txt) + template_repo=$(sed -n 's/^Template: //p' TEMPLATE_ORIGIN.txt | tr -d '[:space:]') + last_applied_commit=$(sed -n 's/^Template Commit: //p' TEMPLATE_ORIGIN.txt | tr -d '[:space:]') - echo "Template repo: $template_repo" - echo "Template branch: $template_branch" - echo "Template commit: $template_commit" - - # Skip if placeholder values - if [ "$template_repo" = "none" ] || [ "$template_branch" = "none" ] || [ "$template_commit" = "none" ]; then - echo "Repository was not created from a template. Skipping sync." + if [ "$template_repo" = "none" ] || [ "$last_applied_commit" = "none" ]; then + echo "Repository is not from a template. Skipping sync." echo "skip_sync=true" >> $GITHUB_OUTPUT exit 0 fi + # Get default branch of template repo dynamically + template_branch=$(gh repo view "$template_repo" --json defaultBranchRef --jq '.defaultBranchRef.name') echo "skip_sync=false" >> $GITHUB_OUTPUT echo "template_repo=$template_repo" >> $GITHUB_OUTPUT echo "template_branch=$template_branch" >> $GITHUB_OUTPUT - echo "template_commit=$template_commit" >> $GITHUB_OUTPUT + echo "last_applied_commit=$last_applied_commit" >> $GITHUB_OUTPUT + # ------------------------------- + # Step 3: Install GitHub CLI and jq + # ------------------------------- - name: Install GitHub CLI if: steps.template.outputs.skip_sync == 'false' - run: sudo apt-get install -y gh jq + run: sudo apt-get update && sudo apt-get install -y gh jq - - name: Fetch commits from template since last sync + # ------------------------------- + # Step 4: Add template repo as remote and fetch commits + # ------------------------------- + - name: Add template repo as remote and fetch if: steps.template.outputs.skip_sync == 'false' - id: commits run: | - # Get the list of commits from template repo that are not yet in this repo - commits=$(gh api repos/${{ steps.template.outputs.template_repo }}/commits --jq '.[] | .sha' | tac) - echo "$commits" > template_commits.txt - echo "Commits to consider:" - cat template_commits.txt - echo "commits=$(cat template_commits.txt)" >> $GITHUB_OUTPUT - - - name: Cherry-pick commits and create PR + git remote add template_repo https://github.com/${{ steps.template.outputs.template_repo }}.git + git fetch template_repo ${{ steps.template.outputs.template_branch }} + + # ------------------------------- + # Step 5: Get new commits from template (only non-merge) + # ------------------------------- + - name: Get new commits from template if: steps.template.outputs.skip_sync == 'false' + id: commits + run: | + last_commit=${{ steps.template.outputs.last_applied_commit }} + branch=${{ steps.template.outputs.template_branch }} + + # List commits after last applied commit, oldest first + all_commits=$(git rev-list --reverse ${last_commit}..template_repo/${branch} || true) + all_commits=$(echo "$all_commits" | grep -v "^$last_commit$" || true) + + # Filter out merge commits (those with >1 parent) + non_merge_commits="" + for sha in $all_commits; do + parent_count=$(git rev-list --parents -n 1 "$sha" | wc -w) + if [ "$parent_count" -le 2 ]; then + non_merge_commits+="$sha"$'\n' + fi + done + + non_merge_commits=$(echo "$non_merge_commits" | grep -v '^$' || true) + commit_count=$(echo "$non_merge_commits" | grep -c . || true) + + echo "commit_count=$commit_count" >> $GITHUB_OUTPUT + + if [ -z "$non_merge_commits" ]; then + echo "No new template commits to cherry-pick." + else + echo "Non-merge commits to cherry-pick:" + echo "$non_merge_commits" + fi + + echo "new_commits<> $GITHUB_OUTPUT + echo "$non_merge_commits" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + + # ------------------------------- + # Step 6: Cherry-pick commits, squash, and create/update PR + # ------------------------------- + - name: Cherry-pick commits, squash, and create/update PR + if: steps.template.outputs.skip_sync == 'false' && steps.commits.outputs.new_commits != '' env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + set -e git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - - branch_name="template-sync-$(date +%Y%m%d%H%M%S)" - git checkout -b "$branch_name" - pr_message="Template Sync Commit Summary:\n\n" + branch_name="template-sync" + target_branch=$(gh repo view $GITHUB_REPOSITORY --json defaultBranchRef --jq '.defaultBranchRef.name') + sync_date=$(date -u +"%Y-%m-%d") + timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + + # Ensure branch exists or create new + if git ls-remote --exit-code --heads origin "$branch_name"; then + git fetch origin "$branch_name" + git checkout "$branch_name" + git pull origin "$branch_name" + else + git checkout -b "$branch_name" origin/"$target_branch" || git checkout -b "$branch_name" + fi + + # Reset to target branch to avoid conflicts + git fetch origin "$target_branch" + git reset --hard origin/"$target_branch" - while read commit_sha; do - # Skip empty lines + # Initialize commit summary + commit_summary="" + newest_commit="" + commit_list=$(echo "${{ steps.commits.outputs.new_commits }}" | tr -d '\r') + + # Cherry-pick each commit safely + while IFS= read -r commit_sha; do [ -z "$commit_sha" ] && continue - echo "Cherry-picking $commit_sha" - git cherry-pick "$commit_sha" || git cherry-pick --abort - pr_message="$pr_message- $commit_sha\n" - done < template_commits.txt - - git push origin "$branch_name" - - # Create PR - gh pr create \ - --title "Sync Template Updates" \ - --body "$pr_message" \ - --base main \ - --head "$branch_name" \ - --label "template-sync" + + short_sha=$(git rev-parse --short=7 "$commit_sha") + commit_msg=$(git log -1 --pretty=%B "$commit_sha") + first_line=${commit_msg%%$'\n'*} + + # Add only once to the summary + commit_summary+="- $short_sha: $first_line"$'\n' + + # Cherry-pick without committing yet + git cherry-pick --no-commit "$commit_sha" + newest_commit="$commit_sha" + done <<< "$commit_list" + + # Squash all cherry-picked commits into a single commit + git commit -m "Template Sync Updates"$'\n\n'"$commit_summary" + + # Update TEMPLATE_ORIGIN.txt (with new Recorded At) + if [ -n "$newest_commit" ]; then + { + echo "Template: ${{ steps.template.outputs.template_repo }}" + echo "Template Branch: ${{ steps.template.outputs.template_branch }}" + echo "Template Commit: $newest_commit" + echo "Recorded At (UTC): $timestamp" + } > TEMPLATE_ORIGIN.txt + git add TEMPLATE_ORIGIN.txt + git commit --amend --no-edit + fi + + # Push changes + git push origin "$branch_name" -f + + # Ensure template-sync label exists + if ! gh label list | grep -q "^template-sync"; then + gh label create template-sync --color BC8F8F --description "Updates synced from template repository" + fi + + # Build PR title with date and non-merge commit count + pr_count="${{ steps.commits.outputs.commit_count }}" + [ -z "$pr_count" ] && pr_count=0 + plural="s" + [ "$pr_count" -eq 1 ] && plural="" + pr_title="Sync Template Updates ($pr_count commit$plural, $sync_date)" + + # Build PR body with summary + source info + pr_body=$(printf "Template Sync Commit Summary:\n\n%s\n_Synced from:_ [%s](https://github.com/%s/tree/%s) at commit \`%s\`\n\n_Last recorded at (UTC): %s_" \ + "$commit_summary" \ + "${{ steps.template.outputs.template_repo }}" \ + "${{ steps.template.outputs.template_repo }}" \ + "${{ steps.template.outputs.template_branch }}" \ + "$newest_commit" \ + "$timestamp") + + existing_pr=$(gh pr list --head "$branch_name" --state open --json number --jq '.[0].number') + + if [ -n "$existing_pr" ]; then + echo "Updating existing PR #$existing_pr" + printf "%s" "$pr_body" | gh pr edit "$existing_pr" --title "$pr_title" --body-file - --add-label "template-sync" + else + echo "Creating a new PR" + printf "%s" "$pr_body" | gh pr create \ + --title "$pr_title" \ + --body-file - \ + --base "$target_branch" \ + --head "$branch_name" \ + --label "template-sync" + fi + + # ------------------------------- + # Step 7: No new commits + # ------------------------------- + - name: No new commits + if: steps.template.outputs.skip_sync == 'false' && steps.commits.outputs.new_commits == '' + run: echo "No new template commits to sync. Exiting." diff --git a/.github/workflows/template-sync.yml b/.github/workflows/template-sync.yml deleted file mode 100644 index e07dae0..0000000 --- a/.github/workflows/template-sync.yml +++ /dev/null @@ -1,32 +0,0 @@ -# File: .github/workflows/template-sync.yml -name: "Sync with Az-RBSI Template" - -on: - # cronjob trigger - # schedule: - # - cron: "30 04 * * 1" - # manual trigger - workflow_dispatch: -jobs: - repo-sync: - runs-on: ubuntu-latest - # https://docs.github.com/en/actions/using-jobs/assigning-permissions-to-jobs - permissions: - contents: write - pull-requests: write - - steps: - # To use this repository's private action, you must check out the repository - - name: Checkout - uses: actions/checkout@v5 - - - name: actions-template-sync - uses: AZ-First/actions-template-sync@v1 - with: - # github_token: ${{ secrets.GITHUB_TOKEN }} - source_repo_path: AZ-First/Az-RBSI - pr_title: "[bot] Update Robot Code with latest version of Az-RBSI" - pr_body: "Automated PR to synchronize this repository's code with the latest version of [Az-RBSI](${SOURCE_REPO}).\nThe target commit in the template repository is [${TEMPLATE_GIT_HASH}](https://github.com/AZ-First/Az-RBSI/commit/${TEMPLATE_GIT_HASH}).\nSee the [Az-RBSI Releases page](https://github.com/AZ-First/Az-RBSI/releases) for more information." - pr_commit_msg: "Update to the latest version of Az-RBSI" - pr_branch_name_prefix: "Az-RBSI_template_sync" - is_pr_cleanup: true diff --git a/src/main/java/frc/robot/generated/TunerConstants.java b/src/main/java/frc/robot/generated/TunerConstants.java index 929773a..46c54d7 100755 --- a/src/main/java/frc/robot/generated/TunerConstants.java +++ b/src/main/java/frc/robot/generated/TunerConstants.java @@ -291,9 +291,9 @@ public TunerSwerveDrivetrain( * @param odometryUpdateFrequency The frequency to run the odometry loop. If unspecified or set * to 0 Hz, this is 250 Hz on CAN FD, and 100 Hz on CAN 2.0. * @param odometryStandardDeviation The standard deviation for odometry calculation in the form - * [x, y, theta]ᵀ, with units in meters and radians + * [x, y, theta]T, with units in meters and radians * @param visionStandardDeviation The standard deviation for vision calculation in the form [x, - * y, theta]ᵀ, with units in meters and radians + * y, theta]T, with units in meters and radians * @param modules Constants for each specific module */ public TunerSwerveDrivetrain( From 37edde3f8ff32f6b21879056691da0f7ea6b190c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 04:27:49 +0000 Subject: [PATCH 3/9] Bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build.yml | 2 +- .github/workflows/dependency-submission.yml | 2 +- .github/workflows/main.yml | 4 ++-- .github/workflows/record-template-origin.yaml | 2 +- .github/workflows/sync-template-updates.yaml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 23362e2..8e92b75 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: container: wpilib/roborio-cross-ubuntu:2024-22.04 steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Grant execute permission run: chmod +x gradlew - name: Build robot code diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml index 3513795..7a7c477 100644 --- a/.github/workflows/dependency-submission.yml +++ b/.github/workflows/dependency-submission.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Java uses: actions/setup-java@v5 with: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e89a56f..45964cf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 # Declares the repository safe and not under dubious ownership. - name: Add repository to git safe directories @@ -44,7 +44,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 - uses: actions/setup-java@v5 diff --git a/.github/workflows/record-template-origin.yaml b/.github/workflows/record-template-origin.yaml index dedb8e8..f4133b0 100644 --- a/.github/workflows/record-template-origin.yaml +++ b/.github/workflows/record-template-origin.yaml @@ -16,7 +16,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/sync-template-updates.yaml b/.github/workflows/sync-template-updates.yaml index 2edfb40..c7df71f 100644 --- a/.github/workflows/sync-template-updates.yaml +++ b/.github/workflows/sync-template-updates.yaml @@ -23,7 +23,7 @@ jobs: # Step 1: Checkout repository # ------------------------------- - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 # Full history needed for cherry-picking From cc4cdb040dc98f7e3c25545d18295cbf7c5ca7c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 04:27:43 +0000 Subject: [PATCH 4/9] Bump actions/github-script from 7 to 8 Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v7...v8) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/create-dependabot-labels.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/create-dependabot-labels.yaml b/.github/workflows/create-dependabot-labels.yaml index b8825fb..d2795e5 100644 --- a/.github/workflows/create-dependabot-labels.yaml +++ b/.github/workflows/create-dependabot-labels.yaml @@ -14,7 +14,7 @@ jobs: issues: write # Needed to create labels via API steps: - name: Create labels for Dependabot PRs - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | const labels = [ From 6d2b0b1885ba5075b1936f686560974ac09852cb Mon Sep 17 00:00:00 2001 From: "Timothy P. Ellsworth Bowers" Date: Fri, 19 Dec 2025 09:32:02 -0700 Subject: [PATCH 5/9] Add "admin" branch for [bot] updates Add `admin-orchestrator` workflow modified: .github/dependabot.yml new file: .github/workflows/admin-orchestrator.yaml --- .github/dependabot.yml | 2 + .github/workflows/admin-orchestrator.yaml | 94 +++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 .github/workflows/admin-orchestrator.yaml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 436a2a8..944a7ee 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,6 +10,7 @@ updates: - package-ecosystem: "github-actions" # Workflow files stored in the default location of `.github/workflows`. (You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.) directory: "/" + target-branch: "admin" labels: - "github_actions" schedule: @@ -19,6 +20,7 @@ updates: - package-ecosystem: "gradle" directory: "/" # Location of package manifests registries: "*" + target-branch: "admin" labels: - "gradle dependencies" schedule: diff --git a/.github/workflows/admin-orchestrator.yaml b/.github/workflows/admin-orchestrator.yaml new file mode 100644 index 0000000..d42745e --- /dev/null +++ b/.github/workflows/admin-orchestrator.yaml @@ -0,0 +1,94 @@ +name: Admin branch orchestration + +on: + create: + push: + branches: + - "**" + schedule: + - cron: "0 3 * * 1" # Weekly, Monday 03:00 UTC + workflow_dispatch: + + pull_request: + branches: + - admin + pull_request_review: + types: + - submitted + check_suite: + types: + - completed + +permissions: + contents: write + pull-requests: write + +jobs: + admin-orchestrator: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # ------------------------------------------------------------ + # Detect default branch + # ------------------------------------------------------------ + - name: Detect default branch + id: default + run: | + DEFAULT_BRANCH=$(git remote show origin | sed -n '/HEAD branch/s/.*: //p') + echo "branch=$DEFAULT_BRANCH" >> "$GITHUB_OUTPUT" + + # ------------------------------------------------------------ + # Ensure admin branch exists + # ------------------------------------------------------------ + - name: Ensure admin branch exists + run: | + if git show-ref --verify --quiet refs/remotes/origin/admin; then + echo "admin branch already exists" + else + git checkout "${{ steps.default.outputs.branch }}" + git checkout -b admin + git push origin admin + fi + + # ------------------------------------------------------------ + # Periodically rebase admin onto default (true rebase) + # ------------------------------------------------------------ + - name: Rebase admin onto default + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + run: | + git fetch origin + git checkout admin + + # Rebase admin commits on top of default branch + git rebase origin/${{ steps.default.outputs.branch }} + + # Push updated admin branch + git push --force-with-lease origin admin + + # ------------------------------------------------------------ + # Guardrail: warn if non-Dependabot PR targets admin + # (no hard failure without branch protection) + # ------------------------------------------------------------ + - name: Warn on non-Dependabot PRs + if: github.event_name == 'pull_request' + run: | + if [[ "${{ github.actor }}" != "dependabot[bot]" ]]; then + echo "::warning::PR to admin opened by non-Dependabot actor" + fi + + # ------------------------------------------------------------ + # Auto-merge Dependabot PRs + # ------------------------------------------------------------ + - name: Auto-merge Dependabot PR + if: | + github.event_name == 'pull_request' && + github.event.pull_request.user.login == 'dependabot[bot]' + uses: peter-evans/enable-pull-request-automerge@v3 + with: + pull-request-number: ${{ github.event.pull_request.number }} + merge-method: squash From 88c91a90484ff5c11acff747797e5863107c7d5b Mon Sep 17 00:00:00 2001 From: "Timothy P. Ellsworth Bowers" Date: Sun, 28 Dec 2025 17:39:20 -0700 Subject: [PATCH 6/9] Clean up CI with caching and directed jobs deleted: .github/workflows/build.yml new file: .github/workflows/ci.yaml deleted: .github/workflows/main.yml new file: gradle.properties --- .github/workflows/build.yml | 18 ------------- .github/workflows/ci.yaml | 40 +++++++++++++++++++++++++++ .github/workflows/main.yml | 54 ------------------------------------- gradle.properties | 18 +++++++++++++ 4 files changed, 58 insertions(+), 72 deletions(-) delete mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/ci.yaml delete mode 100644 .github/workflows/main.yml create mode 100644 gradle.properties diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 8e92b75..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Build - -on: - push: - pull_request: - -jobs: - build: - name: Build - runs-on: ubuntu-latest - container: wpilib/roborio-cross-ubuntu:2024-22.04 - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - name: Grant execute permission - run: chmod +x gradlew - - name: Build robot code - run: ./gradlew build diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..aca6f42 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,40 @@ +name: CI + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + container: wpilib/roborio-cross-ubuntu:2024-22.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Mark repo as safe for git + run: git config --global --add safe.directory $GITHUB_WORKSPACE + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Set up Gradle cache + uses: gradle/actions/setup-gradle@v4 + + # ------------------------------------------------------------ + # Always build (all branches, all PRs) + # ------------------------------------------------------------ + - name: Build robot code + run: ./gradlew build + + # ------------------------------------------------------------ + # Spotless only on main/develop + # ------------------------------------------------------------ + - name: Run Spotless checks + if: | + github.ref == 'refs/heads/main' || + github.ref == 'refs/heads/develop' || + github.base_ref == 'main' || + github.base_ref == 'develop' + run: ./gradlew spotlessCheck diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 45964cf..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,54 +0,0 @@ -# This is a basic workflow to build robot code. - -name: CI - -# Controls when the action will run. Triggers the workflow on push or pull request -# events but only for the main branch. -on: - push: - branches: [ main, develop ] - pull_request: - branches: [ main, develop ] - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a job called "build" which is the main build - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # This grabs the WPILib docker container - container: wpilib/roborio-cross-ubuntu:2024-22.04 - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v6 - - # Declares the repository safe and not under dubious ownership. - - name: Add repository to git safe directories - run: git config --global --add safe.directory $GITHUB_WORKSPACE - - # Grant execute permission for gradlew - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - # Runs a single command using the runners shell - - name: Compile and run tests on robot code - run: ./gradlew build - - # This workflow contains a job called "build" which is the main build - spotless: - # The type of runner that the job will run on - runs-on: ubuntu-latest - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - uses: actions/setup-java@v5 - with: - distribution: 'zulu' - java-version: 17 - - run: ./gradlew spotlessCheck \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..cd9de7c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,18 @@ +# ------------------------------------------------------------ +# Gradle performance & CI tuning +# ------------------------------------------------------------ + +# CI should not use the Gradle daemon +org.gradle.daemon=false + +# Enable parallel task execution +org.gradle.parallel=true + +# Enable build cache +org.gradle.caching=true + +# Reduce configuration time for multi-project builds +org.gradle.configureondemand=true + +# Keep console output readable in CI +org.gradle.console=plain From b035bb582709de47d27c4821964f20ece7c24e9f Mon Sep 17 00:00:00 2001 From: "Timothy P. Ellsworth Bowers" Date: Sun, 28 Dec 2025 18:04:52 -0700 Subject: [PATCH 7/9] Do not run admin-orchestrator on pushes to all branches modified: .github/workflows/admin-orchestrator.yaml --- .github/workflows/admin-orchestrator.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/admin-orchestrator.yaml b/.github/workflows/admin-orchestrator.yaml index d42745e..265ae23 100644 --- a/.github/workflows/admin-orchestrator.yaml +++ b/.github/workflows/admin-orchestrator.yaml @@ -2,9 +2,6 @@ name: Admin branch orchestration on: create: - push: - branches: - - "**" schedule: - cron: "0 3 * * 1" # Weekly, Monday 03:00 UTC workflow_dispatch: From 82459f414f9777391241807207142c2f8a180469 Mon Sep 17 00:00:00 2001 From: "Timothy P. Ellsworth Bowers" Date: Sun, 28 Dec 2025 18:14:55 -0700 Subject: [PATCH 8/9] Enforce CI gradle settings only for CI environment modified: .github/workflows/ci.yaml modified: .github/workflows/dependency-submission.yml deleted: gradle.properties modified: settings.gradle --- .github/workflows/ci.yaml | 41 +++++++++++++-------- .github/workflows/dependency-submission.yml | 4 ++ gradle.properties | 18 --------- settings.gradle | 2 +- 4 files changed, 31 insertions(+), 34 deletions(-) delete mode 100644 gradle.properties diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index aca6f42..c83fbb6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -5,9 +5,13 @@ on: pull_request: jobs: + # ------------------------------------------------------------ + # Main build job + # ------------------------------------------------------------ build: runs-on: ubuntu-latest container: wpilib/roborio-cross-ubuntu:2024-22.04 + if: ${{ github.event_name != 'pull_request' || github.event.pull_request == null }} steps: - name: Checkout repository @@ -22,19 +26,26 @@ jobs: - name: Set up Gradle cache uses: gradle/actions/setup-gradle@v4 - # ------------------------------------------------------------ - # Always build (all branches, all PRs) - # ------------------------------------------------------------ - name: Build robot code - run: ./gradlew build - - # ------------------------------------------------------------ - # Spotless only on main/develop - # ------------------------------------------------------------ - - name: Run Spotless checks - if: | - github.ref == 'refs/heads/main' || - github.ref == 'refs/heads/develop' || - github.base_ref == 'main' || - github.base_ref == 'develop' - run: ./gradlew spotlessCheck + run: ./gradlew build --no-daemon --parallel --build-cache + + # ------------------------------------------------------------ + # Spotless job (separate for branch protection) + # ------------------------------------------------------------ + spotless: + runs-on: ubuntu-latest + container: wpilib/roborio-cross-ubuntu:2024-22.04 + # Only run for PRs or pushes targeting main/develop + if: github.base_ref == 'main' || github.base_ref == 'develop' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop' + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Set up Gradle cache + uses: gradle/actions/setup-gradle@v4 + + - name: Run Spotless check + run: ./gradlew spotlessCheck --no-daemon --parallel --build-cache diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml index 7a7c477..16d9455 100644 --- a/.github/workflows/dependency-submission.yml +++ b/.github/workflows/dependency-submission.yml @@ -1,5 +1,9 @@ name: Dependency Submission +# Purpose: Generate and submit Gradle dependency metadata (graph) to Gradle’s services. +# Trigger: Push to main branch only. +# Effect: Helps with dependency insight, conflict detection, and build analytics. + on: push: branches: [ 'main' ] diff --git a/gradle.properties b/gradle.properties deleted file mode 100644 index cd9de7c..0000000 --- a/gradle.properties +++ /dev/null @@ -1,18 +0,0 @@ -# ------------------------------------------------------------ -# Gradle performance & CI tuning -# ------------------------------------------------------------ - -# CI should not use the Gradle daemon -org.gradle.daemon=false - -# Enable parallel task execution -org.gradle.parallel=true - -# Enable build cache -org.gradle.caching=true - -# Reduce configuration time for multi-project builds -org.gradle.configureondemand=true - -# Keep console output readable in CI -org.gradle.console=plain diff --git a/settings.gradle b/settings.gradle index 969c7b0..0441686 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,7 +4,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2025' + String frcYear = '2026beta' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC') From 462f7f7d33e22fb8035b29bf2c298c7af2a7e2cd Mon Sep 17 00:00:00 2001 From: "Timothy P. Ellsworth Bowers" Date: Sun, 28 Dec 2025 19:11:54 -0700 Subject: [PATCH 9/9] Run admin-orchestrator on pushes to main modified: .github/workflows/admin-orchestrator.yaml modified: settings.gradle --- .github/workflows/admin-orchestrator.yaml | 12 +++++++++++- settings.gradle | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/admin-orchestrator.yaml b/.github/workflows/admin-orchestrator.yaml index 265ae23..eb34da2 100644 --- a/.github/workflows/admin-orchestrator.yaml +++ b/.github/workflows/admin-orchestrator.yaml @@ -2,8 +2,14 @@ name: Admin branch orchestration on: create: + + push: + branches: + - "main" + schedule: - cron: "0 3 * * 1" # Weekly, Monday 03:00 UTC + workflow_dispatch: pull_request: @@ -56,8 +62,12 @@ jobs: # Periodically rebase admin onto default (true rebase) # ------------------------------------------------------------ - name: Rebase admin onto default - if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'push' run: | + set -e + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git fetch origin git checkout admin diff --git a/settings.gradle b/settings.gradle index 0441686..969c7b0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,7 +4,7 @@ pluginManagement { repositories { mavenLocal() gradlePluginPortal() - String frcYear = '2026beta' + String frcYear = '2025' File frcHome if (OperatingSystem.current().isWindows()) { String publicFolder = System.getenv('PUBLIC')