fix(macos): patch vault role post-clone for macOS-standard dirs

The vault role's 'Ensure standalone Vault directories exist' task creates
/etc/vault.d and /opt/vault/data with owner: root and lacks the Darwin guard
its sibling tasks have, so it fails under macOS become=false. Unlike the
bridge dir (owned by the service user, fixable via -e), this owner: root is
hardcoded and not overridable, so the role logic must change.

Since the role lives in a separate playbooks repo, reuse the existing
post-clone patch mechanism (cf. patch_playbook_user_systemd): add
patch_playbook_vault_macos() that, on Darwin only, guards the directory task,
makes vault dirs/binary OS-conditional (macOS -> ~/Library/Application
Support/vault[/data], /opt/homebrew/bin/vault; Linux unchanged), and creates
the user-owned data dir in macos.yml. Idempotent; verified against the real
role. Documents TC-MAC-013.
This commit is contained in:
Haitao Pan 2026-06-18 11:33:42 +00:00
parent 470e5163f5
commit 11701c6037
3 changed files with 109 additions and 1 deletions

View File

@ -107,6 +107,16 @@
| **修复方案** | 双层:①`setup-ai-workspace-all-in-one.sh` 的 Darwin 分支注入 `-e xworkmate_bridge_base_dir="$HOME/Library/Application Support/cloud-neutral/xworkmate-bridge"``curl \| bash` 拉取的是本仓库脚本playbooks 来自独立仓库,故脚本侧 `-e` 是该路径下唯一可生效的修复点②role `defaults/main.yml` 将默认值改为按 `ansible_os_family` 的三元表达式,使离线/本地 playbook 路径亦正确 |
| **生效前提** | `curl \| bash` 从 GitHub `main` 拉取脚本,修复必须先 push 到 `ai-workspace-lab/xworkspace-console``main`否则远端仍是旧脚本extra-vars 优先级最高,若 `-e` 已执行则绝不会回落到 `/opt`,由此可判定执行的是未修复的远端脚本) |
## TC-MAC-013: Vault standalone 目录写入系统路径被拒
| 项目 | 内容 |
|------|------|
| **触发文件** | `roles/vhosts/vault/tasks/main.yml`、`roles/vhosts/vault/vars/main.yml`、`roles/vhosts/vault/tasks/macos.yml` |
| **触发报错** | `TASK [roles/vhosts/vault/ : Ensure standalone Vault directories exist]``[Errno 13] Permission denied: b'/etc/vault.d'`、`b'/opt/vault'` |
| **根因** | “Ensure standalone Vault directories exist” 任务以 `owner: root` 创建 `/etc/vault.d``/opt/vault/data`,且**缺失** vault 角色其余 standalone 任务都带的 `ansible_os_family != 'Darwin'` 守卫。macOS 以 `become=false` 运行,既无权写 `/etc`、`/opt``owner: root` 的 chown 也无法完成。与 bridge 不同(其目录 owner 为服务用户,可由 `-e` 修复),该任务的 `owner: root` 为硬编码,无法用 extra-vars 覆盖,必须改 role 逻辑 |
| **目录策略** | Linux 保持 `/etc/vault.d`、`/opt/vault/data`macOS 改用 Apple 标准 `~/Library/Application Support/vault`、`~/Library/Application Support/vault/data`;二进制路径 macOS 取 `/opt/homebrew/bin/vault`brew 安装位置),免去需 sudo 的 `/usr/local/bin` 软链依赖 |
| **修复方案** | role 位于独立 playbooks 仓库,无法从本仓库直接提交;沿用脚本既有的“克隆后打补丁”机制(参见 `patch_playbook_user_systemd`),在 `setup-ai-workspace-all-in-one.sh` 新增 `patch_playbook_vault_macos()`,仅在 Darwin 下对克隆出的 vault 角色:①给目录创建任务追加 `ansible_os_family != 'Darwin'` 守卫;②把 `vault_config_dir`/`vault_data_dir`/`vault_binary_path` 改为按 OS 的三元表达式;③在 `macos.yml` 前置创建用户属主的数据目录(含 launchd 日志目录 `~/.local/state/xworkspace`)。该补丁对 `curl \| bash` 与本地执行两条路径均生效,幂等,且不改动 Linux 行为 |
---
## 修复维度总结
@ -116,7 +126,8 @@
| 组件获取方式替换 (brew vs binary) | TC-001 |
| 权限收缩 (become: false) | TC-002, TC-006, TC-007, TC-008, TC-009 |
| 用户组适配 (staff vs ubuntu) | TC-003, TC-010 |
| 目录路径降级 ($HOME vs /home/ubuntu, /opt) | TC-004, TC-006, TC-009, TC-010, TC-012 |
| 目录路径降级 ($HOME vs /home/ubuntu, /opt, /etc) | TC-004, TC-006, TC-009, TC-010, TC-012, TC-013 |
| 克隆后补丁注入 (post-clone patch) | TC-013 |
| 包管理器绕过 (skip apt on Darwin) | TC-008, TC-010 |
| 模板变量解耦 (remove nvm/nodejs_version) | TC-005 |
| 路径空格兼容 (argv vs string) | TC-011 |

