Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
X
ximper-system-updater
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ximper Linux
ximper-system-updater
Commits
be23b5bf
Commit
be23b5bf
authored
Mar 28, 2026
by
Roman Alifanov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix update flow: continue on play errors, add progress label and finish log
parent
1f6fe85e
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
196 additions
and
51 deletions
+196
-51
update_service.go
service/update_service.go
+14
-10
actions.go
store/actions.go
+3
-2
process-page.blp
ui/pages/process-page.blp
+51
-9
process-page.ui
ui/pages/process-page.ui
+56
-9
update_process.go
ui/pages/update_process.go
+70
-20
window.go
ui/window.go
+2
-1
No files found.
service/update_service.go
View file @
be23b5bf
...
...
@@ -7,6 +7,7 @@ import (
"context"
"fmt"
"log"
"strings"
"sync"
"time"
)
...
...
@@ -146,22 +147,24 @@ func (us *UpdateService) RunUpdates(ctx context.Context) error {
startTime
:=
time
.
Now
()
packagesUpdated
:=
0
var
updateErr
error
var
categoryErrors
[]
string
systemOrKernelSucceeded
:=
false
// Execute updates sequentially
for
_
,
category
:=
range
categories
{
if
err
:=
us
.
runCategoryUpdate
(
ctx
,
category
,
transaction
);
err
!=
nil
{
updateErr
=
err
log
.
Printf
(
"Update failed for %s: %v"
,
category
,
err
)
break
categoryErrors
=
append
(
categoryErrors
,
fmt
.
Sprintf
(
"%s: %v"
,
category
,
err
))
continue
}
// Count packages
switch
category
{
case
"System"
:
packagesUpdated
+=
len
(
state
.
SystemUpdates
.
Packages
)
systemOrKernelSucceeded
=
true
case
"Kernel"
:
packagesUpdated
+=
len
(
state
.
KernelUpdates
.
Packages
)
systemOrKernelSucceeded
=
true
}
}
...
...
@@ -169,10 +172,11 @@ func (us *UpdateService) RunUpdates(ctx context.Context) error {
close
(
progressDone
)
// Finish update
success
:=
updateErr
==
nil
success
:=
len
(
categoryErrors
)
==
0
us
.
store
.
Dispatch
(
&
store
.
FinishUpdateAction
{
Success
:
success
,
Error
:
fmt
.
Sprintf
(
"%v"
,
updateErr
),
Success
:
success
,
Error
:
strings
.
Join
(
categoryErrors
,
"; "
),
SystemOrKernelSucceeded
:
systemOrKernelSucceeded
,
})
// Record in history
...
...
@@ -190,14 +194,14 @@ func (us *UpdateService) RunUpdates(ctx context.Context) error {
}
if
!
success
{
entry
.
ErrorMessage
=
fmt
.
Sprintf
(
"%v"
,
updateErr
)
entry
.
ErrorMessage
=
strings
.
Join
(
categoryErrors
,
"; "
)
}
us
.
history
.
RecordUpdate
(
entry
)
}
if
updateErr
!=
nil
{
return
updateErr
if
!
success
{
return
fmt
.
Errorf
(
"%s"
,
strings
.
Join
(
categoryErrors
,
"; "
))
}
log
.
Println
(
"Update completed successfully"
)
...
...
store/actions.go
View file @
be23b5bf
...
...
@@ -124,8 +124,9 @@ func (a *UpdateProgressAction) Apply(state *State) error {
}
type
FinishUpdateAction
struct
{
Success
bool
Error
string
Success
bool
Error
string
SystemOrKernelSucceeded
bool
}
func
(
a
*
FinishUpdateAction
)
Apply
(
state
*
State
)
error
{
...
...
ui/pages/process-page.blp
View file @
be23b5bf
...
...
@@ -19,6 +19,15 @@ Adw.NavigationPage process_page {
orientation: vertical;
spacing: 12;
Label progress_label {
label: _("");
ellipsize: end;
styles [
"caption",
]
}
ProgressBar progress_bar {}
ListBox {
...
...
@@ -52,20 +61,53 @@ Adw.NavigationPage process_page {
}
StackPage {
child: Adw.StatusPage {
child: Adw.StatusPage
finish_status_page
{
description: _("Click on button below to restart device");
icon-name: "face-smile-big-symbolic";
title: _("Updating complete!");
Button restart_button {
halign: center;
hexpand: false;
label: _("Reboot");
Adw.Clamp {
maximum-size: 500;
Box {
orientation: vertical;
spacing: 12;
Button restart_button {
halign: center;
hexpand: false;
label: _("Reboot");
styles [
"pill",
"suggested-action",
]
}
ListBox {
selection-mode: none;
styles [
"pill",
"suggested-action",
]
Adw.ExpanderRow {
title: _("Show more information");
ScrolledWindow {
height-request: 300;
hscrollbar-policy: never;
propagate-natural-height: true;
TextView finish_log_view {
cursor-visible: false;
editable: false;
wrap-mode: word_char;
}
}
}
styles [
"boxed-list",
]
}
}
}
};
...
...
ui/pages/process-page.ui
View file @
be23b5bf
...
...
@@ -28,6 +28,15 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
<property
name=
"orientation"
>
1
</property>
<property
name=
"spacing"
>
12
</property>
<child>
<object
class=
"GtkLabel"
id=
"progress_label"
>
<property
name=
"label"
translatable=
"yes"
></property>
<property
name=
"ellipsize"
>
3
</property>
<style>
<class
name=
"caption"
/>
</style>
</object>
</child>
<child>
<object
class=
"GtkProgressBar"
id=
"progress_bar"
></object>
</child>
<child>
...
...
@@ -69,19 +78,57 @@ corresponding .blp file and regenerate this file with blueprint-compiler.
<child>
<object
class=
"GtkStackPage"
>
<property
name=
"child"
>
<object
class=
"AdwStatusPage"
>
<object
class=
"AdwStatusPage"
id=
"finish_status_page"
>
<property
name=
"description"
translatable=
"yes"
>
Click on button below to restart device
</property>
<property
name=
"icon-name"
>
face-smile-big-symbolic
</property>
<property
name=
"title"
translatable=
"yes"
>
Updating complete!
</property>
<child>
<object
class=
"GtkButton"
id=
"restart_button"
>
<property
name=
"halign"
>
3
</property>
<property
name=
"hexpand"
>
false
</property>
<property
name=
"label"
translatable=
"yes"
>
Reboot
</property>
<style>
<class
name=
"pill"
/>
<class
name=
"suggested-action"
/>
</style>
<object
class=
"AdwClamp"
>
<property
name=
"maximum-size"
>
500
</property>
<child>
<object
class=
"GtkBox"
>
<property
name=
"orientation"
>
1
</property>
<property
name=
"spacing"
>
12
</property>
<child>
<object
class=
"GtkButton"
id=
"restart_button"
>
<property
name=
"halign"
>
3
</property>
<property
name=
"hexpand"
>
false
</property>
<property
name=
"label"
translatable=
"yes"
>
Reboot
</property>
<style>
<class
name=
"pill"
/>
<class
name=
"suggested-action"
/>
</style>
</object>
</child>
<child>
<object
class=
"GtkListBox"
>
<property
name=
"selection-mode"
>
0
</property>
<child>
<object
class=
"AdwExpanderRow"
>
<property
name=
"title"
translatable=
"yes"
>
Show more information
</property>
<child>
<object
class=
"GtkScrolledWindow"
>
<property
name=
"height-request"
>
300
</property>
<property
name=
"hscrollbar-policy"
>
2
</property>
<property
name=
"propagate-natural-height"
>
true
</property>
<child>
<object
class=
"GtkTextView"
id=
"finish_log_view"
>
<property
name=
"cursor-visible"
>
false
</property>
<property
name=
"editable"
>
false
</property>
<property
name=
"wrap-mode"
>
3
</property>
</object>
</child>
</object>
</child>
</object>
</child>
<style>
<class
name=
"boxed-list"
/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
...
...
ui/pages/update_process.go
View file @
be23b5bf
...
...
@@ -6,6 +6,7 @@ import (
"SystemUpdater/model"
"github.com/diamondburned/gotk4-adwaita/pkg/adw"
"github.com/diamondburned/gotk4/pkg/glib/v2"
"github.com/diamondburned/gotk4/pkg/gtk/v4"
gtksbuilder
"SystemUpdater/lib/gtks/builder"
...
...
@@ -17,13 +18,18 @@ import (
var
processPageUI
string
type
UpdateProcessPage
struct
{
NavigationPage
*
adw
.
NavigationPage
`gtk:"process_page"`
stack
*
gtk
.
Stack
`gtk:"process_stack"`
statusPage
*
adw
.
StatusPage
`gtk:"status_page"`
progressBar
*
gtk
.
ProgressBar
`gtk:"progress_bar"`
logView
*
gtk
.
TextView
`gtk:"log_view"`
logBuffer
*
gtk
.
TextBuffer
rebootBtn
*
gtk
.
Button
`gtk:"restart_button"`
NavigationPage
*
adw
.
NavigationPage
`gtk:"process_page"`
stack
*
gtk
.
Stack
`gtk:"process_stack"`
statusPage
*
adw
.
StatusPage
`gtk:"status_page"`
finishStatusPage
*
adw
.
StatusPage
`gtk:"finish_status_page"`
progressBar
*
gtk
.
ProgressBar
`gtk:"progress_bar"`
progressLabel
*
gtk
.
Label
`gtk:"progress_label"`
logView
*
gtk
.
TextView
`gtk:"log_view"`
logBuffer
*
gtk
.
TextBuffer
finishLogView
*
gtk
.
TextView
`gtk:"finish_log_view"`
finishLogBuffer
*
gtk
.
TextBuffer
rebootBtn
*
gtk
.
Button
`gtk:"restart_button"`
pulseSourceID
glib
.
SourceHandle
}
func
NewUpdateProcessPage
(
onReboot
func
())
*
UpdateProcessPage
{
...
...
@@ -35,6 +41,7 @@ func NewUpdateProcessPage(onReboot func()) *UpdateProcessPage {
}
page
.
logBuffer
=
page
.
logView
.
Buffer
()
page
.
finishLogBuffer
=
page
.
finishLogView
.
Buffer
()
page
.
progressBar
.
SetShowText
(
true
)
page
.
rebootBtn
.
ConnectClicked
(
func
()
{
...
...
@@ -48,30 +55,52 @@ func NewUpdateProcessPage(onReboot func()) *UpdateProcessPage {
func
(
p
*
UpdateProcessPage
)
UpdateProgress
(
event
model
.
EventData
)
{
if
event
.
Progress
>
0
{
p
.
stopPulse
()
p
.
progressBar
.
SetFraction
(
event
.
Progress
/
100.0
)
}
else
if
p
.
pulseSourceID
==
0
{
p
.
startPulse
()
}
if
event
.
Message
!=
""
&&
event
.
EventType
!=
"PROGRESS"
{
endIter
:=
p
.
logBuffer
.
EndIter
()
p
.
logBuffer
.
Insert
(
endIter
,
event
.
Message
+
"
\n
"
)
if
event
.
Message
!=
""
{
p
.
progressLabel
.
SetLabel
(
event
.
Message
)
mark
:=
p
.
logBuffer
.
CreateMark
(
"end"
,
endIter
,
false
)
p
.
logView
.
ScrollToMark
(
mark
,
0.0
,
false
,
0.0
,
0.0
)
if
event
.
EventType
!=
"PROGRESS"
{
endIter
:=
p
.
logBuffer
.
EndIter
()
p
.
logBuffer
.
Insert
(
endIter
,
event
.
Message
+
"
\n
"
)
mark
:=
p
.
logBuffer
.
CreateMark
(
"end"
,
endIter
,
false
)
p
.
logView
.
ScrollToMark
(
mark
,
0.0
,
false
,
0.0
,
0.0
)
}
}
}
func
(
p
*
UpdateProcessPage
)
ShowComplete
(
success
bool
)
{
func
(
p
*
UpdateProcessPage
)
ShowComplete
(
success
bool
,
showReboot
bool
)
{
if
success
{
p
.
statusPage
.
SetTitle
(
"Updating complete!"
)
p
.
statusPage
.
SetIconName
(
"face-smile-big-symbolic"
)
p
.
statusPage
.
SetDescription
(
"Click on button below to restart device"
)
p
.
finishStatusPage
.
SetTitle
(
"Updating complete!"
)
p
.
finishStatusPage
.
SetIconName
(
"face-smile-big-symbolic"
)
p
.
finishStatusPage
.
SetDescription
(
"Click on button below to restart device"
)
}
else
if
showReboot
{
p
.
finishStatusPage
.
SetTitle
(
"Update Partially Complete"
)
p
.
finishStatusPage
.
SetIconName
(
"dialog-warning-symbolic"
)
p
.
finishStatusPage
.
SetDescription
(
"Some updates failed, but system packages were updated. A reboot is recommended."
)
}
else
{
p
.
statusPage
.
SetTitle
(
"Update Failed"
)
p
.
statusPage
.
SetIconName
(
"dialog-error-symbolic"
)
p
.
statusPage
.
SetDescription
(
"The update encountered an error. Please check the logs."
)
p
.
rebootBtn
.
SetVisible
(
false
)
p
.
finishStatusPage
.
SetTitle
(
"Update Failed"
)
p
.
finishStatusPage
.
SetIconName
(
"dialog-error-symbolic"
)
p
.
finishStatusPage
.
SetDescription
(
"The update encountered an error. Please check the logs."
)
}
p
.
rebootBtn
.
SetVisible
(
showReboot
||
success
)
// Copy log to finish page
start
:=
p
.
logBuffer
.
StartIter
()
end
:=
p
.
logBuffer
.
EndIter
()
logText
:=
p
.
logBuffer
.
Text
(
start
,
end
,
false
)
if
logText
!=
""
{
finishEnd
:=
p
.
finishLogBuffer
.
EndIter
()
p
.
finishLogBuffer
.
Insert
(
finishEnd
,
logText
)
}
p
.
stopPulse
()
p
.
stack
.
SetVisibleChildName
(
"finish"
)
}
...
...
@@ -79,7 +108,28 @@ func (p *UpdateProcessPage) Reset() {
start
:=
p
.
logBuffer
.
StartIter
()
end
:=
p
.
logBuffer
.
EndIter
()
p
.
logBuffer
.
Delete
(
start
,
end
)
finishStart
:=
p
.
finishLogBuffer
.
StartIter
()
finishEnd
:=
p
.
finishLogBuffer
.
EndIter
()
p
.
finishLogBuffer
.
Delete
(
finishStart
,
finishEnd
)
p
.
progressBar
.
SetFraction
(
0.0
)
p
.
progressLabel
.
SetLabel
(
""
)
p
.
stopPulse
()
p
.
stack
.
SetVisibleChildName
(
"default"
)
p
.
rebootBtn
.
SetVisible
(
true
)
}
func
(
p
*
UpdateProcessPage
)
startPulse
()
{
p
.
pulseSourceID
=
glib
.
TimeoutAdd
(
100
,
func
()
bool
{
p
.
progressBar
.
Pulse
()
return
true
})
}
func
(
p
*
UpdateProcessPage
)
stopPulse
()
{
if
p
.
pulseSourceID
!=
0
{
glib
.
SourceRemove
(
p
.
pulseSourceID
)
p
.
pulseSourceID
=
0
}
}
ui/window.go
View file @
be23b5bf
...
...
@@ -135,7 +135,8 @@ func (w *Window) onStateChange(change store.StateChange) {
case
store
.
ChangeUpdateFinish
:
action
,
ok
:=
change
.
Data
.
(
*
store
.
FinishUpdateAction
)
if
ok
{
w
.
processPage
.
ShowComplete
(
action
.
Success
)
showReboot
:=
action
.
Success
||
action
.
SystemOrKernelSucceeded
w
.
processPage
.
ShowComplete
(
action
.
Success
,
showReboot
)
}
case
store
.
ChangeHistory
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment