feat(cli): apply coordinated updates during maintenance

This commit is contained in:
Lemon-miaow committed 2026-10-04 17:36:21 +08:00
1 parent cb3ed94026
commit 4d8e9af240
21 files changed
+990 -457

No files matched your search

+4 -4
View File
@@ -90,9 +90,9 @@
"reviewed_at": "Reviewed At",
"reject_reason": "Rejection Reason",
"updates_title": "Maintenance & Backups",
"updates_subtitle": "Check that the control-plane database backup is fresh, see which components have a newer release, and set the platform-wide maintenance window. Felis never applies an update on its own: `felis update` on the host shows this window and warns before you apply outside it.",
"updates_window_advisory": "The window is advisory. Updates happen only when someone runs the apply commands `felis update` prints; it reads this window and warns when run outside it.",
"updates_current_unset": "No maintenance window set. `felis update` will say so and leave the timing to you.",
"updates_subtitle": "Check database backup freshness, review available versions, and set a maintenance window. Run `felis update` on the host to inspect a target; only an explicit `--apply` installs it.",
"updates_window_advisory": "Apply is allowed inside this window. The host checks it before taking a backup and again before installation. Outside the window, apply is refused unless `--now` explicitly starts manual maintenance; `--force` does not bypass the checks.",
"updates_current_unset": "No maintenance window set. Configure one before applying, or explicitly use `--apply --now` for manual maintenance.",
"updates_start_label": "Start Time",
"updates_end_label": "End Time",
"updates_set_title": "Configure Maintenance Window",
@@ -159,7 +159,7 @@
"versions_state_unknown": "Feed unreachable",
"versions_state_unreadable": "Version unreadable",
"versions_state_pinned": "Pinned",
"versions_apply_hint": "To apply, run this on the host, inside the maintenance window below if you set one. It prints the exact command for each component and warns when run outside the window:",
"versions_apply_hint": "Run this read-only check on the host. It shows the target, scope and an exact `--apply` command. Review it, then run that command during the maintenance window:",
"versions_never_title": "No version check has been recorded yet",
"versions_stale_title": "The newest version check is more than {{hours}} hours old",
"versions_fix_hint": "The versions below may be out of date. Run a check on the host now, then read the timer's log to find out why it did not run:",
+4 -4
View File
@@ -90,9 +90,9 @@
"reviewed_at": "审核时间",
"reject_reason": "驳回理由",
"updates_title": "维护与备份",
"updates_subtitle": "查看控制面数据库备份是否新鲜、哪些组件有新版本,并设置全局维护窗口。Felis 从不自行应用更新:宿主机上的 `felis update` 会显示这个窗口,在窗口外应用前给出警告。",
"updates_window_advisory": "维护窗口是提示性的。只有有人执行 `felis update` 打印的应用命令时才会更新;该命令会读取这个窗口,在窗口外运行时给出警告。",
"updates_current_unset": "当前未设置维护窗口。`felis update` 会提示这一点,何时应用由你决定。",
"updates_subtitle": "查看数据库备份和组件版本,并设置维护窗口。在宿主机运行 `felis update` 检查目标版本;只有显式执行 `--apply` 才会更新。",
"updates_window_advisory": "更新默认只允许在维护窗口内开始。宿主机会在备份前和安装前再次检查窗口;窗口外拒绝执行。临时维护需显式添加 `--now`,`--force` 不会跳过这些检查。",
"updates_current_unset": "尚未设置维护窗口。请先设置窗口,或显式使用 `--apply --now` 开始临时维护。",
"updates_start_label": "开始时间",
"updates_end_label": "结束时间",
"updates_set_title": "配置维护窗口",
@@ -158,7 +158,7 @@
"versions_state_unknown": "无法访问发行源",
"versions_state_unreadable": "读不到版本",
"versions_state_pinned": "已固定",
"versions_apply_hint": "要应用更新,请在主机上运行下面的命令;如果设置了维护窗口,请在窗口内运行。它会列出每个组件的确切命令,并在窗口外运行时发出警告:",
"versions_apply_hint": "在宿主机执行下面的只读检查,查看更新目标、范围和确切的 `--apply` 命令。确认后,在维护窗口内执行该命令:",
"versions_never_title": "还没有记录过版本检查",
"versions_stale_title": "最近一次版本检查已超过 {{hours}} 小时",
"versions_fix_hint": "下面的版本可能已过时。请先在主机上立即检查一次,再查看定时器日志找出它没有运行的原因:",
+1 -1
View File
@@ -1232,7 +1232,7 @@ export interface paths {
};
/**
* Read the SysAdmin-set auto-update maintenance window (admin).
* @description Advisory: Felis applies no update on its own. `felis update` on the host reads this window, reports where now sits against it, and warns before an apply outside it. The single platform-wide maintenance window during which Felis may apply a Scheduled component's update to itself (decision core internal/updates). An unset window — never set, or explicitly cleared — reads back as {start:null,end:null}. API+persistence only: nothing consumes the window until the INTEGRATION runner and executors are wired, so setting it changes no behavior yet.
* @description Felis applies no update on its own. `felis update` checks versions and prints an explicit apply command. `felis update --apply` reads this platform-wide [start,end) window before backup and before installation, refusing outside it unless `--now` explicitly starts manual maintenance. An unreadable window is always a refusal, including with `--now` or `--force`. An unset window reads back as {start:null,end:null}.
*/
get: operations["getUpdateWindow"];
/**
+3 -3
View File
@@ -24,7 +24,7 @@ afterEach(() => {
});
describe("UpdatesPage", () => {
it("says the window is advisory, since nothing applies an update on its own", async () => {
it("explains the maintenance guard and explicit apply flow", async () => {
render(
<MemoryRouter>
<UpdatesPage />
@@ -32,10 +32,10 @@ describe("UpdatesPage", () => {
);
expect(
await screen.findByText(
"The window is advisory. Updates happen only when someone runs the apply commands `felis update` prints; it reads this window and warns when run outside it.",
"Apply is allowed inside this window. The host checks it before taking a backup and again before installation. Outside the window, apply is refused unless `--now` explicitly starts manual maintenance; `--force` does not bypass the checks.",
),
).toBeTruthy();
expect(screen.getByText("No maintenance window set. `felis update` will say so and leave the timing to you.")).toBeTruthy();
expect(screen.getByText("No maintenance window set. Configure one before applying, or explicitly use `--apply --now` for manual maintenance.")).toBeTruthy();
expect(screen.queryByText(/applied automatically|may apply a Scheduled update/)).toBeNull();
});
});