1 Commits
Author SHA1 Message Date
maeneko 0842c4d4a5 Версия 0.5.2
CI / Сборка (Windows) (push) Canceled after 0s
CI / Линт и типы (push) Canceled after 0s
Иконки: пересобраны из мастера 1024×1024. В .icns было одно представление
512×512 — macOS домасштабировала остальные сама; теперь все десять, от 16 до
1024. В .ico добавлен размер 24 и своя картинка вместо заглушки Electron.

Релиз: differentialPackage и publishAutoUpdate выключены — .blockmap и
latest.yml нужны electron-updater, которого в приложении нет, и лежали в
ассетах мёртвым грузом. Вместо них CI кладёт рядом с установщиком .md5.

Версия в интерфейсе больше не константа: renderer спрашивает её у main через
app.getVersion(). Держать константу «в синхроне с package.json» вручную не
вышло ни разу — она осталась на 0.4.0, и релизы 0.5.0 и 0.5.1 показали
пользователю чужую версию.
2026-09-04 08:31:52 +03:00
14 changed files with 82 additions and 12 deletions
+15 -1
View File
@@ -49,7 +49,7 @@ jobs:
# electron-builder.yml просто копирует resources/zapret, а он у чистого чекаута пуст. # electron-builder.yml просто копирует resources/zapret, а он у чистого чекаута пуст.
- run: node scripts/fetch-zapret.mjs - run: node scripts/fetch-zapret.mjs
# electron-builder заливает .exe и .blockmap двумя параллельными publisher'ами: оба # electron-builder заливает артефакты несколькими параллельными publisher'ами: оба
# проверяют, есть ли релиз с этим тегом, оба видят «нет» и оба лезут его создавать — # проверяют, есть ли релиз с этим тегом, оба видят «нет» и оба лезут его создавать —
# второй падает с 422 already_exists и уносит с собой свой файл (на v0.5.0 так потерялся # второй падает с 422 already_exists и уносит с собой свой файл (на v0.5.0 так потерялся
# сам .exe). Создаём релиз заранее, тогда обоим остаётся только залить в готовый. # сам .exe). Создаём релиз заранее, тогда обоим остаётся только залить в готовый.
@@ -74,6 +74,20 @@ jobs:
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# differentialPackage: false и publishAutoUpdate: false в electron-builder.yml убирают
# .blockmap и latest.yml из ассетов (electron-updater в приложении нет — они были бы
# мёртвым грузом). В релизе должен остаться только .exe и контрольная сумма к нему.
- name: Контрольная сумма
if: startsWith(github.ref, 'refs/tags/v')
shell: bash
run: |
set -euo pipefail
exe="$(ls release/*.exe)"
md5sum "$exe" | awk '{ print $1 }' > "$exe.md5"
gh release upload "$GITHUB_REF_NAME" "$exe.md5" --clobber
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Релиз создан с пустым описанием, поэтому список изменений дописываем сами: # Релиз создан с пустым описанием, поэтому список изменений дописываем сами:
# коммиты между предыдущим тегом vX.Y.Z и текущим. # коммиты между предыдущим тегом vX.Y.Z и текущим.
- name: Описание релиза - name: Описание релиза
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 147 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 633 KiB