View File

@ -90,3 +90,30 @@ curl -sfL https://raw.githubusercontent.com/ai-workspace-lab/xworkspace-console/
```
> 注:方式 A 走 `curl | bash` 之外的本地执行路径,脚本侧 `-e` 覆盖会直接生效role 默认的三元表达式则覆盖离线 `PLAYBOOK_DIR` 场景。
---
## 8. 续vault 角色阻塞TC-MAC-01319:24
**进展**bridge 修复生效后,部署从 145 → 154 个任务,新的阻塞点为 vault
```
TASK [roles/vhosts/vault/ : Ensure standalone Vault directories exist]
failed (item=/etc/vault.d): Permission denied: b'/etc/vault.d'
failed (item=/opt/vault/data): Permission denied: b'/opt/vault'
```
**根因**:该目录创建任务以 `owner: root``/etc/vault.d`、`/opt/vault/data`,且缺失 vault 其余 standalone 任务都有的 `ansible_os_family != 'Darwin'` 守卫。`become=false` 下既无权写 `/etc`、`/opt``owner: root` 也无法 chown。
**与 bridge 的关键差异**bridge 目录 owner 是服务用户macOS 即当前用户),故可用脚本 `-e xworkmate_bridge_base_dir=...` 修复vault 这里的 `owner: root` 是**硬编码**extra-vars 无法覆盖,必须改 role 逻辑。而 role 在独立 playbooks 仓库,不在本仓库。
**解法(仍落在本仓库、对 `curl|bash` 与本地执行都生效)**:复用脚本既有的“克隆后打补丁”机制(`patch_playbook_user_systemd` 已有先例),新增 `patch_playbook_vault_macos()`,仅在 Darwin 下对克隆出的 vault 角色:
1. 给“Ensure standalone Vault directories exist”追加 `ansible_os_family != 'Darwin'` 守卫;
2. `vault_config_dir`/`vault_data_dir`/`vault_binary_path` 改为按 OS 的三元表达式Linux 不变macOS 用 `~/Library/Application Support/vault[/data]``/opt/homebrew/bin/vault`
3. `macos.yml` 前置创建用户属主的数据目录(含 launchd 日志目录 `~/.local/state/xworkspace`)。
**目录策略(按用户确认)**Linux → `/opt/vault/data`macOS → `~/Library/Application Support/vault/data`
**验证**`bash -n` 通过;用真实 vault 角色副本跑补丁YAML 合法、Darwin 守卫计数 5→6、二次执行幂等、Linux 渲染仍为原系统路径、macOS 渲染为 Apple 标准路径。
**注意(运行方式)**:上一轮 `bash scripts/setup-...sh | bash -` 的管道是错误用法(会把脚本日志再喂给第二个 bash。本地执行应去掉管道`bash scripts/setup-ai-workspace-all-in-one.sh`。

View File

@ -1110,6 +1110,73 @@ if updated != text:
PY
}
# On macOS the vault role's "Ensure standalone Vault directories exist" task
# targets /etc/vault.d and /opt/vault/data with owner: root. Those paths are not
# writable under become=false and are non-standard for macOS, so patch the
# cloned role to: (1) skip that root-owned directory task on Darwin, (2) point
# the vault dirs/binary at Apple-standard, user-writable locations, and (3)
# create the data dir (user-owned) in the macОS task path. Linux is untouched.
patch_playbook_vault_macos() {
local vars_file="roles/vhosts/vault/vars/main.yml"
local tasks_file="roles/vhosts/vault/tasks/main.yml"
local macos_file="roles/vhosts/vault/tasks/macos.yml"
[ -f "$vars_file" ] && [ -f "$tasks_file" ] && [ -f "$macos_file" ] || return 0
python3 - <<'PY'
from pathlib import Path
vars_path = Path("roles/vhosts/vault/vars/main.yml")
tasks_path = Path("roles/vhosts/vault/tasks/main.yml")
macos_path = Path("roles/vhosts/vault/tasks/macos.yml")
# 1) Make vault dirs and binary path OS-conditional (Linux unchanged).
vars_text = vars_path.read_text()
vars_subs = {
"vault_binary_path: /usr/local/bin/vault":
"vault_binary_path: \"{{ '/opt/homebrew/bin/vault' if ansible_os_family == 'Darwin' else '/usr/local/bin/vault' }}\"",
"vault_config_dir: /etc/vault.d":
"vault_config_dir: \"{{ (ansible_env.HOME ~ '/Library/Application Support/vault') if ansible_os_family == 'Darwin' else '/etc/vault.d' }}\"",
"vault_data_dir: /opt/vault/data":
"vault_data_dir: \"{{ (ansible_env.HOME ~ '/Library/Application Support/vault/data') if ansible_os_family == 'Darwin' else '/opt/vault/data' }}\"",
}
for old, new in vars_subs.items():
if old in vars_text:
vars_text = vars_text.replace(old, new)
vars_path.write_text(vars_text)
# 2) Skip the root-owned directory creation task on macOS.
tasks_text = tasks_path.read_text()
dir_when_old = (
' loop:\n'
' - "{{ vault_config_dir }}"\n'
' - "{{ vault_data_dir }}"\n'
' when:\n'
' - vault_deploy_mode == "standalone"\n'
)
dir_when_new = dir_when_old + " - ansible_os_family != 'Darwin'\n"
if dir_when_old in tasks_text and " - ansible_os_family != 'Darwin'\n\n- name: Deploy standalone Vault systemd" not in tasks_text:
tasks_text = tasks_text.replace(dir_when_old, dir_when_new, 1)
tasks_path.write_text(tasks_text)
# 3) Create the macOS vault dirs (user-owned) before the launchd plist is laid down.
macos_text = macos_path.read_text()
dir_task = (
"- name: Ensure macOS Vault directories exist\n"
" ansible.builtin.file:\n"
" path: \"{{ item }}\"\n"
" state: directory\n"
" mode: \"0755\"\n"
" loop:\n"
" - \"{{ vault_config_dir }}\"\n"
" - \"{{ vault_data_dir }}\"\n"
" - \"{{ ansible_env.HOME }}/.local/state/xworkspace\"\n\n"
)
anchor = "- name: Install HashiCorp Tap\n"
if "Ensure macOS Vault directories exist" not in macos_text and anchor in macos_text:
macos_text = macos_text.replace(anchor, dir_task + anchor, 1)
macos_path.write_text(macos_text)
PY
}
ensure_core_skills_source() {
if [ "${AI_WORKSPACE_PREFETCH_COMPLETED:-false}" = "true" ] &&
[ -d "$XWORKSPACE_CORE_SKILLS_DIR/skills" ]; then
@ -1889,6 +1956,9 @@ else
fi
patch_playbook_user_systemd
if [ "$(detect_os)" = "darwin" ]; then
patch_playbook_vault_macos
fi
prefetch_independent_sources
ensure_core_skills_source
ensure_xworkmate_bridge_source