ci: tauri build macos (#5184)
* ci: tauri build macos * chore: comment out electron builder .zip.sig file to s3 * chore: enable auto updater tauri * chore: comment out s3 upload mac.zip.sig * chore: handle remind me later state * chore: add dll file windows * chore: add debug step verbose * ci: add msvcp140_codecvt_ids.dll bundle windows * chore: update download progress * chore: update app updater UI * chore: remove log * chore: reload app after download app * chore: reset remindmelater
This commit is contained in:
parent
7dc51c5e0f
commit
9c825956e8
@ -243,7 +243,7 @@ jobs:
|
||||
aws s3 cp ./macos/latest-mac.yml s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-${{ inputs.channel }}/latest-mac.yml
|
||||
aws s3 cp ./macos/beta-mac.yml s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-${{ inputs.channel }}/beta-mac.yml
|
||||
aws s3 cp ./macos/jan-${{ inputs.channel }}-mac-universal-${{ inputs.new_version }}.zip s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-${{ inputs.channel }}/jan-${{ inputs.channel }}-mac-universal-${{ inputs.new_version }}.zip
|
||||
aws s3 cp ./macos/jan-${{ inputs.channel }}-mac-universal-${{ inputs.new_version }}.zip.sig s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-${{ inputs.channel }}/jan-${{ inputs.channel }}-mac-universal-${{ inputs.new_version }}.zip.sig
|
||||
# aws s3 cp ./macos/jan-${{ inputs.channel }}-mac-universal-${{ inputs.new_version }}.zip.sig s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-${{ inputs.channel }}/jan-${{ inputs.channel }}-mac-universal-${{ inputs.new_version }}.zip.sig
|
||||
|
||||
# Upload for tauri updater
|
||||
aws s3 cp ./dmg/Jan-${{ inputs.channel }}_${{ inputs.new_version }}_universal.dmg s3://${{ secrets.DELTA_AWS_S3_BUCKET_NAME }}/temp-${{ inputs.channel }}/Jan-${{ inputs.channel }}_${{ inputs.new_version }}_universal.dmg
|
||||
|
||||
@ -171,6 +171,9 @@ jobs:
|
||||
- name: Build app
|
||||
shell: bash
|
||||
run: |
|
||||
curl -L -o ./src-tauri/binaries/vcomp140.dll https://catalog.jan.ai/vcomp140.dll
|
||||
curl -L -o ./src-tauri/binaries/msvcp140_codecvt_ids.dll https://catalog.jan.ai/msvcp140_codecvt_ids.dll
|
||||
ls ./src-tauri/binaries
|
||||
make build-tauri
|
||||
env:
|
||||
AZURE_KEY_VAULT_URI: ${{ secrets.AZURE_KEY_VAULT_URI }}
|
||||
|
||||
@ -640,7 +640,8 @@ Section Install
|
||||
File /a "/oname=cublasLt64_12.dll" "D:\a\jan\jan\src-tauri\binaries\cublasLt64_12.dll"
|
||||
File /a "/oname=cudart64_12.dll" "D:\a\jan\jan\src-tauri\binaries\cudart64_12.dll"
|
||||
File /a "/oname=msvcp140.dll" "D:\a\jan\jan\src-tauri\binaries\msvcp140.dll"
|
||||
; File /a "/oname=vcomp140.dll" "D:\a\jan\jan\src-tauri\binaries\vcomp140.dll"
|
||||
File /a "/oname=vcomp140.dll" "D:\a\jan\jan\src-tauri\binaries\vcomp140.dll"
|
||||
File /a "/oname=msvcp140_codecvt_ids.dll" "D:\a\jan\jan\src-tauri\binaries\msvcp140_codecvt_ids.dll"
|
||||
File /a "/oname=vcruntime140_1.dll" "D:\a\jan\jan\src-tauri\binaries\vcruntime140_1.dll"
|
||||
File /a "/oname=vcruntime140.dll" "D:\a\jan\jan\src-tauri\binaries\vcruntime140.dll"
|
||||
File /a "/oname=vulkan-1.dll" "D:\a\jan\jan\src-tauri\binaries\vulkan-1.dll"
|
||||
|
||||
@ -74,7 +74,7 @@
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": ["nsis", "app", "dmg", "deb", "appimage"],
|
||||
"createUpdaterArtifacts": false,
|
||||
"createUpdaterArtifacts": true,
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
|
||||
@ -10,7 +10,7 @@ import { useModelProvider } from '@/hooks/useModelProvider'
|
||||
import { useAppUpdater } from '@/hooks/useAppUpdater'
|
||||
import { abortDownload } from '@/services/models'
|
||||
import { getProviders } from '@/services/providers'
|
||||
import { DownloadEvent, DownloadState, events } from '@janhq/core'
|
||||
import { DownloadEvent, DownloadState, events, AppEvent } from '@janhq/core'
|
||||
import { IconDownload, IconX } from '@tabler/icons-react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
@ -21,10 +21,67 @@ export function DownloadManagement() {
|
||||
const [isPopoverOpen, setIsPopoverOpen] = useState(false)
|
||||
const { downloads, updateProgress, removeDownload } = useDownloadStore()
|
||||
const { updateState } = useAppUpdater()
|
||||
const downloadCount = useMemo(
|
||||
() => Object.keys(downloads).length,
|
||||
[downloads]
|
||||
|
||||
const [appUpdateState, setAppUpdateState] = useState({
|
||||
isDownloading: false,
|
||||
downloadProgress: 0,
|
||||
downloadedBytes: 0,
|
||||
totalBytes: 0,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setAppUpdateState({
|
||||
isDownloading: updateState.isDownloading,
|
||||
downloadProgress: updateState.downloadProgress,
|
||||
downloadedBytes: updateState.downloadedBytes,
|
||||
totalBytes: updateState.totalBytes,
|
||||
})
|
||||
}, [updateState])
|
||||
|
||||
const onAppUpdateDownloadUpdate = useCallback(
|
||||
(data: {
|
||||
progress?: number
|
||||
downloadedBytes?: number
|
||||
totalBytes?: number
|
||||
}) => {
|
||||
setAppUpdateState((prev) => ({
|
||||
...prev,
|
||||
isDownloading: true,
|
||||
downloadProgress: data.progress || 0,
|
||||
downloadedBytes: data.downloadedBytes || 0,
|
||||
totalBytes: data.totalBytes || 0,
|
||||
}))
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
const onAppUpdateDownloadSuccess = useCallback(() => {
|
||||
setAppUpdateState((prev) => ({
|
||||
...prev,
|
||||
isDownloading: false,
|
||||
downloadProgress: 1,
|
||||
}))
|
||||
toast.success('App Update Downloaded', {
|
||||
description: 'The app update has been downloaded successfully.',
|
||||
})
|
||||
}, [])
|
||||
|
||||
const onAppUpdateDownloadError = useCallback(() => {
|
||||
setAppUpdateState((prev) => ({
|
||||
...prev,
|
||||
isDownloading: false,
|
||||
}))
|
||||
toast.error('App Update Download Failed', {
|
||||
description: 'Failed to download the app update. Please try again.',
|
||||
})
|
||||
}, [])
|
||||
|
||||
const downloadCount = useMemo(() => {
|
||||
const modelDownloads = Object.keys(downloads).length
|
||||
const appUpdateDownload = appUpdateState.isDownloading ? 1 : 0
|
||||
const total = modelDownloads + appUpdateDownload
|
||||
return total
|
||||
}, [downloads, appUpdateState.isDownloading])
|
||||
const downloadProcesses = useMemo(
|
||||
() =>
|
||||
Object.values(downloads).map((download) => ({
|
||||
@ -38,14 +95,31 @@ export function DownloadManagement() {
|
||||
)
|
||||
|
||||
const overallProgress = useMemo(() => {
|
||||
const total = downloadProcesses.reduce((acc, download) => {
|
||||
const modelTotal = downloadProcesses.reduce((acc, download) => {
|
||||
return acc + download.total
|
||||
}, 0)
|
||||
const current = downloadProcesses.reduce((acc, download) => {
|
||||
const modelCurrent = downloadProcesses.reduce((acc, download) => {
|
||||
return acc + download.current
|
||||
}, 0)
|
||||
|
||||
// Include app update progress in overall calculation
|
||||
const appUpdateTotal = appUpdateState.isDownloading
|
||||
? appUpdateState.totalBytes
|
||||
: 0
|
||||
const appUpdateCurrent = appUpdateState.isDownloading
|
||||
? appUpdateState.downloadedBytes
|
||||
: 0
|
||||
|
||||
const total = modelTotal + appUpdateTotal
|
||||
const current = modelCurrent + appUpdateCurrent
|
||||
|
||||
return total > 0 ? current / total : 0
|
||||
}, [downloadProcesses])
|
||||
}, [
|
||||
downloadProcesses,
|
||||
appUpdateState.isDownloading,
|
||||
appUpdateState.totalBytes,
|
||||
appUpdateState.downloadedBytes,
|
||||
])
|
||||
|
||||
const onFileDownloadUpdate = useCallback(
|
||||
async (state: DownloadState) => {
|
||||
@ -97,18 +171,34 @@ export function DownloadManagement() {
|
||||
events.on(DownloadEvent.onFileDownloadSuccess, onFileDownloadSuccess)
|
||||
events.on(DownloadEvent.onFileDownloadStopped, onFileDownloadStopped)
|
||||
|
||||
// Register app update event listeners
|
||||
events.on(AppEvent.onAppUpdateDownloadUpdate, onAppUpdateDownloadUpdate)
|
||||
events.on(AppEvent.onAppUpdateDownloadSuccess, onAppUpdateDownloadSuccess)
|
||||
events.on(AppEvent.onAppUpdateDownloadError, onAppUpdateDownloadError)
|
||||
|
||||
return () => {
|
||||
console.debug('DownloadListener: unregistering event listeners...')
|
||||
events.off(DownloadEvent.onFileDownloadUpdate, onFileDownloadUpdate)
|
||||
events.off(DownloadEvent.onFileDownloadError, onFileDownloadError)
|
||||
events.off(DownloadEvent.onFileDownloadSuccess, onFileDownloadSuccess)
|
||||
events.off(DownloadEvent.onFileDownloadStopped, onFileDownloadStopped)
|
||||
|
||||
// Unregister app update event listeners
|
||||
events.off(AppEvent.onAppUpdateDownloadUpdate, onAppUpdateDownloadUpdate)
|
||||
events.off(
|
||||
AppEvent.onAppUpdateDownloadSuccess,
|
||||
onAppUpdateDownloadSuccess
|
||||
)
|
||||
events.off(AppEvent.onAppUpdateDownloadError, onAppUpdateDownloadError)
|
||||
}
|
||||
}, [
|
||||
onFileDownloadUpdate,
|
||||
onFileDownloadError,
|
||||
onFileDownloadSuccess,
|
||||
onFileDownloadStopped,
|
||||
onAppUpdateDownloadUpdate,
|
||||
onAppUpdateDownloadSuccess,
|
||||
onAppUpdateDownloadError,
|
||||
])
|
||||
|
||||
function renderGB(bytes: number): string {
|
||||
@ -118,8 +208,7 @@ export function DownloadManagement() {
|
||||
|
||||
return (
|
||||
<>
|
||||
{(downloadCount > 0 ||
|
||||
(updateState.isDownloading && updateState.downloadProgress > 0)) && (
|
||||
{downloadCount > 0 && (
|
||||
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
{isLeftPanelOpen ? (
|
||||
@ -162,24 +251,23 @@ export function DownloadManagement() {
|
||||
<p className="text-xs text-main-view-fg/70">Downloading</p>
|
||||
</div>
|
||||
<div className="p-2 max-h-[300px] overflow-y-auto space-y-2">
|
||||
{!updateState.isDownloading &&
|
||||
updateState.downloadProgress > 0 && (
|
||||
<div className="bg-main-view-fg/4 rounded-md p-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="truncate text-main-view-fg/80">
|
||||
App Update
|
||||
</p>
|
||||
</div>
|
||||
<Progress
|
||||
value={updateState.downloadProgress * 100}
|
||||
className="my-2"
|
||||
/>
|
||||
<p className="text-main-view-fg/60 text-xs">
|
||||
{`${renderGB(updateState.downloadedBytes)} / ${renderGB(updateState.totalBytes)}`}{' '}
|
||||
GB ({Math.round(updateState.downloadProgress * 100)}%)
|
||||
{appUpdateState.isDownloading && (
|
||||
<div className="bg-main-view-fg/4 rounded-md p-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="truncate text-main-view-fg/80">
|
||||
App Update
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<Progress
|
||||
value={appUpdateState.downloadProgress * 100}
|
||||
className="my-2"
|
||||
/>
|
||||
<p className="text-main-view-fg/60 text-xs">
|
||||
{`${renderGB(appUpdateState.downloadedBytes)} / ${renderGB(appUpdateState.totalBytes)}`}{' '}
|
||||
GB ({Math.round(appUpdateState.downloadProgress * 100)}%)
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{downloadProcesses.map((download) => (
|
||||
<div className="bg-main-view-fg/4 rounded-md p-2">
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
@ -9,10 +9,13 @@ import { RenderMarkdown } from '../RenderMarkdown'
|
||||
import { isDev } from '@/lib/utils'
|
||||
|
||||
const DialogAppUpdater = () => {
|
||||
const { updateState, downloadAndInstallUpdate, checkForUpdate } =
|
||||
useAppUpdater()
|
||||
const {
|
||||
updateState,
|
||||
downloadAndInstallUpdate,
|
||||
checkForUpdate,
|
||||
setRemindMeLater,
|
||||
} = useAppUpdater()
|
||||
const [showReleaseNotes, setShowReleaseNotes] = useState(false)
|
||||
const [remindMeLater, setRemindMeLater] = useState(false)
|
||||
|
||||
const handleUpdate = () => {
|
||||
downloadAndInstallUpdate()
|
||||
@ -35,7 +38,7 @@ const DialogAppUpdater = () => {
|
||||
checkForUpdate()
|
||||
}, [checkForUpdate])
|
||||
|
||||
if (remindMeLater) return null
|
||||
if (updateState.remindMeLater) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { isDev } from '@/lib/utils'
|
||||
import { check, Update } from '@tauri-apps/plugin-updater'
|
||||
import { useState, useCallback } from 'react'
|
||||
import { events, AppEvent } from '@janhq/core'
|
||||
|
||||
export interface UpdateState {
|
||||
isUpdateAvailable: boolean
|
||||
@ -9,6 +10,7 @@ export interface UpdateState {
|
||||
downloadProgress: number
|
||||
downloadedBytes: number
|
||||
totalBytes: number
|
||||
remindMeLater: boolean
|
||||
}
|
||||
|
||||
export const useAppUpdater = () => {
|
||||
@ -19,10 +21,19 @@ export const useAppUpdater = () => {
|
||||
downloadProgress: 0,
|
||||
downloadedBytes: 0,
|
||||
totalBytes: 0,
|
||||
remindMeLater: false,
|
||||
})
|
||||
|
||||
const checkForUpdate = useCallback(async () => {
|
||||
const checkForUpdate = useCallback(async (resetRemindMeLater = false) => {
|
||||
try {
|
||||
// Reset remindMeLater if requested (e.g., when called from settings)
|
||||
if (resetRemindMeLater) {
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
remindMeLater: false,
|
||||
}))
|
||||
}
|
||||
|
||||
if (!isDev()) {
|
||||
// Production mode - use actual Tauri updater
|
||||
const update = await check()
|
||||
@ -45,6 +56,13 @@ export const useAppUpdater = () => {
|
||||
|
||||
return null
|
||||
}
|
||||
} else {
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
isUpdateAvailable: false,
|
||||
updateInfo: null,
|
||||
}))
|
||||
return null
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking for updates:', error)
|
||||
@ -58,6 +76,13 @@ export const useAppUpdater = () => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
const setRemindMeLater = useCallback((remind: boolean) => {
|
||||
setUpdateState((prev) => ({
|
||||
...prev,
|
||||
remindMeLater: remind,
|
||||
}))
|
||||
}, [])
|
||||
|
||||
const downloadAndInstallUpdate = useCallback(async () => {
|
||||
if (!updateState.updateInfo) return
|
||||
|
||||
@ -79,6 +104,13 @@ export const useAppUpdater = () => {
|
||||
totalBytes: contentLength,
|
||||
}))
|
||||
console.log(`Started downloading ${contentLength} bytes`)
|
||||
|
||||
// Emit app update download started event
|
||||
events.emit(AppEvent.onAppUpdateDownloadUpdate, {
|
||||
progress: 0,
|
||||
downloadedBytes: 0,
|
||||
totalBytes: contentLength,
|
||||
})
|
||||
break
|
||||
case 'Progress': {
|
||||
downloaded += event.data.chunkLength
|
||||
@ -89,6 +121,13 @@ export const useAppUpdater = () => {
|
||||
downloadedBytes: downloaded,
|
||||
}))
|
||||
console.log(`Downloaded ${downloaded} from ${contentLength}`)
|
||||
|
||||
// Emit app update download progress event
|
||||
events.emit(AppEvent.onAppUpdateDownloadUpdate, {
|
||||
progress: progress,
|
||||
downloadedBytes: downloaded,
|
||||
totalBytes: contentLength,
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'Finished':
|
||||
@ -98,10 +137,15 @@ export const useAppUpdater = () => {
|
||||
isDownloading: false,
|
||||
downloadProgress: 1,
|
||||
}))
|
||||
|
||||
// Emit app update download success event
|
||||
events.emit(AppEvent.onAppUpdateDownloadSuccess, {})
|
||||
break
|
||||
}
|
||||
})
|
||||
|
||||
await window.core?.api?.relaunch()
|
||||
|
||||
console.log('Update installed')
|
||||
} catch (error) {
|
||||
console.error('Error downloading update:', error)
|
||||
@ -109,6 +153,11 @@ export const useAppUpdater = () => {
|
||||
...prev,
|
||||
isDownloading: false,
|
||||
}))
|
||||
|
||||
// Emit app update download error event
|
||||
events.emit(AppEvent.onAppUpdateDownloadError, {
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
})
|
||||
}
|
||||
}, [updateState.updateInfo])
|
||||
|
||||
@ -116,5 +165,6 @@ export const useAppUpdater = () => {
|
||||
updateState,
|
||||
checkForUpdate,
|
||||
downloadAndInstallUpdate,
|
||||
setRemindMeLater,
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +63,7 @@ const openFileTitle = (): string => {
|
||||
function General() {
|
||||
const { t } = useTranslation()
|
||||
const { spellCheckChatInput, setSpellCheckChatInput } = useGeneralSetting()
|
||||
const { checkForUpdate } = useAppUpdater()
|
||||
const { checkForUpdate, setRemindMeLater } = useAppUpdater()
|
||||
const [janDataFolder, setJanDataFolder] = useState<string | undefined>()
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
const [selectedNewPath, setSelectedNewPath] = useState<string | null>(null)
|
||||
@ -181,10 +181,11 @@ function General() {
|
||||
|
||||
const handleCheckForUpdate = async () => {
|
||||
setIsCheckingUpdate(true)
|
||||
setRemindMeLater(false)
|
||||
try {
|
||||
if (isDev())
|
||||
return toast.info('You are running a development version of Jan!')
|
||||
const update = await checkForUpdate()
|
||||
const update = await checkForUpdate(true)
|
||||
if (!update) {
|
||||
toast.info('You are using the latest version of Jan!')
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user