+9
View File
@@ -7,9 +7,14 @@ directories:
# releaseType: release — публиковать релиз сразу, без черновика (draft — поведение по # releaseType: release — публиковать релиз сразу, без черновика (draft — поведение по
# умолчанию у electron-builder). С черновиком параллельная загрузка нескольких файлов иногда # умолчанию у electron-builder). С черновиком параллельная загрузка нескольких файлов иногда
# создаёт два черновика с одним и тем же тегом вместо одного релиза. # создаёт два черновика с одним и тем же тегом вместо одного релиза.
#
# publishAutoUpdate: false — не заливать latest.yml. Это метаданные для electron-updater,
# которого в приложении нет (обновляется пользователь вручную, скачивая новый инсталлятор
# со страницы релиза) — файл был бы мёртвым грузом в ассетах.
publish: publish:
provider: github provider: github
releaseType: release releaseType: release
publishAutoUpdate: false
files: files:
- '!**/.vscode/*' - '!**/.vscode/*'
@@ -54,6 +59,10 @@ nsis:
# в src/main/zapret/service.win32.ts) — сама установка приложения per-user безопасна. # в src/main/zapret/service.win32.ts) — сама установка приложения per-user безопасна.
oneClick: true oneClick: true
perMachine: false perMachine: false
# Без electron-updater (см. publishAutoUpdate выше) differential-обновления не нужны —
# differentialPackage: false отключает генерацию .blockmap, который иначе всегда
# заливался бы в ассеты релиза рядом с инсталлятором.
differentialPackage: false
# Удалять %APPDATA%\rknboost (списки сайтов, метка первого запуска) вместе с приложением. # Удалять %APPDATA%\rknboost (списки сайтов, метка первого запуска) вместе с приложением.
# По умолчанию electron-builder этого НЕ делает, и данные оставались бы в системе после # По умолчанию electron-builder этого НЕ делает, и данные оставались бы в системе после
# удаления. Обновление приложения под это не попадает — в шаблоне деинсталлятора стоит # удаления. Обновление приложения под это не попадает — в шаблоне деинсталлятора стоит
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "rknboost", "name": "rknboost",
"version": "0.5.1", "version": "0.5.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "rknboost", "name": "rknboost",
"version": "0.5.1", "version": "0.5.2",
"hasInstallScript": true, "hasInstallScript": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "rknboost", "name": "rknboost",
"version": "0.5.1", "version": "0.5.2",
"description": "Кроссплатформенное десктопное приложение на Electron + React + TypeScript", "description": "Кроссплатформенное десктопное приложение на Electron + React + TypeScript",
"main": "./out/main/index.js", "main": "./out/main/index.js",
"author": "RKNboost", "author": "RKNboost",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 633 KiB

