Skip to content

Check MeshCore Protocol Drift #9

Check MeshCore Protocol Drift

Check MeshCore Protocol Drift #9

name: Check MeshCore Protocol Drift
on:
schedule:
# Weekly, Monday 09:30 UTC (offset from check-protobufs to avoid
# both jobs hitting GitHub API at the same minute).
- cron: "30 9 * * 1"
workflow_dispatch:
jobs:
check-meshcore-drift:
runs-on: ubuntu-latest
permissions:
contents: read
issues: write
env:
PIN_FILE: meshcore_protocol/pin.yml
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
- name: Read pin file
id: pin
run: |
set -euo pipefail
if [ ! -f "$PIN_FILE" ]; then
echo "::error::Pin file $PIN_FILE not found. The drift watcher cannot run."
exit 1
fi
REPO=$(grep -E "^repo:" "$PIN_FILE" | awk '{print $2}')
BRANCH=$(grep -E "^branch:" "$PIN_FILE" | awk '{print $2}')
SHA=$(grep -E "^sha:" "$PIN_FILE" | awk '{print $2}')
PINNED_AT=$(grep -E "^pinned_at:" "$PIN_FILE" | awk '{print $2}')
FW_VER=$(grep -E "^firmware_version:" "$PIN_FILE" | awk '{print $2}')
PATHS=$(awk '
/^watched_paths:/ { flag=1; next }
/^[a-zA-Z]/ { flag=0 }
flag && /^[[:space:]]*-[[:space:]]/ {
sub(/^[[:space:]]*-[[:space:]]+/, "")
sub(/[[:space:]]*#.*$/, "")
sub(/[[:space:]]+$/, "")
if (length($0) > 0) print
}
' "$PIN_FILE")
if [ -z "$REPO" ] || [ -z "$BRANCH" ] || [ -z "$SHA" ] || [ -z "$PATHS" ]; then
echo "::error::Pin file is missing required fields (repo/branch/sha/watched_paths)."
exit 1
fi
{
echo "repo=$REPO"
echo "branch=$BRANCH"
echo "pinned_sha=$SHA"
echo "pinned_at=$PINNED_AT"
echo "firmware_version=$FW_VER"
} >> "$GITHUB_OUTPUT"
{
echo "watched_paths<<PATHS_EOF"
echo "$PATHS"
echo "PATHS_EOF"
} >> "$GITHUB_OUTPUT"
echo "Pinned: $SHA ($PINNED_AT, firmware $FW_VER)"
echo "Branch: $BRANCH"
echo "Watched paths:"
echo "$PATHS" | sed 's/^/ - /'
- name: Resolve upstream HEAD
id: upstream
env:
REPO: ${{ steps.pin.outputs.repo }}
BRANCH: ${{ steps.pin.outputs.branch }}
run: |
set -euo pipefail
HEAD_SHA=$(git ls-remote "$REPO" "refs/heads/$BRANCH" | awk '{print $1}')
if [ -z "$HEAD_SHA" ]; then
echo "::error::Could not resolve $REPO @ refs/heads/$BRANCH."
exit 1
fi
echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT"
echo "Upstream HEAD: $HEAD_SHA"
- name: Diff watched paths
id: diff
env:
REPO: ${{ steps.pin.outputs.repo }}
PINNED_SHA: ${{ steps.pin.outputs.pinned_sha }}
HEAD_SHA: ${{ steps.upstream.outputs.head_sha }}
WATCHED_PATHS: ${{ steps.pin.outputs.watched_paths }}
run: |
set -euo pipefail
if [ "$PINNED_SHA" = "$HEAD_SHA" ]; then
echo "Pinned SHA matches upstream HEAD: no drift."
{
echo "drift=false"
echo "missing="
echo "changed="
echo "diff_body="
} >> "$GITHUB_OUTPUT"
exit 0
fi
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
git clone --quiet "$REPO" "$WORK/upstream"
cd "$WORK/upstream"
if ! git cat-file -e "$PINNED_SHA" 2>/dev/null; then
echo "::error::Pinned SHA $PINNED_SHA is not reachable in $REPO. The pin is corrupt."
exit 1
fi
if ! git cat-file -e "$HEAD_SHA" 2>/dev/null; then
echo "::error::Upstream HEAD $HEAD_SHA could not be fetched."
exit 1
fi
MISSING=""
CHANGED=""
DIFF_BODY=""
while IFS= read -r path; do
[ -z "$path" ] && continue
if ! git cat-file -e "${HEAD_SHA}:${path}" 2>/dev/null; then
MISSING="${MISSING}- ${path}"$'\n'
continue
fi
if ! git diff --quiet "$PINNED_SHA" "$HEAD_SHA" -- "$path"; then
CHANGED="${CHANGED}- ${path}"$'\n'
hunk=$(git diff "$PINNED_SHA" "$HEAD_SHA" -- "$path" | head -200)
DIFF_BODY="${DIFF_BODY}"$'\n'"#### \`${path}\`"$'\n\n'"\`\`\`diff"$'\n'"${hunk}"$'\n'"\`\`\`"$'\n'
fi
done <<< "$WATCHED_PATHS"
if [ -n "$MISSING" ] || [ -n "$CHANGED" ]; then
DRIFT="true"
else
DRIFT="false"
fi
{
echo "drift=$DRIFT"
echo "missing<<MISSING_EOF"
printf '%s' "$MISSING"
echo
echo "MISSING_EOF"
echo "changed<<CHANGED_EOF"
printf '%s' "$CHANGED"
echo
echo "CHANGED_EOF"
echo "diff_body<<DIFF_EOF"
printf '%s' "$DIFF_BODY"
echo
echo "DIFF_EOF"
} >> "$GITHUB_OUTPUT"
if [ -n "$MISSING" ]; then
echo "::warning::Watcher went blind on the following paths (no longer exist upstream):"
printf '%s' "$MISSING"
fi
if [ -n "$CHANGED" ]; then
echo "Changed watched paths:"
printf '%s' "$CHANGED"
fi
- name: Check for existing issue
id: existing
if: steps.diff.outputs.drift == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_SHA: ${{ steps.upstream.outputs.head_sha }}
run: |
set -euo pipefail
SHORT="${HEAD_SHA:0:12}"
EXISTING=$(gh issue list --label "meshcore-update" --state open --json number,title \
--jq ".[] | select(.title | contains(\"$SHORT\")) | .number")
if [ -n "$EXISTING" ]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
echo "Issue #$EXISTING already covers $SHORT."
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi
- name: Create issue
if: steps.diff.outputs.drift == 'true' && steps.existing.outputs.exists == 'false'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ steps.pin.outputs.repo }}
PINNED_SHA: ${{ steps.pin.outputs.pinned_sha }}
PINNED_AT: ${{ steps.pin.outputs.pinned_at }}
PIN_FW_VER: ${{ steps.pin.outputs.firmware_version }}
HEAD_SHA: ${{ steps.upstream.outputs.head_sha }}
MISSING: ${{ steps.diff.outputs.missing }}
CHANGED: ${{ steps.diff.outputs.changed }}
DIFF_BODY: ${{ steps.diff.outputs.diff_body }}
run: |
set -euo pipefail
gh label create "meshcore-update" \
--description "MeshCore upstream protocol drift detected" \
--color "0366d6" 2>/dev/null || true
SHORT_PIN="${PINNED_SHA:0:12}"
SHORT_HEAD="${HEAD_SHA:0:12}"
TODAY=$(date -u +%Y-%m-%d)
{
echo "## MeshCore Upstream Drift Detected"
echo
echo "SocialMesh's MeshCore parser layer is verified against a pinned upstream commit. One or more watched files have changed since that pin, or have disappeared from upstream."
echo
echo "| | Commit | Date | Firmware |"
echo "|---|---|---|---|"
echo "| Pinned | [\`$SHORT_PIN\`]($REPO/commit/$PINNED_SHA) | $PINNED_AT | $PIN_FW_VER |"
echo "| Upstream HEAD | [\`$SHORT_HEAD\`]($REPO/commit/$HEAD_SHA) | $TODAY | (look up at HEAD) |"
echo
if [ -n "$MISSING" ]; then
echo "### Watched paths that no longer exist upstream"
echo
echo "**Watcher went blind on these paths.** They have been moved, renamed, or deleted upstream. Until the pin file is updated, drift cannot be detected on them."
echo
printf '%s\n' "$MISSING"
fi
if [ -n "$CHANGED" ]; then
echo "### Changed watched paths"
echo
printf '%s\n' "$CHANGED"
fi
echo "### Diff"
printf '%s\n' "$DIFF_BODY"
echo
echo "### Maintainer checklist"
echo
echo "- [ ] For each changed file, decide whether the diff touches a wire format SocialMesh parses (CMD_/RESP_CODE_/PUSH_CODE_ defines, SELF_INFO byte layout, CONTACT byte layout, NodePrefs struct order)."
echo "- [ ] If wire formats are affected, update parsers under \`lib/services/meshcore/protocol/\` AND add or refresh fixtures under \`test/services/meshcore/protocol/fixtures/\`."
echo "- [ ] If the change is benign (comments, unrelated code), only the pin file needs updating."
echo "- [ ] Update \`meshcore_protocol/pin.yml\`: bump \`sha:\`, \`pinned_at:\`, and refresh \`firmware_version:\` / \`firmware_ver_code:\` from \`examples/companion_radio/MyMesh.h\` at the new commit."
echo "- [ ] Run \`flutter test test/services/meshcore/protocol/\` and confirm green before closing this issue."
echo
echo "---"
echo "*Filed automatically by the MeshCore protocol drift watcher.*"
} > /tmp/issue_body.md
gh issue create \
--title "MeshCore upstream drift: pin $SHORT_PIN to $SHORT_HEAD" \
--label "meshcore-update,enhancement" \
--body-file /tmp/issue_body.md
- name: Fail loudly on missing watched paths
if: steps.diff.outputs.missing != ''
run: |
echo "::error::One or more watched paths no longer exist upstream. The drift watcher cannot detect changes on them. Update meshcore_protocol/pin.yml."
exit 1
- name: Summary
if: always()
env:
PINNED_SHA: ${{ steps.pin.outputs.pinned_sha }}
HEAD_SHA: ${{ steps.upstream.outputs.head_sha }}
DRIFT: ${{ steps.diff.outputs.drift }}
MISSING: ${{ steps.diff.outputs.missing }}
CHANGED: ${{ steps.diff.outputs.changed }}
run: |
{
echo "## MeshCore Protocol Drift Check"
echo
echo "| | Commit |"
echo "|---|---|"
echo "| Pinned | \`${PINNED_SHA:-(unread)}\` |"
echo "| Upstream HEAD | \`${HEAD_SHA:-(unresolved)}\` |"
echo
if [ "${DRIFT:-}" = "true" ]; then
echo "**Drift detected.**"
if [ -n "${MISSING:-}" ]; then
echo
echo "### Paths no longer upstream (watcher blind)"
printf '%s\n' "$MISSING"
fi
if [ -n "${CHANGED:-}" ]; then
echo
echo "### Changed paths"
printf '%s\n' "$CHANGED"
fi
elif [ "${DRIFT:-}" = "false" ]; then
echo "**Up to date.**"
else
echo "**Check did not complete.** See logs."
fi
} >> "$GITHUB_STEP_SUMMARY"