MacroSIP
EnglishРусский中文

Релизы

Выпуск делается тегом на main. Тег запускает .github/workflows/release.yml, который собирает универсальный DMG, публикует GitHub Release, подписывает и обновляет ленту Sparkle и обновляет cask Homebrew. Первоисточник — раздел «Releasing» в docs/DEVELOPMENT.md, контрольный список в README.md, ADR 0010 и ADR 0013.

Номера версий (ADR 0010)

Версии имеют вид YY.MINOR.PATCH: YY — две цифры года линейки выпусков (26 для 2026), MINOR растёт с выпусками новых возможностей в течение года, PATCH — с исправлениями. Первый выпуск — 26.0.0; выпуск 2027 года начинается с 27.0.0, даже если иначе это было бы повышение MINOR. Теги содержат голый номер.

Предварительная версия помечается тегом N.N.N-rc.N, например 26.0.0-rc.1. release.yml и scripts/make-appcast.sh отвергают любой другой суффикс. Любой тег с - становится pre-release на GitHub и элементом appcast на канале beta.

Три числа должны совпадать:

Где Значение Источник
CFBundleShortVersionString версия, показанная в «О программе» и сверяемая с тегом заданием выпуска make-app.sh: git describe --tags --abbrev=0, до первого тега — Product.version
CFBundleVersion номер сборки, по которому Sparkle упорядочивает обновления make-app.sh: git rev-list --count HEAD
Product.version (Sources/MacroSIPCore/Product.swift) заголовок SIP User-Agent и панель «О программе» правится вручную перед тегом

Помечайте каждый выпуск тегом на более позднем коммите, чем предыдущий, — финальный выпуск после своего кандидата тоже. CFBundleVersion — это число коммитов, Sparkle различает элементы по нему, и generate_appcast молча заменил бы элемент кандидата, если бы финальный выпуск был помечен на том же коммите. make-appcast.sh --check отвергает номер сборки, который уже есть в ленте под другой версией, и workflow выполняет эту проверку до публикации чего-либо.

Контрольный список владельца (из README)

  1. Всё зелёное локально: swift build, scripts/test.sh, scripts/selftest-call.sh, scripts/make-dmg.sh; смонтируйте DMG и запустите приложение.

  2. Поднимите Product.version до выпускаемой версии, обновите docs/ROADMAP.md, закоммитьте.

  3. Поставьте тег и отправьте его:

    git tag 26.0.0-rc.1 && git push origin 26.0.0-rc.1    # candidate
    git tag 26.0.0 && git push origin 26.0.0              # final, on a later commit
  4. Следите за release.yml. Он собирает DMG, создаёт GitHub Release и добавляет его в ленту обновлений; тот же запуск обновляет cask Homebrew, если есть TAP_TOKEN.

  5. Проверьте: brew tap hsmm/tap && brew install --cask hsmm/tap/macrosip, запустите (правая кнопка, «Открыть» в первый раз); curl -s https://raw.githubusercontent.com/HSMM/MacroSIP/gh-pages/appcast.xml показывает новый элемент; установленная старая версия находит его через MacroSIP › «Проверить наличие обновлений…» (кандидат — только при включённом «Включать предварительные версии»).

Что делает release.yml

Workflow выполняется только в HSMM/MacroSIP, на тегах вида [0-9]*.[0-9]*.[0-9]*, с правами contents: write. Группы concurrency нет намеренно: GitHub держит один ожидающий запуск на группу и отменяет остальные, что потеряло бы тег; одновременные выпуски вместо этого выстраиваются в очередь на gh-pages.

Задание dmg на macos-15 (arm64):

  1. Проверка тега: N.N.N или N.N.N-rc.N, и секрет SPARKLE_PRIVATE_KEY должен существовать (без него установленные приложения никогда не увидели бы выпуск).
  2. Сборка и тесты как в CI: кэшированный build-pjsip.sh, swift build, scripts/test.sh, scripts/selftest-call.sh нативно и под Rosetta 2.
  3. Сборка DMG: scripts/make-dmg.sh release; lipo -info на бинарнике; задание падает, если CFBundleShortVersionString отличается от тега. DMG выгружается как артефакт для задания cask.
  4. Проверка appcast: scripts/publish-appcast.sh --check <tag> dist/MacroSIP.app забирает gh-pages и отвергает номер сборки, который есть в ленте под другой версией.
  5. Создание GitHub Release через gh release create: заголовок = тег, сгенерированные заметки с фиксированной первой строкой о том, что после обновления macOS снова запрашивает доступ к микрофону, --prerelease для тегов с -. Повторный запуск сохраняет релиз и заменяет DMG с --clobber.
  6. Обновление appcast: тело релиза превращается в HTML через Markdown API GitHub, и scripts/publish-appcast.sh <tag> <dmg> запускает scripts/make-appcast.sh, который вызывает generate_appcast из Sparkle (из .build/artifacts/sparkle/Sparkle/bin) с ключом EdDSA из SPARKLE_PRIVATE_KEY, URL загрузки ассета релиза, --embed-release-notes, --channel beta для кандидатов и сохранением всех прежних элементов (--maximum-versions 0, без дельт). Результат коммитится в ветку gh-pages (создаётся как orphan при первом запуске; push повторяется до пяти раз после свежего fetch, если другой выпуск успел раньше) и прикрепляется к релизу как appcast.xml.

