Автообновление
deno desktop доступен начиная с Deno v2.9.0. Если у вас более ранняя версия, обновите Deno, чтобы использовать его.
Deno.autoUpdate() опрашивает сервер релизов на наличие новых версий, загружает патчи с бинарными различиями, применяет их к dylib среды выполнения и подготавливает результат к следующему запуску. Если следующий запуск завершается с ошибкой, среда выполнения автоматически откатывается к предыдущей версии. Вместо полной загрузки бинарного файла обновления поставляются в виде небольших патчей bsdiff, а механизм отката встроен в загрузчик.
Применение подготовленных обновлений и откат после неудачного запуска сейчас работают только в macOS и Linux. В Windows патчи по-прежнему загружаются и подготавливаются, но загрузчик пока не заменяет их (загруженную DLL нельзя заменить на месте), поэтому обновления не вступают в силу. Считайте, что автообновление в Windows пока не поддерживается.
Предварительные требования
Необходимы две настройки:
versionв вашемdeno.json:{ "version": "1.4.0" }desktop.release.baseUrlв вашемdeno.json:{ "desktop": { "release": { "baseUrl": "https://releases.example.com/my-app" } } }
Обе настройки встраиваются в скомпилированный бинарный файл. Версия доступна во время выполнения через Deno.desktopVersion:
console.log(Deno.desktopVersion); // "1.4.0", or null if no version was set
Если Deno.desktopVersion равен null, Deno.autoUpdate() ничего не делает: среда выполнения однократно выводит предупреждение и возвращает управление. Так происходит и при deno run, поскольку у нескомпилированной программы нет встроенной версии. В этом случае Deno.autoUpdate() не вызывает исключение, поэтому вызов можно оставить в коде и во время разработки запускать ту же точку входа с deno run.
Вызов autoUpdate()
Deno.autoUpdate({
url: "https://releases.example.com/my-app",
interval: 60 * 60 * 1000, // hourly
onUpdateReady(version) {
console.log("Update", version, "ready; will apply on next launch");
},
onRollback(reason) {
console.warn("Previous launch failed; rolled back:", reason);
},
});
Или передайте строку URL для однократной проверки при запуске:
Deno.autoUpdate("https://releases.example.com/my-app");
| Параметр | Тип | Примечания |
|---|---|---|
url | string | Обязателен, если в deno.json не задан desktop.release.baseUrl. |
interval |
number (миллисекунды) | Интервал опроса. Если не указан, выполняется только одна проверка. |
onUpdateReady | (version: string) => void | Вызывается после применения патча и подготовки его к следующему запуску. |
onRollback | (reason: string) => void | Вызывается вскоре после этого вызова, если предыдущий запуск завершился с ошибкой. |
publicKey | string | Открытый ключ Ed25519 в Base64. Если он задан, манифест должен быть подписан (см. раздел Подписанные манифесты). |
Формат манифеста
Среда выполнения загружает <url>/latest.json и разбирает его как JSON. Каждая запись о патче представляет собой объект с именем файла патча и его хешем SHA-256:
{
"version": "1.5.0",
"patches": {
"1.4.0": { "name": "patch-1.4.0-to-1.5.0.bin", "sha256": "<64-hex-chars>" },
"1.4.1": { "name": "patch-1.4.1-to-1.5.0.bin", "sha256": "<64-hex-chars>" },
"1.3.9": { "name": "patch-1.3.9-to-1.5.0.bin", "sha256": "<64-hex-chars>" }
}
}
| Поле | Значение |
|---|---|
version | Последняя доступная версия. Сравнивается с Deno.desktopVersion. |
patches | Карта «версия-источник → { name, sha256 }». name — имя файла патча относительно URL манифеста; sha256 — SHA-256 байтов патча в виде шестнадцатеричной строки в нижнем регистре. |
Поле sha256 обязательно: среда выполнения отказывается применять патч, если хеш его байтов не совпадает с указанным значением. Поэтому подменённая или усечённая загрузка не может быть применена.
Старые версии, которые больше не нужно поддерживать, можно исключить из patches. Пользователи этих версий увидят сообщение «нет доступного патча для X» и останутся на текущей версии.
URL обновления должен использовать https://: среда выполнения отказывается опрашивать незашифрованный endpoint.
Подписанные манифесты
Чтобы обеспечить защиту от подмены помимо TLS, подпишите манифест ключом Ed25519 и передайте открытый ключ в Deno.autoUpdate(). Если задан publicKey, манифест должен быть обёрнут в конверт:
{
"signed": "{\"version\":\"1.5.0\",\"patches\":{ … }}",
"signature": "<base64 Ed25519 signature over the `signed` string>"
}
Среда выполнения проверяет signature по точным байтам строки signed, используя ваш publicKey, а затем разбирает signed как доверенный манифест. Чтобы не зависеть от реализации канонического JSON, настоящий манифест встраивается без изменений в строку signed, и доверять можно только её содержимому.
Deno.autoUpdate({
url: "https://releases.example.com/my-app",
publicKey: "<base64-encoded 32-byte Ed25519 public key>",
});
Процесс обновления
-
Загрузка манифеста.
GET <url>/latest.json. При ответе, код которого не относится к диапазону 2xx, проверка молча завершается и ожидает следующего интервала. -
Сравнение версий. Если
manifest.version === Deno.desktopVersion, ничего делать не нужно. -
Поиск патча.
manifest.patches[Deno.desktopVersion]→{ name, sha256 }. -
Загрузка патча.
GET <url>/<name>. Весь патч буферизуется в памяти; для типичных результатов bsdiff (несколько МБ) это не проблема. -
Проверка и применение. Среда выполнения проверяет загруженные байты по
sha256из манифеста и при несовпадении отказывается продолжать, затем применяет бинарную разницу к dylib среды выполнения приложения с помощью крейтаqbsdiff. Перед подготовкой исправленные байты проверяются на соответствие формату нативного бинарного файла. -
Подготовка результата. Записывает исправленную dylib как
<dylib>.updateрядом с исходной. Работающая dylib остаётся нетронутой. - Вызов
onUpdateReady.
Работающая dylib остаётся нетронутой до следующего запуска. При перезапуске приложения загрузчик устанавливает подготовленное обновление до выполнения любых других действий.
Откат после неудачного запуска
При запуске загрузчик обрабатывает подготовленные обновления и состояние отката до выполнения любых других действий, используя три файла рядом с dylib среды выполнения: <dylib>.update (подготовленный патч), <dylib>.backup (предыдущая dylib) и <dylib>.update-ok (файл-маркер успешного запуска).
- Если существует
<dylib>.update, загрузчик сохраняет копию текущей dylib в<dylib>.backup(оставляя исходный файл на месте), заменяет dylib подготовленным обновлением и запускает новую версию. После успешной загрузки среда выполнения вызывает внутреннюю операцию «подтвердить обновление», которая создаёт<dylib>.update-ok. - При последующем запуске, если существуют и
<dylib>.backup, и<dylib>.update-ok, предыдущее обновление считается успешно проверенным, и оба файла удаляются. - Если существует
<dylib>.backup, но не существует<dylib>.update-ok, значит, последнее обновление запустилось, но не было подтверждено: произошёл сбой во время запуска. Загрузчик восстанавливает<dylib>.backupповерх dylib, выполняя откат. При следующем вызовеDeno.autoUpdate()вызываетсяonRollbackс указанием причины.
Благодаря этому неисправные обновления исправляются автоматически. Пользователям не нужно знать о сбое: они просто остаются на той же версии, что и раньше.
Создание патчей
Патч — это bsdiff dylib среды выполнения приложения между двумя релизами. Это файл, который обновляет механизм обновления; он находится внутри собранного приложения. qbsdiff читает формат bsdiff 4.x, поэтому классический CLI bsdiff создаёт совместимые файлы:
bsdiff old-dylib new-dylib patch-1.4.0-to-1.5.0.bin
shasum -a 256 patch-1.4.0-to-1.5.0.bin # the sha256 for the manifest entry
Затем добавьте name и sha256 патча в latest.json и загрузите патч вместе с манифестом на сервер релизов.
Чтобы выпускать версии для нескольких архитектур (macOS arm64, x86_64; Windows x86_64; Linux arm64, x86_64), создавайте отдельные патчи для каждой архитектуры. Можно предоставлять подходящий манифест в зависимости от user-agent или включить все патчи под ключами для соответствующих архитектур и выбирать нужный на клиенте:
// release/macos-arm64/latest.json
{
"version": "1.5.0",
"patches": {
"1.4.0": { "name": "patch-1.4.0-to-1.5.0.bin", "sha256": "<64-hex-chars>" }
}
}
const arch = Deno.build.os + "-" + Deno.build.arch;
Deno.autoUpdate({
url: "https://releases.example.com/" + arch,
interval: 60 * 60 * 1000,
});
Рекомендации
-
Подписывайте манифесты. TLS и обязательный
sha256для каждого патча уже предотвращают применение подменённого патча, но любой, кто может публиковать файлы по вашему URL, всё ещё способен отправить вредоносный патч с корректным хешем. Для дополнительной защиты подпишите манифест ключом Ed25519 и настройтеpublicKey(см. раздел Подписанные манифесты). Храните закрытый ключ отдельно от хоста релизов. - Проверяйте патчи на реальной установке. Патч, который успешно применяется, но создаёт неработоспособный бинарный файл, приводит к откату, однако только после неудачного запуска, поэтому пользователи один раз увидят кратковременный сбой при запуске. Перед публикацией манифеста запускайте исправленный бинарный файл в CI.
- Выберите разумный интервал. Для большинства приложений подойдёт проверка раз в час. Опрос чаще, чем раз в несколько минут, неэффективен и для вас, и для пользователей.
-
Обрабатывайте
onRollback. Откат указывает на то, что недавний релиз оказался неисправным хотя бы на одном устройстве. Записывайте такие события в телеметрию, чтобы быстро замечать проблемы с релизами.
Последнее обновление:
© 2018–2025 the Deno authors
Licensed under the MIT License.
https://docs.deno.com/runtime/desktop/auto_update