Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .github/workflows/check_board_conflicts.yaml
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
name: Check for Board Assignment Conflicts

on:
push:
branches:
- 'main'
- 'master'
pull_request:
branches:
- '*'

jobs:
check:
name: Check for Conflicts
Expand All @@ -16,8 +13,10 @@ jobs:
- uses: actions/checkout@v4
with:
submodules: recursive
# Need full history so the script can diff against the PR base ref.
fetch-depth: 0

- name: Run Test
run: |
./Tools/check_board_types_conflicts.txt

git fetch --no-tags --depth=1 origin "${{ github.base_ref }}"
./Tools/check_board_types_conflicts.sh "origin/${{ github.base_ref }}"
33 changes: 33 additions & 0 deletions .github/workflows/weekly_board_conflicts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: Weekly Board Conflict Sweep

on:
schedule:
# Full-file sweep every Sunday at 06:00 UTC.
- cron: '0 6 * * 0'
workflow_dispatch:

permissions:
contents: read
issues: write

jobs:
sweep:
name: Weekly Conflict Sweep
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Run full-file conflict check
id: check
run: ./Tools/check_board_types_conflicts.sh | tee conflict_output.txt

# If the sweep failed, open a tracking issue or append to the existing
# open one so repeated weekly failures collect in a single thread.
- name: Report failure
if: failure()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: ./Tools/report_board_conflict_failure.sh
85 changes: 85 additions & 0 deletions Tools/check_board_types_conflicts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/bash

# Ensure we are running Bash 3 or newer
if ((BASH_VERSINFO[0] < 3)); then
echo "Error: This script requires Bash 3 or newer. Current version: $BASH_VERSION"
exit 1
fi

# Enable strict mode for better error handling
set -euo pipefail

# Get the project root directory (assuming the script is in ./Tools)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
FILE_PATH="$PROJECT_ROOT/board_types.txt"

# Check if the file exists
if [ ! -f "$FILE_PATH" ]; then
echo "Error: File 'board_types.txt' not found in project root: $PROJECT_ROOT"
exit 1
fi

# Optional: only validate board IDs added relative to a base git ref.
# Usage: check_board_types_conflicts.sh [BASE_REF]
# When BASE_REF is provided, the full file is still scanned, but only conflicts
# that involve a newly added board are reported. This keeps the check focused on
# what a PR actually changes while still catching collisions against existing
# entries. Without BASE_REF, the entire file is checked.
BASE_REF="${1:-}"
NEW_IDS=""
if [ -n "$BASE_REF" ]; then
# Collect the board ID (second column) of lines added in the diff.
# Keep the result on a single space-separated line: BSD awk (macOS) rejects
# a newline-containing value passed via -v, which breaks local PR-mode runs.
NEW_IDS="$(git -C "$PROJECT_ROOT" diff "$BASE_REF" -- board_types.txt \
| awk '/^\+[^+]/ { sub(/^\+/, ""); if ($1 !~ /^#/ && $2 != "") print $2 }' \
| sort -u | tr '\n' ' ' || true)"
if [ -z "$NEW_IDS" ]; then
echo "No new board IDs added relative to $BASE_REF; nothing to check."
exit 0
fi
fi

# Process the file to detect conflicts using awk, while ignoring "# same" lines.
# Exit non-zero if any conflict is detected so CI fails loudly.
awk -v new_ids="$NEW_IDS" '
BEGIN {
# Build a set of newly added IDs (empty set means "check everything").
n = split(new_ids, parts, /[ \t\n]+/)
for (i = 1; i <= n; i++) {
if (parts[i] != "") {
new_set[parts[i]] = 1
only_new = 1
}
}
}
{
# If the third column starts with "# same", it is a maintainer-approved
# conflict. Still record its mapping so later lines compare against it,
# but do not treat it as a conflict itself.
if ($3 == "#" && $4 == "same") {
value_map[$2] = $1
next
}

# Check for conflicts on the second column
if ($2 in value_map && value_map[$2] != $1) {
# In new-IDs-only mode, only report conflicts involving an added board.
if (!only_new || ($2 in new_set)) {
print "Conflict detected: Value \"" $2 "\" is associated with both \"" value_map[$2] "\" and \"" $1 "\""
conflicts++
}
}

# Store the mapping
value_map[$2] = $1
}
END {
if (conflicts > 0) {
print "ERROR: " conflicts " board ID conflict(s) detected. See above."
exit 1
}
print "No board ID conflicts detected."
}
' "$FILE_PATH"
42 changes: 0 additions & 42 deletions Tools/check_board_types_conflicts.txt

This file was deleted.

43 changes: 43 additions & 0 deletions Tools/report_board_conflict_failure.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/bin/bash

# Opens a tracking issue when the weekly board conflict check fails, or appends
# a dated comment to the existing open one so repeated weekly failures collect in
# a single thread instead of spawning a new issue each week.
#
# Expects to run in GitHub Actions with the `gh` CLI authenticated via GH_TOKEN.
# Required environment:
# GH_TOKEN - token with issues:write (e.g. secrets.GITHUB_TOKEN)
# RUN_URL - URL of the failing workflow run
# Optional environment:
# OUTPUT_FILE - file containing the check output (default: conflict_output.txt)
# ISSUE_TITLE - tracking issue title (has a sensible default)

set -euo pipefail

TITLE="${ISSUE_TITLE:-Weekly board_types.txt conflict check is failing}"
OUTPUT_FILE="${OUTPUT_FILE:-conflict_output.txt}"
DATE="$(date -u +'%Y-%m-%d')"
OUTPUT="$(cat "$OUTPUT_FILE" 2>/dev/null || echo '(no output captured)')"

BODY_FILE="$(mktemp)"
{
echo "Weekly run on **${DATE}** detected board ID conflict(s)."
echo
echo '```'
echo "${OUTPUT}"
echo '```'
echo
echo "Run: ${RUN_URL:-unknown}"
} > "$BODY_FILE"

# Find an existing OPEN issue with this exact title.
EXISTING="$(gh issue list --state open --search "in:title \"${TITLE}\"" \
--json number,title --jq ".[] | select(.title == \"${TITLE}\") | .number" | head -n1)"

if [ -n "$EXISTING" ]; then
echo "Appending recurrence to existing issue #${EXISTING}"
gh issue comment "$EXISTING" --body-file "$BODY_FILE"
else
echo "Creating new tracking issue"
gh issue create --title "$TITLE" --body-file "$BODY_FILE" --label bug
fi
2 changes: 1 addition & 1 deletion board_types.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# this file lists all board types for boards that use the bootloader
# protocol implemented in this bootloader.
# NOTE: This file is now protected with a CI action that checks for conflicts.
# you can run the tool locally ./Tools/check_board_types_conflicts.txt
# you can run the tool locally ./Tools/check_board_types_conflicts.sh
# MAINTAINERS ONLY: we allow conflicts for very special conditions, in those cases
# you need to add a comment with "# same" followed by the conflict target so the test passes

Expand Down
Loading