Skip to content
This repository was archived by the owner on Nov 12, 2026. It is now read-only.
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
15 changes: 12 additions & 3 deletions mseries/local-ai/github-watch.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
HERE = Path(__file__).resolve().parent
MARKER = '<!-- mseries-upstream-dashboard -->'
TITLE = 'Upstream watch: changes awaiting review'
DIGEST_FIELDS = ('sha', 'base_sha', 'sha256')
DIGEST_CHARS = 12


def api(path, method='GET', data=None):
Expand All @@ -35,8 +37,8 @@ def reconcile(previous, observations, errors, acknowledged=False):
if not acknowledged:
for key, change in previous.get('pending', {}).items():
# Migrate legacy {from, to} entries without losing the first value.
pending[key] = (change['from'] if isinstance(change, dict)
and 'from' in change and 'to' in change else change)
pending[key] = change_marker(change['from'] if isinstance(change, dict)
and 'from' in change and 'to' in change else change)
for key, current in observations.items():
current = change_marker(current)
old = snapshots.get(key)
Expand All @@ -51,7 +53,14 @@ def change_marker(value):
if not isinstance(value, dict):
return value
fields = ('sha', 'base_sha', 'branch', 'release', 'sha256', 'count', 'latest', 'pushed', 'ahead', 'behind', 'status')
return {k: value[k] for k in fields if k in value}
marker = {k: value[k] for k in fields if k in value}
# Hex digests barely compress, and 500+ tracked branch heads at 40 characters overflowed the
# 65,536-character issue body. Twelve characters still detect any move of the same ref, and
# stored full-length values shorten to the same prefix, so migration reports no false changes.
for k in DIGEST_FIELDS:
if isinstance(marker.get(k), str):
marker[k] = marker[k][:DIGEST_CHARS]
return marker


def encode_state(state):
Expand Down
32 changes: 32 additions & 0 deletions mseries/local-ai/test_github_watch.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
"""State migration and bounded dashboard rendering for github-watch.py."""
import hashlib
import importlib.util
from pathlib import Path
import unittest
Expand All @@ -13,6 +14,10 @@
people_spec.loader.exec_module(people)


def sha1(text):
return hashlib.sha1(text.encode()).hexdigest()


class GitHubWatchState(unittest.TestCase):
def test_legacy_pending_change_migrates_without_losing_latest_revision(self):
key = 'branch:example/project:main'
Expand Down Expand Up @@ -137,6 +142,33 @@ def test_large_legacy_dashboard_fits_the_issue_body_limit_after_migration(self):
self.assertEqual(len(state['pending']), 173)
self.assertLess(len(body.encode()), 65000)

def test_full_length_digests_shorten_without_reporting_a_change(self):
key = 'branch:example/project:main'
full, base = 'a1b2c3d4e5f6' + '0' * 28, 'f' * 40
previous = {'snapshots': {key: {'sha': full, 'base_sha': base}},
'pending': {'heads:example/project': {'from': {'sha256': 'e' * 64}, 'to': {'sha256': 'd' * 64}}}}

state = watch.reconcile(previous, {key: {'sha': full, 'base_sha': base}}, [])

self.assertNotIn(key, state['pending'])
self.assertEqual(state['snapshots'][key], {'sha': 'a1b2c3d4e5f6', 'base_sha': 'ffffffffffff'})
self.assertEqual(state['pending']['heads:example/project'], {'sha256': 'eeeeeeeeeeee'})

def test_every_source_pending_fits_with_520_full_length_heads(self):
# 2026-10-31: 510 tracked sources with 40-character SHAs overflowed the issue body.
observations, snapshots = {}, {}
for index in range(520):
key = f'branch:example/repository-{index % 24:02d}:feature/long-branch-name-{index:04d}'
snapshots[key] = {'branch': key.rsplit(':', 1)[1], 'sha': sha1(f'old-{index}'),
'base_sha': sha1(f'base-{index}'), 'ahead': index, 'behind': index % 9,
'status': 'diverged'}
observations[key] = dict(snapshots[key], sha=sha1(f'new-{index}'))

state = watch.reconcile({'snapshots': snapshots}, observations, [])

self.assertEqual(len(state['pending']), 520)
self.assertLess(len(watch.render(state).encode()), 65000)


class PeopleWatchRetry(unittest.TestCase):
class Response:
Expand Down
Loading