@crvouga/dev-fleet 0.9.0 → 0.9.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crvouga/dev-fleet",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
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": "92f24582645e8a05f47124be1076a28f637ec347"
75
+ "gitHead": "3a465d53335387bf4851216ad8df67d1866fe887"
76
76
  }
@@ -54,30 +54,67 @@ function dockerFailure(result: CommandResult): {
54
54
  };
55
55
  }
56
56
 
57
+ /** A word the shell reads back unchanged. */
58
+ const quoted = (word: string): string => `'${word.replaceAll("'", `'\\''`)}'`;
59
+
60
+ /** fleet-sync adds the worker's user to the docker group, but a session
61
+ * started before that keeps its old groups: the group database lists the
62
+ * user in docker while this process does not hold the group. A fresh session
63
+ * made with `sg docker` then reads the group from scratch, as the baseline's
64
+ * own smoke test does with `runuser`, so the first sync of a new worker
65
+ * measures the worker and not the age of its session.
66
+ */
67
+ async function freshDockerSession(run: Runner): Promise<Runner | undefined> {
68
+ const user = await run(['id', '-un']);
69
+ const name = user.stdout.trim();
70
+ if (!user.ok || !name) return undefined;
71
+ const held = await run(['id', '-Gn']);
72
+ const listed = await run(['id', '-Gn', name]);
73
+ const groups = (result: CommandResult): string[] =>
74
+ result.ok ? result.stdout.trim().split(/\s+/) : [];
75
+ if (groups(held).includes('docker') || !groups(listed).includes('docker'))
76
+ return undefined;
77
+ return (command, options) =>
78
+ run(['sg', 'docker', '-c', command.map(quoted).join(' ')], options);
79
+ }
80
+
57
81
  /** Runs the pinned image offline, then builds a one-line image from it. */
58
82
  export async function dockerRunCheck(run: Runner): Promise<Check> {
59
83
  const id = 'worker:docker-run';
60
84
  const { image, timeoutSeconds } = policy.dockerSmokeTest;
61
85
  const timeoutMs = timeoutSeconds * 1000;
62
- const started = await run(
63
- [
64
- 'docker',
65
- 'run',
66
- '--rm',
67
- '--network',
68
- 'none',
69
- '--pull',
70
- 'never',
71
- image,
72
- 'true',
73
- ],
74
- { timeoutMs }
75
- );
86
+ const runContainer = (runner: Runner) =>
87
+ runner(
88
+ [
89
+ 'docker',
90
+ 'run',
91
+ '--rm',
92
+ '--network',
93
+ 'none',
94
+ '--pull',
95
+ 'never',
96
+ image,
97
+ 'true',
98
+ ],
99
+ { timeoutMs }
100
+ );
101
+ let session = run;
102
+ let started = await runContainer(session);
103
+ if (
104
+ !started.ok &&
105
+ dockerFailure(started).code === 'DOCKER_SOCKET_ACCESS_DENIED'
106
+ ) {
107
+ const fresh = await freshDockerSession(run);
108
+ if (fresh) {
109
+ session = fresh;
110
+ started = await runContainer(session);
111
+ }
112
+ }
76
113
  if (!started.ok) {
77
114
  const { code, action } = dockerFailure(started);
78
115
  return failure(id, code, action);
79
116
  }
80
- const built = await run(
117
+ const built = await session(
81
118
  ['docker', 'build', '--quiet', '--pull=false', '--network', 'none', '-'],
82
119
  { input: `FROM ${image}\nRUN true\n`, timeoutMs }
83
120
  );
@@ -95,7 +132,9 @@ export async function dockerRunCheck(run: Runner): Promise<Check> {
95
132
  id,
96
133
  status: 'pass',
97
134
  summary:
98
- 'A container of the pinned image ran and a one-line image built, offline, in this session.',
135
+ session === run
136
+ ? 'A container of the pinned image ran and a one-line image built, offline, in this session.'
137
+ : 'A container of the pinned image ran and a one-line image built, offline, in a fresh session of the docker group: sessions started before fleet-sync granted it need a new login.',
99
138
  };
100
139
  }
101
140