Задание homebrew на ubuntu-latest, после dmg: пропускается, если секрет TAP_TOKEN пуст; иначе клонирует HSMM/homebrew-tap с этим токеном, скачивает артефакт DMG и запускает scripts/update-cask.sh <tag> <dmg> с заданным TAP_DIR, который заполняет packaging/homebrew/macrosip.rb (версия, SHA-256) в Casks/macrosip.rb, коммитит macrosip <tag> и отправляет. Разовая настройка: создайте публичный репозиторий HSMM/homebrew-tap, создайте fine-grained personal access token с правами Contents read и write только на этот репозиторий, сохраните его как секрет Actions TAP_TOKEN в HSMM/MacroSIP. Cask объявляет auto_updates true и depends_on macos: :sonoma; Homebrew принимает 26.0.0-rc.1 как версию.

Вручную, без CI: scripts/update-cask.sh <version> dist/MacroSIP-<version>.dmg пишет dist/macrosip.rb и печатает команды git (с TAP_DIR=<clone> коммитит и отправляет); SPARKLE_PRIVATE_KEY=... APPCAST_IN=<current appcast.xml> scripts/make-appcast.sh <version> <dmg> dist создаёт dist/appcast.xml для загрузки в релиз и в gh-pages. Без переменной окружения generate_appcast читает ключ из записи связки ключей входа, которую создал generate_keys --account macrosip.

Лента обновлений (ADR 0013)

Приложение через несколько секунд после запуска и затем раз в день читает https://raw.githubusercontent.com/HSMM/MacroSIP/gh-pages/appcast.xml (SUFeedURL, записывается make-app.sh; настройка GitHub Pages не нужна) и спрашивает перед установкой. Каждый элемент несёт подпись EdDSA (ed25519) DMG; открытый ключ — SPARKLE_PUBLIC_KEY в make-app.sh (SUPublicEDKey в Info.plist). Закрытый ключ существует ровно в двух местах: секрет Actions SPARKLE_PRIVATE_KEY и связка ключей входа владельца («MacroSIP Sparkle private key», плюс собственная запись Sparkle, account macrosip). Восстановить секрет: security find-generic-password -s "MacroSIP Sparkle private key" -w | gh secret set SPARKLE_PRIVATE_KEY --repo HSMM/MacroSIP. Потеря ключа означает, что установленные копии можно обновить только ручной загрузкой; утечка позволяет любому, кто к тому же может отдавать ленту, доставлять код пользователям.

Кандидаты находятся на канале beta и предлагаются, только если включено Настройки › Основные › Обновления › «Включать предварительные версии» (по умолчанию включено, когда запущенная сборка сама является кандидатом). У финальных выпусков канала нет, и они доходят до всех. Appcast хранит заметки такими, какими они были в момент выпуска; чтобы изменить их, отредактируйте релиз и перезапустите задание appcast. Выпуск, у которого шаг appcast упал, виден на GitHub, но не предлагается в приложении, пока задание не перезапустят.

Подпись

Сборки подписываются ad hoc с hardened runtime (codesign --options runtime), изнутри наружу: службы XPC Sparkle, Autoupdate, Updater.app, фреймворк, затем приложение с packaging/Entitlements.plist. Entitlements — com.apple.security.device.audio-input, com.apple.security.personal-information.addressbook и com.apple.security.cs.disable-library-validation; последний нужен потому, что у подписи ad hoc нет Team ID, и проверка библиотек отвергла бы встроенный фреймворк Sparkle. Подпись без hardened runtime была отвергнута: она также разрешила бы DYLD_INSERT_LIBRARIES в процесс с доступом к микрофону.

Последствия для пользователей (ADR 0007): первый запуск показывает диалог о неизвестном разработчике (правая кнопка, «Открыть», или xattr -dr com.apple.quarantine), обновления, установленные Sparkle, доверены через подпись EdDSA и запускаются без него, а macOS привязывает доступ к микрофону к конкретной сборке, поэтому спрашивает снова после каждого обновления. Подпись Developer ID и нотаризация запланированы позже (docs/ROADMAP.md); когда Developer ID появится, уберите entitlement на отключение проверки библиотек.

См. также Сборка, Тесты, Установка.

Другие страницы: Главная · Home (English)