@quenty/nevermore-cli 4.43.0-canary.783.30683872723.0 → 4.43.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/CHANGELOG.md +13 -16
- package/README.md +10 -0
- package/dist/commands/batch-command/batch-deploy-command.d.ts.map +1 -1
- package/dist/commands/batch-command/batch-deploy-command.js +101 -37
- package/dist/commands/batch-command/batch-deploy-command.js.map +1 -1
- package/dist/commands/deploy-command/index.d.ts +2 -0
- package/dist/commands/deploy-command/index.d.ts.map +1 -1
- package/dist/commands/deploy-command/index.js +35 -22
- package/dist/commands/deploy-command/index.js.map +1 -1
- package/dist/utils/job-context/base-job-context.d.ts.map +1 -1
- package/dist/utils/job-context/base-job-context.js +1 -2
- package/dist/utils/job-context/base-job-context.js.map +1 -1
- package/dist/utils/open-cloud/open-cloud-client.d.ts +8 -4
- package/dist/utils/open-cloud/open-cloud-client.d.ts.map +1 -1
- package/dist/utils/open-cloud/open-cloud-client.js +8 -4
- package/dist/utils/open-cloud/open-cloud-client.js.map +1 -1
- package/dist/utils/watch/github-context.test.js +9 -1
- package/dist/utils/watch/github-context.test.js.map +1 -1
- package/dist/utils/watch/http-watch-registry.js +5 -3
- package/dist/utils/watch/http-watch-registry.js.map +1 -1
- package/dist/utils/watch/stream-watch-loop.d.ts.map +1 -1
- package/dist/utils/watch/stream-watch-loop.js +42 -51
- package/dist/utils/watch/stream-watch-loop.js.map +1 -1
- package/package.json +5 -5
- package/src/commands/batch-command/batch-deploy-command.ts +135 -41
- package/src/commands/deploy-command/index.ts +41 -24
- package/src/utils/job-context/base-job-context.ts +1 -2
- package/src/utils/open-cloud/open-cloud-client.ts +8 -4
- package/src/utils/watch/github-context.test.ts +10 -1
- package/src/utils/watch/http-watch-registry.ts +5 -3
- package/src/utils/watch/stream-watch-loop.ts +73 -59
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -96,42 +96,51 @@ export async function runStreamWatchLoopAsync(
|
|
|
96
96
|
process.on('SIGINT', onSigint);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
/** Everything this loop knows about one watch, in one place. */
|
|
100
|
+
interface WatchState {
|
|
101
|
+
entry: StreamWatchEntry;
|
|
102
|
+
/**
|
|
103
|
+
* What the place has actually been built from here, as an Open Cloud place
|
|
104
|
+
* version. Seeded from the lock and advanced only by a rebuild that
|
|
105
|
+
* succeeded: the service moves its own baseline whether or not anyone was
|
|
106
|
+
* listening, so this is the only durable record of what exists.
|
|
107
|
+
*/
|
|
108
|
+
built: number | undefined;
|
|
109
|
+
/**
|
|
110
|
+
* The last service-side token this watch was settled at. Purely an
|
|
111
|
+
* optimization — a reconnect that reports the same token needs no Open
|
|
112
|
+
* Cloud call — and deliberately not advanced when a rebuild fails, so the
|
|
113
|
+
* next `ready` retries it.
|
|
114
|
+
*/
|
|
115
|
+
settledAt?: string;
|
|
116
|
+
/**
|
|
117
|
+
* Set once the service has told us it cannot read this source, from either
|
|
118
|
+
* direction: the `ready` snapshot on connect, or a `poll-failed` event as
|
|
119
|
+
* it happens.
|
|
120
|
+
*
|
|
121
|
+
* Both are needed. A connection opened before the service's first failed
|
|
122
|
+
* poll gets a clean snapshot, and the next one is an hour away — so without
|
|
123
|
+
* the live tail a private base place would look fine for an hour before the
|
|
124
|
+
* watch admitted it was doing nothing.
|
|
125
|
+
*/
|
|
126
|
+
unreadable: boolean;
|
|
127
|
+
}
|
|
100
128
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
129
|
+
// One record per watch rather than parallel maps under the same key: every
|
|
130
|
+
// field above is a fact about the same thing, and holding them apart only
|
|
131
|
+
// creates the possibility of disagreeing about which watches exist.
|
|
132
|
+
const watches = new Map<string, WatchState>(
|
|
133
|
+
entries.map((e) => [
|
|
134
|
+
e.watchName,
|
|
135
|
+
{ entry: e, built: e.baseline, unreadable: false },
|
|
136
|
+
])
|
|
109
137
|
);
|
|
110
138
|
|
|
111
|
-
/**
|
|
112
|
-
* The last service-side token each watch was settled at. Purely an
|
|
113
|
-
* optimization — a reconnect that reports the same token needs no Open Cloud
|
|
114
|
-
* call — and deliberately not advanced when a rebuild fails, so the next
|
|
115
|
-
* `ready` retries it.
|
|
116
|
-
*/
|
|
117
|
-
const settledAt = new Map<string, string>();
|
|
118
|
-
|
|
119
139
|
let redeploys = 0;
|
|
120
140
|
let failures = 0;
|
|
121
141
|
let lastDropReason: string | undefined;
|
|
122
142
|
let unobservable = false;
|
|
123
143
|
|
|
124
|
-
/**
|
|
125
|
-
* Watches the service has told us it cannot read, from either direction:
|
|
126
|
-
* the `ready` snapshot on connect, or a `poll-failed` event as it happens.
|
|
127
|
-
*
|
|
128
|
-
* Both are needed. A connection opened before the service's first failed
|
|
129
|
-
* poll gets a clean snapshot, and the next one is an hour away — so without
|
|
130
|
-
* the live tail a private base place would look fine for an hour before the
|
|
131
|
-
* watch admitted it was doing nothing.
|
|
132
|
-
*/
|
|
133
|
-
const unreadable = new Set<string>();
|
|
134
|
-
|
|
135
144
|
/**
|
|
136
145
|
* Give up the stream once nothing on it can ever fire, so the caller can
|
|
137
146
|
* poll instead — this machine's credentials reach places the service's
|
|
@@ -141,7 +150,8 @@ export async function runStreamWatchLoopAsync(
|
|
|
141
150
|
// Counted against every entry this run owns, not against what the last
|
|
142
151
|
// frame happened to mention: a watch the service has stopped reporting
|
|
143
152
|
// altogether can no more produce a notification than one it cannot read.
|
|
144
|
-
|
|
153
|
+
const all = [...watches.values()];
|
|
154
|
+
if (all.length === 0 || !all.every((w) => w.unreadable)) {
|
|
145
155
|
return false;
|
|
146
156
|
}
|
|
147
157
|
unobservable = true;
|
|
@@ -149,6 +159,16 @@ export async function runStreamWatchLoopAsync(
|
|
|
149
159
|
return true;
|
|
150
160
|
};
|
|
151
161
|
|
|
162
|
+
/** Marks a watch unreadable, and says whether that was news worth printing. */
|
|
163
|
+
const markUnreadable = (name: string): boolean => {
|
|
164
|
+
const state = watches.get(name);
|
|
165
|
+
if (state == null || state.unreadable) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
state.unreadable = true;
|
|
169
|
+
return true;
|
|
170
|
+
};
|
|
171
|
+
|
|
152
172
|
OutputHelper.info(
|
|
153
173
|
`Watching ${entries.length} base place${
|
|
154
174
|
entries.length === 1 ? '' : 's'
|
|
@@ -172,10 +192,11 @@ export async function runStreamWatchLoopAsync(
|
|
|
172
192
|
* what the place is actually at, which is the same number the build records.
|
|
173
193
|
*/
|
|
174
194
|
const settleAsync = async (
|
|
175
|
-
|
|
195
|
+
state: WatchState,
|
|
176
196
|
token: string | null
|
|
177
197
|
): Promise<void> => {
|
|
178
|
-
|
|
198
|
+
const entry = state.entry;
|
|
199
|
+
if (token != null && state.settledAt === token) {
|
|
179
200
|
return;
|
|
180
201
|
}
|
|
181
202
|
|
|
@@ -196,17 +217,17 @@ export async function runStreamWatchLoopAsync(
|
|
|
196
217
|
return;
|
|
197
218
|
}
|
|
198
219
|
|
|
199
|
-
const previous = built
|
|
220
|
+
const previous = state.built;
|
|
200
221
|
const settle = () => {
|
|
201
222
|
if (token != null) {
|
|
202
|
-
settledAt
|
|
223
|
+
state.settledAt = token;
|
|
203
224
|
}
|
|
204
225
|
};
|
|
205
226
|
|
|
206
227
|
if (previous === undefined) {
|
|
207
228
|
// Never built here and nothing locked: adopt what is there rather than
|
|
208
229
|
// reading "unknown -> something" as a change.
|
|
209
|
-
built
|
|
230
|
+
state.built = latest;
|
|
210
231
|
settle();
|
|
211
232
|
return;
|
|
212
233
|
}
|
|
@@ -221,7 +242,7 @@ export async function runStreamWatchLoopAsync(
|
|
|
221
242
|
try {
|
|
222
243
|
await redeployAsync(entry, latest);
|
|
223
244
|
redeploys++;
|
|
224
|
-
built
|
|
245
|
+
state.built = latest;
|
|
225
246
|
settle();
|
|
226
247
|
OutputHelper.info(`${entry.label}: rebuilt from v${latest}.`);
|
|
227
248
|
} catch (err) {
|
|
@@ -244,7 +265,7 @@ export async function runStreamWatchLoopAsync(
|
|
|
244
265
|
// Every connection starts here, including reconnects. Nothing is
|
|
245
266
|
// replayed while a client is away, so this is the whole catch-up.
|
|
246
267
|
onReadyAsync: async (ready: WatchStreamReady) => {
|
|
247
|
-
const ours = ready.watches.filter((s) =>
|
|
268
|
+
const ours = ready.watches.filter((s) => watches.has(s.name));
|
|
248
269
|
|
|
249
270
|
// A source the service cannot read never fires, and the socket says
|
|
250
271
|
// nothing about it — the failure is visible only here. Waiting
|
|
@@ -268,17 +289,16 @@ export async function runStreamWatchLoopAsync(
|
|
|
268
289
|
// the same set means a partial snapshot falls back too, instead of
|
|
269
290
|
// holding the socket for the ones that remain and silently never
|
|
270
291
|
// rebuilding the rest.
|
|
271
|
-
const missing = [...
|
|
292
|
+
const missing = [...watches.keys()].filter(
|
|
272
293
|
(name) => !ready.watches.some((s) => s.name === name)
|
|
273
294
|
);
|
|
274
295
|
for (const name of missing) {
|
|
275
|
-
if (
|
|
296
|
+
if (!markUnreadable(name)) {
|
|
276
297
|
continue;
|
|
277
298
|
}
|
|
278
|
-
unreadable.add(name);
|
|
279
299
|
OutputHelper.warn(
|
|
280
300
|
`The watch monitor no longer holds ${
|
|
281
|
-
|
|
301
|
+
watches.get(name)!.entry.label
|
|
282
302
|
} — another checkout has probably re-registered it.`
|
|
283
303
|
);
|
|
284
304
|
}
|
|
@@ -287,13 +307,12 @@ export async function runStreamWatchLoopAsync(
|
|
|
287
307
|
}
|
|
288
308
|
|
|
289
309
|
for (const status of failedToRead) {
|
|
290
|
-
if (
|
|
310
|
+
if (!markUnreadable(status.name)) {
|
|
291
311
|
continue;
|
|
292
312
|
}
|
|
293
|
-
unreadable.add(status.name);
|
|
294
313
|
OutputHelper.warn(
|
|
295
314
|
`The watch service cannot read ${
|
|
296
|
-
|
|
315
|
+
watches.get(status.name)!.entry.label
|
|
297
316
|
}` + (status.sourceError ? `: ${status.sourceError}` : '.')
|
|
298
317
|
);
|
|
299
318
|
}
|
|
@@ -302,25 +321,23 @@ export async function runStreamWatchLoopAsync(
|
|
|
302
321
|
}
|
|
303
322
|
|
|
304
323
|
for (const status of ours) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
status.currentVersion
|
|
309
|
-
);
|
|
324
|
+
const state = watches.get(status.name)!;
|
|
325
|
+
if (!state.unreadable) {
|
|
326
|
+
await settleAsync(state, status.currentVersion);
|
|
310
327
|
}
|
|
311
328
|
}
|
|
312
329
|
},
|
|
313
330
|
|
|
314
331
|
onNotifyAsync: async (notify: WatchStreamNotify) => {
|
|
315
|
-
const
|
|
316
|
-
if (!
|
|
332
|
+
const state = watches.get(notify.watchName);
|
|
333
|
+
if (!state) {
|
|
317
334
|
OutputHelper.verbose(
|
|
318
335
|
`Ignoring a notification for "${notify.watchName}", which this ` +
|
|
319
336
|
'run does not watch.'
|
|
320
337
|
);
|
|
321
338
|
return;
|
|
322
339
|
}
|
|
323
|
-
await settleAsync(
|
|
340
|
+
await settleAsync(state, notify.version);
|
|
324
341
|
},
|
|
325
342
|
|
|
326
343
|
onEvent: (message) => {
|
|
@@ -334,17 +351,14 @@ export async function runStreamWatchLoopAsync(
|
|
|
334
351
|
// is polling something that could have been streamed, which still
|
|
335
352
|
// works.
|
|
336
353
|
const name = message.event.watchName;
|
|
337
|
-
if (
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
unreadable.has(name)
|
|
342
|
-
) {
|
|
354
|
+
if (message.event.type !== 'poll-failed' || name == null) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (!markUnreadable(name)) {
|
|
343
358
|
return;
|
|
344
359
|
}
|
|
345
|
-
unreadable.add(name);
|
|
346
360
|
OutputHelper.warn(
|
|
347
|
-
`The watch service cannot read ${
|
|
361
|
+
`The watch service cannot read ${watches.get(name)!.entry.label}: ${
|
|
348
362
|
message.event.message
|
|
349
363
|
}`
|
|
350
364
|
);
|