Щотижневі знімки зворотних посилань у GitHub Actions: план дій
Мета: щопонеділка робочий процес записує поточні доменні посилання та якірні тексти в репозиторій. Нічого більше. Аналіз відмінностей буде пізніше.
Редакція Linkzoid ·
Перед початком
Вам потрібен репозиторій, ключ Linkzoid, збережений як секрет LZ_KEY, та один домен, яким ви володієте. Бюджет: близько 6 кредитів на ціль за один запуск, що становить $0.0006. Знімок — це факт за один місяць, тому щотижневі запуски переважно перевибирають той самий індекс. Налаштування способу перевірки зворотних посилань на мій веб-сайт таким чином займає один вечір.
План дій по порядку
01
1. Додайте секрет
Налаштування репозиторію, потім «Секрети та змінні», потім «Дії». Назвіть його LZ_KEY. Якщо ви вставите його у файл робочого процесу, змініть ключ сьогодні.
02
2. Зафіксуйте скрипт
Збережіть наведений нижче Python-скрипт як snap.py. Він записує data/YYYY-MM-DD/refdomains.json і завершується з помилкою, якщо ok дорівнює false.
03
3. Зафіксуйте робочий процес
Використовуйте наведений нижче YAML. Запустіть його один раз вручну за тригером workflow_dispatch та прочитайте лог, перш ніж довіряти розкладу.
04
4. Перевірте перший файл
Відкрийте JSON. Підтвердіть кількість рядків щодо total та порівняйте число з тим, що повідомляє безкоштовний виклик overview для вашого домену.
05
5. Додайте сповіщення
Невдалийjob за замовчуванням надсилає електронний лист власнику репозиторію. Залиште це так. Job, який тихо завершується, — це звичайний спосіб, яким такі налаштування гинуть.
snap.py
Стандартна бібліотека. Цикл завершується за лічильником, а не за припущенням.
python
import json, os, sys, datetime, pathlib, urllib.request as u
KEY = os.environ['LZ_KEY']
TARGET = sys.argv[1]
def get(**p):
q = '&'.join(f'{k}={v}' for k, v in p.items())
url = f'https://linkzoid.pro/api.php?{q}&key={KEY}'
with u.urlopen(url, timeout=30) as r:
return json.load(r)
rows, page = [], 1
while True:
d = get(action='refdomains', q=TARGET, page=page, per=1000)
if not d['ok']:
sys.exit(f'bad response: {d}')
rows += d['rows']
if not d['rows'] or len(rows) >= d['total']:
break
page += 1
day = datetime.date.today().isoformat()
out = pathlib.Path('data') / day
out.mkdir(parents=True, exist_ok=True)
(out / 'refdomains.json').write_text(json.dumps(rows))
print(len(rows), 'rows, balance', d['_credits'])
.github/workflows/snapshot.yml
Cron у GitHub запускається в UTC і може запізнитися, коли виконавці зайняті. Не будуйте нічого, що залежить від хвилини.
Ключ знаходиться в секретах репозиторію, а YAML посилається на нього лише за іменем.
Файл робочого процесу знаходиться на основній гілці, оскільки саме цю копію читає розклад.
Ручний запуск з workflow_dispatch завершився і створив одну папку з датою.
Кількість рядків у файлі збігається з загальною кількістю, яку повернув API для цього виклику.
Job виходить з ненульовим кодом при поганій відповіді, тому помилка робить запуск червоним.
Помилки та виправлення
Що ламається першим у запланованому jobі зворотних посилань
Помилка
Що ви бачите
Виправлення
Ключ вставлено в YAML
Ключ видно в історії репозиторію
Змініть його, перемістіть у secrets.LZ_KEY
Дивна хвилина, наприклад 0 0
Запуск починається на 20 хвилин пізніше
Оберіть 17 6; початок години перевантажений
Репозиторій бездіяльний 60 днів
GitHub призупиняє розклад
Зафіксуйте щось або повторно увімкніть його на вкладці Actions
Цикл зупиняється лише на порожній сторінці
Безкінечний запуск на ліміті сторінок
Також зупиняйтеся, коли len(rows) >= total
Код виходу ігнорується
Зелена галочка, порожня папка
Використовуйте sys.exit при ok: false
Фіксація не вдається через відсутність змін
Червоний запуск дарма
Додайте || exit 0 після фіксації
Збереження ключа та репозиторія в безпеці
Ключ — єдина річ у цьому налаштуванні, яку варто захищати. Ставтеся до нього як до пароля, бо будь-хто, хто ним володіє, може витратити ваш баланс.
Заплановані jobі тихо витокають. Ключ, вставлений у лог, робочий процес, скопійований у публічний репозиторій, або колишній колега з доступом на запис — це звичайні шляхи. Перевіряйте кожен раз на квартал.
Змінюйте ключ, коли хтось йде
Коли колега з доступом йде, створіть новий ключ, оновіть секрет і зробіть старий неактивним. Потім підтвердьте, що наступний запуск пройшов. Один зелений запуск після зміни — найдешевший тест, який ви зробите за рік. Запишіть дату зміни в нотатках репозиторію, щоб наступна зміна не стала несподіванкою.
Обмежте, хто може редагувати робочий процес
Будь-хто, хто може редагувати YAML, може вивести секрет у лог. Тримайте доступ на запис коротким і переглядайте зміни у файлі робочого процесу так само, як код. Дві особи з доступом на запис зазвичай достатньо для малого репозиторію.
Ніколи не виводьте URL запиту
Виводьте баланс і кількість рядків, ніколи URL запиту. Ключ подорожує в рядку запиту, тому рядок налагодження, який виводить URL, записує ключ у лог. Якщо вам потрібно поділитися запитом у випуску, спочатку замініть ключ на заповнювач.
Що таке знімок і чим він не є
Те, що ви отримуєте, — це місячний знімок. Наш власний краулер оновлює індекс приблизно раз на місяць, тому два щопонеділка запуски в тому ж місяці часто повертають ті самі рядки. Це очікувано.
Щотижнева частота працює. Вона підтримує звичку живою, і ви ловите невдалий job протягом семи днів замість тридцяти. Якщо посилання, яке ви побудували вівторком, відсутнє в понеділок, зачекайте наступного оновлення, перш ніж панікувати.
Зберігайте першу папку як базову. Кожне наступне порівняння вказує на неї, і папка, названа за датою, набагато легше довіряти, ніж пам’яті про те, як виглядали числа минулої весни. Будьте послідовні.
Додавання якірних текстів та топ-сторінок
Одна дія на файл робить diff читабельним. Скопіюйте цикл у snap.py, змініть refdomains на anchors або toppages та запишіть у друге ім’я файлу. Кожен коштує 2 кредити.
Об’єднаний файл приховує, яка частина змінилася, а рецензент, який прокручує pull request, хоче бачити одну концепцію на diff. Тримайте їх окремо. Якщо ви відстежуєте три домени, перебирайте короткий список у робочому процесі за допомогою матриці, щоб кожна ціль отримала свій job і свою помилку.
Якірні тексти — це другий файл зі своїм лімітом у diff, тому переглядайте їх окремо від доменних посилань. Кожен запуск залишається дешевим і повторюваним, що є головною ідеєю.
Читання логу запуску
Відкрийте лог першого запуску. Зробіть це, перш ніж довіряти розкладу. Шукайте кількість рядків, баланс та рядок фіксації. Якщо будь-який з трьох відсутній, job зробив менше, ніж заявляє. Також ведіть короткий запис балансу під час кожного запуску, щоб місячні витрати були видимими без відкриття панелі керування.
Зелена галочка говорить мало. Вона лише означає, що процес завершився чисто. Вона не каже, що файл містить правильні рядки, тому скрипт друкує власне резюме. Зберігайте ці рядки резюме незмінними між версіями скрипта, щоб diff двох логів показував лише реальні зміни.
Як виглядає здоровий лог
Здоровий запуск друкує один рядок із кількістю рядків та балансом кредитів, потім фіксацію з сьогоднішньою датою. Будь-що, що ховає ці рядки під зайвим шумом, варте другого погляду.
Виявлення тихої помилки
Слідкуйте за порожньою папкою, фіксацією, яка ніколи не з’являється, або кількістю рядків, яка відрізняється від total. Кожен з цих пунктів вказує на цикл або парсер, а не на сам API. Збережіть перший здоровий лог як довідку для майбутніх порівнянь.
Деякі помилки залишають папку з меншою кількістю рядків, ніж зазвичай. Порівняйте кількість з попереднім тижнем, перш ніж святкувати зелений запуск, тому що тихе обрізання виглядає точно так само, як тихий місяць.
Безпечне додавання другої цілі
Другий домен — це запис матриці, а не копія всього робочого процесу. Надайте кожній цілі свій job, щоб одна помилка не приховувала іншу. Тримайте кожен job окремо. Переконайтеся, що шляхи містять назву домену, щоб два запуски ніколи не записували в той самий файл.
Почніть лише з другої цілі. Як тільки її папка виглядатиме правильно, додайте решту. Матриця, яка ламається посередині, читається гірше, ніж два простих запуски.
Називайте папки за ціллю
Покладіть домен у шлях, наприклад data/<domain>/YYYY-MM-DD, і папки залишаться окремими, навіть коли два jobи запускаються в одну хвилину. Старі папки можуть залишатися там, де вони є, оскільки дата в шляху робить їх легкими для пошуку.
Використовуйте те саме правило іменування для кожної цілі. Тоді скрипт зможе перелічити папки без спеціальних випадків.
Сплануйте бюджет кредитів спочатку
Помножте виклики на ціль на кількість цілей, потім перевірте загальну суму проти свого балансу перед першим запуском. Матриця множиться витрати так само швидко, як і jobи. Запишіть очікувану місячну суму в репозиторії поруч із робочим процесом. Коли баланс падає швидше, ніж це, цикл, ймовірно, перевибирає сторінки, і ви можете зупинити його до наступного запуску.
Питання, які виникають у перший тиждень
Чи можу я запустити це на приватному репозиторії безкоштовно?
Зазвичай так, у межах місячних хвилин, які надаються безкоштовним обліковим записам GitHub. Job знімка виконується менше хвилини, тому щотижневий розклад використовує крихітну частку. Перевірте поточний ліміт свого плану, оскільки GitHub іноді його змінює.
Де повинен жити ключ, якщо кілька людей роблять push?
В організації або репозиторії, ніколи у файлах. Будь-хто, хто може редагувати робочий процес, може вивести секрет у лог, тому обмежте, хто має доступ на запис, і змініть ключ, якщо хтось залишає проект.
Чому мій запланований запуск не спрацював?
Три звичайні причини. Файл робочого процесу не на основній гілці, репозиторій був бездіяльним 60 днів, і GitHub призупинив розклад, або рядок cron має помилку. Запустіть його один раз з workflow_dispatch, щоб виключити сам скрипт.
Яккими будуть великими папки даних?
Малими. Тисяча доменних посилань — це кілька сотень кілобайт JSON. Рік щотижневих файлів для одного домену залишається значно менше ста мегабайт, що добре для git, хоча ви можете стиснути старі папки, якщо це вас турбує.