Files
okxlin e058ba3f73 fix(images): harden runtimes and publish verified artifacts (#85)
Reject unsafe workstation credentials, fix native ARM64 Java, and remove duplicate extensions, caches and temporary tool layers.

Pin OpenCode's baseline and Gemini's source/runtime inputs; refresh maintained browser and OS packages; fix Nginx and vendored ZIP vulnerabilities. Verify real login, default plugins, browser/CDP persistence and isolated OpenClaw sandbox operations.

Build each platform once and bind release publication to the tested config and manifest digests. Keep service-specific vulnerability gates and refresh DSH APT stages during PR verification.

Validation: all 13 PR verification jobs passed, including native amd64/arm64 workstations and DSH variants, plus both browser variants and OpenClaw on amd64. Publication jobs were skipped for PR verification.
2026-09-13 01:52:07 +08:00

205 lines
7.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# opencode-workstation-builder
这个目录按 `okxlin/release-factory` 的目录习惯放置:
-`1panel-builder/` 平级
- 只承载 `opencode-workstation` 的镜像构建发布内容
- GitHub Actions workflow 放在仓库根下的 `.github/workflows/`
## 目录说明
- `configs/architectures.sh`:维护当前允许发布的平台
- `scripts/resolve-build-params.sh`:把 workflow 输入收敛成最终镜像 tag 与平台列表
- `image/`:独立镜像构建上下文
- `Dockerfile`
- `.env.example`
- `scripts/`
## 当前策略
- 默认发布 `linux/amd64,linux/arm64`
- 默认镜像仓库名:`ghcr.io/<owner>/opencode-workstation`
- workflow 支持手动构建、每周安全刷新,以及按路径触发的 PR 验证
- 默认 tag`latest`
- 可选附带 `latest` 别名
- `amd64``arm64` 分别在原生 runner 构建一次,验证默认插件、服务鉴权及 HOME 持久化后扫描;发布复用同一个镜像摘要,再合并平台 manifest
- workflow 输入会先校验 tag、平台列表和镜像仓库名;BuildKit cache 使用 GitHub Actions cache 的 `mode=min`,减少缓存空间压力
- Tooling: git, gh, ripgrep, fd, jq, yq, shellcheck, shfmt, actionlint, comment-checker, Docker CLI, Go 1.27.0, Rust, Bun 1.4.0, pnpm, yarn
- 默认保留 oh-my-opencode 与可选的 Dynamic Context PruningDCP)支持,不再预装 `opencode-gpt-unlocked`
镜像内置 `image/opencode-runtime/package-lock.json` 固定的 OpenCode 基线,首次启动无需联网下载主程序。
默认 DCP 版本也记录在该目录的 `package.json`,仍在用户目录安装;已有 OpenCode 安装会保留,
可用 `OPENCODE_NPM_PACKAGE``OPENCODE_FORCE_INSTALL=1` 主动切换版本。
TypeScript 由 npm 安装,避免 Debian 的 `node-typescript` 再引入一套旧 Node 运行时。
CI 分别扫描镜像和默认启动后安装的用户目录。OpenCode 是编译后的二进制,Trivy 无法完整识别内部
JavaScript 依赖;上游公告核对及覆盖限制见 [基线说明](image/opencode-runtime/UPSTREAM.md)。
用户自行升级的主程序和插件不等同于该次镜像发布所验证的版本。
## 运行时权限模型
镜像默认仍以 `opencode` 用户运行。入口脚本会先准备 `/workspace``/cache` 和官方 HOME 持久化目录,并在检测到 `/var/run/docker.sock` 时按宿主 socket 的 GID 动态创建/加入容器内用户组,然后刷新到普通用户会话。
如果部署系统临时以 root 启动容器,入口脚本完成目录准备后也会降权回 `opencode` 用户继续运行,避免长期以 root 写入持久化数据。
可选修复开关:
- `FIX_WORKSPACE_OWNERSHIP_RECURSIVE=true`:递归修复 `/workspace` ownership
- `FIX_CACHE_OWNERSHIP_RECURSIVE=true`:递归修复 `/cache` ownership
## 运行时目录模型
当前工作站镜像已经改为**直接按 OpenCode 官方 HOME 路径运行**,不再以 `/config/opencode` 作为主运行语义。
### 运行时真实目录
- `~/.config/opencode`
- `~/.agents`
- `~/.claude`
- `~/.opencode`
- `~/.local/share/opencode`
- `~/.local/share/oh-my-opencode`
- `/workspace`
### 推荐持久化挂载
- `/home/opencode/.config`
- `/home/opencode/.agents`
- `/home/opencode/.claude`
- `/home/opencode/.opencode`
- `/home/opencode/.local/share`
- `/workspace`
这样做的原因:
- 与 OpenCode upstream 源码的目录发现逻辑一致
- `skills` / `agents` / `claude-compatible` 扩展不需要额外路径翻译
- 避免 `/config -> HOME` 的单文件同步漂移
- 后续 upstream 扩展 HOME 目录扫描时兼容风险最低
## 运行时配置分层
推荐按三层使用:
1. **部署级环境变量层**
- 通过 `.env` / CI / 部署平台注入
- 适合:`OPENCODE_MODEL``OPENCODE_SMALL_MODEL``OPENCODE_PROVIDER_ID``OPENCODE_EXTRA_PLUGINS`、以及各类 `*_BASE_URL` / `*_API_KEY`
2. **生成配置层**
- `~/.config/opencode/opencode.json`
-`image/scripts/update_opencode_config.py` 在启动/安装阶段更新
- 这是生成产物,不建议长期手工维护
3. **用户覆盖层**
- `~/.config/opencode/opencode.user.json``~/.config/opencode/opencode.user.jsonc`
- 适合手工追加 provider、models、plugin 高级配置、额外 MCP 条目
当前脚本会在写完 `opencode.json` 后再合并用户覆盖层:
- `plugin` 数组:追加去重
- `provider` / `models` / `mcp` 等对象:深度合并
- 未知键:保留,不主动删除
升级到包含本迁移的镜像后,入口脚本会在首次启动时从生成的全局配置中移除旧版自动写入的
`opencode-gpt-unlocked` 插件和 `experimental.refusal_patcher` 配置。迁移是幂等的,只改动这两类
已废弃条目,不会删除用户覆盖文件或其它 provider、model、MCP 和插件配置。入口还会把 OMO 的
`~/.omo` 数据桥接到已持久化的 `~/.config/.omo`,避免新版本 OMO 在跨挂载点备份配置时触发
`EXDEV`;已有的 `.omo` 目录会先复制并保留备份。
## Skills / Agents / Claude 兼容目录
### OpenCode skills
- `~/.config/opencode/skills/...`
### agent-compatible skills
- `~/.agents/skills/...`
### claude-compatible skills
- `~/.claude/skills/...`
### agent markdown / command markdown
OpenCode 本身还会读取项目内或兼容目录中的:
- `agents/**/*.md`
- `agent/**/*.md`
- `commands/**/*.md`
- `command/**/*.md`
- `AGENTS.md`
- `.opencode/...`
所以 `/workspace` 也应该是长期持久化目录,而不是短暂临时盘。
## `opencode.user.json` 怎么写
### 只加插件
```json
{
"plugin": [
"my-custom-plugin",
"@org/another-plugin"
]
}
```
### 给现有 provider 增加更多模型
```json
{
"provider": {
"mimo": {
"npm": "@ai-sdk/openai-compatible",
"name": "Mimo",
"options": {
"baseURL": "{env:OPENAI_BASE_URL}",
"apiKey": "{env:OPENAI_API_KEY}"
},
"models": {
"mimo-v2.5": { "name": "mimo-v2.5" },
"mimo-v2-pro": { "name": "mimo-v2-pro" }
}
}
}
}
```
### 插件 + provider 一起扩展
```json
{
"plugin": [
"my-custom-plugin"
],
"provider": {
"mimo": {
"npm": "@ai-sdk/openai-compatible",
"name": "Mimo",
"options": {
"baseURL": "{env:OPENAI_BASE_URL}",
"apiKey": "{env:OPENAI_API_KEY}"
},
"models": {
"mimo-v2.5": { "name": "mimo-v2.5" },
"mimo-v2-pro": { "name": "mimo-v2-pro" }
}
}
}
}
```
建议:
- 默认主模型仍优先通过 `OPENCODE_MODEL` 设置
- 对于 `mimo` 这类自定义 provider,推荐在 `opencode.user.json` 中显式声明 `provider.<id>`;环境变量自动写入 `baseURL` 目前只覆盖脚本内置映射的 provider
- 不要把真实密钥硬编码进 `opencode.user.json`,优先用 `{env:...}`
- 不要把 `~/.config/opencode/opencode.json` 当作长期手工配置源
## PR reviewer 该看什么
- `build-opencode-workstation.yml``release-workstations.yml`:原生双架构验证、tag / 平台规则及发布摘要绑定
- `image/Dockerfile`:是否仍然以独立镜像上下文承载运行时依赖
- `image/scripts/entrypoint.sh``image/scripts/bootstrap-opencode-userland.sh``image/scripts/install-oh-my-opencode.sh`:是否继续保证官方 HOME 路径上的持久化语义及废弃配置迁移
- `image/scripts/update_opencode_config.py`:是否继续保留用户覆盖层、插件去重合并和废弃条目清理语义