radar online
github weekly radar · доклад
доклад · задача → решение · 6 контактов на экране

Как
сканировать GitHub

GitHub Trending показывает витрину. Мне нужна была сейсмостанция: ловить репозитории в момент взлёта, отсеивать накрутки и раз в неделю собирать дайджест — без рук и за пять центов. Рассказываю, как я этот радар собрал.

60 ГБ
событий за ~5 секунд
137 → 15
кандидатов → в выпуск
$0.05
себестоимость выпуска
18 мин
полный цикл пайплайна

Каждую неделю на GitHub прилетают тысячи новых репозиториев. Где-то среди них — инструмент, который через полгода будет у всех в проде. Хочется увидеть его сегодня, пока о нём не написали все телеграм-каналы.

Первая мысль, конечно: «возьмём GitHub Trending». Не работает. Trending — витрина без API, с непрозрачным алгоритмом, где неделями висят одни и те же гиганты. Search API ищет по метаданным — «создан тогда-то, звёзд столько-то» — и в упор не видит динамику.

Так что я собрал свой радар. Шесть задач — шесть решений.

КОНТАКТ 01

Сейсмограмма
вместо витрины

Задача

Найти репозитории, которые взлетают прямо сейчас. Trending API не существует. Search API не умеет «дай мне тех, у кого звёзды растут аномально быстро».

Выход нашёлся сбоку: GitHub публикует каждое публичное событие — звёзды, форки, иссью, релизы — в GH Archive, а тот лежит в BigQuery как обычные таблицы. Сейсмограмма всего GitHub, читается обычным SQL.

Решение

SQL-запрос сравнивает две недели: текущую и прошлую. Берём только репо с релизом за неделю, считаем surge ratio — во сколько раз ускорился приток звёзд:

-- githubarchive.day.* : WatchEvent, ForkEvent, IssuesEvent, ReleaseEvent surge_ratio = звёзды_этой_недели / (звёзды_прошлой + 5) -- +5 гасит шум нулей radar_score = surge_ratio × 3.0 + log(звёзды) × 1.0 + log(форки + иссью) × 0.5 -- WHERE star_events >= 10 → отрезаем статистический мусор

Запрос прожёвывает ~60 ГБ событий за 3–5 секунд. Free tier BigQuery — терабайт в месяц, так что еженедельный радар не стоит ни цента. И бонус, которого нет у Search API — радар видит старые репо, которые внезапно проснулись: для потока событий дата создания не существует.

мораль контактаУскорение — метрика, которой нет ни в одном API. Она живёт в потоке событий.
КОНТАКТ 02

«Сколько звёзд за неделю?» — GitHub молчит

Задача

Главная метрика радара — прирост звёзд за 7 дней. Такого поля в API нет вообще: ни в REST, ни в GraphQL.

Зато есть полусекретный режим Stargazers API: если попросить звёзды с особым заголовком, каждая придёт с таймстемпом — когда именно её поставили:

GET /repos/{owner}/{repo}/stargazers Accept: application/vnd.github.star+json ← магия здесь # читаем страницы С КОНЦА и считаем starred_at >= (сегодня − 7 дней)
Грабля

GitHub отдаёт максимум 400 страниц × 100 звёзд = 40 000. Для репо крупнее сорока тысяч звёзд история обрезается — недельный прирост так не посчитать в принципе.

Решение

Каскад из трёх источников, от дешёвого к точному:

1. OSS Insight и BigQuery-радар уже знают прирост — берём готовое.
2. Дельта с собственным снапшотом прошлой недели — пайплайн каждую неделю сохраняет JSON, и разница звёзд работает для репо любого размера.
3. И только для середняков (100–40 000★) — тот самый Stargazers API с чтением с конца.

мораль контактаЕсли API не отдаёт метрику — веди её сам. Снапшот прошлой недели ценнее любого эндпоинта.
КОНТАКТ 03

137 репозиториев и один rate limit

Задача

По каждому кандидату нужно много всего: коммиты за месяц, контрибьюторы, последний релиз с количеством скачиваний, профиль владельца… В REST это 5–6 запросов на репо. 137 кандидатов × 6 = больше восьмисот запросов. Rate limit машет ручкой.

Решение

GraphQL с алиасами: десять репозиториев в одном запросе, и каждый отдаёт сразу всё дерево данных.

query { repo0: repository(owner: "ollama", name: "ollama") { ...всё сразу } repo1: repository(owner: "zed", name: "zed") { ... } ⋮ батч по 10 — и 137 репо это всего 14 запросов вместо ~800 } # внутри фрагмента: звёзды, форки, топики, коммиты за 30 дней, # релиз + download count ассетов, у владельца — репо и фолловеры

Плюс гигиена: каждый ответ проверяю на X-RateLimit-Remaining. Меньше сотни в запасе — пайплайн сам засыпает до X-RateLimit-Reset. До ошибки 429 дело просто не доходит.

мораль контактаREST хорош для одного репо. Для ста тридцати семи — GraphQL-алиасы и привычка читать заголовки rate limit.
КОНТАКТ 04

Детектор накруток — без единого LLM-вызова

Задача

Звёзды покупаются, хайп накручивается ботами и волнами с Product Hunt. Как отличить честный взлёт от накрученного — и не гонять дорогую модель по каждому кандидату?

Решение

Четыре тупых — в хорошем смысле — эвристики на данных, которые уже пришли бесплатно из GraphQL. Первое совпадение вешает бейдж:

growth_hack
возраст < 30 дней ∧ +1000★/нед