+5 -1
View File
@@ -1,4 +1,4 @@
import { BrowserWindow, ipcMain, nativeTheme, type IpcMainInvokeEvent } from 'electron' import { app, BrowserWindow, ipcMain, nativeTheme, type IpcMainInvokeEvent } from 'electron'
import type { IpcChannel, IpcHandlers, ThemeState } from '../shared/ipc-contract' import type { IpcChannel, IpcHandlers, ThemeState } from '../shared/ipc-contract'
import { uninstallApp } from './uninstall.win32' import { uninstallApp } from './uninstall.win32'
import { import {
@@ -46,6 +46,10 @@ function themeState(): ThemeState {
export function registerIpcHandlers(): void { export function registerIpcHandlers(): void {
handle('theme:get', () => themeState()) handle('theme:get', () => themeState())
// Ровно то же значение, что electron-builder кладёт в имя установщика и в
// «Установленные приложения» — оба берут его из "version" в package.json.
handle('app:version', () => app.getVersion())
// Кнопка «Удалить приложение» есть в интерфейсе только на Windows (SettingsView.tsx), // Кнопка «Удалить приложение» есть в интерфейсе только на Windows (SettingsView.tsx),
// но канал регистрируем всегда — так и должно быть по контракту, просто на других // но канал регистрируем всегда — так и должно быть по контракту, просто на других
// платформах он никогда не вызывается. // платформах он никогда не вызывается.
+3
View File
@@ -43,6 +43,9 @@ const api = {
* службы только на Windows (см. SettingsView.tsx). */ * службы только на Windows (см. SettingsView.tsx). */
platform: process.platform, platform: process.platform,
/** Версия запущенной сборки — из package.json через `app.getVersion()` в main. */
getAppVersion: (): Promise<string> => invoke('app:version'),
getTheme: (): Promise<ThemeState> => invoke('theme:get'), getTheme: (): Promise<ThemeState> => invoke('theme:get'),
onThemeChanged: (listener: (state: ThemeState) => void): (() => void) => onThemeChanged: (listener: (state: ThemeState) => void): (() => void) =>
subscribe('theme:changed', listener), subscribe('theme:changed', listener),
+9 -6
View File
@@ -1,6 +1,7 @@
import { useState } from 'react' import { useState } from 'react'
import type { ZapretListId, ZapretStatus } from '@shared/ipc-contract' import type { ZapretListId, ZapretStatus } from '@shared/ipc-contract'
import { GITHUB_URL } from '@shared/links' import { GITHUB_URL } from '@shared/links'
import { useAppVersion } from '../hooks/useAppVersion'
import { useZapretLists } from '../hooks/useZapretLists' import { useZapretLists } from '../hooks/useZapretLists'
import { LIST_TITLES } from '../lib/zapretListMeta' import { LIST_TITLES } from '../lib/zapretListMeta'
import ListEditor from './ListEditor' import ListEditor from './ListEditor'
@@ -12,9 +13,6 @@ import ListEditor from './ListEditor'
*/ */
const isWindows = window.api.platform === 'win32' const isWindows = window.api.platform === 'win32'
const APP_CHANNEL = 'beta' const APP_CHANNEL = 'beta'
// Держать в синхроне с "version" в package.json — оттуда её берёт electron-builder
// для имени установщика и для «Установленные приложения» в Windows.
const APP_VERSION = '0.4.0'
export default function SettingsView({ export default function SettingsView({
onBack, onBack,
@@ -27,6 +25,7 @@ export default function SettingsView({
}): React.JSX.Element { }): React.JSX.Element {
const { lists, autoHostlist, save, reset, clearAuto } = useZapretLists() const { lists, autoHostlist, save, reset, clearAuto } = useZapretLists()
const [openListId, setOpenListId] = useState<ZapretListId | null>(null) const [openListId, setOpenListId] = useState<ZapretListId | null>(null)
const appVersion = useAppVersion()
const openList = lists.find((list) => list.id === openListId) const openList = lists.find((list) => list.id === openListId)
if (openList) { if (openList) {
@@ -119,9 +118,13 @@ export default function SettingsView({
<p className="settings__support-hint">Поддержите звёздочкой</p> <p className="settings__support-hint">Поддержите звёздочкой</p>
</section> </section>
<p className="settings__version"> {/* Пока версия не пришла из main, строки нет вовсе: иначе на первом кадре
{APP_CHANNEL}-{APP_VERSION}-{window.api.platform} мелькнуло бы «beta--win32» с пустотой на месте номера. */}
</p> {appVersion && (
<p className="settings__version">
{APP_CHANNEL}-{appVersion}-{window.api.platform}
</p>
)}
{isWindows && ( {isWindows && (
<button <button
+1 -1
View File
@@ -52,7 +52,7 @@ export default function ZapretCard(): React.JSX.Element {
)} )}
<div className="settings__field"> <div className="settings__field">
<span className="settings__label">Стратегия</span> <span className="settings__label">Способ</span>
{/* На время переключения список заблокирован: смена стратегии на работающем {/* На время переключения список заблокирован: смена стратегии на работающем
обходе останавливает и перенастраивает службу, и второй выбор посреди этого обходе останавливает и перенастраивает службу, и второй выбор посреди этого
наложился бы на первый. */} наложился бы на первый. */}
+29
View File
@@ -0,0 +1,29 @@
import { useEffect, useState } from 'react'
/**
* Версия запущенной сборки — та же, что в имени установщика и в «Установленных
* приложениях»: main отдаёт её через `app.getVersion()`, то есть прямо из package.json.
*
* Раньше renderer держал версию своей константой с пометкой «держать в синхроне с
* package.json». Синхронизировать вручную забыли: константа осталась на 0.4.0, и релизы
* 0.5.0 и 0.5.1 показывали пользователю чужую версию. Спрашивать у main дороже на один
* IPC-вызов, зато разъехаться уже нечему.
*
* `null` — версия ещё не пришла; показывать её в этот момент нечего.
*/
export function useAppVersion(): string | null {
const [version, setVersion] = useState<string | null>(null)
useEffect(() => {
// Окно могло закрыться, пока шёл ответ — тогда setState уже некому применять.
let cancelled = false
void window.api.getAppVersion().then((value) => {
if (!cancelled) setVersion(value)
})
return () => {
cancelled = true
}
}, [])
return version
}
+8
View File
@@ -101,6 +101,14 @@ export interface ZapretDiagnosticResult extends ZapretDiagnosticTarget {
export interface IpcHandlers { export interface IpcHandlers {
'theme:get': () => ThemeState 'theme:get': () => ThemeState
/**
* Версия приложения — `app.getVersion()`, то есть `"version"` из package.json той сборки,
* которая сейчас запущена. Раньше renderer держал её отдельной константой «в синхроне
* с package.json» вручную: константа осталась на 0.4.0 и релизы 0.5.0 и 0.5.1 показали
* пользователю чужую версию. Спрашивать у main — единственный способ не разъехаться.
*/
'app:version': () => string
/** Windows: подтверждает диалогом ОС, запускает деинсталлятор и закрывает приложение. */ /** Windows: подтверждает диалогом ОС, запускает деинсталлятор и закрывает приложение. */
'app:uninstall': () => void 'app:uninstall': () => void