@crvouga/dev-fleet 0.1.0 → 0.3.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 +265 -37
- package/agents/setup.md +42 -12
- package/ansible/macos/manager.yml +10 -7
- package/ansible/macos/policy.json +2 -10
- package/ansible/macos/t3-policy.yml +49 -0
- package/ansible/monitoring/README.md +86 -25
- package/ansible/monitoring/agent.yml +40 -2
- package/ansible/monitoring/hub-route-darwin.yml +51 -0
- package/ansible/monitoring/lock.json +5 -0
- package/ansible/pyproject.toml +12 -0
- package/ansible/ubuntu/baseline.yml +1 -1
- package/ansible/ubuntu/disk-gc.py +336 -0
- package/ansible/ubuntu/policy.json +13 -3
- package/ansible/ubuntu/t3-desktop.yml +19 -0
- package/ansible/ubuntu/t3-health.sh +93 -22
- package/ansible/ubuntu/t3-idle.py +42 -0
- package/ansible/ubuntu/t3-policy.yml +4 -11
- package/ansible/ubuntu/tailscale.yml +8 -5
- package/ansible/ubuntu/tasks/recovery.yml +60 -1
- package/ansible/ubuntu/tasks/resources.yml +61 -1
- package/ansible/ubuntu/tasks/t3-restart.yml +37 -0
- package/ansible/ubuntu/wip-guard.py +121 -0
- package/ansible/uv.lock +190 -0
- package/bootstrap/bootstrap.sh +25 -18
- package/bootstrap/doctor.ts +46 -1
- package/bootstrap/fleet-manager-run.sh +42 -15
- package/bootstrap/fleet-remote-bootstrap.sh +85 -12
- package/bootstrap/global-config.ts +64 -6
- package/bootstrap/install-launcher.sh +3 -0
- package/bootstrap/native-t3.ts +22 -11
- package/bootstrap/npm.ts +10 -2
- package/bootstrap/release-version.ts +157 -0
- package/cliproxy/README.md +3 -3
- package/cliproxy/scripts/accounts.py +216 -0
- package/cliproxy/scripts/checkpoint.py +29 -1
- package/cliproxy/scripts/cli.py +1 -1
- package/cliproxy/scripts/clientkeys.py +99 -0
- package/cliproxy/scripts/lease.py +80 -0
- package/cliproxy/scripts/ops +137 -25
- package/cliproxy/scripts/restore-verify +15 -0
- package/docs/accounts.md +154 -0
- package/docs/consumer-setup.md +408 -0
- package/docs/declared.md +91 -0
- package/docs/durability.md +26 -0
- package/docs/machine-setup.md +400 -0
- package/docs/operations.md +276 -0
- package/docs/releasing.md +103 -0
- package/docs/state.md +254 -0
- package/docs/topology.md +69 -0
- package/docs/ubuntu-sync.md +357 -0
- package/docs/worker-contract.md +67 -0
- package/examples/fleet.json +5 -0
- package/llms.txt +36 -0
- package/mise.lock +29 -0
- package/mise.toml +15 -2
- package/package.json +11 -2
- package/schema/fleet.schema.json +137 -15
- package/src/accounts/checks.ts +220 -0
- package/src/accounts/context.ts +76 -0
- package/src/accounts/propagate.ts +62 -0
- package/src/accounts/roster.ts +85 -0
- package/src/accounts/selection.ts +67 -0
- package/src/accounts/sessions.ts +120 -0
- package/src/agents/account-relay.ts +176 -26
- package/src/agents/fleet-verify.ts +9 -0
- package/src/agents/launch.ts +84 -5
- package/src/agents/native-verify.ts +293 -22
- package/src/agents/provider-errors.ts +9 -2
- package/src/agents/registry.ts +7 -1
- package/src/cli/agent-guide.ts +39 -5
- package/src/cli/app.ts +11 -4
- package/src/cli/cli.ts +7 -0
- package/src/cli/command-index.ts +7 -0
- package/src/cli/fleet-accounts.ts +253 -0
- package/src/cli/fleet-state.ts +499 -0
- package/src/cli/fleet-sync-accounts.ts +58 -0
- package/src/cli/fleet-sync-checkout.ts +56 -17
- package/src/cli/fleet-sync-enrollment.ts +19 -23
- package/src/cli/fleet-sync-forward.ts +1 -1
- package/src/cli/fleet-sync-manager.ts +84 -12
- package/src/cli/fleet-sync-monitoring.ts +5 -5
- package/src/cli/fleet-sync-output.ts +7 -3
- package/src/cli/fleet-sync-remote.ts +31 -18
- package/src/cli/fleet-sync-state.ts +86 -0
- package/src/cli/fleet-sync.ts +159 -57
- package/src/cli/fleet.ts +4 -2
- package/src/cli/managed-health.ts +4 -4
- package/src/cli/node-sync.ts +7 -0
- package/src/cli/program.ts +20 -4
- package/src/cli/project.ts +51 -0
- package/src/cli/register-fleet.ts +86 -1
- package/src/cli/registry.ts +41 -1
- package/src/cli/roster.ts +46 -30
- package/src/cli/settings.ts +15 -12
- package/src/cli/source-operations.ts +27 -3
- package/src/cli/tailnet.ts +2 -2
- package/src/definition.ts +116 -4
- package/src/diagnostics.ts +1 -1
- package/src/fleet-config.ts +105 -19
- package/src/ide/definition.ts +86 -0
- package/src/ide/ide.ts +43 -0
- package/src/ide/index.ts +5 -0
- package/src/ide/orchestrate.ts +50 -0
- package/src/ide/t3.ts +111 -0
- package/src/machine/source.ts +3 -3
- package/src/machine/state.ts +15 -27
- package/src/manager-sync.ts +9 -18
- package/src/monitoring/agent-evidence.ts +146 -0
- package/src/monitoring/apply.ts +15 -3
- package/src/monitoring/beszel.ts +11 -4
- package/src/monitoring/enroll.ts +2 -1
- package/src/monitoring/hub-access.ts +4 -3
- package/src/monitoring/hub-backup.ts +121 -0
- package/src/monitoring/hub-viewer.ts +8 -8
- package/src/monitoring/hub.ts +3 -3
- package/src/monitoring/manager-hub.ts +30 -14
- package/src/node-converge-linux.ts +30 -7
- package/src/package-info.ts +45 -0
- package/src/platform/ansible.ts +19 -15
- package/src/platform/darwin.ts +0 -13
- package/src/platform/fallback.ts +0 -8
- package/src/platform/linux.ts +0 -10
- package/src/platform/resource-verify.ts +8 -4
- package/src/platform/t3-policy.ts +33 -6
- package/src/platform/t3-service.ts +104 -0
- package/src/platform/types.ts +0 -4
- package/src/platform/ubuntu-audit.ts +5 -2
- package/src/platform/windows.ts +0 -14
- package/src/remote-bootstrap.ts +17 -19
- package/src/remote-entry.ts +15 -1
- package/src/repair-catalog.ts +1 -1
- package/src/runner.ts +1 -1
- package/src/service-auth.ts +15 -5
- package/src/state/bucket.ts +208 -0
- package/src/state/check.ts +74 -0
- package/src/state/connection.ts +92 -0
- package/src/state/enrollment.ts +45 -0
- package/src/state/legacy.ts +89 -0
- package/src/state/litestream.ts +143 -0
- package/src/state/operator.ts +20 -0
- package/src/state/paths.ts +40 -0
- package/src/state/roster.ts +62 -0
- package/src/state/store.ts +169 -0
- package/src/state/sync.ts +352 -0
- package/src/t3/accounts.ts +7 -1
- package/src/t3/models.ts +133 -0
- package/src/t3/pairing.ts +31 -0
- package/src/t3/profile.ts +203 -0
- package/src/t3/version.ts +119 -0
- package/src/t3-endpoint.ts +64 -8
- package/src/t3-linked-check.ts +24 -2
- package/src/toolchain-checks.ts +25 -0
- package/src/transport.ts +3 -2
- package/src/types.ts +4 -0
- package/src/worker-access-checks.ts +56 -3
- package/src/worker-host-checks.ts +41 -13
- package/src/workstation-checks.ts +1 -1
- package/src/index.ts +0 -6
- package/src/test-fixtures.ts +0 -36
package/ansible/uv.lock
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 5
|
|
3
|
+
requires-python = "==3.13.16"
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "ansible-core"
|
|
7
|
+
version = "2.19.9"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
dependencies = [
|
|
10
|
+
{ name = "cryptography" },
|
|
11
|
+
{ name = "jinja2" },
|
|
12
|
+
{ name = "packaging" },
|
|
13
|
+
{ name = "pyyaml" },
|
|
14
|
+
{ name = "resolvelib" },
|
|
15
|
+
]
|
|
16
|
+
sdist = { url = "https://files.pythonhosted.org/packages/26/5b/161275ff5fa673dfc1329ed19a44331a4f491166a9f0cb907b44037ad72e/ansible_core-2.19.9.tar.gz", hash = "sha256:74107de13d188ff579fb215bc3eb875c9198803215d6378ed588c7f35aba12f5", size = 3431475, upload-time = "2026-04-21T00:47:09.194Z" }
|
|
17
|
+
wheels = [
|
|
18
|
+
{ url = "https://files.pythonhosted.org/packages/3d/b9/f2c07e6cdba6a5882342d666ceea4eeeb6fea20ec325fac68c27bfcea722/ansible_core-2.19.9-py3-none-any.whl", hash = "sha256:18de80e3ea5a89d2ea84e4d4a3c6d7c121ea62fc3af2877ba083784e3d90b419", size = 2416269, upload-time = "2026-04-21T00:47:06.777Z" },
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[[package]]
|
|
22
|
+
name = "cffi"
|
|
23
|
+
version = "2.1.1"
|
|
24
|
+
source = { registry = "https://pypi.org/simple" }
|
|
25
|
+
dependencies = [
|
|
26
|
+
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
|
|
27
|
+
]
|
|
28
|
+
sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" }
|
|
29
|
+
wheels = [
|
|
30
|
+
{ url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" },
|
|
31
|
+
{ url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" },
|
|
32
|
+
{ url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" },
|
|
33
|
+
{ url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" },
|
|
34
|
+
{ url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" },
|
|
35
|
+
{ url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" },
|
|
36
|
+
{ url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" },
|
|
37
|
+
{ url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" },
|
|
38
|
+
{ url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" },
|
|
39
|
+
{ url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" },
|
|
40
|
+
{ url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" },
|
|
41
|
+
{ url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" },
|
|
42
|
+
{ url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" },
|
|
43
|
+
{ url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" },
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[[package]]
|
|
47
|
+
name = "cryptography"
|
|
48
|
+
version = "50.0.2"
|
|
49
|
+
source = { registry = "https://pypi.org/simple" }
|
|
50
|
+
dependencies = [
|
|
51
|
+
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
|
52
|
+
]
|
|
53
|
+
sdist = { url = "https://files.pythonhosted.org/packages/9d/af/182eb91b0df3fe75c4d9f26fe70684569566745f6ba7e5c9c73a862c5252/cryptography-50.0.2.tar.gz", hash = "sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5", size = 880623, upload-time = "2026-09-30T15:30:04.884Z" }
|
|
54
|
+
wheels = [
|
|
55
|
+
{ url = "https://files.pythonhosted.org/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb", size = 3914904, upload-time = "2026-09-30T14:43:44.339Z" },
|
|
56
|
+
{ url = "https://files.pythonhosted.org/packages/d9/69/c9bd862c3bf43d6399c433caf002df16e2dffd4be49bdf515cda38038711/cryptography-50.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0", size = 4731146, upload-time = "2026-09-30T14:43:47.113Z" },
|
|
57
|
+
{ url = "https://files.pythonhosted.org/packages/21/69/64cef1f702bf6657e0cc186ed1a2891d50d29fb41586b254e1c07adea261/cryptography-50.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630ebfea3bf689d075f82316324ff7433dc447fe6bc1bfc76524b74b4a9567d2", size = 4719841, upload-time = "2026-09-30T14:43:49.01Z" },
|
|
58
|
+
{ url = "https://files.pythonhosted.org/packages/38/6b/61a3f8d8c5e1e49a6cddccafc4015cc1c0021360ab0acb4080e7a423644a/cryptography-50.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f9f6143a8c75945eb960d9eb98905a441394abfa24afaae239d514ffb2586480", size = 4738340, upload-time = "2026-09-30T14:43:50.932Z" },
|
|
59
|
+
{ url = "https://files.pythonhosted.org/packages/7b/2e/7212ca32fd43dc91f2f41db20160b268098874b4c9a0e7be94d6835f5b2e/cryptography-50.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a582ab2ae1d34f67112cadc86702774c9ea4374df6bca6afe672817203c99134", size = 5367029, upload-time = "2026-09-30T14:43:52.911Z" },
|
|
60
|
+
{ url = "https://files.pythonhosted.org/packages/1a/f1/b474e930c4d910328780e3940da76f5aa5cbc48ce1fc14e44d239d9ea9db/cryptography-50.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4061c0079120205fb760c58acab6443e217307dcf05e3702cf970e0689972856", size = 4753050, upload-time = "2026-09-30T14:43:55.272Z" },
|
|
61
|
+
{ url = "https://files.pythonhosted.org/packages/7c/52/9af10e80ac16b0fcc2123f9cbd5e7afbd0fd5075bb7a607c592258a39cda/cryptography-50.0.2-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:ac9ed99d81760c62fe89d5f0815cdfa1ba9a35141cf30f1c2d044f04b4803d2e", size = 4376724, upload-time = "2026-09-30T14:43:57.24Z" },
|
|
62
|
+
{ url = "https://files.pythonhosted.org/packages/71/37/6202e488cc1eb625ea110c292c6bda92823176e023f427d8d5660ce8d632/cryptography-50.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:87e9ce85beb6b328ba370cc6e6aea483c92617b4c95b1d33a49297eb662bfb04", size = 4737859, upload-time = "2026-09-30T14:43:59.541Z" },
|
|
63
|
+
{ url = "https://files.pythonhosted.org/packages/8f/30/e86d7d518489b0ae2497091a35287abcb1a2ce4037837a34afbe9b1d6964/cryptography-50.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f265528741e048bce55c3463ed721fb0aa45a5888d8add8cfeccb3035451bbdc", size = 5324103, upload-time = "2026-09-30T14:44:01.901Z" },
|
|
64
|
+
{ url = "https://files.pythonhosted.org/packages/d3/69/2c833a049475e0a3444e94c7d0aca0aa51d166374a449b09e92ac98138de/cryptography-50.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dab55f57c74c3cad24c323bacbbd04be4705ba6eb0d92e920b1fc4837ed5079", size = 4752576, upload-time = "2026-09-30T14:44:04.545Z" },
|
|
65
|
+
{ url = "https://files.pythonhosted.org/packages/6c/5d/906970b83bbfc1f5bbfb677a143c181f2801f23b6a7204a3b47c42c97e65/cryptography-50.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25784ce8b9621c90c643efb9e1e2162ab3b0224cae446ad5e70e7fcb1ce18b51", size = 4870819, upload-time = "2026-09-30T14:44:06.884Z" },
|
|
66
|
+
{ url = "https://files.pythonhosted.org/packages/68/e3/f2298d3bb55e0c4a91841ec4d01b3f020ba8c5fbf15ccdcc6dcf03f97025/cryptography-50.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:85d0d9a31b9098e98534226d5686b47264b95e62ce459dc2e62fdfc809f9fe93", size = 5030152, upload-time = "2026-09-30T14:44:09.443Z" },
|
|
67
|
+
{ url = "https://files.pythonhosted.org/packages/9a/4f/adfc442765721292fff86d314ce385d3249d22db42295c0dd057727b60f3/cryptography-50.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:7afa5a6602a9f29af1f3a2965f831bae7c9d5d597b7cbb716d41ab3b7d89879c", size = 3824692, upload-time = "2026-09-30T14:44:11.671Z" },
|
|
68
|
+
{ url = "https://files.pythonhosted.org/packages/2d/49/93f6a6e7a87c9aa68d44d3e1cdb5fe8f60c90d5d2f46acae9a56892816b8/cryptography-50.0.2-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:edc3342adf8f697fc5f59c887a304356f147b397809440ed64e2fa6af2f50f37", size = 4133708, upload-time = "2026-09-30T14:44:41.807Z" },
|
|
69
|
+
{ url = "https://files.pythonhosted.org/packages/8c/75/32ac2a56243d778805c16ca6a32b8f74fb757df7e28d7ecb560afafb59cf/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d370b8d1dfcdf7130178137f6fbee6140774a1acc6cacefc4b42643ec11d0a3a", size = 4956267, upload-time = "2026-09-30T14:44:43.693Z" },
|
|
70
|
+
{ url = "https://files.pythonhosted.org/packages/aa/a4/2c8d734e43d97f0842ee9f1b7b4bfb3d0cf5e19edebf43c2afe6675c2320/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2f9bd7f90c64fe89253f0a2c05e3c4856072660429ce8831b4235bf29403a67", size = 4966465, upload-time = "2026-09-30T14:44:45.769Z" },
|
|
71
|
+
{ url = "https://files.pythonhosted.org/packages/c2/58/ee288c829a6f41f6235ae9dd33d82fd19b45442b65b4c8a3da36963d9f7a/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_aarch64.whl", hash = "sha256:e275096ea1e60cc595cda2836fd4a6c725d1125108b868be17f53684d164e2cc", size = 4959356, upload-time = "2026-09-30T14:44:48.211Z" },
|
|
72
|
+
{ url = "https://files.pythonhosted.org/packages/92/20/9ded6d51ddd9897f6b6e81fb9ebea7951d7cc5d6c890b0ed8abf77a51a80/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_ppc64le.whl", hash = "sha256:b13478603dcd0a2479ff8e87e2c19a7d525734686fe3c49542472293a204212d", size = 5548822, upload-time = "2026-09-30T14:44:50.86Z" },
|
|
73
|
+
{ url = "https://files.pythonhosted.org/packages/02/a8/8df951850d6b31d2a00218f19e2b3f999523437ed7a819df7fa427942fca/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_x86_64.whl", hash = "sha256:58a0c478eeca76fe5e07993c5a0703def34a6dc6a0cda4f5564639b33112ffe7", size = 5001199, upload-time = "2026-09-30T14:44:53.379Z" },
|
|
74
|
+
{ url = "https://files.pythonhosted.org/packages/8b/f9/36b3022218ce75b7cdf068fb95f809f9bd0d820e4955ef43b90c255cc7ac/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_31_armv7l.whl", hash = "sha256:d38cdff612d06fa6a32840d5e1b1f7a27cee4a349aa9085d94a67789d6bfd408", size = 4629333, upload-time = "2026-09-30T14:44:55.635Z" },
|
|
75
|
+
{ url = "https://files.pythonhosted.org/packages/8c/72/20f99a219f6af47cdd1cbd978c243b92d71496e168a746138af44ded4f29/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_aarch64.whl", hash = "sha256:fdd28f912fccfec1846a94e2e1e8f9b0012f557f0c46fe4f3eb0d7a87afcf90b", size = 4958822, upload-time = "2026-09-30T14:44:59.639Z" },
|
|
76
|
+
{ url = "https://files.pythonhosted.org/packages/f2/20/196f112617fb08eb4d608a2a6c422373d46f9cc2857f38fc0667033c0899/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_ppc64le.whl", hash = "sha256:cbc8738fd8526d80f35cb3a40d41f41a2e7030bb3b18b09a6778ef63d291c2fd", size = 5506351, upload-time = "2026-09-30T14:45:02.267Z" },
|
|
77
|
+
{ url = "https://files.pythonhosted.org/packages/24/95/83378121ef3eaaaf71d4b781577ff794acb39b9e1b87a3f156898c8497ed/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_x86_64.whl", hash = "sha256:e105ab60406787da31fccc883fc0f733af1efd78f0136a4599692c4083a73d0c", size = 5000859, upload-time = "2026-09-30T14:45:05.009Z" },
|
|
78
|
+
{ url = "https://files.pythonhosted.org/packages/22/f7/70fd7ae4d1dbfa7ba29b02e1b9068771519a86027756510b700ce81086a8/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6f8700550aa1474a91e5dc07049c46f98b423b5b1ddd0483e0b51362eeeaf5be", size = 5092151, upload-time = "2026-09-30T15:29:15.932Z" },
|
|
79
|
+
{ url = "https://files.pythonhosted.org/packages/d4/be/688367b74de86984bd58d8efacfc7c9e68b89a6a22ced0fb4f38db50254a/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:c71be1cbfa5cd9a41ee452acf1eccd82b2c05950358b106ec8ceb83411d1a020", size = 5286120, upload-time = "2026-09-30T15:29:18.309Z" },
|
|
80
|
+
{ url = "https://files.pythonhosted.org/packages/39/d1/55f8a3f2ef5d1529e16835ef10cf0fe3d559ce237b46dddc440c0bba3649/cryptography-50.0.2-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:c423ab384a46c4dff7217b2ea5ba2e11cffdeab6441acd04cf65a369caf0366c", size = 4111557, upload-time = "2026-09-30T15:29:20.155Z" },
|
|
81
|
+
{ url = "https://files.pythonhosted.org/packages/23/ad/ac987755d00e1e64273760228d2635ae38dae2be83e3c6e0d3289d91dec3/cryptography-50.0.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:0ec5f09541743261e66e291b4a0cbf0fb2997aeaab6d9e9c740b9dba1b58d1c2", size = 3943588, upload-time = "2026-09-30T15:29:22.265Z" },
|
|
82
|
+
{ url = "https://files.pythonhosted.org/packages/d5/8d/6d585339bedf85d45044c85d8412dac53f2bb6f918e8b7777efba1787844/cryptography-50.0.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c5e67125c7dca78d199ec4e116aa93dbb83494808ecbb8211a2cb09b1bf41dbd", size = 4756166, upload-time = "2026-09-30T15:29:24.58Z" },
|
|
83
|
+
{ url = "https://files.pythonhosted.org/packages/bf/f1/1c1f6874e8550cfddd4b688ceb38cefb6ed15ceed224d56f133f3d88c214/cryptography-50.0.2-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee247f5c245c9a2fe7c8e2214e295918838e44e00a45a6718451e4004219e767", size = 4749145, upload-time = "2026-09-30T15:29:26.807Z" },
|
|
84
|
+
{ url = "https://files.pythonhosted.org/packages/c1/63/61b15dc1a8de03fe0adbe3fd7608b3ad5c73bf50993bbcb1faaa930afe33/cryptography-50.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dfe9763530994147d9af1def057a5b9658b00e8f8fe8743d144d1e0911c2e454", size = 4763638, upload-time = "2026-09-30T15:29:28.588Z" },
|
|
85
|
+
{ url = "https://files.pythonhosted.org/packages/fc/35/b345bdfa40c9126df1a9d33236aa98418367931b8725f84fc3ae2b98dc59/cryptography-50.0.2-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:58ddb5a8e3179d12f19e4ea34d2d32e9d63a4baa142c875c1eb59f41b7243acd", size = 5382217, upload-time = "2026-09-30T15:29:30.589Z" },
|
|
86
|
+
{ url = "https://files.pythonhosted.org/packages/4f/87/ef344a9e616871f2519c22d6afcda79ddd5d35e9592d95eb6e677608d055/cryptography-50.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f21e8a22c8605750c7af886bab299a363721264061b4ac0a30efb73cfd58efc5", size = 4781387, upload-time = "2026-09-30T15:29:32.605Z" },
|
|
87
|
+
{ url = "https://files.pythonhosted.org/packages/90/5b/f2fdb13cd0b96f6f932c8627bb292a45f11c64d21620a8e120aee9a3b848/cryptography-50.0.2-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:9c8402a82ea0dc4ceeab793db05f0fafa8ca139ca34fcde5df0f596103c74107", size = 4403790, upload-time = "2026-09-30T15:29:34.374Z" },
|
|
88
|
+
{ url = "https://files.pythonhosted.org/packages/bc/ce/7e4f662b1e3c393513569e402cfc85ac7da0bd3d5435e122a3140219eb2d/cryptography-50.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:0ddc924c04591c2811ca024d62ecad4f7f6f08af8939c211438f48a16bd23602", size = 4764319, upload-time = "2026-09-30T15:29:36.149Z" },
|
|
89
|
+
{ url = "https://files.pythonhosted.org/packages/3c/3f/86ff33ce34cc0de6847fb96e035a1a760d81652e38643f617c02ad32ef7a/cryptography-50.0.2-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a6557e5f38e065ca9fbdaf7cfc7435ecb1d113aa81a022d1b51921ee7432e227", size = 5338560, upload-time = "2026-09-30T15:29:39.053Z" },
|
|
90
|
+
{ url = "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c", size = 4780973, upload-time = "2026-09-30T15:29:41.251Z" },
|
|
91
|
+
{ url = "https://files.pythonhosted.org/packages/10/bf/8d6ebc7dded797bd0f0160d52188021211f011a2b164ef0ae1dac4587465/cryptography-50.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e", size = 4897738, upload-time = "2026-09-30T15:29:43.106Z" },
|
|
92
|
+
{ url = "https://files.pythonhosted.org/packages/d4/aa/f3f6e0de7e6253b8baa8b2d8fb9d50924fa75cee3d4624bd4bc1208ee923/cryptography-50.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94", size = 5058280, upload-time = "2026-09-30T15:29:44.827Z" },
|
|
93
|
+
{ url = "https://files.pythonhosted.org/packages/f6/b6/a1faf3a27ae9405fb34b1713cc73b2d8a26b04d5c561578fa2e6ef3e5bb9/cryptography-50.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de", size = 3854095, upload-time = "2026-09-30T15:29:46.782Z" },
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
[[package]]
|
|
97
|
+
name = "dev-fleet-ansible"
|
|
98
|
+
version = "0"
|
|
99
|
+
source = { virtual = "." }
|
|
100
|
+
dependencies = [
|
|
101
|
+
{ name = "ansible-core" },
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
[package.metadata]
|
|
105
|
+
requires-dist = [{ name = "ansible-core", specifier = "==2.19.9" }]
|
|
106
|
+
|
|
107
|
+
[[package]]
|
|
108
|
+
name = "jinja2"
|
|
109
|
+
version = "3.1.6"
|
|
110
|
+
source = { registry = "https://pypi.org/simple" }
|
|
111
|
+
dependencies = [
|
|
112
|
+
{ name = "markupsafe" },
|
|
113
|
+
]
|
|
114
|
+
sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
|
|
115
|
+
wheels = [
|
|
116
|
+
{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
[[package]]
|
|
120
|
+
name = "markupsafe"
|
|
121
|
+
version = "3.0.4"
|
|
122
|
+
source = { registry = "https://pypi.org/simple" }
|
|
123
|
+
sdist = { url = "https://files.pythonhosted.org/packages/38/9b/e422a865e1d5d57d0e509b4e0bf1c1a70a7f6382c29a5aa428df994c8bc8/markupsafe-3.0.4.tar.gz", hash = "sha256:2e9ad7dd851bf45fab9f75cbff4cb493fee9979e8d8c7c9c3ee119022518edd6", size = 153777, upload-time = "2026-10-02T23:07:22.29Z" }
|
|
124
|
+
wheels = [
|
|
125
|
+
{ url = "https://files.pythonhosted.org/packages/6d/18/4bc5ba32499e87bb2b0ef5b3a9bb9c00a131fa961ddf0be548cb550f548b/markupsafe-3.0.4-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:de8b364c423ef0a4bad9069657d617f9a5d2b2062457a89b1fa16ee199c399c1", size = 12786, upload-time = "2026-10-02T23:05:08.709Z" },
|
|
126
|
+
{ url = "https://files.pythonhosted.org/packages/4e/6f/17f0c099bf25f3e31e63cc19244d9f6af861a9a4ab778c203997903cfdd0/markupsafe-3.0.4-cp313-cp313-android_24_x86_64.whl", hash = "sha256:34bdde374c5932765d7dc685c4a1d191a3207852d67e8e0a9eb6ea85156181f1", size = 12510, upload-time = "2026-10-02T23:05:09.93Z" },
|
|
127
|
+
{ url = "https://files.pythonhosted.org/packages/11/af/1a141081b905036ee904ec4bd945e1f70b4e1b32d33c4e59e8cf1d58b247/markupsafe-3.0.4-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:6bd9e1788e15bfcf6a9082de42e30387e7b85d211ab21e57a939bb8cfaaf8d96", size = 11583, upload-time = "2026-10-02T23:05:10.884Z" },
|
|
128
|
+
{ url = "https://files.pythonhosted.org/packages/e7/0a/a89385ae590232622a03e091805cff12f24fabe6c11e0e8bae096cece81c/markupsafe-3.0.4-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:5066b244f576f91afc8ee3ba029a89f99d39c79b1853fe9d39bea9f0afbec148", size = 11801, upload-time = "2026-10-02T23:05:11.913Z" },
|
|
129
|
+
{ url = "https://files.pythonhosted.org/packages/ed/85/ea548dc013962eb73653124bc595635fbf9e0fa41d1f181a967ccb784dfb/markupsafe-3.0.4-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7a83aa6e4805df46fed18e989d3d16f86ef60cb50bbc8d9ce3a6be89165fbf6e", size = 11443, upload-time = "2026-10-02T23:05:12.887Z" },
|
|
130
|
+
{ url = "https://files.pythonhosted.org/packages/cc/72/15f2e5ec9cf2eb00d5cdfe968d94e4156a7bd7303832c3f3b2c403a36839/markupsafe-3.0.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2d1b7d9308288661f56672b1b157d75fc536714d3638487bbea17b6318a78248", size = 11665, upload-time = "2026-10-02T23:05:13.829Z" },
|
|
131
|
+
{ url = "https://files.pythonhosted.org/packages/ca/e0/4030bea613677e333c8a2c901fd405055f657f9d06acba5b7357984b6ef7/markupsafe-3.0.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73e77980c7207854f00fc4e71fb1626868d5740ab4012623d55c7a99ad122a72", size = 12061, upload-time = "2026-10-02T23:05:14.807Z" },
|
|
132
|
+
{ url = "https://files.pythonhosted.org/packages/f3/a5/28b76a7449eb702966b88bef599e2360b411fbb3afeee8fe560939be06ec/markupsafe-3.0.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7018d4af1cd272e847aa5917983ab5e83e4f6579f9dbfecd4a79c0ca80b144c2", size = 24395, upload-time = "2026-10-02T23:05:15.909Z" },
|
|
133
|
+
{ url = "https://files.pythonhosted.org/packages/07/6c/21232811afc3a063b5e934b1ae2efda52f46154ec382f585149c020e61fe/markupsafe-3.0.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c90d5b3d4e944e065a301d741b3c1d784f6bd1f503aa68b4967e32b2ba313d85", size = 27183, upload-time = "2026-10-02T23:05:16.976Z" },
|
|
134
|
+
{ url = "https://files.pythonhosted.org/packages/14/38/6ccdfa5b59049cb36fb80cbc80aee9cf1fc9bb77d1335ad435f2070b08cf/markupsafe-3.0.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:18a801868a884f216e784d7d14db2a4077143ce7610440aee2ce8f734e7cfcde", size = 24772, upload-time = "2026-10-02T23:05:18.209Z" },
|
|
135
|
+
{ url = "https://files.pythonhosted.org/packages/63/e0/cec6865dfe88cb48fedd4b20aed6af5158e41092adcbf3e028bcc6ec2108/markupsafe-3.0.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:434139499bb20b502ed3baa1f169e618f924a97e7a777fea1a49446d80106cf6", size = 23017, upload-time = "2026-10-02T23:05:19.286Z" },
|
|
136
|
+
{ url = "https://files.pythonhosted.org/packages/ee/76/6ed4940bb7648a9aac457c14f870cfdd5105f139a0fb1f29cd61fafa47d1/markupsafe-3.0.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9e227f3dbe6bde7491cf0a9965d00b88c6b1a4a95d11480ddf88bb96d397c19f", size = 22036, upload-time = "2026-10-02T23:05:20.352Z" },
|
|
137
|
+
{ url = "https://files.pythonhosted.org/packages/a1/4f/ed476226d4fe46a09090a36025bf319296810028df55eb12f1253b540f3a/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b8cd1f918b26fd7b1832ece557cc18f2d8747309ff8b3f0ef9d4250c5ad67a39", size = 23786, upload-time = "2026-10-02T23:05:21.576Z" },
|
|
138
|
+
{ url = "https://files.pythonhosted.org/packages/9a/35/66ff30450e35ef5fba9ebc930c9411747e537fd9447b65e44f5007e2b84d/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a5fcffb37e602b0b3c1638a97746b9b96125caa9bcf6fa41d337a9261de231ee", size = 22672, upload-time = "2026-10-02T23:05:22.922Z" },
|
|
139
|
+
{ url = "https://files.pythonhosted.org/packages/32/0b/72f45ce4b4efcbca4b80cf1b06703eff0be8d37e82abb78f66c85a7ead1e/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5989cb26b2e1efc6a42216a9f6b5ee495ce5ace2e5b352a9af489976b32d1ee2", size = 24107, upload-time = "2026-10-02T23:05:24.175Z" },
|
|
140
|
+
{ url = "https://files.pythonhosted.org/packages/d2/03/71776e5fdcba04614b384cc102e8a4198208579d896fd1394cb7cb9aa900/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:add96447a86d205ab616665d53b2950ee81083757f56e6ea833c8b2917646b46", size = 21615, upload-time = "2026-10-02T23:05:25.215Z" },
|
|
141
|
+
{ url = "https://files.pythonhosted.org/packages/ab/5f/801ce02a02e7aee0f784b1ec7843026178f6adeb9c93ac67eb1992a9a84d/markupsafe-3.0.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2628d3a8cb648ecebb3c5d6b0a1052d400e4d8b7ac0fb786be8d285b50040d17", size = 23076, upload-time = "2026-10-02T23:05:26.423Z" },
|
|
142
|
+
{ url = "https://files.pythonhosted.org/packages/4a/85/c43776625428f3bb4a61e8633940400e3efe6409e3c6f5bff26de5e45618/markupsafe-3.0.4-cp313-cp313-win32.whl", hash = "sha256:672d207103e6b16ca098611b0f9efad6bc00afd47c03d6ef62186495ca677dc0", size = 14118, upload-time = "2026-10-02T23:05:27.716Z" },
|
|
143
|
+
{ url = "https://files.pythonhosted.org/packages/6f/36/163da64de88a13db79214ef75fa041be7fa13bdb42261cf5b7484de14bfb/markupsafe-3.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:1f1f9477e174582b0a1b583d60b66e1f2cf5d3fe12cee985e4aedf44766600e5", size = 14313, upload-time = "2026-10-02T23:05:28.749Z" },
|
|
144
|
+
{ url = "https://files.pythonhosted.org/packages/9f/a8/9b662783ffaa1149221432a923cee562f78b9cbbb8baa3df9b3753e63e1e/markupsafe-3.0.4-cp313-cp313-win_arm64.whl", hash = "sha256:06de8ef6331f6e822c28d577dc8bf43fe398800477c49498f38fc38b67ff33fc", size = 14189, upload-time = "2026-10-02T23:05:29.917Z" },
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
[[package]]
|
|
148
|
+
name = "packaging"
|
|
149
|
+
version = "26.3"
|
|
150
|
+
source = { registry = "https://pypi.org/simple" }
|
|
151
|
+
sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
|
|
152
|
+
wheels = [
|
|
153
|
+
{ url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
|
|
154
|
+
]
|
|
155
|
+
|
|
156
|
+
[[package]]
|
|
157
|
+
name = "pycparser"
|
|
158
|
+
version = "3.12"
|
|
159
|
+
source = { registry = "https://pypi.org/simple" }
|
|
160
|
+
sdist = { url = "https://files.pythonhosted.org/packages/cb/27/eeb9f307bf07060f3d9f9aa0fb8c15b01bdfded0a941e4b2719c7f94cacd/pycparser-3.12.tar.gz", hash = "sha256:f03387877eab82ce01bbdfd0ba7252ee55a90b1c708d5129e2ce915475cf1db6", size = 158785, upload-time = "2026-10-22T13:40:03.56Z" }
|
|
161
|
+
wheels = [
|
|
162
|
+
{ url = "https://files.pythonhosted.org/packages/b6/ae/315bbeee126a769cbde43cf4908acb4b40408d8bf8bbf334dd7368d8dadf/pycparser-3.12-py3-none-any.whl", hash = "sha256:a076f3f489b39b43ed13563a1dafe42447445bec2e294154ec5c57fcdda4c85e", size = 62600, upload-time = "2026-10-22T13:40:02.021Z" },
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
[[package]]
|
|
166
|
+
name = "pyyaml"
|
|
167
|
+
version = "6.0.3"
|
|
168
|
+
source = { registry = "https://pypi.org/simple" }
|
|
169
|
+
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
|
170
|
+
wheels = [
|
|
171
|
+
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
|
|
172
|
+
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
|
|
173
|
+
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
|
|
174
|
+
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
|
|
175
|
+
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
|
|
176
|
+
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
|
|
177
|
+
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
|
|
178
|
+
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
|
|
179
|
+
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
|
|
180
|
+
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
[[package]]
|
|
184
|
+
name = "resolvelib"
|
|
185
|
+
version = "1.2.1"
|
|
186
|
+
source = { registry = "https://pypi.org/simple" }
|
|
187
|
+
sdist = { url = "https://files.pythonhosted.org/packages/1d/14/4669927e06631070edb968c78fdb6ce8992e27c9ab2cde4b3993e22ac7af/resolvelib-1.2.1.tar.gz", hash = "sha256:7d08a2022f6e16ce405d60b68c390f054efcfd0477d4b9bd019cc941c28fad1c", size = 24575, upload-time = "2025-10-11T01:07:44.582Z" }
|
|
188
|
+
wheels = [
|
|
189
|
+
{ url = "https://files.pythonhosted.org/packages/e2/23/c941a0d0353681ca138489983c4309e0f5095dfd902e1357004f2357ddf2/resolvelib-1.2.1-py3-none-any.whl", hash = "sha256:fb06b66c8da04172d9e72a21d7d06186d8919e32ae5ab5cdf5b9d920be805ac2", size = 18737, upload-time = "2025-10-11T01:07:43.081Z" },
|
|
190
|
+
]
|
package/bootstrap/bootstrap.sh
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
#
|
|
2
|
+
# Installs the pinned toolchain for the checkout this script is in. fleet-sync
|
|
3
|
+
# runs it on every machine from the checkout of the revision it applies;
|
|
4
|
+
# nobody clones dev-fleet to use it (see README.md).
|
|
3
5
|
set -euo pipefail
|
|
4
|
-
MISE_VERSION=2026.10.24
|
|
5
|
-
repo=https://github.com/crvouga/dev-fleet.git
|
|
6
6
|
os=$(uname -s)
|
|
7
7
|
case "$os" in
|
|
8
8
|
Darwin) platform=macos ;;
|
|
@@ -37,16 +37,20 @@ for tool in curl tar unzip; do
|
|
|
37
37
|
command -v "$tool" >/dev/null || { echo "Install $tool, then rerun." >&2; exit 1; }
|
|
38
38
|
done
|
|
39
39
|
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
40
|
-
|
|
41
|
-
checkout
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
40
|
+
[[ -f "$script_dir/../mise.toml" ]] || {
|
|
41
|
+
echo 'Run this from a dev-fleet checkout. To use dev-fleet, run its CLI instead: fleet-sync installs the toolchain.' >&2; exit 1;
|
|
42
|
+
}
|
|
43
|
+
checkout=$(cd "$script_dir/.." && pwd)
|
|
49
44
|
cd "$checkout"
|
|
45
|
+
# mise is pinned where every other tool is: `min_version` in mise.toml is the
|
|
46
|
+
# version installed, and [vars] holds the digest of its release archive for
|
|
47
|
+
# each platform. mise cannot read them for us before it exists.
|
|
48
|
+
pin() { sed -n "s/^$1 = \"\\(.*\\)\"\$/\\1/p" "$checkout/mise.toml" | head -n 1; }
|
|
49
|
+
MISE_VERSION=$(pin min_version)
|
|
50
|
+
mise_sha256=$(pin "mise_${platform}_${arch}_sha256")
|
|
51
|
+
[[ -n "$MISE_VERSION" && -n "$mise_sha256" ]] || {
|
|
52
|
+
echo 'mise.toml does not pin mise for this platform.' >&2; exit 1;
|
|
53
|
+
}
|
|
50
54
|
# Reuse an observed matching managed executable. Repeated reconciliation must
|
|
51
55
|
# not download/reinstall mise solely because a previous run finished.
|
|
52
56
|
installed_mise=$("$HOME/.local/bin/mise" --version 2>/dev/null | awk '{print $1}') || installed_mise=''
|
|
@@ -55,12 +59,15 @@ temp=$(mktemp -d)
|
|
|
55
59
|
trap 'rm -rf "$temp"' EXIT
|
|
56
60
|
asset="mise-v${MISE_VERSION}-${platform}-${arch}.tar.gz"
|
|
57
61
|
# Alpine/musl is not part of the supported workstation baseline.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
curl -fsSL "https://github.com/jdx/mise/releases/download/v${MISE_VERSION}/$asset" -o "$temp/$asset"
|
|
63
|
+
if command -v sha256sum >/dev/null; then
|
|
64
|
+
actual=$(sha256sum "$temp/$asset" | cut -d' ' -f1)
|
|
65
|
+
else
|
|
66
|
+
actual=$(shasum -a 256 "$temp/$asset" | cut -d' ' -f1)
|
|
67
|
+
fi
|
|
68
|
+
[[ "$actual" == "$mise_sha256" ]] || {
|
|
69
|
+
echo 'The mise archive does not match the digest pinned in mise.toml.' >&2; exit 1;
|
|
70
|
+
}
|
|
64
71
|
tar -xzf "$temp/$asset" -C "$temp"
|
|
65
72
|
mkdir -p "$HOME/.local/bin"
|
|
66
73
|
install -m 0755 "$temp/mise/bin/mise" "$HOME/.local/bin/mise"
|
package/bootstrap/doctor.ts
CHANGED
|
@@ -4,7 +4,13 @@ import { spawnSync } from 'node:child_process';
|
|
|
4
4
|
import { readFileSync } from 'node:fs';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { homedir, tmpdir } from 'node:os';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
AGENT_WORKTREE_ROOTS,
|
|
9
|
+
globalConfigDir,
|
|
10
|
+
globalConfigStanding,
|
|
11
|
+
workspaceRoot,
|
|
12
|
+
type GlobalConfigStanding,
|
|
13
|
+
} from './global-config';
|
|
8
14
|
|
|
9
15
|
type Tool = string | { version: string; lazy?: boolean; os?: string[] };
|
|
10
16
|
type Config = {
|
|
@@ -151,6 +157,44 @@ export function worktreeTrustCheck(
|
|
|
151
157
|
};
|
|
152
158
|
}
|
|
153
159
|
|
|
160
|
+
const STANDING: Record<GlobalConfigStanding, string> = {
|
|
161
|
+
published: 'Published by dev-fleet from this revision.',
|
|
162
|
+
missing: 'There is no global mise configuration.',
|
|
163
|
+
replaced:
|
|
164
|
+
'Another project or tool replaced the global mise configuration dev-fleet publishes.',
|
|
165
|
+
changed:
|
|
166
|
+
'The global mise configuration is not the one this revision publishes.',
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/** On a fleet machine the global mise configuration belongs to dev-fleet.
|
|
170
|
+
* When anything else replaced it, its pins and the trusted agent worktree
|
|
171
|
+
* roots are gone until the next fleet-sync.
|
|
172
|
+
*/
|
|
173
|
+
export function globalConfigCheck(
|
|
174
|
+
dir?: string,
|
|
175
|
+
root: string = workspaceRoot
|
|
176
|
+
): Check {
|
|
177
|
+
let standing: GlobalConfigStanding;
|
|
178
|
+
try {
|
|
179
|
+
standing = globalConfigStanding(dir ?? globalConfigDir(), root);
|
|
180
|
+
} catch {
|
|
181
|
+
return {
|
|
182
|
+
id: 'global-mise-config',
|
|
183
|
+
status: 'fail',
|
|
184
|
+
detail:
|
|
185
|
+
'MISE_GLOBAL_CONFIG_FILE moves the global mise configuration away from the one dev-fleet publishes; unset it.',
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
id: 'global-mise-config',
|
|
190
|
+
status: standing === 'published' ? 'pass' : 'fail',
|
|
191
|
+
detail:
|
|
192
|
+
standing === 'published'
|
|
193
|
+
? STANDING[standing]
|
|
194
|
+
: `${STANDING[standing]} Run fleet-sync; projects pin their own tools in a project mise.toml and never publish a global one.`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
154
198
|
export function checkMachine(requireDocker = false, worker = false): Check[] {
|
|
155
199
|
const config = Bun.TOML.parse(
|
|
156
200
|
readFileSync(join(workspaceRoot, 'mise.toml'), 'utf8')
|
|
@@ -164,6 +208,7 @@ export function checkMachine(requireDocker = false, worker = false): Check[] {
|
|
|
164
208
|
),
|
|
165
209
|
...dockerChecks(requireDocker),
|
|
166
210
|
worktreeTrustCheck(),
|
|
211
|
+
globalConfigCheck(),
|
|
167
212
|
{
|
|
168
213
|
id: 'github-auth',
|
|
169
214
|
status: auth.ok ? 'pass' : 'warn',
|
|
@@ -12,15 +12,29 @@ shift
|
|
|
12
12
|
case "$revision" in *[!a-f0-9]*|'') exit 2;; esac
|
|
13
13
|
[ "${#revision}" = 40 ] || exit 2
|
|
14
14
|
say() { printf 'fleet-sync (manager): %s\n' "$1" >&2; }
|
|
15
|
-
#
|
|
16
|
-
#
|
|
15
|
+
# A preview (--dry-run) changes nothing outside dev-fleet's own disposable
|
|
16
|
+
# caches: it never publishes the global mise configuration, never edits shell
|
|
17
|
+
# profiles and never prunes run checkouts. Only an apply does.
|
|
18
|
+
preview=false
|
|
19
|
+
for arg in "$@"; do [ "$arg" != --dry-run ] || preview=true; done
|
|
20
|
+
# mise is installed here by the toolchain step below; a login shell is not
|
|
21
|
+
# required to find it.
|
|
22
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
23
|
+
fleet="$HOME/.local/share/personal/fleet"
|
|
24
|
+
# The repository to take the revision from. Nobody keeps a clone of dev-fleet:
|
|
25
|
+
# fleet-sync names the one it prepared, and a hand-off (which names none) uses
|
|
26
|
+
# the manager's own copy of the source repository, or the checkout the devfleet
|
|
27
|
+
# launcher is linked into.
|
|
17
28
|
repo=${DEVFLEET_REPO:-}
|
|
29
|
+
if [ -z "$repo" ] && git -C "$fleet/manager/source" rev-parse --git-dir >/dev/null 2>&1; then
|
|
30
|
+
repo="$fleet/manager/source"
|
|
31
|
+
fi
|
|
18
32
|
if [ -z "$repo" ]; then
|
|
19
|
-
launcher=$(command -v devfleet) || { say '
|
|
33
|
+
launcher=$(command -v devfleet) || { say 'this manager has no copy of the source yet; run fleet-sync once on the manager itself, from the project that holds your fleet.json.'; exit 3; }
|
|
20
34
|
repo=$(dirname "$(readlink -f "$launcher")")
|
|
21
35
|
fi
|
|
22
|
-
repo=$(git -C "$repo" rev-parse --show-toplevel) || { say 'cannot
|
|
23
|
-
runs="$
|
|
36
|
+
repo=$(git -C "$repo" rev-parse --show-toplevel) || { say 'cannot read the source repository on the manager; run fleet-sync once on the manager itself to fetch it again.'; exit 3; }
|
|
37
|
+
runs="$fleet/manager-runs"
|
|
24
38
|
dir="$runs/$revision"
|
|
25
39
|
ready() {
|
|
26
40
|
[ "$(git -C "$dir" rev-parse HEAD 2>/dev/null)" = "$revision" ] &&
|
|
@@ -28,6 +42,7 @@ ready() {
|
|
|
28
42
|
}
|
|
29
43
|
if ! ready; then
|
|
30
44
|
git -C "$repo" cat-file -e "$revision^{commit}" 2>/dev/null || git -C "$repo" fetch --quiet origin || true
|
|
45
|
+
git -C "$repo" cat-file -e "$revision^{commit}" 2>/dev/null || git -C "$repo" fetch --quiet --no-tags origin "$revision" || true
|
|
31
46
|
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
47
|
mkdir -p "$runs"
|
|
33
48
|
chmod 700 "$runs"
|
|
@@ -54,9 +69,16 @@ failed() {
|
|
|
54
69
|
say "toolchain or dependency install failed in the run checkout; full log: $log"
|
|
55
70
|
exit 5
|
|
56
71
|
}
|
|
57
|
-
if [ "$
|
|
72
|
+
if [ "$preview" = true ]; then
|
|
73
|
+
# The revision's own locked Bun is enough to preview; mise keeps it beside
|
|
74
|
+
# its other versions without making it the global one.
|
|
75
|
+
command -v mise >/dev/null 2>&1 || { say 'this manager has no toolchain yet, so a preview cannot run; fleet-sync without --dry-run installs it.'; exit 6; }
|
|
76
|
+
mise install --locked bun >"$log" 2>&1 || failed
|
|
77
|
+
elif [ "$(cat "$stamp" 2>/dev/null)" != "$key" ] || [ -z "$(find "$stamp" -mmin -1440 2>/dev/null)" ]; then
|
|
58
78
|
rm -f -- "$stamp"
|
|
59
|
-
|
|
79
|
+
# The toolchain bootstrap installs the pinned mise first when the manager
|
|
80
|
+
# has none (or another version), so Git is all a new manager needs.
|
|
81
|
+
bash bootstrap/bootstrap.sh --tools-only >"$log" 2>&1 || failed
|
|
60
82
|
printf '%s' "$key" >"$stamp"
|
|
61
83
|
fi
|
|
62
84
|
if [ ! -d node_modules ]; then
|
|
@@ -64,13 +86,18 @@ if [ ! -d node_modules ]; then
|
|
|
64
86
|
fi
|
|
65
87
|
# Keep the three most recent run checkouts (names are commit SHAs) and the
|
|
66
88
|
# applied one: fleet/current points at it and T3's providers launch from there.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
89
|
+
# A preview leaves them for the next apply to prune.
|
|
90
|
+
if [ "$preview" = false ]; then
|
|
91
|
+
applied=$(basename "$(readlink "$(dirname "$runs")/current" 2>/dev/null || true)")
|
|
92
|
+
# shellcheck disable=SC2012
|
|
93
|
+
for old in $(ls -1td "$runs"/*/ | tail -n +4); do
|
|
94
|
+
[ "$(basename "$old")" != "$applied" ] || continue
|
|
95
|
+
# A checkout made from another repository (an earlier clone) is not this
|
|
96
|
+
# repository's worktree: remove the directory itself.
|
|
97
|
+
git -C "$repo" worktree remove --force "$old" 2>/dev/null || rm -rf -- "$old"
|
|
98
|
+
rm -f -- "${old%/}.setup.log"
|
|
99
|
+
done
|
|
100
|
+
git -C "$repo" worktree prune
|
|
101
|
+
fi
|
|
75
102
|
export PERSONAL_WORKSPACE="$dir" PERSONAL_FLEET_RUN_CHECKOUT="$revision"
|
|
76
103
|
exec mise exec --locked bun -- bun src/cli/cli.ts fleet-sync --revision "$revision" "$@"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
# Executed over strict SSH from the selected verified Git revision. stdin is
|
|
3
|
-
#
|
|
2
|
+
# Executed over strict SSH from the selected verified Git revision. stdin is
|
|
3
|
+
# one line holding the base64 fleet configuration (empty when there is none),
|
|
4
|
+
# then the base64 Git bundle (nothing in cached mode). All executable content
|
|
5
|
+
# remains in this tracked source.
|
|
4
6
|
set -eu
|
|
5
7
|
umask 077
|
|
6
8
|
revision=$1
|
|
@@ -16,12 +18,47 @@ case "$node" in *[!a-z0-9-]*|'') exit 2;; esac
|
|
|
16
18
|
phase=bootstrap
|
|
17
19
|
bundle=''
|
|
18
20
|
code=0
|
|
21
|
+
apt_log=''
|
|
19
22
|
failure_code=REMOTE_BOOTSTRAP_INCOMPLETE
|
|
20
23
|
die() { failure_code=$1; exit 1; }
|
|
24
|
+
# One remedy per failure: what the operator does next. Never raw output.
|
|
25
|
+
remedy() {
|
|
26
|
+
case "$1" in
|
|
27
|
+
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
|
+
ROOT_DEVELOPMENT_USER_REJECTED) echo 'Connect as the non-root developer account the fleet configuration names.';;
|
|
29
|
+
UBUNTU_BASELINE_MISMATCH|UBUNTU_ARCHITECTURE_UNSUPPORTED|MACOS_BASELINE_MISMATCH|MACOS_ARCHITECTURE_UNSUPPORTED|PLATFORM_UNSUPPORTED) echo 'Workers run Ubuntu 26.04 on x86_64 or aarch64. Reinstall the machine; fleet-sync never upgrades an operating system.';;
|
|
30
|
+
FLEET_STATE_PATH_UNSAFE) echo 'A fleet state directory under ~/.local/share/personal is a symbolic link. Replace it with a real directory.';;
|
|
31
|
+
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
|
+
SOURCE_DIGEST_MISMATCH) echo 'The bundle changed in transit. Rerun fleet-sync; if it repeats, check the SSH route.';;
|
|
33
|
+
SUDO_AUTHORIZATION_REQUIRED) echo 'Grant the developer account passwordless sudo, or run fleet-sync interactively once to authorize it.';;
|
|
34
|
+
APT_LOCK_CONTENDED) echo 'Another package operation held the APT lock past the bounded wait. Let it finish (it is never killed), then rerun fleet-sync.';;
|
|
35
|
+
APT_DNS_UNAVAILABLE) echo 'The worker cannot resolve the Ubuntu archive. Restore its DNS, then rerun fleet-sync.';;
|
|
36
|
+
APT_REPOSITORY_UNAVAILABLE) echo 'The Ubuntu archive could not be reached. Check the network or archive status, then rerun fleet-sync.';;
|
|
37
|
+
APT_DISK_FULL) echo 'Package installation ran out of disk space. Free space under /var and /usr, then rerun fleet-sync.';;
|
|
38
|
+
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
|
+
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
|
+
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
|
+
CONFIGURATION_INSTALL_FAILED) echo 'The fleet configuration could not be written under ~/.config/dev-fleet. Check that directory, then rerun fleet-sync.';;
|
|
44
|
+
TOOLCHAIN_*) echo 'The pinned toolchain did not converge. Rerun fleet-sync; it resumes where it stopped. If it repeats, run devfleet doctor on the worker.';;
|
|
45
|
+
*) echo 'Inspect the remote operation lock and rerun the approved revision. No success receipt advanced.';;
|
|
46
|
+
esac
|
|
47
|
+
}
|
|
48
|
+
# Names an APT failure by the fixed messages of the pinned Ubuntu release.
|
|
49
|
+
apt_failure() {
|
|
50
|
+
if grep -q 'Could not get lock' "$apt_log" 2>/dev/null; then die APT_LOCK_CONTENDED
|
|
51
|
+
elif grep -q 'No space left on device' "$apt_log" 2>/dev/null; then die APT_DISK_FULL
|
|
52
|
+
elif grep -qE 'Temporary failure resolving|Could not resolve' "$apt_log" 2>/dev/null; then die APT_DNS_UNAVAILABLE
|
|
53
|
+
elif grep -qE 'Failed to fetch|Unable to connect|Connection failed|Unable to locate package' "$apt_log" 2>/dev/null; then die APT_REPOSITORY_UNAVAILABLE
|
|
54
|
+
elif grep -q 'dpkg was interrupted' "$apt_log" 2>/dev/null; then die DPKG_INTERRUPTED
|
|
55
|
+
else die "$1"
|
|
56
|
+
fi
|
|
57
|
+
}
|
|
21
58
|
fail() {
|
|
22
|
-
printf '{"version":1,"checks":[{"id":"bootstrap:%s","status":"fail","summary":"%s","remedy":{"action":"
|
|
59
|
+
printf '{"version":1,"checks":[{"id":"bootstrap:%s","status":"fail","summary":"%s","remedy":{"action":"%s"}}]}\n' "$phase" "$failure_code" "$(remedy "$failure_code")"
|
|
23
60
|
}
|
|
24
|
-
trap 'code=$?; [ -z "$bundle" ] || rm -f -- "$bundle"; if [ "$code" != 0 ]; then fail; fi' EXIT
|
|
61
|
+
trap 'code=$?; [ -z "$bundle" ] || rm -f -- "$bundle"; [ -z "$apt_log" ] || rm -f -- "$apt_log"; if [ "$code" != 0 ]; then fail; fi' EXIT
|
|
25
62
|
# Preserve the lock holder if its SSH channel disappears mid-install.
|
|
26
63
|
trap '' HUP
|
|
27
64
|
trap 'exit 1' INT TERM
|
|
@@ -61,6 +98,16 @@ else
|
|
|
61
98
|
python3 -c 'import fcntl; fcntl.flock(9, fcntl.LOCK_EX | fcntl.LOCK_NB)' 9>&9 || die FLEET_REMOTE_LOCKED
|
|
62
99
|
fi
|
|
63
100
|
printf '{"version":1,"pid":%s,"revision":"%s","node":"%s"}\n' "$$" "$revision" "$node" >"$root/remote-operation.json"
|
|
101
|
+
# The fleet configuration is installed under the lock, so two overlapping
|
|
102
|
+
# runs never write it at once. It holds no secret.
|
|
103
|
+
phase='configuration-install'
|
|
104
|
+
IFS= read -r configuration || configuration=''
|
|
105
|
+
if [ -n "$configuration" ]; then
|
|
106
|
+
config_dir="$HOME/.config/dev-fleet"
|
|
107
|
+
mkdir -p "$config_dir" || die CONFIGURATION_INSTALL_FAILED
|
|
108
|
+
printf '%s' "$configuration" | base64 -d >"$config_dir/fleet.json.new" || die CONFIGURATION_INSTALL_FAILED
|
|
109
|
+
mv "$config_dir/fleet.json.new" "$config_dir/fleet.json" || die CONFIGURATION_INSTALL_FAILED
|
|
110
|
+
fi
|
|
64
111
|
phase=resources
|
|
65
112
|
# A bootstrap floor protects source transfer and minimal prerequisites. The
|
|
66
113
|
# Workstation baseline subsequently enforces its stricter declared capacity.
|
|
@@ -87,11 +134,30 @@ for package in git curl ca-certificates unzip xz-utils build-essential python3 a
|
|
|
87
134
|
done
|
|
88
135
|
if [ -n "$missing" ]; then
|
|
89
136
|
sudo -n true >/dev/null 2>&1 || die SUDO_AUTHORIZATION_REQUIRED
|
|
137
|
+
# APT's own messages, in a private file read only for fixed patterns and
|
|
138
|
+
# removed on exit: they tell a held lock, DNS, an unreachable archive and a
|
|
139
|
+
# full disk apart. Nothing of them is reported.
|
|
140
|
+
apt_log=$(mktemp "$root/apt.XXXXXX")
|
|
141
|
+
pending="$root/prerequisites-pending"
|
|
142
|
+
# An installation this script started and that was interrupted (the shell
|
|
143
|
+
# killed, the machine powered off) leaves dpkg half-configured. Finishing
|
|
144
|
+
# our own transaction is safe; one fleet-sync did not start is reported.
|
|
145
|
+
# dpkg reads its database lock even to audit, which needs root.
|
|
146
|
+
if [ -n "$(sudo -n dpkg --audit 2>/dev/null)" ]; then
|
|
147
|
+
[ -f "$pending" ] || die DPKG_INTERRUPTED
|
|
148
|
+
# shellcheck disable=SC2024
|
|
149
|
+
sudo -n timeout 600 env DEBIAN_FRONTEND=noninteractive LC_ALL=C dpkg --configure -a >"$apt_log" 2>&1 || apt_failure APT_INSTALL_FAILED
|
|
150
|
+
fi
|
|
151
|
+
: >"$pending"
|
|
152
|
+
# The log belongs to the developer account; sudo only runs the command.
|
|
153
|
+
# shellcheck disable=SC2024
|
|
90
154
|
# Never remove APT locks or kill package processes. Timeout keeps genuine
|
|
91
155
|
# transactions bounded; Ubuntu archive signatures/TLS remain enabled.
|
|
92
|
-
sudo -n timeout 300 apt-get -o DPkg::Lock::Timeout=120 update
|
|
93
|
-
# shellcheck disable=SC2086
|
|
94
|
-
sudo -n timeout 600 env DEBIAN_FRONTEND=noninteractive apt-get -o DPkg::Lock::Timeout=120 --no-install-recommends install -y $missing
|
|
156
|
+
sudo -n timeout 300 env LC_ALL=C apt-get -o DPkg::Lock::Timeout=120 update >"$apt_log" 2>&1 || apt_failure APT_UPDATE_FAILED
|
|
157
|
+
# shellcheck disable=SC2086,SC2024
|
|
158
|
+
sudo -n timeout 600 env DEBIAN_FRONTEND=noninteractive LC_ALL=C apt-get -o DPkg::Lock::Timeout=120 --no-install-recommends install -y $missing >>"$apt_log" 2>&1 || apt_failure APT_INSTALL_FAILED
|
|
159
|
+
rm -f -- "$pending" "$apt_log"
|
|
160
|
+
apt_log=''
|
|
95
161
|
fi
|
|
96
162
|
else
|
|
97
163
|
# Apple Command Line Tools provide git and python3; Ansible runs through the
|
|
@@ -107,18 +173,25 @@ if [ ! -e "$deployment/.git" ]; then
|
|
|
107
173
|
[ -z "$(ls -A "$deployment")" ] || exit 1
|
|
108
174
|
git -C "$deployment" init -q >/dev/null 2>&1 || exit 1
|
|
109
175
|
fi
|
|
110
|
-
# An interrupted initial fetch
|
|
111
|
-
#
|
|
176
|
+
# An interrupted initial fetch or checkout leaves a repository without a
|
|
177
|
+
# commit, possibly with some of the revision's files written. Resume only
|
|
178
|
+
# that state: every file present must be a file of the revision, so the
|
|
179
|
+
# checkout may write over it. An existing commit or other work is never reset.
|
|
112
180
|
if ! git -C "$deployment" rev-parse --verify HEAD >/dev/null 2>&1; then
|
|
113
181
|
[ "$source_mode" = transfer ] || die SOURCE_CACHE_CHANGED
|
|
114
182
|
[ "$(git -C "$deployment" rev-parse --show-toplevel)" = "$deployment" ] || exit 1
|
|
115
|
-
clean=$(git -C "$deployment" status --porcelain) || die SOURCE_STATUS_FAILED
|
|
116
|
-
[ -z "$clean" ] || die SOURCE_CHECKOUT_CONFLICT
|
|
117
183
|
# The bundle holds this commit without its history: declare the checkout
|
|
118
184
|
# shallow at it so Git accepts a commit whose parents are absent.
|
|
119
185
|
printf '%s\n' "$revision" >"$deployment/.git/shallow"
|
|
120
186
|
git -C "$deployment" fetch --no-tags "$bundle" "$revision" >/dev/null 2>&1 || exit 1
|
|
121
|
-
|
|
187
|
+
present=$(mktemp "$root/present.XXXXXX")
|
|
188
|
+
expected=$(mktemp "$root/expected.XXXXXX")
|
|
189
|
+
git -C "$deployment" -c core.quotePath=false ls-files --others | LC_ALL=C sort >"$present" || { rm -f -- "$present" "$expected"; die SOURCE_STATUS_FAILED; }
|
|
190
|
+
git -C "$deployment" -c core.quotePath=false ls-tree -r --name-only "$revision" | LC_ALL=C sort >"$expected" || { rm -f -- "$present" "$expected"; die SOURCE_STATUS_FAILED; }
|
|
191
|
+
foreign=$(LC_ALL=C comm -23 "$present" "$expected")
|
|
192
|
+
rm -f -- "$present" "$expected"
|
|
193
|
+
[ -z "$foreign" ] || die SOURCE_CHECKOUT_CONFLICT
|
|
194
|
+
git -C "$deployment" checkout --force --detach "$revision" >/dev/null 2>&1 || exit 1
|
|
122
195
|
fi
|
|
123
196
|
[ -z "$bundle" ] || rm -f "$bundle"
|
|
124
197
|
bundle=''
|