Молодой и уже вирусный. Либо гений, либо ферма ботов — повод присмотреться.

dormant_awakening
возраст > 365 дней ∧ +500★/нед

Год тишины — и вдруг всплеск. Часто это самые интересные истории недели.

hype_spike
+1000★/нед, любой возраст

Чистая скорость. Кто-то где-то нажал на кнопку — выясняем, кто и где.

celebrity_project
≤ 1 публичный репо ∧ > 1000 фолловеров

Звезда твиттера выложила первый репозиторий — звёзды тут капают за имя.

Тонкость: бейдж ничего не выкидывает. Он едет дальше по пайплайну прямо в промпт редактора: «у этого репо growth_hack, +4 000 звёзд за неделю при возрасте 12 дней — учти при выборе угла подачи».

мораль контактаАномалия — это сюжет. Детектор подсказывает, решает редактор.
КОНТАКТ 05

Один топ — это всегда топ гигантов

Задача

Если ранжировать всех одной формулой, репо со 100k звёзд раздавит молодую жемчужину с пятью сотнями. Каждый выпуск превратится в «опять ollama, опять langchain».

Решение

Три независимых рейтинга, у каждого своя формула и свой вопрос:

🔥 Hype

stars_7d ×.40
trending ×.20
commits_30d ×.15
release ×.10 · velocity ×.10
downloads ×.05

Что горит прямо сейчас. Нет активности — штраф ×0.1.

💪 Power

commits_30d ×.25
contributors_90d ×.20
stars_7d ×.20
log(stars) ×.15
release+notes ×.20

Живая команда и регулярные релизы. Логарифм не даёт гигантам задавить остальных.

💎 Gems

velocity ×.35
commits_30d ×.25
stars_7d ×.20
trending ×.10 · homepage ×.10

Только <5k★ и моложе 180 дней. Охота за будущими звёздами.

И внутри каждого рейтинга — тиры по размеру: малыши соревнуются с малышами, гиганты — с гигантами:

🌱 Rising · до 5k★
🌿 Growing · 5k–50k★
🌳 Major · 50k+★, только с живой активностью
мораль контактаlog(stars) — самый дешёвый антимонопольный закон.
КОНТАКТ 06

LLM заходит последним

Задача

Дальше включаются языковые модели — и счётчик долларов. Если кормить модели всем подряд, дайджест начинает стоить как обед. Как удержать выпуск в районе пяти центов?

Решение

Воронка «от бесплатного к дорогому». Каждый следующий слой видит только то, что пережило предыдущий:

слой 0 denylist + эвристики $0 awesome-list, tutorial, roadmap, archived, форки — мимо слой 1 снапшоты, Flash-lite $0.008 по выжатым README (1200 симв., бейджи и картинки вырезаны) слой 2 редактор (GLM) $0.023 видит ВСЮ неделю разом → тема, 5–7 героев, угол для каждого слой 3 глубокие тексты $0.014 только для героев редактора слой 4 полировка (Haiku) $0.003 дубли теглайнов, штампы ───────────────────────────── итого ≈ $0.05 за выпуск · $0.20 в месяц

Сердце воронки — редактор-в-контуре: одна модель видит все снапшоты недели разом и решает, что в номер, что одной строкой, что в корзину — и под каким углом писать каждую карточку. Тексты пишутся по заданию редактора, поэтому и не похожи на шаблонную нарезку.

мораль контактаДешёвое — фильтрует, дорогое — пишет. Никогда наоборот.
ЖУРНАЛ ГРАБЛЕЙ

Сколько стоили мои ошибки

Радар калибровался не бесплатно: неделя R&D по постмортему — $1.003. За что заплатил — по строчкам:

$0.150

Ordering bug: обогатил всех вместо избранных

Запустил LLM-ресёрч до отбора топа — модель прилежно обработала все 137 кандидатов вместо нужных 17. Ошибка, которую code review ловит за 30 секунд, в e2e-прогоне стоит живых денег.

$0.135

Гонял дорогие модели, когда дешёвые уже работали

Прогнал Opus и Gemini Pro «на всякий случай», хотя Sonnet и GLM уже давали тот же результат. Сначала смотри прайс, потом запускай эксперимент.

$0.052

Модель «подумала» на все токены

GLM с reasoning-режимом съел весь лимит в 6000 токенов на размышления и вернул content = None. Заплатил за молчание, потом ещё раз — за повтор. Лечится просто: лимит повыше, reasoning — exclude.

Разрыв между R&D и продакшеном — ~18×: $1 за неделю отладки против $0.05 за выпуск. Нормальная цена калибровки — пока знаешь, на что именно ушли деньги. Поэтому у каждого фейла есть строчка в постмортеме.

Одна мысль,
повторённая шесть раз

Вся система держится на одном: сигнал живёт в динамике. События вместо витрины. Дельта вместо абсолюта. Каскад вместо единственного источника. Сигнал редактору вместо жёсткого фильтра. Логарифм вместо сырых звёзд. И дорогие модели — только на последнем слое, где они пишут.

Вход

~137 кандидатов из трёх независимых источников: BigQuery-радар, GitHub Search, OSS Insight. Любой может отвалиться — остальные прикроют.

Просев

Denylist → GraphQL-обогащение → детектор аномалий → три рейтинга → редактор. 13–17 репо доживают до текста.

Выход

Готовый выпуск в Telegraph + Telegram за ~18 минут и пять центов. Каждую неделю, без рук.

финальная моральДанные открыты и общие. Радар достаётся тому, кто слушает их внимательнее всех.