@crvouga/dev-fleet 0.0.0-stage → 0.1.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.
- package/.mise/locks/aws-cdk/2.1146.1/aube-lock.yaml +24 -0
- package/.mise/locks/aws-cdk/2.1146.1/package.json +7 -0
- package/.mise/locks/azure-cli/2.91.0~a837e014/pyproject.toml +5 -0
- package/.mise/locks/azure-cli/2.91.0~a837e014/uv.lock +2794 -0
- package/.mise/locks/gemini/0.66.0~84aac8e6/aube-lock.yaml +123 -0
- package/.mise/locks/gemini/0.66.0~84aac8e6/package.json +12 -0
- package/.mise/locks/npm-devcontainers-cli/0.90.0/aube-lock.yaml +24 -0
- package/.mise/locks/npm-devcontainers-cli/0.90.0/package.json +7 -0
- package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/aube-lock.yaml +1432 -0
- package/.mise/locks/npm-modelcontextprotocol-inspector/2.13.0~4c109bea/package.json +7 -0
- package/.mise/locks/npm-vercel/65.8.0/aube-lock.yaml +3595 -0
- package/.mise/locks/npm-vercel/65.8.0/package.json +7 -0
- package/.mise/locks/npm-wrangler/4.156.0/aube-lock.yaml +895 -0
- package/.mise/locks/npm-wrangler/4.156.0/package.json +7 -0
- package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/aube-lock.yaml +24 -0
- package/.mise/locks/npm-yarnpkg-cli-dist/4.18.1/package.json +7 -0
- package/LICENSE +21 -0
- package/README.md +157 -2
- package/agents/antigravity-proxy.ts +3 -0
- package/agents/audit.md +89 -0
- package/agents/claude-proxy +5 -0
- package/agents/claude-proxy.ts +3 -0
- package/agents/codex-proxy.ts +3 -0
- package/agents/grok-proxy.ts +3 -0
- package/agents/managed-proxy.ts +55 -0
- package/agents/opencode-proxy.ts +3 -0
- package/agents/registry.json +209 -0
- package/agents/registry.schema.json +149 -0
- package/agents/setup.md +128 -0
- package/agents/t3.json +40 -0
- package/ansible/macos/manager.yml +30 -0
- package/ansible/macos/policy.json +17 -0
- package/ansible/monitoring/README.md +154 -0
- package/ansible/monitoring/agent-darwin.yml +198 -0
- package/ansible/monitoring/agent.yml +134 -0
- package/ansible/monitoring/beszel-agent.service +42 -0
- package/ansible/monitoring/compose.yml +74 -0
- package/ansible/monitoring/hub-darwin.yml +102 -0
- package/ansible/monitoring/lock.json +11 -0
- package/ansible/net/remote-probe.sh +13 -0
- package/ansible/ubuntu/authorize-sudo.sh +51 -0
- package/ansible/ubuntu/baseline.yml +89 -0
- package/ansible/ubuntu/disk-guard.py +104 -0
- package/ansible/ubuntu/policy.json +43 -0
- package/ansible/ubuntu/recovery.yml +15 -0
- package/ansible/ubuntu/t3/policy.json +7 -0
- package/ansible/ubuntu/t3-desktop.yml +220 -0
- package/ansible/ubuntu/t3-health.sh +33 -0
- package/ansible/ubuntu/t3-policy.yml +76 -0
- package/ansible/ubuntu/t3-route.yml +28 -0
- package/ansible/ubuntu/tailscale.yml +75 -0
- package/ansible/ubuntu/tasks/credentials.yml +47 -0
- package/ansible/ubuntu/tasks/docker.yml +31 -0
- package/ansible/ubuntu/tasks/packages.yml +56 -0
- package/ansible/ubuntu/tasks/power.yml +51 -0
- package/ansible/ubuntu/tasks/recovery.yml +39 -0
- package/ansible/ubuntu/tasks/resources.yml +252 -0
- package/ansible/ubuntu/tasks/services.yml +31 -0
- package/ansible/ubuntu/tasks/ssh.yml +71 -0
- package/ansible/ubuntu/tasks/user.yml +120 -0
- package/bin/devfleet.mjs +36 -0
- package/bootstrap/bootstrap.ps1 +50 -0
- package/bootstrap/bootstrap.sh +73 -0
- package/bootstrap/devfleet +7 -0
- package/bootstrap/docker-desktop.sh +33 -0
- package/bootstrap/doctor.ts +189 -0
- package/bootstrap/fleet-manager-run.sh +76 -0
- package/bootstrap/fleet-remote-bootstrap.sh +185 -0
- package/bootstrap/global-config.ts +113 -0
- package/bootstrap/install-launcher.sh +7 -0
- package/bootstrap/native-aws.ts +86 -0
- package/bootstrap/native-t3.ts +154 -0
- package/bootstrap/npm.ts +350 -0
- package/bootstrap/setup-status.ts +51 -0
- package/bootstrap/setup.ts +88 -0
- package/bootstrap/shell-path.ts +105 -0
- package/bootstrap/verify-t3-archive.py +49 -0
- package/bootstrap/worker-auth.ts +65 -0
- package/bootstrap/worker-github.ts +47 -0
- package/bootstrap/worker-tools.ts +24 -0
- package/cliproxy/AGENTS.md +11 -0
- package/cliproxy/README.md +269 -0
- package/cliproxy/clients/bootstrap.py +143 -0
- package/cliproxy/clients/launcher.py +57 -0
- package/cliproxy/compose.yaml +90 -0
- package/cliproxy/config.template.yaml +70 -0
- package/cliproxy/docs/cloud-adoption.md +16 -0
- package/cliproxy/docs/deployment.png +0 -0
- package/cliproxy/docs/deployment.svg +109 -0
- package/cliproxy/docs/health-workflow.example.yml +185 -0
- package/cliproxy/docs/migration.md +131 -0
- package/cliproxy/docs/model-health-report.schema.json +364 -0
- package/cliproxy/docs/model-health.md +393 -0
- package/cliproxy/image.lock.json +5 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-backup-check.plist +38 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-backup.plist +48 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-fleet.plist +32 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-gc.plist +38 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-monitor.plist +29 -0
- package/cliproxy/launchd/dev.devfleet.cliproxy-vault-renew.plist +31 -0
- package/cliproxy/models.json +1618 -0
- package/cliproxy/requirements.lock +78 -0
- package/cliproxy/scripts/accounts.py +38 -0
- package/cliproxy/scripts/audit.py +123 -0
- package/cliproxy/scripts/backup +234 -0
- package/cliproxy/scripts/check.py +34 -0
- package/cliproxy/scripts/checkpoint.py +226 -0
- package/cliproxy/scripts/cli.py +64 -0
- package/cliproxy/scripts/cliproxy_host.py +257 -0
- package/cliproxy/scripts/cloud_state.py +150 -0
- package/cliproxy/scripts/container-health +6 -0
- package/cliproxy/scripts/docker +24 -0
- package/cliproxy/scripts/external-health.py +79 -0
- package/cliproxy/scripts/fleet_config.py +43 -0
- package/cliproxy/scripts/fleet_sync.py +274 -0
- package/cliproxy/scripts/initialize +84 -0
- package/cliproxy/scripts/install-dependencies +48 -0
- package/cliproxy/scripts/install.py +406 -0
- package/cliproxy/scripts/model_health.py +16 -0
- package/cliproxy/scripts/modelhealth/__init__.py +5 -0
- package/cliproxy/scripts/modelhealth/checks.py +429 -0
- package/cliproxy/scripts/modelhealth/cli.py +117 -0
- package/cliproxy/scripts/modelhealth/config.py +186 -0
- package/cliproxy/scripts/modelhealth/emulator.py +536 -0
- package/cliproxy/scripts/modelhealth/expect.py +88 -0
- package/cliproxy/scripts/modelhealth/findings.py +183 -0
- package/cliproxy/scripts/modelhealth/protocol.py +330 -0
- package/cliproxy/scripts/modelhealth/redact.py +28 -0
- package/cliproxy/scripts/modelhealth/report.py +150 -0
- package/cliproxy/scripts/modelhealth/runner.py +390 -0
- package/cliproxy/scripts/modelhealth/scenario.py +131 -0
- package/cliproxy/scripts/modelhealth/schema.py +63 -0
- package/cliproxy/scripts/modelhealth/simulate.py +111 -0
- package/cliproxy/scripts/modelhealth/transport.py +213 -0
- package/cliproxy/scripts/monitor +68 -0
- package/cliproxy/scripts/ops +527 -0
- package/cliproxy/scripts/public-endpoint +127 -0
- package/cliproxy/scripts/recover_node.py +177 -0
- package/cliproxy/scripts/restore-verify +60 -0
- package/cliproxy/scripts/retention.py +144 -0
- package/cliproxy/scripts/service-start +7 -0
- package/cliproxy/scripts/validate-config +56 -0
- package/cliproxy/scripts/vault-access.py +57 -0
- package/cliproxy/scripts/vault-backup-login +55 -0
- package/cliproxy/scripts/vault-recovery +56 -0
- package/cliproxy/scripts/vault-renew +30 -0
- package/cliproxy/systemd/cliproxy-backup-check.service +10 -0
- package/cliproxy/systemd/cliproxy-backup-check.timer +8 -0
- package/cliproxy/systemd/cliproxy-backup.service +13 -0
- package/cliproxy/systemd/cliproxy-backup.timer +8 -0
- package/cliproxy/systemd/cliproxy-fleet.service +17 -0
- package/cliproxy/systemd/cliproxy-gc.service +13 -0
- package/cliproxy/systemd/cliproxy-gc.timer +8 -0
- package/cliproxy/systemd/cliproxy-monitor.service +8 -0
- package/cliproxy/systemd/cliproxy-monitor.timer +7 -0
- package/cliproxy/systemd/cliproxy-vault-renew.service +8 -0
- package/cliproxy/systemd/cliproxy-vault-renew.timer +7 -0
- package/cliproxy/vendor/LICENSE +22 -0
- package/cliproxy/vendor/config.upstream.yaml +1349 -0
- package/examples/fleet.json +95 -0
- package/examples/roster.json +13 -0
- package/mise.lock +1492 -0
- package/mise.toml +99 -0
- package/package.json +65 -4
- package/schema/fleet.schema.json +364 -0
- package/schema/roster.schema.json +77 -0
- package/src/agents/account-relay.ts +111 -0
- package/src/agents/accounts.ts +85 -0
- package/src/agents/audit.ts +77 -0
- package/src/agents/cli.ts +13 -0
- package/src/agents/command.ts +204 -0
- package/src/agents/converge.ts +123 -0
- package/src/agents/doctor.ts +107 -0
- package/src/agents/errors.ts +2 -0
- package/src/agents/files.ts +73 -0
- package/src/agents/fleet-verify.ts +156 -0
- package/src/agents/launch.ts +290 -0
- package/src/agents/native-verify.ts +226 -0
- package/src/agents/provider-errors.ts +50 -0
- package/src/agents/registry.ts +55 -0
- package/src/agents/render.ts +110 -0
- package/src/agents/round-trip.ts +192 -0
- package/src/agents/secrets.ts +13 -0
- package/src/agents/toolchain.ts +34 -0
- package/src/agents/verify-cache.ts +103 -0
- package/src/agents/verify.ts +127 -0
- package/src/auth-checks.ts +163 -0
- package/src/cli/agent-guide.ts +95 -0
- package/src/cli/app.ts +115 -0
- package/src/cli/cli.ts +21 -0
- package/src/cli/command-index.ts +50 -0
- package/src/cli/contracts.ts +25 -0
- package/src/cli/fleet-reference.ts +17 -0
- package/src/cli/fleet-sync-checkout.ts +91 -0
- package/src/cli/fleet-sync-enrollment.ts +144 -0
- package/src/cli/fleet-sync-forward.ts +134 -0
- package/src/cli/fleet-sync-manager.ts +193 -0
- package/src/cli/fleet-sync-monitoring.ts +185 -0
- package/src/cli/fleet-sync-output.ts +314 -0
- package/src/cli/fleet-sync-privilege.ts +27 -0
- package/src/cli/fleet-sync-progress.ts +109 -0
- package/src/cli/fleet-sync-remote.ts +236 -0
- package/src/cli/fleet-sync-report.ts +26 -0
- package/src/cli/fleet-sync-schedule.ts +138 -0
- package/src/cli/fleet-sync-vault.ts +166 -0
- package/src/cli/fleet-sync.ts +309 -0
- package/src/cli/fleet.ts +163 -0
- package/src/cli/managed-health.ts +219 -0
- package/src/cli/node-deployment.ts +34 -0
- package/src/cli/node-sync.ts +114 -0
- package/src/cli/node-t3.ts +31 -0
- package/src/cli/options.ts +141 -0
- package/src/cli/probe-source.ts +7 -0
- package/src/cli/program.ts +169 -0
- package/src/cli/register-fleet.ts +246 -0
- package/src/cli/registry.ts +158 -0
- package/src/cli/repair-apply.ts +67 -0
- package/src/cli/repair-guard.ts +11 -0
- package/src/cli/repair-inspect.ts +150 -0
- package/src/cli/repair-script.ts +76 -0
- package/src/cli/repair.ts +209 -0
- package/src/cli/roster.ts +89 -0
- package/src/cli/search.ts +35 -0
- package/src/cli/settings.ts +64 -0
- package/src/cli/source-operations.ts +78 -0
- package/src/cli/t3/command.ts +90 -0
- package/src/cli/t3/register.ts +55 -0
- package/src/cli/tailnet.ts +127 -0
- package/src/cli/ui-actions.ts +133 -0
- package/src/cli/ui-controller.ts +113 -0
- package/src/cli/ui-data.ts +106 -0
- package/src/cli/ui-execute.ts +163 -0
- package/src/cli/ui-repairs.ts +34 -0
- package/src/cli/ui-reports.ts +211 -0
- package/src/cli/ui-targets.ts +100 -0
- package/src/cli/ui-terminal.ts +109 -0
- package/src/cli/ui-types.ts +49 -0
- package/src/cli/ui-wizard.ts +101 -0
- package/src/cli/ui.ts +9 -0
- package/src/cli/worker-health-output.ts +37 -0
- package/src/config.ts +186 -0
- package/src/configure.ts +255 -0
- package/src/definition.ts +260 -0
- package/src/diagnostics.ts +92 -0
- package/src/doctor-check.ts +10 -0
- package/src/errors.ts +1 -0
- package/src/fingerprint.ts +120 -0
- package/src/fleet-config.ts +88 -0
- package/src/index.ts +6 -0
- package/src/input-hashes.ts +73 -0
- package/src/machine/local-source.ts +52 -0
- package/src/machine/lock-error.ts +2 -0
- package/src/machine/platform.ts +44 -0
- package/src/machine/source.ts +68 -0
- package/src/machine/state.ts +185 -0
- package/src/machine-health.ts +95 -0
- package/src/managed-source.ts +47 -0
- package/src/manager-failure.ts +48 -0
- package/src/manager-sync.ts +400 -0
- package/src/manager.ts +62 -0
- package/src/monitoring/apply.ts +171 -0
- package/src/monitoring/beszel.ts +191 -0
- package/src/monitoring/enroll.ts +110 -0
- package/src/monitoring/hub-access.ts +113 -0
- package/src/monitoring/hub-viewer.ts +142 -0
- package/src/monitoring/hub.ts +93 -0
- package/src/monitoring/manager-agent.ts +73 -0
- package/src/monitoring/manager-hub.ts +232 -0
- package/src/monitoring/remote.ts +65 -0
- package/src/network-definition.ts +83 -0
- package/src/node-converge-linux.ts +372 -0
- package/src/node-converge-shared.ts +83 -0
- package/src/node-converge.ts +40 -0
- package/src/onboard.ts +35 -0
- package/src/platform/ansible-receipt.ts +128 -0
- package/src/platform/ansible.ts +62 -0
- package/src/platform/darwin.ts +203 -0
- package/src/platform/detect.ts +11 -0
- package/src/platform/fallback.ts +110 -0
- package/src/platform/index.ts +38 -0
- package/src/platform/launchd.ts +98 -0
- package/src/platform/linux.ts +139 -0
- package/src/platform/ps.ts +45 -0
- package/src/platform/resource-verify.ts +128 -0
- package/src/platform/systemd-user.ts +95 -0
- package/src/platform/t3-policy.ts +137 -0
- package/src/platform/t3-route.ts +68 -0
- package/src/platform/t3-service.ts +75 -0
- package/src/platform/types.ts +123 -0
- package/src/platform/ubuntu-audit.ts +63 -0
- package/src/platform/ubuntu-verify.ts +105 -0
- package/src/platform/windows.ts +148 -0
- package/src/power-recovery.ts +55 -0
- package/src/probe.ts +51 -0
- package/src/protocol.ts +119 -0
- package/src/proxy-converge.ts +85 -0
- package/src/remote-bootstrap.ts +212 -0
- package/src/remote-entry.ts +89 -0
- package/src/remote-lock.ts +12 -0
- package/src/repair-catalog.ts +206 -0
- package/src/repair-plan.ts +78 -0
- package/src/repair-runner.ts +84 -0
- package/src/report.ts +71 -0
- package/src/runner.ts +104 -0
- package/src/secrets.ts +169 -0
- package/src/service-auth.ts +115 -0
- package/src/ssh.ts +135 -0
- package/src/sync.ts +104 -0
- package/src/t3/accounts.ts +81 -0
- package/src/t3/desired.ts +165 -0
- package/src/t3/files.ts +81 -0
- package/src/t3/names-check.ts +53 -0
- package/src/t3/names.ts +155 -0
- package/src/t3/plan.ts +82 -0
- package/src/t3/providers.ts +74 -0
- package/src/t3/reconcile.ts +123 -0
- package/src/t3-endpoint.ts +48 -0
- package/src/t3-linked-check.ts +101 -0
- package/src/tailnet-health.ts +231 -0
- package/src/tailnet-matching.ts +37 -0
- package/src/tailscale.ts +141 -0
- package/src/test-fixtures.ts +36 -0
- package/src/toolchain-checks.ts +83 -0
- package/src/topology.ts +24 -0
- package/src/transport.ts +122 -0
- package/src/types.ts +95 -0
- package/src/vault-config.ts +38 -0
- package/src/worker-access-checks.ts +115 -0
- package/src/worker-capacity.ts +34 -0
- package/src/worker-host-checks.ts +176 -0
- package/src/worker-policy.ts +64 -0
- package/src/worker-readiness.ts +64 -0
- package/src/workstation-checks.ts +29 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
- name: Inspect existing designated-user sudo policy ownership
|
|
3
|
+
ansible.builtin.stat:
|
|
4
|
+
path: '/etc/sudoers.d/personal-fleet-{{ development_user }}'
|
|
5
|
+
register: sudo_owned_file
|
|
6
|
+
- name: Read existing designated-user sudo policy for ownership validation
|
|
7
|
+
ansible.builtin.slurp:
|
|
8
|
+
src: '/etc/sudoers.d/personal-fleet-{{ development_user }}'
|
|
9
|
+
when: sudo_owned_file.stat.exists
|
|
10
|
+
register: sudo_previous
|
|
11
|
+
no_log: true
|
|
12
|
+
- name: Preserve conflicting sudo policy until a reviewed migration
|
|
13
|
+
ansible.builtin.assert:
|
|
14
|
+
that:
|
|
15
|
+
- not sudo_owned_file.stat.islnk | default(false)
|
|
16
|
+
- "not sudo_owned_file.stat.exists or (sudo_previous.content | b64decode).startswith('# Managed by personal Fleet: designated development user')"
|
|
17
|
+
fail_msg: SUDOERS_OWNERSHIP_CONFLICT; preserve and review the existing fragment before adoption.
|
|
18
|
+
- name: Validate and install designated-user sudo policy
|
|
19
|
+
ansible.builtin.copy:
|
|
20
|
+
dest: '/etc/sudoers.d/personal-fleet-{{ development_user }}'
|
|
21
|
+
content: |
|
|
22
|
+
# Managed by personal Fleet: designated development user
|
|
23
|
+
{{ development_user }} ALL=(ALL:ALL) NOPASSWD: ALL
|
|
24
|
+
owner: root
|
|
25
|
+
group: root
|
|
26
|
+
mode: '0440'
|
|
27
|
+
validate: /usr/sbin/visudo -cf %s
|
|
28
|
+
backup: true
|
|
29
|
+
- name: Enable lingering without requiring an open desktop session
|
|
30
|
+
ansible.builtin.command:
|
|
31
|
+
argv: [loginctl, enable-linger, '{{ development_user }}']
|
|
32
|
+
creates: '/var/lib/systemd/linger/{{ development_user }}'
|
|
33
|
+
- name: Check whether GNOME settings schemas are installed
|
|
34
|
+
ansible.builtin.stat:
|
|
35
|
+
path: /usr/share/glib-2.0/schemas/org.gnome.desktop.screensaver.gschema.xml
|
|
36
|
+
register: gnome_schema
|
|
37
|
+
- name: Check whether the GNOME power plugin schema is installed
|
|
38
|
+
ansible.builtin.stat:
|
|
39
|
+
path: /usr/share/glib-2.0/schemas/org.gnome.settings-daemon.plugins.power.gschema.xml
|
|
40
|
+
register: gnome_power_schema
|
|
41
|
+
- name: Ensure user configuration directory exists
|
|
42
|
+
ansible.builtin.file:
|
|
43
|
+
path: '{{ development_home }}/.config'
|
|
44
|
+
state: directory
|
|
45
|
+
owner: '{{ development_user }}'
|
|
46
|
+
mode: '0700'
|
|
47
|
+
when: gnome_schema.stat.exists
|
|
48
|
+
- name: Inspect designated-user GNOME lock and idle policy
|
|
49
|
+
become: true
|
|
50
|
+
become_user: '{{ development_user }}'
|
|
51
|
+
ansible.builtin.command:
|
|
52
|
+
argv:
|
|
53
|
+
[
|
|
54
|
+
dbus-run-session,
|
|
55
|
+
'--',
|
|
56
|
+
gsettings,
|
|
57
|
+
get,
|
|
58
|
+
'{{ item.schema }}',
|
|
59
|
+
'{{ item.key }}',
|
|
60
|
+
]
|
|
61
|
+
environment:
|
|
62
|
+
HOME: '{{ development_home }}'
|
|
63
|
+
XDG_CONFIG_HOME: '{{ development_home }}/.config'
|
|
64
|
+
loop: '{{ gnome_session_settings + (gnome_power_settings if gnome_power_schema.stat.exists else []) }}'
|
|
65
|
+
vars:
|
|
66
|
+
gnome_session_settings:
|
|
67
|
+
- {
|
|
68
|
+
schema: org.gnome.desktop.screensaver,
|
|
69
|
+
key: lock-enabled,
|
|
70
|
+
value: 'false',
|
|
71
|
+
observed: 'false',
|
|
72
|
+
}
|
|
73
|
+
- {
|
|
74
|
+
schema: org.gnome.desktop.session,
|
|
75
|
+
key: idle-delay,
|
|
76
|
+
value: '0',
|
|
77
|
+
observed: 'uint32 0',
|
|
78
|
+
}
|
|
79
|
+
gnome_power_settings:
|
|
80
|
+
- {
|
|
81
|
+
schema: org.gnome.settings-daemon.plugins.power,
|
|
82
|
+
key: sleep-inactive-ac-type,
|
|
83
|
+
value: 'nothing',
|
|
84
|
+
observed: "'nothing'",
|
|
85
|
+
}
|
|
86
|
+
- {
|
|
87
|
+
schema: org.gnome.settings-daemon.plugins.power,
|
|
88
|
+
key: sleep-inactive-battery-type,
|
|
89
|
+
value: 'nothing',
|
|
90
|
+
observed: "'nothing'",
|
|
91
|
+
}
|
|
92
|
+
- {
|
|
93
|
+
schema: org.gnome.settings-daemon.plugins.power,
|
|
94
|
+
key: idle-dim,
|
|
95
|
+
value: 'false',
|
|
96
|
+
observed: 'false',
|
|
97
|
+
}
|
|
98
|
+
when: gnome_schema.stat.exists
|
|
99
|
+
changed_when: false
|
|
100
|
+
register: gnome_settings
|
|
101
|
+
- name: Repair only changed designated-user settings
|
|
102
|
+
become: true
|
|
103
|
+
become_user: '{{ development_user }}'
|
|
104
|
+
ansible.builtin.command:
|
|
105
|
+
argv:
|
|
106
|
+
[
|
|
107
|
+
dbus-run-session,
|
|
108
|
+
'--',
|
|
109
|
+
gsettings,
|
|
110
|
+
set,
|
|
111
|
+
'{{ item.item.schema }}',
|
|
112
|
+
'{{ item.item.key }}',
|
|
113
|
+
'{{ item.item.value }}',
|
|
114
|
+
]
|
|
115
|
+
environment:
|
|
116
|
+
HOME: '{{ development_home }}'
|
|
117
|
+
XDG_CONFIG_HOME: '{{ development_home }}/.config'
|
|
118
|
+
loop: '{{ gnome_settings.results }}'
|
|
119
|
+
when: gnome_schema.stat.exists and item.stdout | default('') != item.item.observed
|
|
120
|
+
changed_when: true
|
package/bin/devfleet.mjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The npm entry point: `npx @crvouga/dev-fleet <command>`. The CLI is TypeScript
|
|
3
|
+
// run by Bun, and Node refuses to run TypeScript from node_modules, so this
|
|
4
|
+
// plain-JavaScript launcher hands the arguments to Bun and nothing else.
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { createRequire } from 'node:module';
|
|
7
|
+
import process from 'node:process';
|
|
8
|
+
import { fileURLToPath, URL } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const cli = fileURLToPath(new URL('../src/cli/cli.ts', import.meta.url));
|
|
11
|
+
|
|
12
|
+
/** The Bun this release pins (a dependency), then any Bun on PATH. The
|
|
13
|
+
* dependency's binary is a stub when its install script was skipped.
|
|
14
|
+
*/
|
|
15
|
+
function bunCandidates() {
|
|
16
|
+
const candidates = ['bun'];
|
|
17
|
+
try {
|
|
18
|
+
candidates.unshift(
|
|
19
|
+
createRequire(import.meta.url).resolve('bun/bin/bun.exe')
|
|
20
|
+
);
|
|
21
|
+
} catch {
|
|
22
|
+
// Not installed beside this package.
|
|
23
|
+
}
|
|
24
|
+
return candidates;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const bun of bunCandidates()) {
|
|
28
|
+
const child = spawnSync(bun, [cli, ...process.argv.slice(2)], {
|
|
29
|
+
stdio: 'inherit',
|
|
30
|
+
});
|
|
31
|
+
if (!child.error) process.exit(child.status ?? 1);
|
|
32
|
+
}
|
|
33
|
+
process.stderr.write(
|
|
34
|
+
'dev-fleet needs Bun and could not start it. Install it from https://bun.sh and retry.\n'
|
|
35
|
+
);
|
|
36
|
+
process.exit(127);
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Native Windows, PowerShell 5.1+. Download and run this file (not a bash script).
|
|
2
|
+
param([string]$WorkspaceDir = "$HOME\workspace")
|
|
3
|
+
$ErrorActionPreference = 'Stop'
|
|
4
|
+
$MiseVersion = '2026.10.24'
|
|
5
|
+
function Invoke-Checked([string]$Program, [string[]]$Arguments) {
|
|
6
|
+
& $Program @Arguments
|
|
7
|
+
if ($LASTEXITCODE -ne 0) { throw "$Program failed with exit code $LASTEXITCODE" }
|
|
8
|
+
}
|
|
9
|
+
if (-not (Get-Command git -ErrorAction SilentlyContinue)) {
|
|
10
|
+
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
|
|
11
|
+
throw 'Install Git for Windows (https://git-scm.com/download/win), reopen PowerShell and rerun. WinGet is available through Microsoft App Installer.'
|
|
12
|
+
}
|
|
13
|
+
Invoke-Checked winget @('install', '--id', 'Git.Git', '--exact', '--source', 'winget', '--accept-source-agreements', '--accept-package-agreements')
|
|
14
|
+
$env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
15
|
+
if (-not (Get-Command git -ErrorAction SilentlyContinue)) { throw 'Reopen PowerShell and rerun to pick up Git on PATH.' }
|
|
16
|
+
}
|
|
17
|
+
$LocalRoot = Join-Path $PSScriptRoot '..'
|
|
18
|
+
if (Test-Path (Join-Path $LocalRoot 'mise.toml')) {
|
|
19
|
+
$WorkspaceDir = (Resolve-Path $LocalRoot).Path
|
|
20
|
+
} else {
|
|
21
|
+
if (-not (Test-Path (Join-Path $WorkspaceDir '.git'))) {
|
|
22
|
+
Invoke-Checked git @('clone', 'https://github.com/crvouga/dev-fleet.git', $WorkspaceDir)
|
|
23
|
+
}
|
|
24
|
+
$Origin = & git -C $WorkspaceDir remote get-url origin
|
|
25
|
+
if ($LASTEXITCODE -ne 0 -or $Origin -ne 'https://github.com/crvouga/dev-fleet.git') { throw "Unexpected checkout at $WorkspaceDir; choose an empty -WorkspaceDir." }
|
|
26
|
+
}
|
|
27
|
+
# The baseline uses Windows x64; ARM machines can use an x64 PowerShell/process.
|
|
28
|
+
if ($env:PROCESSOR_ARCHITECTURE -ne 'AMD64') { throw 'Use Windows x64 PowerShell. Native Windows ARM64 tool coverage is not yet validated.' }
|
|
29
|
+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
30
|
+
$Temp = Join-Path ([IO.Path]::GetTempPath()) ([Guid]::NewGuid().ToString())
|
|
31
|
+
New-Item -ItemType Directory -Path $Temp | Out-Null
|
|
32
|
+
try {
|
|
33
|
+
$Asset = "mise-v$MiseVersion-windows-x64.zip"
|
|
34
|
+
$Base = "https://github.com/jdx/mise/releases/download/v$MiseVersion"
|
|
35
|
+
Invoke-WebRequest "$Base/$Asset" -OutFile "$Temp\$Asset" -UseBasicParsing
|
|
36
|
+
Invoke-WebRequest "$Base/SHASUMS256.txt" -OutFile "$Temp\SHASUMS256.txt" -UseBasicParsing
|
|
37
|
+
$Line = Get-Content "$Temp\SHASUMS256.txt" | Where-Object { $_ -match ('\s+\./' + [regex]::Escape($Asset) + '$') }
|
|
38
|
+
if (-not $Line) { throw 'Missing mise checksum.' }
|
|
39
|
+
$Expected = ($Line -split '\s+')[0]
|
|
40
|
+
if ((Get-FileHash "$Temp\$Asset" -Algorithm SHA256).Hash -ne $Expected) { throw 'mise checksum mismatch.' }
|
|
41
|
+
Expand-Archive "$Temp\$Asset" -DestinationPath $Temp
|
|
42
|
+
$Bin = "$HOME\.local\bin"
|
|
43
|
+
New-Item -ItemType Directory -Force -Path $Bin | Out-Null
|
|
44
|
+
Copy-Item "$Temp\mise\bin\*.exe" $Bin -Force
|
|
45
|
+
$env:Path = "$Bin;$env:Path"
|
|
46
|
+
Set-Location $WorkspaceDir
|
|
47
|
+
Invoke-Checked mise @('trust', $WorkspaceDir)
|
|
48
|
+
Invoke-Checked mise @('install', '--locked', 'bun')
|
|
49
|
+
Invoke-Checked mise @('exec', '--locked', 'bun', '--', 'bun', 'bootstrap/setup.ts')
|
|
50
|
+
} finally { Remove-Item $Temp -Recurse -Force }
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Download to a file and run with bash; also works directly from a checkout.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
MISE_VERSION=2026.10.24
|
|
5
|
+
repo=https://github.com/crvouga/dev-fleet.git
|
|
6
|
+
os=$(uname -s)
|
|
7
|
+
case "$os" in
|
|
8
|
+
Darwin) platform=macos ;;
|
|
9
|
+
Linux) platform=linux ;;
|
|
10
|
+
*) echo 'Use bootstrap.ps1 for native Windows.' >&2; exit 1 ;;
|
|
11
|
+
esac
|
|
12
|
+
case "$(uname -m)" in
|
|
13
|
+
arm64|aarch64) arch=arm64 ;;
|
|
14
|
+
x86_64|amd64) arch=x64 ;;
|
|
15
|
+
*) echo 'Supported: x64 and arm64.' >&2; exit 1 ;;
|
|
16
|
+
esac
|
|
17
|
+
# Git, TLS roots and extraction are OS prerequisites, outside the mise lock.
|
|
18
|
+
if ! git --version >/dev/null 2>&1; then
|
|
19
|
+
case "$os" in
|
|
20
|
+
Darwin)
|
|
21
|
+
xcode-select --install || true
|
|
22
|
+
echo 'Finish installing Apple Command Line Tools, then rerun this script.' >&2
|
|
23
|
+
exit 1 ;;
|
|
24
|
+
Linux)
|
|
25
|
+
if command -v apt-get >/dev/null; then
|
|
26
|
+
sudo apt-get update
|
|
27
|
+
sudo apt-get install -y git curl ca-certificates unzip xz-utils build-essential
|
|
28
|
+
elif command -v dnf >/dev/null; then
|
|
29
|
+
sudo dnf install -y git curl ca-certificates unzip xz gcc gcc-c++ make
|
|
30
|
+
else
|
|
31
|
+
echo 'Install git, curl, CA certificates, unzip, xz and a C/C++ toolchain, then rerun.' >&2
|
|
32
|
+
exit 1
|
|
33
|
+
fi ;;
|
|
34
|
+
esac
|
|
35
|
+
fi
|
|
36
|
+
for tool in curl tar unzip; do
|
|
37
|
+
command -v "$tool" >/dev/null || { echo "Install $tool, then rerun." >&2; exit 1; }
|
|
38
|
+
done
|
|
39
|
+
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
40
|
+
if [[ -f "$script_dir/../mise.toml" ]]; then
|
|
41
|
+
checkout=$(cd "$script_dir/.." && pwd)
|
|
42
|
+
else
|
|
43
|
+
checkout=${WORKSPACE_DIR:-"$HOME/workspace"}
|
|
44
|
+
if [[ ! -d "$checkout/.git" ]]; then git clone "$repo" "$checkout"; fi
|
|
45
|
+
[[ "$(git -C "$checkout" remote get-url origin)" == "$repo" ]] || {
|
|
46
|
+
echo "Unexpected checkout at $checkout; choose an empty WORKSPACE_DIR." >&2; exit 1;
|
|
47
|
+
}
|
|
48
|
+
fi
|
|
49
|
+
cd "$checkout"
|
|
50
|
+
# Reuse an observed matching managed executable. Repeated reconciliation must
|
|
51
|
+
# not download/reinstall mise solely because a previous run finished.
|
|
52
|
+
installed_mise=$("$HOME/.local/bin/mise" --version 2>/dev/null | awk '{print $1}') || installed_mise=''
|
|
53
|
+
if [[ "$installed_mise" != "$MISE_VERSION" ]]; then
|
|
54
|
+
temp=$(mktemp -d)
|
|
55
|
+
trap 'rm -rf "$temp"' EXIT
|
|
56
|
+
asset="mise-v${MISE_VERSION}-${platform}-${arch}.tar.gz"
|
|
57
|
+
# Alpine/musl is not part of the supported workstation baseline.
|
|
58
|
+
base="https://github.com/jdx/mise/releases/download/v${MISE_VERSION}"
|
|
59
|
+
curl -fsSL "$base/$asset" -o "$temp/$asset"
|
|
60
|
+
curl -fsSL "$base/SHASUMS256.txt" -o "$temp/SHASUMS256.txt"
|
|
61
|
+
(cd "$temp"; awk -v file="./$asset" '$2 == file { print }' SHASUMS256.txt > check.txt
|
|
62
|
+
test -s check.txt
|
|
63
|
+
if command -v sha256sum >/dev/null; then sha256sum -c check.txt; else shasum -a 256 -c check.txt; fi)
|
|
64
|
+
tar -xzf "$temp/$asset" -C "$temp"
|
|
65
|
+
mkdir -p "$HOME/.local/bin"
|
|
66
|
+
install -m 0755 "$temp/mise/bin/mise" "$HOME/.local/bin/mise"
|
|
67
|
+
fi
|
|
68
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
69
|
+
# Trust the checkout directory, not only its file, so `mise trust --show` there
|
|
70
|
+
# reports trusted.
|
|
71
|
+
mise trust "$checkout"
|
|
72
|
+
mise install --locked bun
|
|
73
|
+
mise exec --locked bun -- bun bootstrap/setup.ts "$@"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# The devfleet command. ~/.local/bin/devfleet links here; nothing is copied.
|
|
3
|
+
set -eu
|
|
4
|
+
self=$(readlink -f "$0")
|
|
5
|
+
root=$(cd "$(dirname "$self")/.." && pwd)
|
|
6
|
+
export PATH="$HOME/.local/bin:$HOME/.local/share/mise/shims:$PATH"
|
|
7
|
+
exec bun "$root/src/cli/cli.ts" "$@"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Private macOS workstation installer; invoke with dev-fleet tools docker-setup.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.."
|
|
5
|
+
case "$(uname -s)/$(uname -m)" in
|
|
6
|
+
Darwin/arm64) docker_arch=arm64 ;;
|
|
7
|
+
Darwin/x86_64) docker_arch=amd64 ;;
|
|
8
|
+
*)
|
|
9
|
+
echo 'Docker Desktop setup is supported here only on macOS.' >&2
|
|
10
|
+
exit 2
|
|
11
|
+
;;
|
|
12
|
+
esac
|
|
13
|
+
if [[ ! -d /Applications/Docker.app ]]; then
|
|
14
|
+
read -r docker_build docker_hash < <(mise exec -- bun -e '
|
|
15
|
+
const {vars}=Bun.TOML.parse(await Bun.file("mise.toml").text());
|
|
16
|
+
console.log(vars.docker_desktop_build,vars["docker_desktop_"+process.argv[1]+"_sha256"]);
|
|
17
|
+
' "$docker_arch")
|
|
18
|
+
[[ "$docker_build" =~ ^[0-9]+$ && "$docker_hash" =~ ^[a-f0-9]{64}$ ]]
|
|
19
|
+
docker_cache="${XDG_CACHE_HOME:-$HOME/.cache}/personal/docker-desktop/$docker_build/$docker_arch"
|
|
20
|
+
mkdir -p "$docker_cache"
|
|
21
|
+
docker_image="$docker_cache/Docker.dmg"
|
|
22
|
+
if ! echo "$docker_hash $docker_image" | shasum -a 256 -c - >/dev/null 2>&1; then
|
|
23
|
+
curl --fail --location --retry 3 "https://desktop.docker.com/mac/main/$docker_arch/$docker_build/Docker.dmg" -o "$docker_image"
|
|
24
|
+
fi
|
|
25
|
+
echo "$docker_hash $docker_image" | shasum -a 256 -c -
|
|
26
|
+
docker_mount=$(mktemp -d)
|
|
27
|
+
trap 'hdiutil detach "$docker_mount" >/dev/null 2>&1 || true; rmdir "$docker_mount" 2>/dev/null || true' EXIT
|
|
28
|
+
hdiutil attach "$docker_image" -nobrowse -mountpoint "$docker_mount"
|
|
29
|
+
codesign --verify --deep --strict "$docker_mount/Docker.app"
|
|
30
|
+
ditto "$docker_mount/Docker.app" /Applications/Docker.app
|
|
31
|
+
fi
|
|
32
|
+
open -a /Applications/Docker.app
|
|
33
|
+
printf '%s\n' 'Docker Desktop opened. Complete any first-run agreement/setup in the app, then rerun dev-fleet tools check --docker.'
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { workerTools } from './worker-tools';
|
|
2
|
+
/** Read-only toolchain checks; no logins, installations or changes to Docker. */
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { homedir, tmpdir } from 'node:os';
|
|
7
|
+
import { AGENT_WORKTREE_ROOTS, workspaceRoot } from './global-config';
|
|
8
|
+
|
|
9
|
+
type Tool = string | { version: string; lazy?: boolean; os?: string[] };
|
|
10
|
+
type Config = {
|
|
11
|
+
tools: Record<string, Tool>;
|
|
12
|
+
vars?: { worker_tools?: unknown };
|
|
13
|
+
};
|
|
14
|
+
export type Check = {
|
|
15
|
+
id: string;
|
|
16
|
+
status: 'pass' | 'warn' | 'fail';
|
|
17
|
+
detail: string;
|
|
18
|
+
};
|
|
19
|
+
export type Runner = (command: string[]) => { ok: boolean; output: string };
|
|
20
|
+
const COMMANDS: Record<string, string> = {
|
|
21
|
+
'npm:@yarnpkg/cli-dist': 'yarn',
|
|
22
|
+
'npm:vercel': 'vercel',
|
|
23
|
+
'npm:wrangler': 'wrangler',
|
|
24
|
+
'aqua:anomalyco/opencode': 'opencode',
|
|
25
|
+
'docker-cli': 'docker',
|
|
26
|
+
opentofu: 'tofu',
|
|
27
|
+
ripgrep: 'rg',
|
|
28
|
+
rust: 'rustc',
|
|
29
|
+
'npm:@devcontainers/cli': 'devcontainer',
|
|
30
|
+
'http:grok': 'grok',
|
|
31
|
+
'http:antigravity': 'antigravity',
|
|
32
|
+
};
|
|
33
|
+
const VERSION_ARGS: Record<string, string[]> = {
|
|
34
|
+
go: ['version'],
|
|
35
|
+
gitleaks: ['version'],
|
|
36
|
+
kubectl: ['version', '--client=true', '--output=json'],
|
|
37
|
+
helm: ['version', '--short'],
|
|
38
|
+
};
|
|
39
|
+
const run: Runner = (command) => {
|
|
40
|
+
const result = spawnSync(command[0]!, command.slice(1), {
|
|
41
|
+
encoding: 'utf8',
|
|
42
|
+
timeout: 30_000,
|
|
43
|
+
windowsHide: true,
|
|
44
|
+
cwd: tmpdir(),
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
ok: result.status === 0,
|
|
48
|
+
output: `${result.stdout ?? ''}${result.stderr ?? ''}`.trim(),
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/** mise's `os` names for the platforms it installs a tool on. */
|
|
53
|
+
const MISE_OS: Record<string, string> = {
|
|
54
|
+
darwin: 'macos',
|
|
55
|
+
linux: 'linux',
|
|
56
|
+
win32: 'windows',
|
|
57
|
+
};
|
|
58
|
+
/** Installed on Windows by a native installer (native-aws.ts), not by mise. */
|
|
59
|
+
const NATIVE_WINDOWS = new Set(['aws']);
|
|
60
|
+
|
|
61
|
+
function unavailable(id: string, tool: Tool, platform: string): boolean {
|
|
62
|
+
if (typeof tool === 'string' || !tool.os) return false;
|
|
63
|
+
if (platform === 'win32' && NATIVE_WINDOWS.has(id)) return false;
|
|
64
|
+
return !tool.os.includes(MISE_OS[platform] ?? platform);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function toolChecks(
|
|
68
|
+
tools: Record<string, Tool>,
|
|
69
|
+
execute: Runner = run,
|
|
70
|
+
platform: string = process.platform
|
|
71
|
+
): Check[] {
|
|
72
|
+
const checks: Check[] = [];
|
|
73
|
+
for (const [id, tool] of Object.entries(tools)) {
|
|
74
|
+
if (typeof tool !== 'string' && tool.lazy) continue;
|
|
75
|
+
// A tool pinned only for other platforms has no artifact to verify here.
|
|
76
|
+
if (unavailable(id, tool, platform)) continue;
|
|
77
|
+
const version = typeof tool === 'string' ? tool : tool.version;
|
|
78
|
+
const command = COMMANDS[id] ?? id;
|
|
79
|
+
const result = execute([command, ...(VERSION_ARGS[id] ?? ['--version'])]);
|
|
80
|
+
const exact = new RegExp(
|
|
81
|
+
`(?:^|[^0-9])${version.replaceAll('.', '\\.')}($|(?![0-9]|\\.[0-9]))`
|
|
82
|
+
);
|
|
83
|
+
const matches = result.ok && exact.test(result.output);
|
|
84
|
+
checks.push({
|
|
85
|
+
id,
|
|
86
|
+
status: matches ? 'pass' : 'fail',
|
|
87
|
+
detail: matches
|
|
88
|
+
? version
|
|
89
|
+
: `Expected ${command} ${version}; run dev-fleet tools sync and reopen the shell. Check for competing PATH entries.`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
for (const command of ['git', 'npm']) {
|
|
93
|
+
const result = execute([command, '--version']);
|
|
94
|
+
checks.push({
|
|
95
|
+
id: command,
|
|
96
|
+
status: result.ok ? 'pass' : 'fail',
|
|
97
|
+
detail: result.ok
|
|
98
|
+
? result.output
|
|
99
|
+
: `Missing ${command}; rerun the OS bootstrap.`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return checks;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function dockerChecks(
|
|
106
|
+
required: boolean,
|
|
107
|
+
execute: Runner = run
|
|
108
|
+
): Check[] {
|
|
109
|
+
return [
|
|
110
|
+
['docker-engine', ['docker', 'info', '--format', '{{.ServerVersion}}']],
|
|
111
|
+
['docker-compose', ['docker', 'compose', 'version']],
|
|
112
|
+
['docker-buildx', ['docker', 'buildx', 'version']],
|
|
113
|
+
].map(([id, command]) => {
|
|
114
|
+
const result = execute(command as string[]);
|
|
115
|
+
return {
|
|
116
|
+
id: id as string,
|
|
117
|
+
status: result.ok ? 'pass' : required ? 'fail' : 'warn',
|
|
118
|
+
detail: result.ok
|
|
119
|
+
? result.output
|
|
120
|
+
: 'Install/start Docker Desktop or Docker Engine with Compose + Buildx. See docs/machine-setup.md.',
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Agent worktree roots must be trusted, or every mise shim fails there. */
|
|
126
|
+
export function worktreeTrustCheck(
|
|
127
|
+
execute: Runner = run,
|
|
128
|
+
home: string = homedir()
|
|
129
|
+
): Check {
|
|
130
|
+
const expand = (path: string) =>
|
|
131
|
+
path === '~' || path.startsWith('~/') ? home + path.slice(1) : path;
|
|
132
|
+
const result = execute(['mise', 'settings', 'get', 'trusted_config_paths']);
|
|
133
|
+
let trusted: string[] = [];
|
|
134
|
+
try {
|
|
135
|
+
const parsed: unknown = result.ok ? JSON.parse(result.output) : [];
|
|
136
|
+
if (Array.isArray(parsed))
|
|
137
|
+
trusted = parsed.filter((p): p is string => typeof p === 'string');
|
|
138
|
+
} catch {
|
|
139
|
+
trusted = [];
|
|
140
|
+
}
|
|
141
|
+
const have = new Set(trusted.map(expand));
|
|
142
|
+
const missing = AGENT_WORKTREE_ROOTS.filter(
|
|
143
|
+
(root) => !have.has(expand(root))
|
|
144
|
+
);
|
|
145
|
+
return {
|
|
146
|
+
id: 'agent-worktree-trust',
|
|
147
|
+
status: missing.length ? 'fail' : 'pass',
|
|
148
|
+
detail: missing.length
|
|
149
|
+
? `mise does not trust ${missing.join(', ')}; agent CLIs fail in new worktrees there. Run dev-fleet tools sync.`
|
|
150
|
+
: AGENT_WORKTREE_ROOTS.join(', '),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function checkMachine(requireDocker = false, worker = false): Check[] {
|
|
155
|
+
const config = Bun.TOML.parse(
|
|
156
|
+
readFileSync(join(workspaceRoot, 'mise.toml'), 'utf8')
|
|
157
|
+
) as Config;
|
|
158
|
+
const auth = run(['gh', 'auth', 'status']);
|
|
159
|
+
return [
|
|
160
|
+
...toolChecks(
|
|
161
|
+
worker
|
|
162
|
+
? workerTools(config.tools, config.vars?.worker_tools)
|
|
163
|
+
: config.tools
|
|
164
|
+
),
|
|
165
|
+
...dockerChecks(requireDocker),
|
|
166
|
+
worktreeTrustCheck(),
|
|
167
|
+
{
|
|
168
|
+
id: 'github-auth',
|
|
169
|
+
status: auth.ok ? 'pass' : 'warn',
|
|
170
|
+
detail: auth.ok
|
|
171
|
+
? 'Authenticated'
|
|
172
|
+
: 'Run gh auth login, then gh auth setup-git.',
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (import.meta.main) {
|
|
178
|
+
const checks = checkMachine(
|
|
179
|
+
process.argv.includes('--docker'),
|
|
180
|
+
process.argv.includes('--worker')
|
|
181
|
+
);
|
|
182
|
+
const ok = checks.every((check) => check.status !== 'fail');
|
|
183
|
+
if (process.argv.includes('--json'))
|
|
184
|
+
console.log(JSON.stringify({ ok, checks }, null, 2));
|
|
185
|
+
else
|
|
186
|
+
for (const check of checks)
|
|
187
|
+
console.log(`${check.status.toUpperCase()} ${check.id}: ${check.detail}`);
|
|
188
|
+
process.exitCode = ok ? 0 : 1;
|
|
189
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Every fleet-sync on the manager runs through this, whether started there or
|
|
3
|
+
# handed off from a worker over strict SSH. Runs fleet-sync from a clean,
|
|
4
|
+
# fleet-owned checkout of the exact approved revision, so the result never
|
|
5
|
+
# depends on the working tree it was started in (uncommitted edits, another
|
|
6
|
+
# branch, a removed worktree). Checkouts are reused when warm and pruned to the
|
|
7
|
+
# three most recent plus the one T3's providers launch from. Never touches the
|
|
8
|
+
# human's working tree.
|
|
9
|
+
set -eu
|
|
10
|
+
revision=$1
|
|
11
|
+
shift
|
|
12
|
+
case "$revision" in *[!a-f0-9]*|'') exit 2;; esac
|
|
13
|
+
[ "${#revision}" = 40 ] || exit 2
|
|
14
|
+
say() { printf 'fleet-sync (manager): %s\n' "$1" >&2; }
|
|
15
|
+
# The dev-fleet clone to take the revision from: named by the caller, or the
|
|
16
|
+
# one the devfleet launcher is linked into.
|
|
17
|
+
repo=${DEVFLEET_REPO:-}
|
|
18
|
+
if [ -z "$repo" ]; then
|
|
19
|
+
launcher=$(command -v devfleet) || { say 'set DEVFLEET_REPO to a dev-fleet clone, or install the launcher with sh bootstrap/install-launcher.sh.'; exit 3; }
|
|
20
|
+
repo=$(dirname "$(readlink -f "$launcher")")
|
|
21
|
+
fi
|
|
22
|
+
repo=$(git -C "$repo" rev-parse --show-toplevel) || { say 'cannot locate the dev-fleet clone on the manager.'; exit 3; }
|
|
23
|
+
runs="$HOME/.local/share/personal/fleet/manager-runs"
|
|
24
|
+
dir="$runs/$revision"
|
|
25
|
+
ready() {
|
|
26
|
+
[ "$(git -C "$dir" rev-parse HEAD 2>/dev/null)" = "$revision" ] &&
|
|
27
|
+
[ -z "$(git -C "$dir" status --porcelain 2>/dev/null)" ]
|
|
28
|
+
}
|
|
29
|
+
if ! ready; then
|
|
30
|
+
git -C "$repo" cat-file -e "$revision^{commit}" 2>/dev/null || git -C "$repo" fetch --quiet origin || true
|
|
31
|
+
git -C "$repo" cat-file -e "$revision^{commit}" 2>/dev/null || { say "revision $revision is neither on the manager nor on origin; push it first."; exit 4; }
|
|
32
|
+
mkdir -p "$runs"
|
|
33
|
+
chmod 700 "$runs"
|
|
34
|
+
# A fleet-owned, disposable run checkout: recreate it when stale or dirty.
|
|
35
|
+
if [ -e "$dir" ]; then git -C "$repo" worktree remove --force "$dir" 2>/dev/null || rm -rf -- "$dir"; fi
|
|
36
|
+
git -C "$repo" worktree prune
|
|
37
|
+
git -C "$repo" worktree add --quiet --detach "$dir" "$revision"
|
|
38
|
+
fi
|
|
39
|
+
touch "$dir"
|
|
40
|
+
cd "$dir"
|
|
41
|
+
mise trust --quiet "$dir" >/dev/null 2>&1 || true
|
|
42
|
+
# The manager runs the same locked toolchain (and so the same agent versions)
|
|
43
|
+
# as every worker: publish this revision's global config, install it and the
|
|
44
|
+
# frozen dependencies. Warm: the toolchain is skipped when the same inputs
|
|
45
|
+
# (tool pins, locks, setup code) were installed within a day, so a revision
|
|
46
|
+
# that changes none of them only installs its own dependencies.
|
|
47
|
+
# Stamp and log sit beside the checkout: inside it they would make it dirty.
|
|
48
|
+
stamp="$runs/toolchain-ready"
|
|
49
|
+
log="$runs/$revision.setup.log"
|
|
50
|
+
key=$(git rev-parse HEAD:mise.toml HEAD:mise.lock HEAD:.mise HEAD:bun.lock HEAD:package.json HEAD:bootstrap 2>/dev/null | tr '\n' ' ')
|
|
51
|
+
[ -n "$key" ] || key=$revision
|
|
52
|
+
failed() {
|
|
53
|
+
tail -n 20 "$log" >&2
|
|
54
|
+
say "toolchain or dependency install failed in the run checkout; full log: $log"
|
|
55
|
+
exit 5
|
|
56
|
+
}
|
|
57
|
+
if [ "$(cat "$stamp" 2>/dev/null)" != "$key" ] || [ -z "$(find "$stamp" -mmin -1440 2>/dev/null)" ]; then
|
|
58
|
+
rm -f -- "$stamp"
|
|
59
|
+
mise exec --locked bun -- bun bootstrap/setup.ts --tools-only >"$log" 2>&1 || failed
|
|
60
|
+
printf '%s' "$key" >"$stamp"
|
|
61
|
+
fi
|
|
62
|
+
if [ ! -d node_modules ]; then
|
|
63
|
+
mise exec --locked bun -- bun install --frozen-lockfile --silent >>"$log" 2>&1 || failed
|
|
64
|
+
fi
|
|
65
|
+
# Keep the three most recent run checkouts (names are commit SHAs) and the
|
|
66
|
+
# applied one: fleet/current points at it and T3's providers launch from there.
|
|
67
|
+
applied=$(basename "$(readlink "$(dirname "$runs")/current" 2>/dev/null || true)")
|
|
68
|
+
# shellcheck disable=SC2012
|
|
69
|
+
for old in $(ls -1td "$runs"/*/ | tail -n +4); do
|
|
70
|
+
[ "$(basename "$old")" != "$applied" ] || continue
|
|
71
|
+
git -C "$repo" worktree remove --force "$old" 2>/dev/null || true
|
|
72
|
+
rm -f -- "${old%/}.setup.log"
|
|
73
|
+
done
|
|
74
|
+
git -C "$repo" worktree prune
|
|
75
|
+
export PERSONAL_WORKSPACE="$dir" PERSONAL_FLEET_RUN_CHECKOUT="$revision"
|
|
76
|
+
exec mise exec --locked bun -- bun src/cli/cli.ts fleet-sync --revision "$revision" "$@"
|