Ситуация и цель
Mellow Hub живёт в браузере. 23 октября 2026 года основатель попросил вывести его цифры на рабочий стол Mac, виджетами, для всех пользователей. Наше правило для нативных приложений строгое: локальная сборка — не выпуск. Выпуск засчитывается, когда сборка подписана, нотаризована, опубликована, а установленная кем-то копия сама обновилась до следующей версии.
Поэтому цель была двойной: приложение в строке меню с виджетами WidgetKit, которое можно скачать без Mac App Store, и путь обновления, которому можно доверять с первого дня. Ниже — как оно прошло от 1.0.0 23 октября до 1.3.9 7 ноября 2026 года: 14 выпусков за 16 дней.
Что мы сделали, по шагам
1. Путь выпуска — раньше сертификата
23 октября, ещё без сертификата, мы собрали всё вокруг него:
- Один скрипт выпуска делает сборку Developer ID с hardened runtime, отправляет её в нотариальную службу Apple, прикрепляет билет, собирает образ диска и пишет подписанную ленту обновлений. Он отказывается ещё до сборки, если нет сертификата, данных для нотаризации, ключа обновлений или заметок к выпуску, и говорит, чего именно. Режим проверки ничего не собирает; режим ad hoc проходит весь путь локально с одноразовым ключом.
- Sparkle 2.10.0, закреплённый ровно на этой версии. Приложение работает в песочнице, поэтому служба Installer launcher едет внутри фреймворка, подписанная вместе с приложением, а в разрешениях названы две её Mach-службы (
-spksи-spki, как требует руководство Sparkle по песочнице). Лента обязана быть подписана, каждое обновление проверяется до распаковки. - Лента отвечает с первого дня. До первого выпуска она отдаёт пустую корректную ленту, никогда не 404, а ссылка на скачивание ведёт на страницу.
- Заметки к выпуску на 14 языках — их показывает окно обновления.
До всего настоящего мы отрепетировали обновление 1.0.0 → 1.0.1 на локальной ленте с одноразовым ключом.
2. Сертификат: нужен человек, а не API
Сертификат Developer ID Application может создать только владелец аккаунта Apple Developer: справка Apple называет нужную роль — Account Holder. App Store Connect API на попытку ответил 403. Основатель вошёл на портал разработчика и загрузил запрос на подпись, который наш скрипт сгенерировал в отдельную связку ключей.
Профиль подготовки не понадобился. На macOS разрешение App Groups не ограничено, если id группы начинается с team id, — так пишет инженер поддержки разработчиков Apple на форумах Apple, — а все остальные разрешения приложения тоже не ограничены. Тест падает, если ограниченное разрешение когда-нибудь появится.
3. 1.0.0 и что нашла настоящая установка
1.0.0 вышла вечером 23 октября: образ диска 5,1 МБ, приложение и образ приняты нотариальной службой и с прикреплённым билетом, Gatekeeper видит Notarized Developer ID. Первая настоящая установка за час нашла три проблемы:
- кнопка входа ждала ответа на запрос уведомлений (исправлено в 1.0.1);
- виджеты показывали заглушки, потому что расширение было зарегистрировано из нескольких папок сборки (снимите регистрацию с продуктов сборки:
pluginkit -rиlsregister -u); - копия, установленная из терминала, сохранила флаг карантина, запускалась транслоцированной, и Sparkle отказывался её обновлять (копируйте как Finder, через
ditto --noqtn).
В ту же ночь Sparkle предложил 1.0.1 установленной 1.0.0 на Mac основателя и поставил её, а его вход пережил обновление.
4. Темп, который последовал
Дальше приложение росло быстро: весь кабинет Hub в нативном окне (1.1.0, 24 октября) и Cerebro — разбор фото, который читает медиатеку «Фото» на Mac (1.2.0 3 ноября и большинство выпусков после; как разбирает фото его браузерная половина — отдельный кейс).
| Дата (2026) | Версии | Что изменилось |
|---|---|---|
| 23 окт | 1.0.0, 1.0.1 | Виджеты, вход, обновления; исправление входа |
| 24 окт | 1.1.0 | Окно Hub, двенадцать виджетов в четырёх оформлениях |
| 3 нояб | 1.2.0, 1.3.0, 1.3.1 | Cerebro, любая папка, первый выпуск со второго Mac |
| 4 нояб | 1.3.2 | Резкое фото при открытии |
| 5 нояб | с 1.3.3 по 1.3.8 | Исправление перезапуска, быстрее чтение, запросы на обновление, альбомы |
| 7 нояб | 1.3.9 | Каждый запуск открывает окно Hub |

