Repository navigation
✨ 新增 sctl service:把 serve 注册为用户级系统服务 - #11
Merged
Merged
Conversation
sctl serve 只能前台运行,文档让用户自己配置服务管理器却不提供工具,手写定义还容易 和 CLI 的数据目录、监听地址对不上。 - sctl service install|uninstall|start|stop|restart|status:macOS launchd LaunchAgent、 Linux systemd --user、Windows 登录触发的计划任务,不需要管理员权限 - install 固化当前生效的 --data-dir(绝对路径)、--listen-address、非默认 --log-level; 二进制优先用 PATH 里的同一文件,Homebrew 升级后不失效 - 隐藏参数 serve --service:日志只写文件;Windows 上脱离独占的控制台 - 等 daemon 时以控制令牌变化识别新进程;安装前和 start 前遇到已在运行的 daemon 时不启动服务 - schtasks / launchctl 只把非零退出当作“不存在”,命令本身无法运行时如实报错 - spec:docs/specs/2026-10-23-service.md
Member
|
是只适配了linux么?mac/windows是不是也要处理一下 |
Member
|
我晚点去windows里面跑一下 |
|
辛苦哥哥。 哥哥这个sctl原生功能真是太好了,ggnb!!已在本机严肃配置,哈哈哈哈。我现在在本机是用一个每 2 分钟的计划任务轮询 |
在 Windows 11 真机上验证发现: - restart 和覆盖安装只靠 /Run 配 StopExisting,任务计划程序停掉旧实例后立刻启动新实例,新实例绑定端口 失败就退出,服务从此不在运行。/End 约 20ms 返回,旧进程约 1 秒后才退出,所以 stop、uninstall、restart 和覆盖安装结束实例后都等监听地址能被绑定(/End 前地址就空着时不等),再按需 /Run;不等的话 stop 后紧接着 start 会把还没退出的旧实例当成在运行,什么都不启动。MultipleInstancesPolicy 改为 IgnoreNew - RestartOnFailure 不会拉起启动后崩溃的 daemon(强制结束后 3 分钟内没有重新运行),删掉它,cli.md 与 spec 改为如实说明 Windows 上崩溃后要 sctl service start - service.New 接收监听地址 - logging 测试在 Windows 上死锁:对管道 Sync(FlushFileBuffers)会阻塞到读端读完,读端改为并行;日志文件 句柄不关闭,Windows 上不用 t.TempDir - TestLaunchd 的期望路径按宿主平台拼接
Member
|
在一台 Windows 11(Build 26200)真机上把这个 PR 跑了一遍,修复已推到本分支(69b17b0)。 发现的问题
修复
修复后真机验证(隔离数据目录与端口)
遗留
|
|
辛苦哥哥,ggnb!! 哥哥说的这个「给 sctl 任务加重复触发器」。我实际是另开了一个任务轮询端口,跟 sctl 任务分开的,所以 stop 语义那个问题我这边是用 哥哥说的这个「进程活着只是不响应」确实我漏了。原来 Stop 完固定睡 2 秒再 Start。现在改成两个条件都满足才继续: $released = $false
for ($i = 0; $i -lt 40; $i++) {
$state = Get-State # 任务状态
$busy = Test-Port -P $port # 端口占用
if ($state -ne 'Running' -and -not $busy) { $released = $true; break }
Start-Sleep -Milliseconds 500
}
if (-not $released) {
# 20 秒还没释放,记一笔跳过这轮,下轮再说
return
}
Start-ScheduledTask -TaskName 'sctl serve'任务得不再是 Running(否则
|
…住的 uninstall - stop 只停止服务、下次登录仍会启动;新增 disable(停止并取消登录自启)和 enable(恢复并启动)。被禁用时 start / restart 退出码 3 并提示 enable:launchd 的禁用标记和被禁用的计划任务本来就不能手动启动,systemd 可以,统一拒绝。status 显示是否登录自启,install 总会重新启用 - Windows 结束实例前读任务“上次运行结果”(267009 为正在运行,不随系统语言变化)判断有没有实例:没有时既不 /End 也不等,用户手动启动的 daemon 占着端口时 stop / uninstall 不再等满 10 秒后失败、uninstall 也不再删不掉任务 - Windows 等的是任务定义里固化的监听地址(覆盖安装等旧定义的地址),不是本次调用的 --listen-address: 按错误地址判断“已放开”会紧接着 /Run 一个绑定失败的实例。install 因此总是写上 --listen-address 且放在最前, schtasks 按系统代码页输出定义,只有排在非 ASCII 路径之前的内容能可靠读回。service.New 不再需要地址 - 首次安装时服务管理器拒绝启用(例如 systemctl --user 没有会话总线),删掉刚写的定义,不留下“已安装”的假象 - 文档:cli.md、architecture.md 与 spec 同步上述行为;更正 architecture.md 中“不与 daemon 通信”、spec 中 “-o json 适用于所有子命令”“控制台只在登录时闪一下”等与代码不符的描述,补上 Windows 不收集崩溃输出
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


背景
sctl serve只能在前台运行。文档让用户“交给系统服务管理器”,但没有提供工具;手写的服务定义也容易和 CLI 的数据目录、监听地址对不上。Spec:docs/specs/2026-10-23-service.md。改动
sctl service install|uninstall|start|stop|restart|enable|disable|status~/Library/LaunchAgents/com.scriptscat.sctl.plist)~/.config/systemd/user/sctl.service,按绝对路径 enable)IgnoreNew)stop只停止当前运行的服务,下次登录仍会启动;disable停止并取消登录自启,enable恢复并启动。被禁用时start/restart退出码 3 并提示enable;install总会重新启用。install固化本次调用实际生效的--listen-address(总是写上,且放在最前)、--data-dir(转成绝对路径,包括来自SCTL_DATA_DIR的)和非默认的--log-level。二进制路径优先用PATH里的同一个文件,Homebrew 升级后服务不会失效。首次安装时服务管理器拒绝启用(例如没有用户会话总线),删掉刚写的定义。serve --service:日志只写<data dir>/logs,stderr 只留给运行时崩溃信息;Windows 上进程独占控制台时脱离它。install前和start前,只要已有 daemon 能连上(包括其他数据目录的 daemon),就不再启动服务。/End约 20ms 返回,旧进程约 1 秒后才退出;StopExisting停掉旧实例后立刻启动新实例,新实例绑定端口失败。所以 stop、uninstall、restart、覆盖安装先看任务有没有实例在运行(读不随系统语言变化的“上次运行结果”,267009 为正在运行),有就/End,再等任务定义里固化的监听地址能被绑定,需要时再/Run;没有实例时既不结束也不等。schtasks按系统代码页输出,非 ASCII 路径读回来不可靠,所以监听地址放在参数最前。RestartOnFailure,文档说明崩溃后用sctl service start。control.Address()/control.Healthy(),logging.Setup增加toStderr参数。验证
go build/vet ./...、go test ./... -race通过;golangci-lint 在 darwin/linux/windows 三个平台上都是 0 issues;Windows 11 上go test ./...全部通过。kill -9后约 3 秒被拉起;restart、覆盖安装、stop 紧接 start、uninstall 紧接 install;disable 后print-disabled为 disabled、start / restart 退出码 3,enable 后恢复并运行,disable 后再 install 重新启用;service.stderr.log为 0 字节;uninstall 幂等;前台 daemon 占着端口时 install 被拒绝;无残留。kill -9后 8 秒内被拉起(NRestarts=1);路径含空格、%、$、引号、反斜杠时单元转义正确,进程实际参数一致;相对路径的SCTL_DATA_DIR固化为绝对路径;没有用户会话总线时首次 install 退出码 3 且不留单元文件。taskkill /F后 3 分钟内不会被拉起(与文档一致)。~/Documents下时,launchd 启动的进程会被 macOS 隐私保护(TCC)卡住,cli.md 已写明。已知限制
sctl service start(暂不处理)。--data-dir/--listen-address判断 daemon 是否起来,参数与安装时不同会误报;文档要求带同样的参数。brew services二选一(同一个端口),文档已说明。