samagotchi 0.146.0 → 0.147.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -1
- data/docs/architecture.md +14 -5
- data/docs/cli.md +5 -2
- data/docs/configuration.md +16 -3
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +1 -1
- data/lib/samagotchi/web/auto_update.rb +60 -5
- data/lib/samagotchi/web/public/app.js +5 -16
- data/lib/samagotchi/web/public/update_notice.js +22 -11
- data/lib/samagotchi/web/public/update_sheet.js +4 -1
- data/lib/samagotchi/web/session_access_routes.rb +1 -1
- data/lib/samagotchi/web/session_hub.rb +17 -4
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 29546fb5487086302ed4d68c168fa9e7f4f576bee252e59cfdaf08a4e74081b4
|
|
4
|
+
data.tar.gz: 3c05877e89bccf719dbdf8f4287c8343222f897484c2f22a4c3245bdc92a2af1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 953166016279e2b81ebe64c9642df2a245cc3d7fe4520850bf4023557b5d1b7fd620589fd7c49df66d458e59eeae32dffb3020e6286ea8be85edf2710c2bd7d4
|
|
7
|
+
data.tar.gz: 6c01b8ac5e044770f34e4fd5057915116e8b56b81a6f505fac3259449911a02b091262aad2554417ebf0493068ace77b1636d3fb8187769225268d12540c5354
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and commands may change between minor versions. How releases are made:
|
|
|
7
7
|
[docs/releasing.md](docs/releasing.md).
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.147.0] - 2026-11-03
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- With `update.auto: install` a newer chi installed some other way, such as `chi update` in a terminal, now gets the
|
|
15
|
+
same unattended restarts as one chi web installed itself: idle workers and chi web move onto it within about 10
|
|
16
|
+
minutes once no turn runs, and the phone hears "chi updated to X". The page offers no **Restart** buttons meanwhile,
|
|
17
|
+
unless the restart failed or has waited an hour; `update.restart: never` keeps them.
|
|
18
|
+
[docs/configuration.md](docs/configuration.md#unattended-updates-updateauto)
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- An automatic update's sheet no longer offers **Restart workers** and **Restart chi web** beside a run already doing
|
|
23
|
+
both, and its worker restarts no longer stack sticky toasts unless one fails.
|
|
24
|
+
|
|
10
25
|
## [0.146.0] - 2026-11-03
|
|
11
26
|
|
|
12
27
|
### Added
|
|
@@ -5580,7 +5595,8 @@ and long-lived sessions.
|
|
|
5580
5595
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
5581
5596
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
5582
5597
|
|
|
5583
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
5598
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.147.0...HEAD
|
|
5599
|
+
[0.147.0]: https://github.com/dm1try/samagotchi/compare/v0.146.0...v0.147.0
|
|
5584
5600
|
[0.146.0]: https://github.com/dm1try/samagotchi/compare/v0.145.0...v0.146.0
|
|
5585
5601
|
[0.145.0]: https://github.com/dm1try/samagotchi/compare/v0.144.0...v0.145.0
|
|
5586
5602
|
[0.144.0]: https://github.com/dm1try/samagotchi/compare/v0.143.0...v0.144.0
|
data/docs/architecture.md
CHANGED
|
@@ -335,9 +335,10 @@ reload in a toast, since an open tab keeps its old JS across a `chi web` upgrade
|
|
|
335
335
|
`installed`, the newest chi on this machine (`InstalledVersions`: the highest `samagotchi-<v>.gemspec`
|
|
336
336
|
in RubyGems' specification dirs, or a checkout's `version.rb` on disk; looked at again on each full
|
|
337
337
|
probe, every 10 s). A `chi` frame and the `GET /api/events` snapshot carry the full state
|
|
338
|
-
`{version, installed, available, update_blocked, auto, restart}` (`auto`: `update.auto`, `off` or `install`, which the page's
|
|
339
|
-
"available" wording follows; `restart`: `update.restart
|
|
340
|
-
|
|
338
|
+
`{version, installed, available, update_blocked, auto, restart, auto_offers}` (`auto`: `update.auto`, `off` or `install`, which the page's
|
|
339
|
+
"available" wording follows; `restart`: `update.restart`; `auto_offers`: the version AutoUpdate says the page should offer
|
|
340
|
+
the restarts for. With `auto: install`, `restart: idle` and no block the page offers no Restart toast for a newer installed
|
|
341
|
+
chi, however it got installed, unless `auto_offers` names it); `/api/info` and the session-command wake response carry the same fields. `SessionHub#update_available` publishes the checker result under the hub's
|
|
341
342
|
Monitor, but fetching never holds it.
|
|
342
343
|
|
|
343
344
|
`Web::UpdateCheck` runs its own thread beside the hub. When `update.notify: on` (the default) or
|
|
@@ -385,7 +386,14 @@ is `update.auto: install`. Every 5 minutes (the first step 60 s after start) it
|
|
|
385
386
|
and installs it through `UpdateOps#install(wait: true, auto: true)` when the install isn't blocked, no operation
|
|
386
387
|
runs, no hub summary has `status: running` or a `pending_question`, and the version isn't in `auto_failed` or
|
|
387
388
|
already in the `auto` record (one automatic try per version). Its frames carry `auto: true` (the page: "Automatic
|
|
388
|
-
chi update in progress", "(automatic)" in the sheet's title
|
|
389
|
+
chi update in progress", "(automatic)" in the sheet's title, no restart buttons in the sheet, no toast for a worker pass
|
|
390
|
+
unless it fails). A newer chi installed some other way (`AutoUpdate#adopt_installed`: `update.restart: idle`, the same gate, no
|
|
391
|
+
available release to install first, none on record for that version) is **adopted** once seen for `SETTLE` (60 s, in
|
|
392
|
+
practice the next step, as `chi update` merges the bundles after installing the gem and leaves no lock to look at): its
|
|
393
|
+
record starts at `phase: installed` and the run goes on as below. Ahead of every gate each step calls
|
|
394
|
+
`SessionHub#auto_offer_restart` (`auto_offers`, in memory) when a run on record for the newest installed chi is closed with
|
|
395
|
+
this chi web still older, or that chi has waited `HELD_FOR` (1 h): the trying goes on, and the page has its buttons back;
|
|
396
|
+
`restart_failed` and a failed install call it at once. Its record is `update_check.json`'s `auto`:
|
|
389
397
|
`{version, from, at, kept, phase: installed|workers|web|retrying|done|failed, workers: {restarted, left}, detail, pushed}`,
|
|
390
398
|
in Ruby the value object `Web::AutoUpdateRecord`, which owns that shape (`from_h`, `to_store`).
|
|
391
399
|
With `update.restart: never` the run ends at the install (`chi_notice` `installed`). With `idle` it goes on
|
|
@@ -395,7 +403,8 @@ the worker's own atomic refusal still applies); then, with no
|
|
|
395
403
|
turn running, `Server.restart_preflight` (the restart's command with `--version`, 20 s, killed on a timeout; it must
|
|
396
404
|
print a chi at least the release) and, if it passes, the record's `phase: web` is written **before**
|
|
397
405
|
`UpdateOps#restart_web`. A failed preflight, or a refused restart, publishes `restart_failed` and ends the run
|
|
398
|
-
(`failed`). `
|
|
406
|
+
(`failed`). A run at `installed` or `workers` that finds this chi web already on its version (restarted by hand from the
|
|
407
|
+
page's offer) goes straight to the finish. `AutoUpdate#stop` waits `STOP_WAIT` (25 s, longer than the preflight's timeout) for its thread, and a
|
|
399
408
|
preflight that ends after a stop leads to nothing (no `web`, no exec): the next start takes the run up again. The restarted chi web's AutoUpdate (first step after 5 s) finds `phase: web`, checks it runs the
|
|
400
409
|
release, counts the workers once more and publishes `updated`, then `done`, or `retrying` while workers are left. Those are
|
|
401
410
|
retried each step until none is left or 24 h (`RETRY_FOR`) have passed since the install, under the same filter, and from
|
data/docs/cli.md
CHANGED
|
@@ -267,7 +267,9 @@ done
|
|
|
267
267
|
in place, the notice gives the blocker and its command instead.
|
|
268
268
|
With `update.auto: install` chi web installs the release by itself once no
|
|
269
269
|
turn runs anywhere, then restarts idle workers and itself (`update.restart`),
|
|
270
|
-
and the notice reads "installs by itself" with no **Update** button
|
|
270
|
+
and the notice reads "installs by itself" with no **Update** button. A newer
|
|
271
|
+
chi installed from a terminal gets those restarts too, and no **Restart**
|
|
272
|
+
buttons unless the restart failed or has waited an hour; see
|
|
271
273
|
[Unattended updates](configuration.md#unattended-updates-updateauto).
|
|
272
274
|
|
|
273
275
|
- **The chi home.** With a [chi home](configuration.md#the-chi-home), a `chi
|
|
@@ -355,7 +357,8 @@ done
|
|
|
355
357
|
terminal, no reminder) and `session.idle_exit_minutes` pass (never with 0).
|
|
356
358
|
A new or woken worker always starts on the newest chi installed. A running
|
|
357
359
|
`chi web` (on the port it was started with, else `web.port`) on an older chi needs a restart (Ctrl-C, then `chi web`):
|
|
358
|
-
its page says so too.
|
|
360
|
+
its page says so too. With `update.auto: install` it restarts by itself once no turn
|
|
361
|
+
runs ([Unattended updates](configuration.md#unattended-updates-updateauto)). One from before workers started on the newest chi
|
|
359
362
|
also starts its new sessions on its own version until then (the row says so).
|
|
360
363
|
|
|
361
364
|
`--dry-run` shows the table with "would update" and changes nothing. It is
|
data/docs/configuration.md
CHANGED
|
@@ -2163,9 +2163,22 @@ Both are read when chi web starts (restart it to apply a change). What it does,
|
|
|
2163
2163
|
([Phone notifications](#phone-notifications-web-push)); without it the page's toasts and
|
|
2164
2164
|
`$XDG_STATE_HOME/samagotchi/update_check.json` (`auto`) are the record.
|
|
2165
2165
|
|
|
2166
|
+
**A chi installed some other way** (`chi update` in a terminal, a release newer than `update.min_age_hours` allows, a
|
|
2167
|
+
hand `gem install`) gets steps 3 and 4 too, with `update.restart: idle` and an install that isn't blocked: chi web notices it,
|
|
2168
|
+
waits for the next 5-minute look (`chi update` merges the bundles after installing the gem) and for the quiet moment of
|
|
2169
|
+
step 1, then restarts as above, so chi web is on it within about 10 minutes when nothing runs. A release it can install
|
|
2170
|
+
goes first. One run per version here too: a restart that didn't take isn't tried again.
|
|
2171
|
+
|
|
2172
|
+
The page offers no **Restart** buttons while chi web is going to restart by itself. They come back, as a toast, when it
|
|
2173
|
+
won't (a failed run) or when something has held it for an hour (a forgotten question, a long turn); it keeps trying behind
|
|
2174
|
+
them. Worker restarts in such a run show no toast unless one fails, and the sheet behind **View** has only **Close**;
|
|
2175
|
+
the session's own **Restart** in its bar is always there.
|
|
2176
|
+
|
|
2166
2177
|
There is no watchdog: chi web restarts into the newest chi it just proved can start, and nothing brings an old one
|
|
2167
|
-
back by itself. To roll back,
|
|
2168
|
-
|
|
2178
|
+
back by itself. To roll back, set `update.auto: off` (or the next check installs the release again), uninstall the newer
|
|
2179
|
+
gem with the chi home's Ruby (`gem uninstall samagotchi -v Y`: chi always starts the newest one installed, so installing an
|
|
2180
|
+
older gem beside it changes nothing), and restart chi web. `chi _X_ web` stays on X only with `update.auto: off`: a restart
|
|
2181
|
+
starts the newest. `chi update --to` refuses a version that isn't newer than the running chi. The desktop helper isn't rebuilt (that
|
|
2169
2182
|
stays a manual `chi update`).
|
|
2170
2183
|
|
|
2171
2184
|
## All settings
|
|
@@ -2288,7 +2301,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2288
2301
|
| `update.bundles` | `true` | | `false`: `chi update` leaves the shipped bundles to `chi bundle upgrade` (`--no-bundles`). |
|
|
2289
2302
|
| `update.desktop` | `true` | | `false`: `chi update` leaves the desktop helper alone (`--no-desktop`). |
|
|
2290
2303
|
| `update.notify` | `on` | | `on`: chi web checks RubyGems once a day and shows aged stable releases newer than both this chi and the newest installed chi; `off`: no thread or network check. |
|
|
2291
|
-
| `update.auto` | `off` | | `install`: chi web installs the release its checker found (aged by `update.min_age_hours`) once no turn runs and no question waits in any session, as the **Update** button would (shipped bundles merged, your edits kept and reported), then pushes how it went; `update.restart` says what follows. An install that fails is tried once per version (a newer release, or the **Update** button, tries again). The check runs even with `update.notify: off` (which then only silences the page's and the phone's "available"). An install that can't run here (a checkout, Bundler, an unwritable gem dir) stays a notice. `off`: notice only. See [Unattended updates](#unattended-updates-updateauto). |
|
|
2304
|
+
| `update.auto` | `off` | | `install`: chi web installs the release its checker found (aged by `update.min_age_hours`) once no turn runs and no question waits in any session, as the **Update** button would (shipped bundles merged, your edits kept and reported), then pushes how it went; `update.restart` says what follows, also for a chi installed some other way (`chi update` in a terminal). An install that fails is tried once per version (a newer release, or the **Update** button, tries again). The check runs even with `update.notify: off` (which then only silences the page's and the phone's "available"). An install that can't run here (a checkout, Bundler, an unwritable gem dir) stays a notice. `off`: notice only. See [Unattended updates](#unattended-updates-updateauto). |
|
|
2292
2305
|
| `update.restart` | `idle` | | With `update.auto: install`: `idle` restarts the stale workers whose session is idle and untouched for 10 minutes, then chi web itself, once no turn runs anywhere and the newest chi has answered `chi --version`; it pushes "chi updated to X". `never`: the install only (the page's toast offers the restarts, workers also exit on their own when idle). |
|
|
2293
2306
|
| `update.check_hours` | `24` | | Hours between RubyGems checks (at least 1). The first check starts about 30 seconds after chi web starts. |
|
|
2294
2307
|
| `update.min_age_hours` | `24` | | A release must be this old before chi web offers it; `0` disables the age delay. |
|
data/lib/samagotchi/version.rb
CHANGED
data/lib/samagotchi/web/app.rb
CHANGED
|
@@ -1018,7 +1018,7 @@ module Samagotchi
|
|
|
1018
1018
|
def handle_info(req)
|
|
1019
1019
|
return participant_info unless principal(req).owner?
|
|
1020
1020
|
|
|
1021
|
-
chi_state = @hub&.chi_state || { version: Samagotchi::VERSION, installed: nil, available: nil, update_blocked: nil, auto: "off", restart: "idle" }
|
|
1021
|
+
chi_state = @hub&.chi_state || { version: Samagotchi::VERSION, installed: nil, available: nil, update_blocked: nil, auto: "off", restart: "idle", auto_offers: nil }
|
|
1022
1022
|
json_response(200, { app: "chi-web", **chi_state, pid: Process.pid,
|
|
1023
1023
|
cwd: Dir.pwd, start_dir: start_dir, features: INFO_FEATURES, lan: @lan && @lan[:ip],
|
|
1024
1024
|
proxy: @public_url && { identity_header: @public_url.identity_header } })
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "rubygems/version"
|
|
4
4
|
require "time"
|
|
5
5
|
require_relative "../config"
|
|
6
|
+
require_relative "../installed_versions"
|
|
6
7
|
require_relative "../log"
|
|
7
8
|
require_relative "../version"
|
|
8
9
|
require_relative "auto_update_record"
|
|
@@ -37,6 +38,17 @@ module Samagotchi
|
|
|
37
38
|
# A preflight that fails leaves chi web as it is and says so.
|
|
38
39
|
# update.restart: never stops after the install.
|
|
39
40
|
#
|
|
41
|
+
# A newer chi installed some other way (a terminal's `chi update`, inside
|
|
42
|
+
# update.min_age_hours or not) is adopted: once it has settled and the
|
|
43
|
+
# gate is open, it gets a run from step 1 on. One run per version here
|
|
44
|
+
# too, so a restart that didn't take isn't tried again.
|
|
45
|
+
#
|
|
46
|
+
# The page offers no restart buttons for a run that will do them itself.
|
|
47
|
+
# It offers them again when this says so (SessionHub#auto_offer_restart):
|
|
48
|
+
# a run on record for the version ended with chi web still older, or
|
|
49
|
+
# something held it for HELD_FOR, a forgotten question or a long turn.
|
|
50
|
+
# The trying goes on behind that offer.
|
|
51
|
+
#
|
|
40
52
|
# The run's record (AutoUpdateRecord) lives in update_check.json
|
|
41
53
|
# (UpdateCheck#save_auto), so a chi web restart finds it. See
|
|
42
54
|
# docs/architecture.md.
|
|
@@ -56,6 +68,13 @@ module Samagotchi
|
|
|
56
68
|
OPEN_PHASES = %w[installed workers web].freeze
|
|
57
69
|
# The phase after "chi updated" while workers are left.
|
|
58
70
|
RETRYING = "retrying"
|
|
71
|
+
# A newer chi seen installed is adopted only this long after: `chi update`
|
|
72
|
+
# installs the gem first and merges the bundles after, with no lock or
|
|
73
|
+
# marker to look at. In practice the next POLL.
|
|
74
|
+
SETTLE = 60
|
|
75
|
+
# How long a newer installed chi may wait for its restart before the page
|
|
76
|
+
# offers the buttons again.
|
|
77
|
+
HELD_FOR = 3600
|
|
59
78
|
# How long stop waits for the thread: long enough for a preflight
|
|
60
79
|
# (Server::PREFLIGHT_TIMEOUT, 20 s; a healthy one takes about a
|
|
61
80
|
# second) to end on its own, so a stop never abandons one mid-way.
|
|
@@ -84,6 +103,7 @@ module Samagotchi
|
|
|
84
103
|
@wakeup = Queue.new
|
|
85
104
|
@thread = nil
|
|
86
105
|
@stopped = false
|
|
106
|
+
@seen = nil
|
|
87
107
|
end
|
|
88
108
|
|
|
89
109
|
def start
|
|
@@ -108,17 +128,17 @@ module Samagotchi
|
|
|
108
128
|
def step
|
|
109
129
|
return unless @auto == "install"
|
|
110
130
|
|
|
131
|
+
note_installed
|
|
111
132
|
record = @check.auto_state
|
|
112
133
|
return carry_on(record) if record && OPEN_PHASES.include?(record.phase)
|
|
113
134
|
|
|
114
135
|
retry_left(record) if record&.phase == RETRYING
|
|
136
|
+
return if @ops.blocked_reason || @ops.running? || busy_turn?
|
|
115
137
|
|
|
116
138
|
version = @check.available
|
|
117
|
-
return
|
|
118
|
-
return if @check.auto_failed?(version) || tried?(version)
|
|
119
|
-
return if @ops.blocked_reason || @ops.running? || busy_turn?
|
|
139
|
+
return install(version) if version && !@check.auto_failed?(version) && !tried?(version)
|
|
120
140
|
|
|
121
|
-
|
|
141
|
+
adopt_installed
|
|
122
142
|
end
|
|
123
143
|
|
|
124
144
|
private
|
|
@@ -158,6 +178,37 @@ module Samagotchi
|
|
|
158
178
|
end
|
|
159
179
|
end
|
|
160
180
|
|
|
181
|
+
# What is installed, ahead of every gate: since when a newer chi is, and
|
|
182
|
+
# whether the page should offer the restarts for it. A chi web restart
|
|
183
|
+
# starts the clock over, and finds a closed run's offer again here.
|
|
184
|
+
def note_installed
|
|
185
|
+
installed = @hub.refresh_installed
|
|
186
|
+
return @seen = nil unless InstalledVersions.newer?(installed)
|
|
187
|
+
|
|
188
|
+
now = @clock.call
|
|
189
|
+
@seen = [installed, now] unless @seen&.first == installed
|
|
190
|
+
@hub.auto_offer_restart(installed) if closed_run?(installed) || now - @seen.last >= HELD_FOR
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# A run on record for +version+ that is over, with chi web still older.
|
|
194
|
+
def closed_run?(version)
|
|
195
|
+
record = @check.auto_state
|
|
196
|
+
!record.nil? && record.version == version && !OPEN_PHASES.include?(record.phase)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# A newer chi this didn't install, settled: a run of its own from here.
|
|
200
|
+
# The terminal's table already told what `chi update` kept.
|
|
201
|
+
def adopt_installed
|
|
202
|
+
return unless @restart == "idle" && @seen
|
|
203
|
+
|
|
204
|
+
installed, seen_at = @seen
|
|
205
|
+
now = @clock.call
|
|
206
|
+
return if tried?(installed) || now - seen_at < SETTLE
|
|
207
|
+
|
|
208
|
+
Log.info(:web, "auto_update_adopted", version: installed, from: VERSION)
|
|
209
|
+
carry_on(save(AutoUpdateRecord.new(version: installed, from: VERSION, at: now.iso8601, phase: "installed")))
|
|
210
|
+
end
|
|
211
|
+
|
|
161
212
|
def install(version)
|
|
162
213
|
Log.info(:web, "auto_update_start", version: version, from: VERSION)
|
|
163
214
|
result = @ops.install(version, wait: true, auto: true)
|
|
@@ -187,7 +238,9 @@ module Samagotchi
|
|
|
187
238
|
# The next step of a run on record.
|
|
188
239
|
def carry_on(record)
|
|
189
240
|
case record.phase
|
|
190
|
-
when "installed", "workers"
|
|
241
|
+
when "installed", "workers"
|
|
242
|
+
# Restarted by hand on the way (the page's offer): nothing left to exec.
|
|
243
|
+
InstalledVersions.newer?(record.version) ? workers_then_web(record) : finish(record)
|
|
191
244
|
when "web" then finish(record)
|
|
192
245
|
end
|
|
193
246
|
end
|
|
@@ -260,6 +313,7 @@ module Samagotchi
|
|
|
260
313
|
def restart_failed(record, detail, event)
|
|
261
314
|
Log.warn(:web, event, version: record.version, detail: detail)
|
|
262
315
|
save(record.with(phase: "failed", detail: detail, pushed: true))
|
|
316
|
+
@hub.auto_offer_restart(record.version)
|
|
263
317
|
@hub.publish_notice(kind: "restart_failed", version: record.version, detail: detail)
|
|
264
318
|
end
|
|
265
319
|
|
|
@@ -307,6 +361,7 @@ module Samagotchi
|
|
|
307
361
|
Log.warn(:web, "auto_update_failed_install", version: record.version, detail: detail)
|
|
308
362
|
gem_installed = rows.any? { |candidate| candidate["component"] == "chi (gem)" && !candidate["failed"] }
|
|
309
363
|
@check.save_auto(record.with(phase: "failed", detail: detail, pushed: true), failed: record.version)
|
|
364
|
+
@hub.auto_offer_restart(record.version)
|
|
310
365
|
@hub.publish_notice(kind: "failed", version: record.version, detail: detail.empty? ? nil : detail,
|
|
311
366
|
installed: gem_installed)
|
|
312
367
|
end
|
|
@@ -95,7 +95,7 @@ import { waitingOn } from "./notify.js";
|
|
|
95
95
|
import { applySessionEvent, sortedByUpdated } from "./sessions_list.js";
|
|
96
96
|
import { createNotifications } from "./notifications.js";
|
|
97
97
|
import { renameShown, tabTitle, topBarTitle } from "./session_title.js";
|
|
98
|
-
import { mergeVersionState, restartConfirmText, versionNotice } from "./update_notice.js";
|
|
98
|
+
import { mergeVersionState, restartConfirmText, updateFrameToast, versionNotice } from "./update_notice.js";
|
|
99
99
|
import {
|
|
100
100
|
appendAboveLiveTiming, mergeTiming, normalizeTiming,
|
|
101
101
|
} from "./timing.js";
|
|
@@ -585,7 +585,7 @@ function noticeVersions(data) {
|
|
|
585
585
|
const notice = versionNotice({ loaded: document.body.dataset.version, served: versions.served,
|
|
586
586
|
installed: versions.installed, available: versions.available,
|
|
587
587
|
updateBlocked: versions.updateBlocked, auto: versions.auto, restart: versions.restart,
|
|
588
|
-
|
|
588
|
+
autoOffers: versions.autoOffers, shown: versionNoticesShown });
|
|
589
589
|
store.touch("infoBar"); // the worker badge compares with the newest installed
|
|
590
590
|
if (!notice) return;
|
|
591
591
|
versionNoticesShown.add(notice.key);
|
|
@@ -614,20 +614,9 @@ function noticeVersions(data) {
|
|
|
614
614
|
function updateFrame(data, { snapshot = false } = {}) {
|
|
615
615
|
const wasOpen = updateSheet.isOpen();
|
|
616
616
|
if (!updateSheet.receive(data)) return;
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
if (data.operation === "install") {
|
|
621
|
-
noticeVersions({ ...(data.state === "done" && data.version ? { installed: data.version } : {}), chi_update: data });
|
|
622
|
-
}
|
|
623
|
-
const label = `${data.auto ? "Automatic " : ""}${data.operation === "install" ? "chi update" : "worker restart"}`
|
|
624
|
-
.replace(/^./, (c) => c.toUpperCase());
|
|
625
|
-
if (!wasOpen && data.state === "running") {
|
|
626
|
-
showToast(`${label} in progress`, { label: "View", run: () => updateSheet.view() }, { sticky: true });
|
|
627
|
-
} else if (!snapshot && !wasOpen && ["done", "failed"].includes(data.state)
|
|
628
|
-
&& !(data.operation === "install" && data.state === "done")) {
|
|
629
|
-
showToast(`${label} ${data.state}`, { label: "View", run: () => updateSheet.view() }, { sticky: true });
|
|
630
|
-
}
|
|
617
|
+
if (data.operation === "install" && data.state === "done" && data.version) noticeVersions({ installed: data.version });
|
|
618
|
+
const text = updateFrameToast(data, { wasOpen, snapshot });
|
|
619
|
+
if (text) showToast(text, { label: "View", run: () => updateSheet.view() }, { sticky: true });
|
|
631
620
|
}
|
|
632
621
|
|
|
633
622
|
function copyUpdateCommand(command) {
|
|
@@ -17,12 +17,7 @@ export function mergeVersionState(current = {}, frame = {}) {
|
|
|
17
17
|
if (has(frame, "update_blocked")) next.updateBlocked = known(frame.update_blocked) ? frame.update_blocked : null;
|
|
18
18
|
if (has(frame, "auto")) next.auto = frame.auto === "install" ? "install" : "off";
|
|
19
19
|
if (has(frame, "restart")) next.restart = frame.restart === "never" ? "never" : "idle";
|
|
20
|
-
|
|
21
|
-
// version an automatic one installs, while it hasn't failed.
|
|
22
|
-
const run = frame.chi_update;
|
|
23
|
-
if (run && run.operation === "install" && known(run.version)) {
|
|
24
|
-
next.autoInstall = run.auto === true && run.state !== "failed" ? run.version : null;
|
|
25
|
-
}
|
|
20
|
+
if (has(frame, "auto_offers")) next.autoOffers = known(frame.auto_offers) ? frame.auto_offers : null;
|
|
26
21
|
return next;
|
|
27
22
|
}
|
|
28
23
|
|
|
@@ -53,22 +48,23 @@ export function newerVersion(a, b) {
|
|
|
53
48
|
// The toast for the versions as the page knows them, or null:
|
|
54
49
|
// - loaded ≠ served: chi web restarted on another version; reload the page;
|
|
55
50
|
// - installed newer than served: chi web runs an older chi than installed;
|
|
56
|
-
// restart its workers or chi web; not
|
|
57
|
-
//
|
|
58
|
-
//
|
|
51
|
+
// restart its workers or chi web; not with update.auto: install and
|
|
52
|
+
// update.restart: idle (and not blocked), where chi web restarts them by
|
|
53
|
+
// itself and pushes how it went, however that chi got installed, unless
|
|
54
|
+
// chi web says it won't or has been held for long (+autoOffers+);
|
|
59
55
|
// - available newer than served: install it, unless the install is blocked;
|
|
60
56
|
// with update.auto: install (and not blocked) chi web installs it by itself
|
|
61
57
|
// once no turn runs: the notice says so, with no Update button.
|
|
62
58
|
// Each has a key; one in +shown+ (a Set: what this tab already showed, as
|
|
63
59
|
// every reconnect's snapshot says it again) is not shown twice.
|
|
64
60
|
// @return {{key, text, reload: boolean} | null}
|
|
65
|
-
export function versionNotice({ loaded, served, installed, available, updateBlocked, auto, restart,
|
|
61
|
+
export function versionNotice({ loaded, served, installed, available, updateBlocked, auto, restart, autoOffers,
|
|
66
62
|
shown = new Set() } = {}) {
|
|
67
63
|
let notice = null;
|
|
68
64
|
if (known(loaded) && known(served) && loaded !== served) {
|
|
69
65
|
notice = { key: `reload:${served}`, text: `chi was updated to ${served}`, reload: true };
|
|
70
66
|
} else if (newerVersion(installed, served)) {
|
|
71
|
-
const restartsByItself = auto === "install" && restart !== "never" &&
|
|
67
|
+
const restartsByItself = auto === "install" && restart !== "never" && !updateBlocked && autoOffers !== installed;
|
|
72
68
|
if (!restartsByItself) {
|
|
73
69
|
notice = {
|
|
74
70
|
key: `installed:${installed}`,
|
|
@@ -101,6 +97,21 @@ export function versionNotice({ loaded, served, installed, available, updateBloc
|
|
|
101
97
|
return notice && !shown.has(notice.key) ? notice : null;
|
|
102
98
|
}
|
|
103
99
|
|
|
100
|
+
// The toast's words for an update operation's frame, or null: what runs and how
|
|
101
|
+
// it ended, for a tab whose sheet isn't open. An automatic run's worker passes
|
|
102
|
+
// say something only when one fails (the run ends in the Reload toast); a done
|
|
103
|
+
// install leaves it to the version toast; a snapshot repeats no ending.
|
|
104
|
+
export function updateFrameToast(data, { wasOpen = false, snapshot = false } = {}) {
|
|
105
|
+
if (wasOpen) return null;
|
|
106
|
+
if (data.auto && data.operation === "workers" && data.state !== "failed") return null;
|
|
107
|
+
const label = `${data.auto ? "Automatic " : ""}${data.operation === "install" ? "chi update" : "worker restart"}`
|
|
108
|
+
.replace(/^./, (c) => c.toUpperCase());
|
|
109
|
+
if (data.state === "running") return `${label} in progress`;
|
|
110
|
+
if (snapshot || !["done", "failed"].includes(data.state)) return null;
|
|
111
|
+
if (data.operation === "install" && data.state === "done") return null;
|
|
112
|
+
return `${label} ${data.state}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
104
115
|
// The info bar's badge for the open session's worker when it runs an older
|
|
105
116
|
// chi than the newest installed (or than chi web, while that is unknown),
|
|
106
117
|
// else null. A worker with the "restart" feature offers Restart; an older
|
|
@@ -45,9 +45,10 @@ function statusText(model) {
|
|
|
45
45
|
|
|
46
46
|
// Both restarts, once a newer chi is installed (an install done here, or the
|
|
47
47
|
// installed notice's toast) and nothing runs: either result keeps the other in reach.
|
|
48
|
+
// Not on an automatic run: it restarts them by itself.
|
|
48
49
|
function restartActions(model) {
|
|
49
50
|
const busy = ["starting", "running", "restarting"].includes(model.state);
|
|
50
|
-
if (!model.restartable || busy) return nothing;
|
|
51
|
+
if (!model.restartable || model.auto || busy) return nothing;
|
|
51
52
|
return html`<button type="button" class="ghost" data-act="restart-workers">Restart workers</button>
|
|
52
53
|
<button type="button" class="ghost" data-act="restart-web">Restart chi web</button>`;
|
|
53
54
|
}
|
|
@@ -125,6 +126,8 @@ export function createUpdateSheet({ dialog, update, restartWorkers, restartWeb }
|
|
|
125
126
|
model = {
|
|
126
127
|
...model,
|
|
127
128
|
...data,
|
|
129
|
+
// A frame names auto only when it is true: this run's word, not the last one's.
|
|
130
|
+
auto: data.auto === true,
|
|
128
131
|
rows: Array.isArray(data.rows) ? data.rows : [],
|
|
129
132
|
};
|
|
130
133
|
if (data.operation === "install" && data.state === "done") restartable = true;
|
|
@@ -274,7 +274,7 @@ module Samagotchi
|
|
|
274
274
|
model_name: session_access.show(id).model_name,
|
|
275
275
|
default_model: ModelProfile.default_model_name,
|
|
276
276
|
parent_strategy: strategy_json(session_access.parent_strategy(id)),
|
|
277
|
-
**(@hub&.chi_state || { version: VERSION, installed: nil, available: nil, update_blocked: nil, auto: "off", restart: "idle" }) })
|
|
277
|
+
**(@hub&.chi_state || { version: VERSION, installed: nil, available: nil, update_blocked: nil, auto: "off", restart: "idle", auto_offers: nil }) })
|
|
278
278
|
rescue SessionManager::OwnedByTUI => e
|
|
279
279
|
owned_by_tui(e)
|
|
280
280
|
rescue SessionAccess::Refused => e
|
|
@@ -41,8 +41,7 @@ module Samagotchi
|
|
|
41
41
|
|
|
42
42
|
# What a subscriber gets: `session` with {session: summary} for an
|
|
43
43
|
# upsert, `session_gone` with {id:} for a removal, `chi` with
|
|
44
|
-
#
|
|
45
|
-
# state changes. seq is monotonic per
|
|
44
|
+
# the #chi_state fields when chi version state changes. seq is monotonic per
|
|
46
45
|
# hub, for the log's sake: there is no replay.
|
|
47
46
|
Event = Struct.new(:type, :seq, :data, keyword_init: true)
|
|
48
47
|
|
|
@@ -76,7 +75,7 @@ module Samagotchi
|
|
|
76
75
|
# @param installed_versions [#newest] what is installed (InstalledVersions)
|
|
77
76
|
# @param update_auto [String] update.auto, said in the chi state (the page's wording)
|
|
78
77
|
# @param update_restart [String] update.restart, said beside it (whether
|
|
79
|
-
#
|
|
78
|
+
# a newer installed chi restarts by itself: no Restart toast)
|
|
80
79
|
def initialize(state_dir:, manager: SessionManager, now: nil, scan_interval: SCAN_INTERVAL,
|
|
81
80
|
full_probe_interval: FULL_PROBE_INTERVAL, installed_versions: InstalledVersions.new,
|
|
82
81
|
update_auto: Config.get("update.auto"), update_restart: Config.get("update.restart"))
|
|
@@ -87,6 +86,7 @@ module Samagotchi
|
|
|
87
86
|
@installed = nil
|
|
88
87
|
@available = nil
|
|
89
88
|
@update_blocked = nil
|
|
89
|
+
@auto_offers = nil
|
|
90
90
|
@update_state = nil
|
|
91
91
|
@manager = manager
|
|
92
92
|
@now = now || -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }
|
|
@@ -164,7 +164,7 @@ module Samagotchi
|
|
|
164
164
|
def chi_state
|
|
165
165
|
@monitor.synchronize do
|
|
166
166
|
{ version: VERSION, installed: @installed, available: @available, update_blocked: @update_blocked,
|
|
167
|
-
auto: @update_auto, restart: @update_restart }
|
|
167
|
+
auto: @update_auto, restart: @update_restart, auto_offers: @auto_offers }
|
|
168
168
|
end
|
|
169
169
|
end
|
|
170
170
|
|
|
@@ -180,6 +180,19 @@ module Samagotchi
|
|
|
180
180
|
nil
|
|
181
181
|
end
|
|
182
182
|
|
|
183
|
+
# AutoUpdate won't (or hasn't, for long) restart onto +version+ by itself:
|
|
184
|
+
# the page offers the restarts for it, in spite of update.auto. In memory:
|
|
185
|
+
# AutoUpdate says it again after a chi web restart.
|
|
186
|
+
def auto_offer_restart(version)
|
|
187
|
+
@monitor.synchronize do
|
|
188
|
+
next if @auto_offers == version
|
|
189
|
+
|
|
190
|
+
@auto_offers = version
|
|
191
|
+
emit("chi", chi_state)
|
|
192
|
+
end
|
|
193
|
+
nil
|
|
194
|
+
end
|
|
195
|
+
|
|
183
196
|
# Publish the owner-triggered update's progress or result for current
|
|
184
197
|
# tabs, and retain the latest state for a reconnect's snapshot.
|
|
185
198
|
def publish_update(state:, rows:, progress: nil, operation: nil, version: nil, auto: false)
|