Результаты
- 14 подписанных и нотаризованных выпусков с 23 октября по 7 ноября 2026 года, шесть — 5 ноября. У каждого номер сборки выше предыдущего, потому что Sparkle сравнивает именно сборки.
- Образ диска вырос с 5,1 МБ до 7 598 242 байт, а набор тестов XCTest — со 135 до 308.
- Обновления доказаны на установленных копиях, а не только в тестах: 1.0.0 → 1.0.1 на Mac основателя, затем 1.3.0 → 1.3.1, 1.3.1 → 1.3.2 и 1.3.2 → 1.3.3 на копии, поставленной из живой ссылки на втором Mac; каждый раз обновление скачалось само, подпись цела, нотаризация на месте.
- Выпуски переехали между машинами. 1.3.0 стал первым выпуском с Mac mini. Там сначала сгенерировали новый ключ обновлений, но его так и не использовали: всё подписанное им отвергла бы каждая установленная копия. Основатель перенёс исходный ключ.

Что не сработало и что мы поменяли
- «Restart to update» выглядел сломанным. 5 ноября основатель нажал его — и ничего не открылось. Репетиция показала: Sparkle перезапустил приложение через 0,3 с после выхода старой копии, но приложение в строке меню возвращается только значком. С 1.3.3 перезапускающаяся копия запоминает страницу своего окна, и новая копия открывает окно на ней.
- Обновления, которых никто не видел. До 1.3.5 Sparkle проверял ленту раз в 12 часов, скачивал молча и ставил при выходе, которого у приложения в строке меню почти не бывает. Вердикт основателя: обновления «либо происходят сами, либо не приходят никогда». С 1.3.6 приложение живёт в Dock, читает ленту каждый час и при выходе на передний план, а готовое обновление приходит уведомлением с Restart Now и окном Install and Restart / Later; Later спрашивает снова через четыре часа. Старым копиям нужен был ещё один выход, поэтому веб-кабинет теперь сам сообщает копии старше 1.3.6, узнанной по user agent, что есть новая версия.
- Изменения сайта — не обновления приложения. Окно показывает живой сайт, так что веб-правке выпуск не нужен. 4 ноября основатель ждал запроса на обновление, которого веб-деплой не приносит; теперь мы говорим, какого рода каждое изменение.
- Репетиции могут зависать. Тестовая копия, которая читает общий контейнер настоящего приложения под другой подписью, ждёт системного запроса macOS. Репетиционным копиям — свой bundle id и никакой App Group.
- Ещё не доказано: установка на новую учётную запись macOS из нашего же чек-листа на 8 ноября 2026 года не записана.

Как сделать это у себя
- Пусть владелец аккаунта создаст сертификат Developer ID Application; ключ API этого не умеет.
- Закрепите Sparkle на точной версии, требуйте подписанную ленту и проверку до распаковки, а для приложения в песочнице кладите службу Installer launcher с двумя разрешениями Mach-lookup.
- Сгенерируйте собственный ключ обновлений EdDSA и сразу сделайте резервную копию закрытой половины. Создать его заново нельзя. Sparkle описывает смену ключа через обновление, подписанное тем же Developer ID, но если вы, как мы, требуете подписанную ленту, для этого нужен запасной параметр, заданный до потери ключа.
- Напишите скрипт выпуска, который отказывает заранее, и никогда не правьте подписанную ленту: выпускайте сборку с большим номером.
- Отдавайте пустую корректную ленту ещё до первого выпуска.
- Репетируйте обновление локально с одноразовым ключом и отдельным bundle id, а потом один раз докажите его на копии, поставленной из настоящей ссылки.
- В приложении из строки меню открывайте окно после перезапуска, читайте ленту чаще двух раз в сутки и спрашивайте перед перезапуском.
Само приложение — на странице Mellow Hub для Mac, все заметки к выпускам собраны на одной странице, а приложения для iPhone и Mac — рядом на странице приложений.



