@crvouga/dev-fleet 0.4.0 → 0.5.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/README.md +3 -1
- package/ansible/macos/policy.json +1 -0
- package/ansible/platforms.json +111 -0
- package/ansible/ubuntu/authorize-sudo.sh +7 -16
- package/ansible/ubuntu/baseline.yml +4 -28
- package/ansible/ubuntu/disk-gc.py +186 -4
- package/ansible/ubuntu/policy.json +18 -3
- package/ansible/ubuntu/t3-desktop.yml +2 -2
- package/ansible/ubuntu/tailscale.yml +20 -9
- package/ansible/ubuntu/tasks/dpkg.yml +46 -0
- package/ansible/ubuntu/tasks/install.yml +38 -0
- package/ansible/ubuntu/tasks/packages.yml +4 -55
- package/ansible/ubuntu/tasks/release.yml +25 -0
- package/ansible/ubuntu/tasks/resources.yml +6 -5
- package/ansible/ubuntu/tasks/snapshot.yml +50 -0
- package/ansible/ubuntu/tasks/unattended-upgrades.yml +24 -0
- package/bootstrap/fleet-remote-bootstrap.sh +18 -41
- package/docs/consumer-setup.md +32 -30
- package/docs/declared.md +4 -5
- package/docs/runbooks.md +3 -6
- package/docs/support-matrix.md +36 -0
- package/docs/ubuntu-sync.md +9 -7
- package/docs/worker-contract.md +28 -1
- package/package.json +2 -2
- package/schema/check-ids.json +8 -4
- package/schema/fleet.schema.json +7 -3
- package/src/cli/fleet-sync-remote.ts +1 -1
- package/src/definition.ts +18 -7
- package/src/machine/platform.ts +48 -28
- package/src/machine/support-matrix.ts +76 -0
- package/src/manager-sync.ts +15 -7
- package/src/network-definition.ts +1 -1
- package/src/node-converge.ts +1 -1
- package/src/platform/debian-version.ts +62 -0
- package/src/platform/ubuntu-audit.ts +11 -4
- package/src/platform/ubuntu-release.ts +41 -0
- package/src/remote-bootstrap.ts +2 -0
- package/src/worker-host-checks.ts +36 -10
|
@@ -1,56 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
owner: root
|
|
7
|
-
group: root
|
|
8
|
-
mode: '0755'
|
|
9
|
-
- name: Declare snapshot sources used only for managed package transactions
|
|
10
|
-
ansible.builtin.copy:
|
|
11
|
-
dest: /etc/personal-fleet/ubuntu.sources
|
|
12
|
-
owner: root
|
|
13
|
-
group: root
|
|
14
|
-
mode: '0644'
|
|
15
|
-
backup: true
|
|
16
|
-
content: |
|
|
17
|
-
Types: deb
|
|
18
|
-
URIs: https://snapshot.ubuntu.com/ubuntu/{{ snapshot }}/
|
|
19
|
-
Suites: resolute resolute-updates resolute-security
|
|
20
|
-
Components: main universe restricted multiverse
|
|
21
|
-
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
|
|
22
|
-
register: snapshot_sources
|
|
23
|
-
- name: Isolate snapshot selection from unrelated APT operations
|
|
24
|
-
ansible.builtin.copy:
|
|
25
|
-
dest: /etc/personal-fleet/apt.conf
|
|
26
|
-
owner: root
|
|
27
|
-
group: root
|
|
28
|
-
mode: '0644'
|
|
29
|
-
content: |
|
|
30
|
-
Dir::Etc::sourcelist "/etc/personal-fleet/ubuntu.sources";
|
|
31
|
-
Dir::Etc::sourceparts "-";
|
|
32
|
-
DPkg::Lock::Timeout "{{ aptLockTimeoutSeconds }}";
|
|
33
|
-
- name: Inspect immutable snapshot package index
|
|
34
|
-
ansible.builtin.stat:
|
|
35
|
-
path: '/var/lib/apt/lists/snapshot.ubuntu.com_ubuntu_{{ snapshot }}_dists_resolute_InRelease'
|
|
36
|
-
register: snapshot_index
|
|
37
|
-
- name: Fetch signed snapshot indexes after source change or interrupted setup
|
|
38
|
-
ansible.builtin.apt:
|
|
39
|
-
update_cache: true
|
|
40
|
-
cache_valid_time: 0
|
|
41
|
-
lock_timeout: '{{ aptLockTimeoutSeconds }}'
|
|
42
|
-
environment:
|
|
43
|
-
APT_CONFIG: /etc/personal-fleet/apt.conf
|
|
44
|
-
when: snapshot_sources.changed or not snapshot_index.stat.exists
|
|
45
|
-
- name: Install exact managed packages without downgrading or restarting services
|
|
46
|
-
ansible.builtin.apt:
|
|
47
|
-
name: '{{ item.key }}={{ item.value }}'
|
|
48
|
-
state: present
|
|
49
|
-
lock_timeout: '{{ aptLockTimeoutSeconds }}'
|
|
50
|
-
allow_downgrade: false
|
|
51
|
-
allow_unauthenticated: false
|
|
52
|
-
fail_on_autoremove: true
|
|
53
|
-
policy_rc_d: 101
|
|
54
|
-
environment:
|
|
55
|
-
APT_CONFIG: /etc/personal-fleet/apt.conf
|
|
56
|
-
loop: '{{ packages | dict2items }}'
|
|
2
|
+
- ansible.builtin.include_tasks: install.yml
|
|
3
|
+
vars:
|
|
4
|
+
install_pins: '{{ packages }}'
|
|
5
|
+
- ansible.builtin.import_tasks: unattended-upgrades.yml
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
# The accepted releases are named once, in policy.json. This selects the
|
|
3
|
+
# entry of the running release and refuses everything else.
|
|
4
|
+
- name: Select the accepted release this machine runs
|
|
5
|
+
ansible.builtin.set_fact:
|
|
6
|
+
ubuntu_release: "{{ releases | selectattr('release', 'equalto', ansible_facts.distribution_version) | first | default({}) }}"
|
|
7
|
+
- name: Require an accepted release, its codename and one of its architectures
|
|
8
|
+
ansible.builtin.assert:
|
|
9
|
+
that:
|
|
10
|
+
- ansible_facts.distribution == 'Ubuntu'
|
|
11
|
+
- ubuntu_release.release is defined
|
|
12
|
+
- ansible_facts.distribution_release == ubuntu_release.codename
|
|
13
|
+
- "(ansible_facts.architecture | replace('x86_64', 'amd64') | replace('aarch64', 'arm64')) in ubuntu_release.architectures"
|
|
14
|
+
fail_msg: UBUNTU_BASELINE_MISMATCH; workers run a release docs/support-matrix.md lists. No distribution upgrade is permitted.
|
|
15
|
+
- name: Pin the packages of this release, its own pins over the common ones
|
|
16
|
+
ansible.builtin.set_fact:
|
|
17
|
+
packages: '{{ packages | combine(ubuntu_release.packages | default({})) }}'
|
|
18
|
+
- name: Read the kernel release to tell WSL2 apart from a machine of its own
|
|
19
|
+
ansible.builtin.slurp:
|
|
20
|
+
src: /proc/sys/kernel/osrelease
|
|
21
|
+
register: kernel_release
|
|
22
|
+
- name: Refuse WSL2 by name
|
|
23
|
+
ansible.builtin.assert:
|
|
24
|
+
that: not (kernel_release.content | b64decode | lower is search('microsoft|wsl'))
|
|
25
|
+
fail_msg: WORKER_ENVIRONMENT_UNSUPPORTED; WSL2 is not a worker. Run the worker on a physical or virtual machine of its own (docs/support-matrix.md).
|
|
@@ -49,10 +49,11 @@
|
|
|
49
49
|
line: /swap.fleet none swap sw 0 0
|
|
50
50
|
when: active_swap.stdout | trim == ''
|
|
51
51
|
|
|
52
|
-
- name: Install the systemd userspace OOM killer
|
|
53
|
-
ansible.builtin.
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
- name: Install the systemd userspace OOM killer at its pin
|
|
53
|
+
ansible.builtin.include_tasks: install.yml
|
|
54
|
+
vars:
|
|
55
|
+
install_pins:
|
|
56
|
+
systemd-oomd: "{{ packages['systemd-oomd'] }}"
|
|
56
57
|
- name: Create memory and disk policy directories
|
|
57
58
|
ansible.builtin.file:
|
|
58
59
|
path: '{{ item }}'
|
|
@@ -276,7 +277,7 @@
|
|
|
276
277
|
After=docker.service
|
|
277
278
|
[Service]
|
|
278
279
|
Type=oneshot
|
|
279
|
-
ExecStart=/usr/bin/python3 -I -B {{ disk_gc_source.stat.path }} --home {{ development_home }} --path / --path {{ development_home }} --idle-days {{ resourceSafety.diskGcIdleDays }} --pressure-mib {{ resourceSafety.diskGcPressureMiB }} --pressure-inode-percent {{ resourceSafety.inodeWarnFreePercent }} --pressure-idle-hours {{ resourceSafety.diskGcPressureIdleHours }} --docker-age-hours {{ resourceSafety.diskGcDockerAgeHours }} --pressure-docker-age-hours {{ resourceSafety.diskGcPressureDockerAgeHours }}{% for cache in resourceSafety.diskGcPressureCaches %} --pressure-cache {{ cache }}{% endfor %}
|
|
280
|
+
ExecStart=/usr/bin/python3 -I -B {{ disk_gc_source.stat.path }} --home {{ development_home }} --path / --path {{ development_home }} --idle-days {{ resourceSafety.diskGcIdleDays }} --pressure-mib {{ resourceSafety.diskGcPressureMiB }} --pressure-inode-percent {{ resourceSafety.inodeWarnFreePercent }} --pressure-idle-hours {{ resourceSafety.diskGcPressureIdleHours }} --docker-age-hours {{ resourceSafety.diskGcDockerAgeHours }} --pressure-docker-age-hours {{ resourceSafety.diskGcPressureDockerAgeHours }}{% for cache in resourceSafety.diskGcPressureCaches %} --pressure-cache {{ cache }}{% endfor %}{% for root in resourceSafety.diskGcWorktreeRoots %} --worktree-root {{ root }}{% endfor %} --worktree-idle-days {{ resourceSafety.diskGcWorktreeIdleDays }}{% for cache, mib in resourceSafety.diskGcCacheCapsMiB.items() %} --cache-cap {{ cache }}={{ mib }}{% endfor %} --keep-newest /var/cache/personal-fleet={{ resourceSafety.diskGcPackagesKept }} --mise {{ development_home }}/.local/bin/mise --user {{ development_user }}
|
|
280
281
|
|
|
281
282
|
TimeoutStartSec=2h
|
|
282
283
|
Nice=19
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
# The snapshot every managed package comes from, separate from the system's
|
|
3
|
+
# own sources. Included before any pinned package is installed; a second
|
|
4
|
+
# inclusion in one run changes nothing.
|
|
5
|
+
- name: Create owned APT configuration directory
|
|
6
|
+
ansible.builtin.file:
|
|
7
|
+
path: /etc/personal-fleet
|
|
8
|
+
state: directory
|
|
9
|
+
owner: root
|
|
10
|
+
group: root
|
|
11
|
+
mode: '0755'
|
|
12
|
+
- name: Declare snapshot sources used only for managed package transactions
|
|
13
|
+
ansible.builtin.copy:
|
|
14
|
+
dest: /etc/personal-fleet/ubuntu.sources
|
|
15
|
+
owner: root
|
|
16
|
+
group: root
|
|
17
|
+
mode: '0644'
|
|
18
|
+
backup: true
|
|
19
|
+
content: |
|
|
20
|
+
Types: deb
|
|
21
|
+
URIs: https://snapshot.ubuntu.com/ubuntu/{{ snapshot }}/
|
|
22
|
+
Suites: {{ ubuntu_release.codename }} {{ ubuntu_release.codename }}-updates {{ ubuntu_release.codename }}-security
|
|
23
|
+
Components: main universe restricted multiverse
|
|
24
|
+
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
|
|
25
|
+
register: snapshot_sources
|
|
26
|
+
# Fetching the snapshot indexes must leave the indexes of the system's own
|
|
27
|
+
# sources in place: the system's APT and unattended upgrades still use them.
|
|
28
|
+
- name: Isolate snapshot selection from unrelated APT operations
|
|
29
|
+
ansible.builtin.copy:
|
|
30
|
+
dest: /etc/personal-fleet/apt.conf
|
|
31
|
+
owner: root
|
|
32
|
+
group: root
|
|
33
|
+
mode: '0644'
|
|
34
|
+
content: |
|
|
35
|
+
Dir::Etc::sourcelist "/etc/personal-fleet/ubuntu.sources";
|
|
36
|
+
Dir::Etc::sourceparts "-";
|
|
37
|
+
APT::Get::List-Cleanup "false";
|
|
38
|
+
DPkg::Lock::Timeout "{{ aptLockTimeoutSeconds }}";
|
|
39
|
+
- name: Inspect immutable snapshot package index
|
|
40
|
+
ansible.builtin.stat:
|
|
41
|
+
path: '/var/lib/apt/lists/snapshot.ubuntu.com_ubuntu_{{ snapshot }}_dists_{{ ubuntu_release.codename }}_InRelease'
|
|
42
|
+
register: snapshot_index
|
|
43
|
+
- name: Fetch signed snapshot indexes after source change or interrupted setup
|
|
44
|
+
ansible.builtin.apt:
|
|
45
|
+
update_cache: true
|
|
46
|
+
cache_valid_time: 0
|
|
47
|
+
lock_timeout: '{{ aptLockTimeoutSeconds }}'
|
|
48
|
+
environment:
|
|
49
|
+
APT_CONFIG: /etc/personal-fleet/apt.conf
|
|
50
|
+
when: snapshot_sources.changed or not snapshot_index.stat.exists
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
# Ubuntu installs security updates by itself, and they keep flowing: a pin is
|
|
3
|
+
# a minimum, never a hold. What runs unattended is declared here so it is
|
|
4
|
+
# known: security updates only, never a reboot, never a removal. The fleet's
|
|
5
|
+
# own file sorts after the package's 50unattended-upgrades and replaces its
|
|
6
|
+
# origins.
|
|
7
|
+
- name: Declare which updates Ubuntu installs unattended
|
|
8
|
+
ansible.builtin.copy:
|
|
9
|
+
dest: /etc/apt/apt.conf.d/52personal-fleet-unattended-upgrades
|
|
10
|
+
owner: root
|
|
11
|
+
group: root
|
|
12
|
+
mode: '0644'
|
|
13
|
+
content: |
|
|
14
|
+
// Managed by personal Fleet: unattended upgrades
|
|
15
|
+
APT::Periodic::Update-Package-Lists "1";
|
|
16
|
+
APT::Periodic::Unattended-Upgrade "1";
|
|
17
|
+
#clear Unattended-Upgrade::Allowed-Origins;
|
|
18
|
+
#clear Unattended-Upgrade::Origins-Pattern;
|
|
19
|
+
Unattended-Upgrade::Allowed-Origins {
|
|
20
|
+
"${distro_id}:${distro_codename}-security";
|
|
21
|
+
};
|
|
22
|
+
Unattended-Upgrade::Automatic-Reboot "false";
|
|
23
|
+
Unattended-Upgrade::Remove-Unused-Dependencies "false";
|
|
24
|
+
Unattended-Upgrade::Remove-New-Unused-Dependencies "false";
|
|
@@ -2,14 +2,17 @@
|
|
|
2
2
|
# Executed over strict SSH from the selected verified Git revision. stdin is
|
|
3
3
|
# one line holding the base64 fleet configuration (empty when there is none),
|
|
4
4
|
# then the base64 Git bundle (nothing in cached mode). All executable content
|
|
5
|
-
# remains in this tracked source.
|
|
5
|
+
# remains in this tracked source. The Ubuntu releases workers may run are
|
|
6
|
+
# named once, in ansible/ubuntu/policy.json; the manager passes them in.
|
|
6
7
|
set -eu
|
|
7
8
|
umask 077
|
|
8
9
|
revision=$1
|
|
9
10
|
bundle_sha=$2
|
|
10
11
|
node=$3
|
|
11
12
|
source_mode=${4:-transfer}
|
|
13
|
+
releases=${5:-}
|
|
12
14
|
case "$source_mode" in cached|transfer) ;; *) exit 2;; esac
|
|
15
|
+
case "$releases" in *[!0-9.\ ]*|'') exit 2;; esac
|
|
13
16
|
case "$revision" in *[!a-f0-9]*|'') exit 2;; esac
|
|
14
17
|
[ "${#revision}" = 40 ] || exit 2
|
|
15
18
|
case "$bundle_sha" in *[!a-f0-9]*|'') exit 2;; esac
|
|
@@ -26,7 +29,8 @@ remedy() {
|
|
|
26
29
|
case "$1" in
|
|
27
30
|
FLEET_REMOTE_LOCKED) echo 'Another fleet operation holds this machine. Wait for it to finish; rerun fleet-sync afterwards. Never remove the lock while its holder runs.';;
|
|
28
31
|
ROOT_DEVELOPMENT_USER_REJECTED) echo 'Connect as the non-root developer account the fleet configuration names.';;
|
|
29
|
-
UBUNTU_BASELINE_MISMATCH|UBUNTU_ARCHITECTURE_UNSUPPORTED|
|
|
32
|
+
UBUNTU_BASELINE_MISMATCH|UBUNTU_ARCHITECTURE_UNSUPPORTED|PLATFORM_UNSUPPORTED) echo 'Workers run an Ubuntu release docs/support-matrix.md lists, on x86_64 or aarch64. Reinstall the machine; fleet-sync never upgrades an operating system.';;
|
|
33
|
+
WORKER_ENVIRONMENT_UNSUPPORTED) echo 'WSL2 is not a worker. Run the worker on a physical or virtual machine of its own (docs/support-matrix.md).';;
|
|
30
34
|
FLEET_STATE_PATH_UNSAFE) echo 'A fleet state directory under ~/.local/share/personal is a symbolic link. Replace it with a real directory.';;
|
|
31
35
|
BOOTSTRAP_CAPACITY_UNVERIFIED|BOOTSTRAP_DISK_CAPACITY_REQUIRED) echo 'Free at least 2 GiB and 50000 inodes on the home file system, then rerun fleet-sync.';;
|
|
32
36
|
SOURCE_DIGEST_MISMATCH) echo 'The bundle changed in transit. Rerun fleet-sync; if it repeats, check the SSH route.';;
|
|
@@ -37,8 +41,6 @@ remedy() {
|
|
|
37
41
|
APT_DISK_FULL) echo 'Package installation ran out of disk space. Free space under /var and /usr, then rerun fleet-sync.';;
|
|
38
42
|
DPKG_INTERRUPTED) echo 'An earlier package installation that fleet-sync did not start was interrupted. Run sudo dpkg --configure -a once no package process is running, then rerun fleet-sync.';;
|
|
39
43
|
APT_UPDATE_FAILED|APT_INSTALL_FAILED) echo 'Run sudo apt-get update and install the bootstrap packages by hand to see the error, then rerun fleet-sync.';;
|
|
40
|
-
XCODE_COMMAND_LINE_TOOLS_REQUIRED) echo 'Install the Xcode Command Line Tools (xcode-select --install), then rerun fleet-sync.';;
|
|
41
|
-
BOOTSTRAP_LOCK_TOOL_REQUIRED) echo 'Install python3 so the operation lock can be taken, then rerun fleet-sync.';;
|
|
42
44
|
SOURCE_CACHE_CHANGED|SOURCE_STATUS_FAILED|SOURCE_CHECKOUT_CONFLICT) echo 'The fleet-owned checkout of this revision holds changes fleet-sync did not make. Inspect and remove that revisions directory, then rerun fleet-sync.';;
|
|
43
45
|
CONFIGURATION_INSTALL_FAILED) echo 'The fleet configuration could not be written under ~/.config/dev-fleet. Check that directory, then rerun fleet-sync.';;
|
|
44
46
|
TOOLCHAIN_*) echo 'The pinned toolchain did not converge. Rerun fleet-sync; it resumes where it stopped. If it repeats, run devfleet fleet-status --node for this worker.';;
|
|
@@ -64,22 +66,14 @@ trap '' HUP
|
|
|
64
66
|
trap 'exit 1' INT TERM
|
|
65
67
|
# No shell startup assumptions, and no implicit root development user.
|
|
66
68
|
[ "$(id -u)" != 0 ] || die ROOT_DEVELOPMENT_USER_REJECTED
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
case "$(uname -m)" in x86_64|aarch64) ;; *) die UBUNTU_ARCHITECTURE_UNSUPPORTED;; esac ;;
|
|
76
|
-
Darwin)
|
|
77
|
-
major=$(sw_vers -productVersion | cut -d. -f1)
|
|
78
|
-
case "$major" in ''|*[!0-9]*) die MACOS_BASELINE_MISMATCH;; esac
|
|
79
|
-
[ "$major" -ge 14 ] || die MACOS_BASELINE_MISMATCH
|
|
80
|
-
case "$(uname -m)" in arm64|x86_64) ;; *) die MACOS_ARCHITECTURE_UNSUPPORTED;; esac ;;
|
|
81
|
-
*) die PLATFORM_UNSUPPORTED;;
|
|
82
|
-
esac
|
|
69
|
+
# Workers are the Ubuntu releases the policy accepts (docs/support-matrix.md).
|
|
70
|
+
[ "$(uname -s)" = Linux ] || die PLATFORM_UNSUPPORTED
|
|
71
|
+
# shellcheck source=/dev/null
|
|
72
|
+
. /etc/os-release
|
|
73
|
+
[ "$ID" = ubuntu ] || die UBUNTU_BASELINE_MISMATCH
|
|
74
|
+
case " $releases " in *" $VERSION_ID "*) ;; *) die UBUNTU_BASELINE_MISMATCH;; esac
|
|
75
|
+
case "$(uname -m)" in x86_64|aarch64) ;; *) die UBUNTU_ARCHITECTURE_UNSUPPORTED;; esac
|
|
76
|
+
! grep -qiE 'microsoft|wsl' /proc/sys/kernel/osrelease 2>/dev/null || die WORKER_ENVIRONMENT_UNSUPPORTED
|
|
83
77
|
root="$HOME/.local/share/personal/fleet"
|
|
84
78
|
# Refuse symlinked state roots rather than allowing writes through them.
|
|
85
79
|
for path in "$HOME/.local" "$HOME/.local/share" "$HOME/.local/share/personal" "$root"; do
|
|
@@ -89,14 +83,8 @@ mkdir -p "$root"
|
|
|
89
83
|
phase=lock
|
|
90
84
|
[ ! -L "$root/remote-operation.lock" ] || exit 1
|
|
91
85
|
exec 9>"$root/remote-operation.lock"
|
|
92
|
-
#
|
|
93
|
-
|
|
94
|
-
if command -v flock >/dev/null 2>&1; then
|
|
95
|
-
flock -n 9 || die FLEET_REMOTE_LOCKED
|
|
96
|
-
else
|
|
97
|
-
command -v python3 >/dev/null 2>&1 || die BOOTSTRAP_LOCK_TOOL_REQUIRED
|
|
98
|
-
python3 -c 'import fcntl; fcntl.flock(9, fcntl.LOCK_EX | fcntl.LOCK_NB)' 9>&9 || die FLEET_REMOTE_LOCKED
|
|
99
|
-
fi
|
|
86
|
+
# The lock follows this shell's open descriptor and is released when it exits.
|
|
87
|
+
flock -n 9 || die FLEET_REMOTE_LOCKED
|
|
100
88
|
# The operation record: a manager that lost its session reads it to follow
|
|
101
89
|
# this operation to its end instead of starting another (see
|
|
102
90
|
# src/remote-operation.ts). The result of the last operation is beside it.
|
|
@@ -132,14 +120,9 @@ if [ "$source_mode" = transfer ]; then
|
|
|
132
120
|
bundle=$(mktemp "$root/source.XXXXXX")
|
|
133
121
|
# Leave no executable payload copies; this temporary file is only Git data.
|
|
134
122
|
base64 -d >"$bundle"
|
|
135
|
-
|
|
136
|
-
printf '%s %s\n' "$bundle_sha" "$bundle" | sha256sum -c - >/dev/null 2>&1 || die SOURCE_DIGEST_MISMATCH
|
|
137
|
-
else
|
|
138
|
-
[ "$(shasum -a 256 "$bundle" | cut -d' ' -f1)" = "$bundle_sha" ] || die SOURCE_DIGEST_MISMATCH
|
|
139
|
-
fi
|
|
123
|
+
printf '%s %s\n' "$bundle_sha" "$bundle" | sha256sum -c - >/dev/null 2>&1 || die SOURCE_DIGEST_MISMATCH
|
|
140
124
|
fi
|
|
141
125
|
phase=prerequisites
|
|
142
|
-
if [ "$platform" = Linux ]; then
|
|
143
126
|
missing=''
|
|
144
127
|
for package in git curl ca-certificates unzip xz-utils build-essential python3 ansible-core; do
|
|
145
128
|
dpkg-query -W -f='${db:Status-Status}' "$package" 2>/dev/null | grep -qx installed || missing="$missing $package"
|
|
@@ -171,12 +154,6 @@ if [ -n "$missing" ]; then
|
|
|
171
154
|
rm -f -- "$pending" "$apt_log"
|
|
172
155
|
apt_log=''
|
|
173
156
|
fi
|
|
174
|
-
else
|
|
175
|
-
# Apple Command Line Tools provide git and python3; Ansible runs through the
|
|
176
|
-
# pinned uv tool runner installed by the shared mise toolchain below.
|
|
177
|
-
xcode-select -p >/dev/null 2>&1 || die XCODE_COMMAND_LINE_TOOLS_REQUIRED
|
|
178
|
-
command -v git >/dev/null 2>&1 || die XCODE_COMMAND_LINE_TOOLS_REQUIRED
|
|
179
|
-
fi
|
|
180
157
|
phase=source-verification
|
|
181
158
|
deployment="$root/revisions/$revision"
|
|
182
159
|
[ ! -L "$root/revisions" ] && [ ! -L "$deployment" ] || exit 1
|
|
@@ -235,7 +212,7 @@ else
|
|
|
235
212
|
toolchain_warm=''
|
|
236
213
|
rm -f -- "$status_file" "$ready_file"
|
|
237
214
|
fi
|
|
238
|
-
|
|
215
|
+
bounded='timeout 1800'
|
|
239
216
|
# shellcheck disable=SC2086
|
|
240
217
|
if [ -z "$toolchain_warm" ] && ! $bounded bash bootstrap/bootstrap.sh --worker --node "$node" --non-interactive --status-file "$status_file" >/dev/null 2>&1; then
|
|
241
218
|
tool_phase=$(sed -n 's/.*"phase":"\([a-z0-9-]*\)".*/\1/p' "$status_file" 2>/dev/null) || tool_phase=''
|
package/docs/consumer-setup.md
CHANGED
|
@@ -153,35 +153,35 @@ npx devfleet fleet-state --example > fleet.json
|
|
|
153
153
|
Then replace every placeholder. Each field, from
|
|
154
154
|
[`schema/fleet.schema.json`](../schema/fleet.schema.json):
|
|
155
155
|
|
|
156
|
-
| Field | Value
|
|
157
|
-
| ----------------------------------------------------- |
|
|
158
|
-
| `version` | `3`
|
|
159
|
-
| `source.repository` | `https://github.com/crvouga/dev-fleet.git`, or your fork if you publish one. Machines only accept code from it.
|
|
160
|
-
| `state` | `{ "bucket", "endpoint"?, "region"?, "prefix"? }` or `{ "directory": "/absolute/path" }`. Never credentials.
|
|
161
|
-
| `secrets` | See [secrets](#secrets).
|
|
162
|
-
| `manager.id` | A short lowercase id, `^[a-z][a-z0-9-]{0,62}$`, e.g. `manager-mac`.
|
|
163
|
-
| `manager.os` | `"macOS"`
|
|
164
|
-
| `manager.network.tailscaleName` | The manager's MagicDNS name: `tailscale status --json` → `.Self.DNSName` without the trailing dot.
|
|
165
|
-
| `manager.network.user` | The manager's login name (`id -un`).
|
|
166
|
-
| `manager.neverSleepOnPower` | `true` (workers depend on the manager's services).
|
|
167
|
-
| `workers[].id` | A short lowercase id. The **first** worker is the canary: a new revision must pass there before the others get it.
|
|
168
|
-
| `workers[].ssh` | The worker's alias in the manager's `~/.ssh/config`.
|
|
169
|
-
| `workers[].os` | `"linux"`
|
|
170
|
-
| `workers[].network.tailscaleName` | The worker's MagicDNS name (from `tailscale status --json` → `.Peer[].DNSName` on the manager).
|
|
171
|
-
| `workers[].network.sshPort` | `22` unless the worker's sshd listens elsewhere.
|
|
172
|
-
| `workers[].network.user` | The worker account (`ssh WORKER_ALIAS id -un`).
|
|
173
|
-
| `workers[].network.t3Port` | `3773`
|
|
174
|
-
| `services.proxy.endpoint` | The public HTTPS origin of the CLI proxy, e.g. `https://cli-proxy.example.com`.
|
|
175
|
-
| `services.monitoring.hubPort` | `8443`
|
|
176
|
-
| `services.monitoring.publicUrl` | The public HTTPS origin of the monitoring dashboard.
|
|
177
|
-
| `services.monitoring.managerAccount` | The email of the monitoring account the manager enrolls machines with.
|
|
178
|
-
| `services.monitoring.viewerAccount` | The email of the read-only account visitors are signed in as.
|
|
179
|
-
| `ubuntu` | `{ "release": "26.04", "architectures": ["amd64", "arm64"] }
|
|
180
|
-
| `worker.requiredCapabilities` | Normally all of `github`, `vault`, `t3`, `toolchain`, `workstation`, `worker`.
|
|
181
|
-
| `worker.githubLogin` | The GitHub login every worker must be signed in as.
|
|
182
|
-
| `worker.vaultUser` | The Vault user each worker's token is issued for.
|
|
183
|
-
| `worker.toolchain`, `agentProfile` | `"mise.lock"`, `"proxy"` (fixed).
|
|
184
|
-
| `worker.unattendedSudo`, `persistentT3`, `neverSleep` | `true` (fixed policy).
|
|
156
|
+
| Field | Value |
|
|
157
|
+
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
158
|
+
| `version` | `3` |
|
|
159
|
+
| `source.repository` | `https://github.com/crvouga/dev-fleet.git`, or your fork if you publish one. Machines only accept code from it. |
|
|
160
|
+
| `state` | `{ "bucket", "endpoint"?, "region"?, "prefix"? }` or `{ "directory": "/absolute/path" }`. Never credentials. |
|
|
161
|
+
| `secrets` | See [secrets](#secrets). |
|
|
162
|
+
| `manager.id` | A short lowercase id, `^[a-z][a-z0-9-]{0,62}$`, e.g. `manager-mac`. |
|
|
163
|
+
| `manager.os` | `"macOS"` |
|
|
164
|
+
| `manager.network.tailscaleName` | The manager's MagicDNS name: `tailscale status --json` → `.Self.DNSName` without the trailing dot. |
|
|
165
|
+
| `manager.network.user` | The manager's login name (`id -un`). |
|
|
166
|
+
| `manager.neverSleepOnPower` | `true` (workers depend on the manager's services). |
|
|
167
|
+
| `workers[].id` | A short lowercase id. The **first** worker is the canary: a new revision must pass there before the others get it. |
|
|
168
|
+
| `workers[].ssh` | The worker's alias in the manager's `~/.ssh/config`. |
|
|
169
|
+
| `workers[].os` | `"linux"` |
|
|
170
|
+
| `workers[].network.tailscaleName` | The worker's MagicDNS name (from `tailscale status --json` → `.Peer[].DNSName` on the manager). |
|
|
171
|
+
| `workers[].network.sshPort` | `22` unless the worker's sshd listens elsewhere. |
|
|
172
|
+
| `workers[].network.user` | The worker account (`ssh WORKER_ALIAS id -un`). |
|
|
173
|
+
| `workers[].network.t3Port` | `3773` |
|
|
174
|
+
| `services.proxy.endpoint` | The public HTTPS origin of the CLI proxy, e.g. `https://cli-proxy.example.com`. |
|
|
175
|
+
| `services.monitoring.hubPort` | `8443` |
|
|
176
|
+
| `services.monitoring.publicUrl` | The public HTTPS origin of the monitoring dashboard. |
|
|
177
|
+
| `services.monitoring.managerAccount` | The email of the monitoring account the manager enrolls machines with. |
|
|
178
|
+
| `services.monitoring.viewerAccount` | The email of the read-only account visitors are signed in as. |
|
|
179
|
+
| `ubuntu` | `{ "release": "26.04", "architectures": ["amd64", "arm64"] }`: a release of the [support matrix](support-matrix.md). |
|
|
180
|
+
| `worker.requiredCapabilities` | Normally all of `github`, `vault`, `t3`, `toolchain`, `workstation`, `worker`. |
|
|
181
|
+
| `worker.githubLogin` | The GitHub login every worker must be signed in as. |
|
|
182
|
+
| `worker.vaultUser` | The Vault user each worker's token is issued for. |
|
|
183
|
+
| `worker.toolchain`, `agentProfile` | `"mise.lock"`, `"proxy"` (fixed). |
|
|
184
|
+
| `worker.unattendedSudo`, `persistentT3`, `neverSleep` | `true` (fixed policy). |
|
|
185
185
|
|
|
186
186
|
Use the same values in every place they repeat. Validate before going on:
|
|
187
187
|
|
|
@@ -352,7 +352,9 @@ Read `summary` and `remedy.action` of the failing check. The common ones:
|
|
|
352
352
|
| `TAILSCALE_NODE_OFFLINE` | The worker is off or off the tailnet. It is listed and skipped; others continue. |
|
|
353
353
|
| `TAILSCALE_DEVICE_REPLACED` | The tailnet device behind a worker changed. Nothing is written to it until the human confirms the replacement. |
|
|
354
354
|
| `SUDO_AUTHORIZATION_REQUIRED` | The worker account cannot use sudo without a password. Run the first sync interactively. |
|
|
355
|
-
| `UBUNTU_BASELINE_MISMATCH` | The worker is not Ubuntu
|
|
355
|
+
| `UBUNTU_BASELINE_MISMATCH` | The worker is not an Ubuntu release the [support matrix](support-matrix.md) lists. |
|
|
356
|
+
| `WORKER_ENVIRONMENT_UNSUPPORTED` | The worker is WSL2 or a container. Use a physical or virtual machine of its own. |
|
|
357
|
+
| `PACKAGE_NEWER_THAN_PIN` (warning) | Ubuntu's security updates moved a pinned package past its pin. It is kept; the pin is a minimum. |
|
|
356
358
|
| `ROOT_DEVELOPMENT_USER_REJECTED` | The worker's SSH user is root. Use a normal account with sudo. |
|
|
357
359
|
| `BOOTSTRAP_DISK_CAPACITY_REQUIRED` | Less than 2 GiB free (or too few inodes) where the fleet keeps its files on the worker. |
|
|
358
360
|
| `FLEET_REMOTE_LOCKED` | Another operation is running on that worker. Wait and run again. |
|
package/docs/declared.md
CHANGED
|
@@ -47,11 +47,10 @@ stays in the playbook: the playbook is its declaration.
|
|
|
47
47
|
launchd job from TypeScript and has never worked from the published package.
|
|
48
48
|
It should become a playbook.
|
|
49
49
|
- **The rest of a worker's packages.** Only the packages in `policy.json` are
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
is a policy choice, not a missing line.
|
|
50
|
+
pinned. The T3 desktop libraries are installed at whatever version the
|
|
51
|
+
machine's own archive holds, and Ubuntu's security updates move everything
|
|
52
|
+
else, pinned packages included: a pin is a minimum
|
|
53
|
+
([worker contract](worker-contract.md#package-pins-and-security-updates)).
|
|
55
54
|
- **What a worker needs before Ansible exists.** `fleet-remote-bootstrap.sh`
|
|
56
55
|
installs Git, curl, CA certificates, Python and `ansible-core` from the
|
|
57
56
|
machine's own archive (the snapshot is HTTPS, and a new machine has no CA
|
package/docs/runbooks.md
CHANGED
|
@@ -8,29 +8,26 @@ person (a login, an approval, a choice) is marked **needs a person**.
|
|
|
8
8
|
`features/rules/runbooks.feature` fails when a code in the source has no
|
|
9
9
|
entry here, or a `devfleet` command quoted in the documents does not exist.
|
|
10
10
|
|
|
11
|
-
There are
|
|
11
|
+
There are 238 codes; 29 need a person.
|
|
12
12
|
|
|
13
13
|
## Bootstrap of a new or returning worker
|
|
14
14
|
|
|
15
15
|
- **Meaning:** The remote bootstrap (`bootstrap/fleet-remote-bootstrap.sh`) stopped before node convergence. It reports the phase and the code, never its output.
|
|
16
16
|
- **Confirm:** Run `devfleet fleet-sync --dry-run` and read the `bootstrap:*` check of the worker; `ssh WORKER` and look under `~/.local/share/personal/fleet`.
|
|
17
17
|
- **Fix:** Follow the remedy the check prints, then run `devfleet fleet-sync`. The bootstrap resumes where it stopped.
|
|
18
|
-
- **Escalate:** A platform or architecture code means the machine must be reinstalled
|
|
18
|
+
- **Escalate:** A platform or architecture code means the machine must be reinstalled with a release [the support matrix](support-matrix.md) lists, on a machine of its own.
|
|
19
19
|
|
|
20
20
|
- `BOOTSTRAP_CAPACITY_UNVERIFIED`: bootstrap capacity unverified.
|
|
21
21
|
- `BOOTSTRAP_DISK_CAPACITY_REQUIRED`: bootstrap disk capacity required.
|
|
22
|
-
- `BOOTSTRAP_LOCK_TOOL_REQUIRED`: bootstrap lock tool required.
|
|
23
22
|
- `CONFIGURATION_INSTALL_FAILED`: configuration install failed.
|
|
24
23
|
- `FLEET_APPROVED_BOOTSTRAP_UNAVAILABLE`: fleet approved bootstrap unavailable.
|
|
25
24
|
- `FLEET_STATE_PATH_UNSAFE`: fleet state path unsafe.
|
|
26
|
-
- `MACOS_ARCHITECTURE_UNSUPPORTED`: macos architecture unsupported. **Needs a person.**
|
|
27
|
-
- `MACOS_BASELINE_MISMATCH`: macos baseline mismatch. **Needs a person.**
|
|
28
25
|
- `PLATFORM_UNSUPPORTED`: platform unsupported. **Needs a person.**
|
|
29
26
|
- `REMOTE_BOOTSTRAP_EVIDENCE_INVALID`: remote bootstrap evidence invalid.
|
|
30
27
|
- `REMOTE_BOOTSTRAP_EXIT_FAILED`: remote bootstrap exit failed.
|
|
31
28
|
- `ROOT_DEVELOPMENT_USER_REJECTED`: root development user rejected.
|
|
32
29
|
- `UBUNTU_ARCHITECTURE_UNSUPPORTED`: ubuntu architecture unsupported. **Needs a person.**
|
|
33
|
-
- `
|
|
30
|
+
- `WORKER_ENVIRONMENT_UNSUPPORTED`: worker environment unsupported: a container or WSL2. **Needs a person.**
|
|
34
31
|
|
|
35
32
|
## Ubuntu packages
|
|
36
33
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Support matrix
|
|
2
|
+
|
|
3
|
+
Which platforms dev-fleet runs on, in which role, and what happens to the
|
|
4
|
+
rest. The default is one macOS manager and Ubuntu workers treated as cattle.
|
|
5
|
+
|
|
6
|
+
- **supported**: converged, measured and tested on every pull request.
|
|
7
|
+
- **best effort**: works and is not tested as a fleet role.
|
|
8
|
+
- **refused**: stopped with the named reason before anything is installed.
|
|
9
|
+
|
|
10
|
+
<!-- Generated by `bun src/machine/support-matrix.ts` from ansible/macos/policy.json, ansible/ubuntu/policy.json and ansible/platforms.json; features/rules/support-matrix.feature fails when it differs. -->
|
|
11
|
+
|
|
12
|
+
| Role | Platform | Versions | Architectures | Status | Note |
|
|
13
|
+
| -------------- | --------- | ------------------- | --------------- | ----------- | ----------------------------------------------------------------------------------------- |
|
|
14
|
+
| manager | macOS | 14 or newer | arm64, x86_64 | supported | ansible/macos/policy.json |
|
|
15
|
+
| worker | Ubuntu | 26.04 (resolute) | amd64, arm64 | supported | ansible/ubuntu/policy.json; a machine of its own, physical or virtual |
|
|
16
|
+
| manager | any other | any | any | refused | The fleet configuration accepts only a macOS manager. |
|
|
17
|
+
| worker | Ubuntu | any other release | any | refused | UBUNTU_BASELINE_MISMATCH |
|
|
18
|
+
| worker | Ubuntu | an accepted release | any other | refused | UBUNTU_BASELINE_MISMATCH |
|
|
19
|
+
| worker | Debian | any | any | refused | UBUNTU_BASELINE_MISMATCH |
|
|
20
|
+
| worker | macOS | any | any | refused | The fleet configuration accepts only Linux workers. |
|
|
21
|
+
| worker | Windows | any | any | refused | The fleet configuration accepts only Linux workers. |
|
|
22
|
+
| worker | WSL2 | any | any | refused | WORKER_ENVIRONMENT_UNSUPPORTED |
|
|
23
|
+
| worker | container | any | any | refused | WORKER_ENVIRONMENT_UNSUPPORTED |
|
|
24
|
+
| toolchain only | macOS | any | arm64, x86_64 | best effort | No fleet role: devfleet bootstrap installs the pinned tools (docs/machine-setup.md). |
|
|
25
|
+
| toolchain only | Linux | any | x86_64, aarch64 | best effort | No fleet role: devfleet bootstrap installs the pinned tools (docs/machine-setup.md). |
|
|
26
|
+
| toolchain only | WSL2 | any | x86_64 | best effort | Treated as Linux: devfleet bootstrap installs the pinned tools (docs/machine-setup.md). |
|
|
27
|
+
| toolchain only | Windows | any | x64 | best effort | No fleet role: bootstrap/bootstrap.ps1 installs the pinned tools (docs/machine-setup.md). |
|
|
28
|
+
| toolchain only | Alpine | any | any | refused | musl: the pinned mise build needs glibc. |
|
|
29
|
+
|
|
30
|
+
A worker is refused by the first check that can tell: the fleet configuration
|
|
31
|
+
refuses a worker that is not Linux, the platform probe refuses another
|
|
32
|
+
release, CPU or environment (`ubuntu:release`, `platform:environment`), and
|
|
33
|
+
the bootstrap and the Ubuntu baseline refuse them again on the machine.
|
|
34
|
+
|
|
35
|
+
Supporting another platform means adding it to the policy that names it, with
|
|
36
|
+
its package pins, a fresh-box job and its row here.
|
package/docs/ubuntu-sync.md
CHANGED
|
@@ -61,10 +61,9 @@ resolve enrolled fleet
|
|
|
61
61
|
|
|
62
62
|
Bare invocation selects every canonical Linux and macOS node. It does not mean
|
|
63
63
|
“this computer,” and the manager is not implicitly a deployment target. Its own
|
|
64
|
-
node is targeted only if explicitly declared in Fleet membership.
|
|
65
|
-
the
|
|
66
|
-
|
|
67
|
-
Windows machines stay declared for diagnostics and are never converged.
|
|
64
|
+
node is targeted only if explicitly declared in Fleet membership. Workers are the
|
|
65
|
+
Ubuntu releases the [support matrix](support-matrix.md) lists; it also says what
|
|
66
|
+
is refused (macOS and Windows workers, WSL2, containers) and why.
|
|
68
67
|
`--node ID` filters troubleshooting/rollout. `--all` is an explicit spelling of the
|
|
69
68
|
default. Neither flag authorizes managing arbitrary Tailscale peers.
|
|
70
69
|
|
|
@@ -217,9 +216,12 @@ provider configuration: agent profiles are applied by `fleet-sync`. See
|
|
|
217
216
|
|
|
218
217
|
## Ubuntu and remote-work safety
|
|
219
218
|
|
|
220
|
-
The
|
|
221
|
-
|
|
222
|
-
|
|
219
|
+
The accepted releases are named once, in `ansible/ubuntu/policy.json`, and
|
|
220
|
+
listed in the [support matrix](support-matrix.md). Other releases receive
|
|
221
|
+
`UBUNTU_BASELINE_MISMATCH`, and WSL2 and containers
|
|
222
|
+
`WORKER_ENVIRONMENT_UNSUPPORTED`; there is no automatic release upgrade or
|
|
223
|
+
package downgrade. Package pins are minimums: see the
|
|
224
|
+
[worker contract](worker-contract.md#package-pins-and-security-updates). Fleet audits release, packages, service state and dpkg
|
|
223
225
|
consistency, declares snapshot sources for managed transactions, and pins its
|
|
224
226
|
selected APT packages. Unrelated applications and package sources are preserved.
|
|
225
227
|
|
package/docs/worker-contract.md
CHANGED
|
@@ -12,7 +12,8 @@ devfleet fleet-status --workers
|
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
The manager needs Node/npm, Git, SSH, Tailscale, and its own authorized T3 link.
|
|
15
|
-
Initially each
|
|
15
|
+
Initially each worker (an Ubuntu release the [support matrix](support-matrix.md)
|
|
16
|
+
lists) needs a
|
|
16
17
|
designated non-root account, trusted SSH access and administrator authorization. Standard Ubuntu cloud-image users
|
|
17
18
|
already commonly have noninteractive sudo; otherwise establish the repository's
|
|
18
19
|
`authorize-sudo.sh` policy once during administrator enrollment. Routine commands
|
|
@@ -39,6 +40,32 @@ HEAD. Private rosters contain connection paths and identities, not weaker policy
|
|
|
39
40
|
| No routine password prompts | Verified BatchMode SSH, `sudo -n`, source-owned NOPASSWD policy; missing initial authorization is a failure |
|
|
40
41
|
| Keep service authorization alive | Native service credentials stay per-node; Vault self-renewal timer and GitHub recovery from authorized Vault credentials; expiring T3 links are reported before expiry |
|
|
41
42
|
|
|
43
|
+
## Package pins and security updates
|
|
44
|
+
|
|
45
|
+
A pin in `ansible/ubuntu/policy.json` is a **minimum**, never a hold. Ubuntu
|
|
46
|
+
keeps installing security updates by itself, and a worker is not turned red by
|
|
47
|
+
one:
|
|
48
|
+
|
|
49
|
+
- The Ubuntu baseline installs a pinned package from the snapshot only when it
|
|
50
|
+
is absent or older than its pin, and never downgrades
|
|
51
|
+
(`ansible/ubuntu/tasks/install.yml`; every pinned package, `systemd-oomd`
|
|
52
|
+
included, goes through it).
|
|
53
|
+
- What Ubuntu installs unattended is declared in
|
|
54
|
+
`/etc/apt/apt.conf.d/52personal-fleet-unattended-upgrades`: security updates
|
|
55
|
+
only, no automatic reboot, no removals.
|
|
56
|
+
- `worker:package:NAME` passes at the pin, **warns** with
|
|
57
|
+
`PACKAGE_NEWER_THAN_PIN` above it, and fails with `PACKAGE_BELOW_PIN` below
|
|
58
|
+
it or when the package is missing. A warning does not block convergence or
|
|
59
|
+
readiness; it says the pin can be raised when the snapshot moves.
|
|
60
|
+
- Tailscale follows the same rule: a newer Tailscale is kept; an older one
|
|
61
|
+
stops with `TAILSCALE_UPGRADE_PENDING`, because upgrading an active VPN can
|
|
62
|
+
drop SSH.
|
|
63
|
+
|
|
64
|
+
A package run cut off by a power loss is finished by the next sync: the
|
|
65
|
+
baseline waits for the APT lock, runs `dpkg --configure -a` once (bounded to
|
|
66
|
+
ten minutes) and audits again; only a database still inconsistent stops with
|
|
67
|
+
`APT_STATE_BROKEN`.
|
|
68
|
+
|
|
42
69
|
Health does not mutate configuration, renew tokens, log in, install software, or
|
|
43
70
|
make billable inference requests. It audits the immutable desired worker deployment,
|
|
44
71
|
not an unrelated developer branch. Every declared worker must be enrolled and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crvouga/dev-fleet",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Keeps a set of development machines in sync: one manager and many workers",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "github:crvouga/dev-fleet",
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"engines": {
|
|
73
73
|
"node": ">=22"
|
|
74
74
|
},
|
|
75
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "620b8de05d662b4dc9072457580ca4b4eab54242"
|
|
76
76
|
}
|
package/schema/check-ids.json
CHANGED
|
@@ -174,10 +174,6 @@
|
|
|
174
174
|
"id": "login:*",
|
|
175
175
|
"meaning": "A native per-machine login is in place."
|
|
176
176
|
},
|
|
177
|
-
{
|
|
178
|
-
"id": "macos:release",
|
|
179
|
-
"meaning": "The manager runs a supported macOS release."
|
|
180
|
-
},
|
|
181
177
|
{
|
|
182
178
|
"id": "manager:docker",
|
|
183
179
|
"meaning": "The manager runs the pinned Docker Desktop."
|
|
@@ -202,6 +198,10 @@
|
|
|
202
198
|
"id": "notify:configured",
|
|
203
199
|
"meaning": "Every notification target fleet.json names has its address available as a secret."
|
|
204
200
|
},
|
|
201
|
+
{
|
|
202
|
+
"id": "platform:environment",
|
|
203
|
+
"meaning": "The worker runs on a physical or virtual machine of its own, not in a container or WSL2."
|
|
204
|
+
},
|
|
205
205
|
{
|
|
206
206
|
"id": "platform:unsupported",
|
|
207
207
|
"meaning": "The machine runs a supported operating system."
|
|
@@ -486,6 +486,10 @@
|
|
|
486
486
|
"id": "worker:github-principal",
|
|
487
487
|
"meaning": "The worker's GitHub login is the declared account."
|
|
488
488
|
},
|
|
489
|
+
{
|
|
490
|
+
"id": "worker:package:*",
|
|
491
|
+
"meaning": "The pinned Ubuntu package is installed at or past its pin."
|
|
492
|
+
},
|
|
489
493
|
{
|
|
490
494
|
"id": "worker:power-loss-recovery",
|
|
491
495
|
"meaning": "The worker powers on after AC loss."
|