feat(release): add dev prereleases and update notices
Publish bleeding-edge builds from dev to GitHub prereleases and npm dist-tag 'dev'. Dev builds poll GitHub prereleases and surface update availability via /api/meta for UI notifications.
This commit is contained in:
@@ -30,6 +30,10 @@ export const appMessages = {
|
||||
"releases.uiUpdated.title": "UI updated",
|
||||
"releases.uiUpdated.message": "UI is now updated to {version}.",
|
||||
|
||||
"releases.devUpdateAvailable.title": "Dev build available",
|
||||
"releases.devUpdateAvailable.message": "A new dev build is available: {version}.",
|
||||
"releases.devUpdateAvailable.action": "View release",
|
||||
|
||||
"theme.mode.system": "System",
|
||||
"theme.mode.light": "Light",
|
||||
"theme.mode.dark": "Dark",
|
||||
|
||||
@@ -26,4 +26,11 @@ export const appMessages = {
|
||||
"releases.upgradeRequired.message.withVersion": "Actualiza a CodeNomad {version} para usar la UI más reciente.",
|
||||
"releases.upgradeRequired.message.noVersion": "Actualiza CodeNomad para usar la UI más reciente.",
|
||||
"releases.upgradeRequired.action.getUpdate": "Obtener actualización",
|
||||
|
||||
"releases.uiUpdated.title": "UI actualizada",
|
||||
"releases.uiUpdated.message": "La UI ahora está actualizada a {version}.",
|
||||
|
||||
"releases.devUpdateAvailable.title": "Compilación dev disponible",
|
||||
"releases.devUpdateAvailable.message": "Hay una nueva compilación dev disponible: {version}.",
|
||||
"releases.devUpdateAvailable.action": "Ver release",
|
||||
} as const
|
||||
|
||||
@@ -26,4 +26,11 @@ export const appMessages = {
|
||||
"releases.upgradeRequired.message.withVersion": "Mettez à jour vers CodeNomad {version} pour utiliser la dernière UI.",
|
||||
"releases.upgradeRequired.message.noVersion": "Mettez à jour CodeNomad pour utiliser la dernière UI.",
|
||||
"releases.upgradeRequired.action.getUpdate": "Obtenir la mise à jour",
|
||||
|
||||
"releases.uiUpdated.title": "UI mise à jour",
|
||||
"releases.uiUpdated.message": "L'UI est maintenant mise à jour vers {version}.",
|
||||
|
||||
"releases.devUpdateAvailable.title": "Build dev disponible",
|
||||
"releases.devUpdateAvailable.message": "Un nouveau build dev est disponible : {version}.",
|
||||
"releases.devUpdateAvailable.action": "Voir la release",
|
||||
} as const
|
||||
|
||||
@@ -26,4 +26,11 @@ export const appMessages = {
|
||||
"releases.upgradeRequired.message.withVersion": "最新の UI を使うには CodeNomad {version} に更新してください。",
|
||||
"releases.upgradeRequired.message.noVersion": "最新の UI を使うには CodeNomad を更新してください。",
|
||||
"releases.upgradeRequired.action.getUpdate": "更新を取得",
|
||||
|
||||
"releases.uiUpdated.title": "UI を更新しました",
|
||||
"releases.uiUpdated.message": "UI が {version} に更新されました。",
|
||||
|
||||
"releases.devUpdateAvailable.title": "開発版が利用可能",
|
||||
"releases.devUpdateAvailable.message": "新しい開発版が利用可能です: {version}。",
|
||||
"releases.devUpdateAvailable.action": "リリースを見る",
|
||||
} as const
|
||||
|
||||
@@ -26,4 +26,11 @@ export const appMessages = {
|
||||
"releases.upgradeRequired.message.withVersion": "Обновите CodeNomad до версии {version}, чтобы использовать последний UI.",
|
||||
"releases.upgradeRequired.message.noVersion": "Обновите CodeNomad, чтобы использовать последний UI.",
|
||||
"releases.upgradeRequired.action.getUpdate": "Получить обновление",
|
||||
|
||||
"releases.uiUpdated.title": "UI обновлён",
|
||||
"releases.uiUpdated.message": "UI теперь обновлён до {version}.",
|
||||
|
||||
"releases.devUpdateAvailable.title": "Доступна dev-сборка",
|
||||
"releases.devUpdateAvailable.message": "Доступна новая dev-сборка: {version}.",
|
||||
"releases.devUpdateAvailable.action": "Открыть релиз",
|
||||
} as const
|
||||
|
||||
@@ -26,4 +26,11 @@ export const appMessages = {
|
||||
"releases.upgradeRequired.message.withVersion": "更新到 CodeNomad {version} 以使用最新的 UI。",
|
||||
"releases.upgradeRequired.message.noVersion": "更新 CodeNomad 以使用最新的 UI。",
|
||||
"releases.upgradeRequired.action.getUpdate": "获取更新",
|
||||
|
||||
"releases.uiUpdated.title": "UI 已更新",
|
||||
"releases.uiUpdated.message": "UI 已更新到 {version}。",
|
||||
|
||||
"releases.devUpdateAvailable.title": "可用的开发版",
|
||||
"releases.devUpdateAvailable.message": "有新的开发版可用:{version}。",
|
||||
"releases.devUpdateAvailable.action": "查看发布",
|
||||
} as const
|
||||
|
||||
@@ -11,12 +11,15 @@ const log = getLogger("actions")
|
||||
const [supportInfo, setSupportInfo] = createSignal<SupportMeta | null>(null)
|
||||
|
||||
const UI_VERSION_STORAGE_KEY = "codenomad:lastSeenUiVersion"
|
||||
const DEV_RELEASE_STORAGE_KEY = "codenomad:lastSeenDevRelease"
|
||||
const META_REFRESH_INTERVAL_MS = 10 * 60 * 1000
|
||||
|
||||
let initialized = false
|
||||
let visibilityEffectInitialized = false
|
||||
let activeToast: ToastHandle | null = null
|
||||
let activeToastKey: string | null = null
|
||||
let uiUpdateToasted = false
|
||||
let metaRefreshInterval: ReturnType<typeof setInterval> | null = null
|
||||
|
||||
function dismissActiveToast() {
|
||||
if (activeToast) {
|
||||
@@ -80,6 +83,8 @@ async function refreshFromMeta() {
|
||||
const meta = await getServerMeta(true)
|
||||
setSupportInfo(meta.support ?? null)
|
||||
maybeNotifyUiUpdated(meta)
|
||||
maybeNotifyDevReleaseAvailable(meta)
|
||||
ensureMetaRefresh(meta)
|
||||
} catch (error) {
|
||||
log.warn("Unable to load server metadata for support info", error)
|
||||
}
|
||||
@@ -115,6 +120,46 @@ function maybeNotifyUiUpdated(meta: ServerMeta) {
|
||||
})
|
||||
}
|
||||
|
||||
function maybeNotifyDevReleaseAvailable(meta: ServerMeta) {
|
||||
const update = meta.update
|
||||
if (!update || !update.version || !update.url) return
|
||||
|
||||
const lastSeen = safeReadLocalStorage(DEV_RELEASE_STORAGE_KEY)
|
||||
if (lastSeen === update.version) {
|
||||
return
|
||||
}
|
||||
|
||||
safeWriteLocalStorage(DEV_RELEASE_STORAGE_KEY, update.version)
|
||||
|
||||
showToastNotification({
|
||||
title: tGlobal("releases.devUpdateAvailable.title"),
|
||||
message: tGlobal("releases.devUpdateAvailable.message", { version: update.version }),
|
||||
variant: "info",
|
||||
duration: 12000,
|
||||
position: "bottom-right",
|
||||
action: {
|
||||
label: tGlobal("releases.devUpdateAvailable.action"),
|
||||
href: update.url,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function ensureMetaRefresh(meta: ServerMeta) {
|
||||
if (metaRefreshInterval) return
|
||||
|
||||
const version = meta.serverVersion?.trim() ?? ""
|
||||
const looksLikeDev = version.includes("-dev.")
|
||||
const hasDevUpdateChannel = Boolean(meta.update)
|
||||
|
||||
if (!looksLikeDev && !hasDevUpdateChannel) {
|
||||
return
|
||||
}
|
||||
|
||||
metaRefreshInterval = setInterval(() => {
|
||||
void refreshFromMeta()
|
||||
}, META_REFRESH_INTERVAL_MS)
|
||||
}
|
||||
|
||||
function safeReadLocalStorage(key: string): string | null {
|
||||
try {
|
||||
if (typeof window === "undefined" || !window.localStorage) return null
|
||||
|
||||
Reference in New Issue
Block a user