@gravito/launchpad 1.2.1 → 1.3.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/CHANGELOG.md +55 -0
- package/README.md +51 -43
- package/README.zh-TW.md +110 -0
- package/dist/index.d.mts +324 -7
- package/dist/index.d.ts +324 -7
- package/dist/index.js +407 -29
- package/dist/index.mjs +395 -29
- package/package.json +7 -5
- package/scripts/check-coverage.ts +64 -0
- package/src/Application/MissionQueue.ts +143 -0
- package/src/Application/PoolManager.ts +170 -18
- package/src/Application/RefurbishUnit.ts +24 -12
- package/src/Domain/Events.ts +46 -0
- package/src/Domain/Interfaces.ts +82 -0
- package/src/Domain/Rocket.ts +18 -0
- package/src/Infrastructure/Docker/DockerAdapter.ts +80 -3
- package/src/Infrastructure/Git/ShellGitAdapter.ts +1 -1
- package/src/index.ts +3 -0
- package/tests/Deployment.test.ts +3 -1
- package/tests/pool-manager.test.ts +3 -1
- package/tests/resource-limits.test.ts +241 -0
- package/tsconfig.json +14 -26
- package/.turbo/turbo-build.log +0 -18
- package/.turbo/turbo-test$colon$coverage.log +0 -183
- package/.turbo/turbo-test.log +0 -100
- package/.turbo/turbo-typecheck.log +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _gravito_ripple from '@gravito/ripple';
|
|
2
2
|
import { OrbitRipple } from '@gravito/ripple';
|
|
3
3
|
import { GravitoOrbit, PlanetCore } from '@gravito/core';
|
|
4
|
-
import { ValueObject, AggregateRoot } from '@gravito/enterprise';
|
|
4
|
+
import { ValueObject, AggregateRoot, DomainEvent } from '@gravito/enterprise';
|
|
5
5
|
|
|
6
6
|
interface MissionProps {
|
|
7
7
|
id: string;
|
|
@@ -9,6 +9,15 @@ interface MissionProps {
|
|
|
9
9
|
branch: string;
|
|
10
10
|
commitSha: string;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Mission represents a deployment task for a specific code version.
|
|
14
|
+
*
|
|
15
|
+
* It is a Value Object containing information about the source repository,
|
|
16
|
+
* branch, and commit to be deployed.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
* @since 3.0.0
|
|
20
|
+
*/
|
|
12
21
|
declare class Mission extends ValueObject<MissionProps> {
|
|
13
22
|
get id(): string;
|
|
14
23
|
get repoUrl(): string;
|
|
@@ -28,6 +37,16 @@ declare enum RocketStatus {
|
|
|
28
37
|
DECOMMISSIONED = "DECOMMISSIONED"
|
|
29
38
|
}
|
|
30
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Rocket represents a managed application container within Launchpad.
|
|
42
|
+
*
|
|
43
|
+
* It acts as an Aggregate Root in the domain, maintaining its lifecycle state
|
|
44
|
+
* (IDLE, PREPARING, ORBITING, etc.) and emitting domain events when
|
|
45
|
+
* state transitions occur.
|
|
46
|
+
*
|
|
47
|
+
* @public
|
|
48
|
+
* @since 3.0.0
|
|
49
|
+
*/
|
|
31
50
|
declare class Rocket extends AggregateRoot<string> {
|
|
32
51
|
private _status;
|
|
33
52
|
private _currentMission;
|
|
@@ -62,6 +81,17 @@ declare class Rocket extends AggregateRoot<string> {
|
|
|
62
81
|
* 火箭除役 (移除容器)
|
|
63
82
|
*/
|
|
64
83
|
decommission(): void;
|
|
84
|
+
/**
|
|
85
|
+
* 替換底層容器(僅在 REFURBISHING 狀態時允許)
|
|
86
|
+
* 用於 destroy-recreate 策略
|
|
87
|
+
*
|
|
88
|
+
* @param newContainerId - 新容器 ID
|
|
89
|
+
* @throws {Error} 當火箭不在 REFURBISHING 狀態時
|
|
90
|
+
*
|
|
91
|
+
* @public
|
|
92
|
+
* @since 1.3.0
|
|
93
|
+
*/
|
|
94
|
+
replaceContainer(newContainerId: string): void;
|
|
65
95
|
toJSON(): {
|
|
66
96
|
id: string;
|
|
67
97
|
containerId: string;
|
|
@@ -129,6 +159,13 @@ interface IRouterAdapter {
|
|
|
129
159
|
unregister(domain: string): void;
|
|
130
160
|
start(port: number): void;
|
|
131
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* 負責與 GitHub API 互動的轉接器
|
|
164
|
+
*/
|
|
165
|
+
interface IGitHubAdapter {
|
|
166
|
+
verifySignature(payload: string, signature: string, secret: string): boolean;
|
|
167
|
+
postComment(repoOwner: string, repoName: string, prNumber: number, comment: string): Promise<void>;
|
|
168
|
+
}
|
|
132
169
|
/**
|
|
133
170
|
* 負責持久化火箭狀態的儲存庫介面
|
|
134
171
|
*/
|
|
@@ -139,7 +176,68 @@ interface IRocketRepository {
|
|
|
139
176
|
findAll(): Promise<Rocket[]>;
|
|
140
177
|
delete(id: string): Promise<void>;
|
|
141
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Pool 管理配置
|
|
181
|
+
*
|
|
182
|
+
* @public
|
|
183
|
+
* @since 1.3.0
|
|
184
|
+
*/
|
|
185
|
+
interface PoolConfig {
|
|
186
|
+
/** 最大 Rocket 數量(硬限制) */
|
|
187
|
+
maxRockets: number;
|
|
188
|
+
/** 初始預熱數量 */
|
|
189
|
+
warmupCount: number;
|
|
190
|
+
/** 最大等待隊列長度 */
|
|
191
|
+
maxQueueSize: number;
|
|
192
|
+
/** 隊列超時時間(毫秒) */
|
|
193
|
+
queueTimeoutMs: number;
|
|
194
|
+
/** 資源不足時的策略:'queue' | 'reject' | 'dynamic' */
|
|
195
|
+
exhaustionStrategy: 'queue' | 'reject' | 'dynamic';
|
|
196
|
+
/** 動態建立的限制(僅當 exhaustionStrategy 為 'dynamic' 時) */
|
|
197
|
+
dynamicLimit?: number;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Pool 管理預設配置
|
|
201
|
+
*
|
|
202
|
+
* @public
|
|
203
|
+
* @since 1.3.0
|
|
204
|
+
*/
|
|
205
|
+
declare const DEFAULT_POOL_CONFIG: PoolConfig;
|
|
206
|
+
/**
|
|
207
|
+
* 容器清理策略配置
|
|
208
|
+
*
|
|
209
|
+
* @public
|
|
210
|
+
* @since 1.3.0
|
|
211
|
+
*/
|
|
212
|
+
interface RefurbishConfig {
|
|
213
|
+
/** 清理策略:'deep-clean' | 'destroy-recreate' */
|
|
214
|
+
strategy: 'deep-clean' | 'destroy-recreate';
|
|
215
|
+
/** 深度清理命令(僅 deep-clean 策略) */
|
|
216
|
+
cleanupCommands?: string[];
|
|
217
|
+
/** 清理超時(毫秒) */
|
|
218
|
+
cleanupTimeoutMs: number;
|
|
219
|
+
/** 清理失敗時的處理:'decommission' | 'retry' */
|
|
220
|
+
failureAction: 'decommission' | 'retry';
|
|
221
|
+
/** 最大重試次數(僅 retry 模式) */
|
|
222
|
+
maxRetries: number;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* 容器清理預設配置
|
|
226
|
+
*
|
|
227
|
+
* @public
|
|
228
|
+
* @since 1.3.0
|
|
229
|
+
*/
|
|
230
|
+
declare const DEFAULT_REFURBISH_CONFIG: RefurbishConfig;
|
|
142
231
|
|
|
232
|
+
/**
|
|
233
|
+
* PayloadInjector is responsible for deploying application code into Rocket containers.
|
|
234
|
+
*
|
|
235
|
+
* It clones the repository, copies files into the container, installs
|
|
236
|
+
* dependencies (using Bun), and starts the application.
|
|
237
|
+
*
|
|
238
|
+
* @public
|
|
239
|
+
* @since 3.0.0
|
|
240
|
+
*/
|
|
143
241
|
declare class PayloadInjector {
|
|
144
242
|
private docker;
|
|
145
243
|
private git;
|
|
@@ -147,35 +245,94 @@ declare class PayloadInjector {
|
|
|
147
245
|
deploy(rocket: Rocket): Promise<void>;
|
|
148
246
|
}
|
|
149
247
|
|
|
248
|
+
/**
|
|
249
|
+
* RefurbishUnit handles the cleaning and restoration of Rocket containers.
|
|
250
|
+
*
|
|
251
|
+
* After a mission is complete, this unit resets the container environment
|
|
252
|
+
* (clearing files, stopping processes) so the container can be returned
|
|
253
|
+
* to the idle pool for future reuse.
|
|
254
|
+
*
|
|
255
|
+
* @public
|
|
256
|
+
* @since 3.0.0
|
|
257
|
+
*/
|
|
150
258
|
declare class RefurbishUnit {
|
|
151
259
|
private docker;
|
|
152
|
-
|
|
260
|
+
private readonly config;
|
|
261
|
+
constructor(docker: IDockerAdapter, config?: Partial<RefurbishConfig>);
|
|
153
262
|
/**
|
|
154
263
|
* 執行火箭翻新邏輯
|
|
155
264
|
*/
|
|
156
265
|
refurbish(rocket: Rocket): Promise<void>;
|
|
157
266
|
}
|
|
158
267
|
|
|
268
|
+
/**
|
|
269
|
+
* PoolManager handles the lifecycle and pooling of Rocket containers.
|
|
270
|
+
*
|
|
271
|
+
* It manages a pool of pre-warmed containers to ensure fast deployment of
|
|
272
|
+
* new missions. It also handles rocket assignment and recycling (refurbishment).
|
|
273
|
+
*
|
|
274
|
+
* @public
|
|
275
|
+
* @since 3.0.0
|
|
276
|
+
*/
|
|
159
277
|
declare class PoolManager {
|
|
160
278
|
private dockerAdapter;
|
|
161
279
|
private rocketRepository;
|
|
162
280
|
private refurbishUnit?;
|
|
163
281
|
private router?;
|
|
164
|
-
|
|
282
|
+
private readonly config;
|
|
283
|
+
private readonly missionQueue;
|
|
284
|
+
private dynamicRocketCount;
|
|
285
|
+
constructor(dockerAdapter: IDockerAdapter, rocketRepository: IRocketRepository, refurbishUnit?: RefurbishUnit | undefined, router?: IRouterAdapter | undefined, config?: Partial<PoolConfig>);
|
|
286
|
+
/**
|
|
287
|
+
* 取得當前 Pool 狀態
|
|
288
|
+
*/
|
|
289
|
+
getPoolStatus(): Promise<{
|
|
290
|
+
total: number;
|
|
291
|
+
idle: number;
|
|
292
|
+
orbiting: number;
|
|
293
|
+
refurbishing: number;
|
|
294
|
+
decommissioned: number;
|
|
295
|
+
dynamicCount: number;
|
|
296
|
+
maxRockets: number;
|
|
297
|
+
queueLength: number;
|
|
298
|
+
}>;
|
|
165
299
|
/**
|
|
166
300
|
* 初始化發射場:預先準備指定數量的火箭
|
|
167
301
|
*/
|
|
168
|
-
warmup(count
|
|
302
|
+
warmup(count?: number): Promise<void>;
|
|
169
303
|
/**
|
|
170
304
|
* 獲取一架可用的火箭並分配任務
|
|
305
|
+
*
|
|
306
|
+
* @throws {PoolExhaustedException} 當 Pool 耗盡且無法處理請求時
|
|
171
307
|
*/
|
|
172
308
|
assignMission(mission: Mission): Promise<Rocket>;
|
|
173
309
|
/**
|
|
174
310
|
* 回收指定任務的火箭
|
|
175
311
|
*/
|
|
176
312
|
recycle(missionId: string): Promise<void>;
|
|
313
|
+
/**
|
|
314
|
+
* 處理等待隊列中的任務
|
|
315
|
+
*/
|
|
316
|
+
private processQueue;
|
|
317
|
+
/**
|
|
318
|
+
* 取得隊列統計資訊
|
|
319
|
+
*/
|
|
320
|
+
getQueueStats(): {
|
|
321
|
+
length: number;
|
|
322
|
+
maxSize: number;
|
|
323
|
+
oldestWaitMs: number | null;
|
|
324
|
+
};
|
|
177
325
|
}
|
|
178
326
|
|
|
327
|
+
/**
|
|
328
|
+
* MissionControl orchestrates the high-level process of launching missions.
|
|
329
|
+
*
|
|
330
|
+
* it coordinates between the `PoolManager`, `PayloadInjector`, and `DockerAdapter`
|
|
331
|
+
* to deploy code into containers, assign domains, and monitor execution.
|
|
332
|
+
*
|
|
333
|
+
* @public
|
|
334
|
+
* @since 3.0.0
|
|
335
|
+
*/
|
|
179
336
|
declare class MissionControl {
|
|
180
337
|
private poolManager;
|
|
181
338
|
private injector;
|
|
@@ -185,16 +342,176 @@ declare class MissionControl {
|
|
|
185
342
|
launch(mission: Mission, onTelemetry: (type: string, data: any) => void): Promise<string>;
|
|
186
343
|
}
|
|
187
344
|
|
|
345
|
+
interface QueuedMission {
|
|
346
|
+
mission: Mission;
|
|
347
|
+
resolve: (rocket: Rocket) => void;
|
|
348
|
+
reject: (error: Error) => void;
|
|
349
|
+
enqueuedAt: number;
|
|
350
|
+
timeoutId: ReturnType<typeof setTimeout>;
|
|
351
|
+
}
|
|
188
352
|
/**
|
|
189
|
-
*
|
|
353
|
+
* MissionQueue manages pending mission requests when pool is exhausted.
|
|
354
|
+
*
|
|
355
|
+
* Implements FIFO queue with timeout handling and backpressure support.
|
|
356
|
+
*
|
|
357
|
+
* @public
|
|
358
|
+
* @since 1.3.0
|
|
359
|
+
*/
|
|
360
|
+
declare class MissionQueue {
|
|
361
|
+
private queue;
|
|
362
|
+
private readonly maxSize;
|
|
363
|
+
private readonly timeoutMs;
|
|
364
|
+
constructor(maxSize?: number, timeoutMs?: number);
|
|
365
|
+
get length(): number;
|
|
366
|
+
get isFull(): boolean;
|
|
367
|
+
/**
|
|
368
|
+
* 將任務加入隊列,返回 Promise 等待分配
|
|
369
|
+
*/
|
|
370
|
+
enqueue(mission: Mission): Promise<Rocket>;
|
|
371
|
+
/**
|
|
372
|
+
* 取出下一個等待的任務
|
|
373
|
+
*/
|
|
374
|
+
dequeue(): QueuedMission | null;
|
|
375
|
+
/**
|
|
376
|
+
* 從隊列中移除指定任務
|
|
377
|
+
*/
|
|
378
|
+
private removeFromQueue;
|
|
379
|
+
/**
|
|
380
|
+
* 清空隊列並拒絕所有等待的任務
|
|
381
|
+
*/
|
|
382
|
+
clear(reason: string): void;
|
|
383
|
+
/**
|
|
384
|
+
* 取得隊列統計資訊
|
|
385
|
+
*/
|
|
386
|
+
getStats(): {
|
|
387
|
+
length: number;
|
|
388
|
+
maxSize: number;
|
|
389
|
+
oldestWaitMs: number | null;
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Pool 資源耗盡異常
|
|
394
|
+
*
|
|
395
|
+
* @public
|
|
396
|
+
* @since 1.3.0
|
|
397
|
+
*/
|
|
398
|
+
declare class PoolExhaustedException extends Error {
|
|
399
|
+
constructor(message: string);
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* 隊列等待超時異常
|
|
403
|
+
*
|
|
404
|
+
* @public
|
|
405
|
+
* @since 1.3.0
|
|
406
|
+
*/
|
|
407
|
+
declare class QueueTimeoutException extends Error {
|
|
408
|
+
constructor(message: string);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Event emitted when a mission is assigned to a rocket.
|
|
413
|
+
*
|
|
414
|
+
* @public
|
|
415
|
+
* @since 3.0.0
|
|
416
|
+
*/
|
|
417
|
+
declare class MissionAssigned extends DomainEvent {
|
|
418
|
+
readonly rocketId: string;
|
|
419
|
+
readonly missionId: string;
|
|
420
|
+
constructor(rocketId: string, missionId: string);
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Event emitted when a rocket starts its mission (application started).
|
|
424
|
+
*
|
|
425
|
+
* @public
|
|
426
|
+
* @since 3.0.0
|
|
427
|
+
*/
|
|
428
|
+
declare class RocketIgnited extends DomainEvent {
|
|
429
|
+
readonly rocketId: string;
|
|
430
|
+
constructor(rocketId: string);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Event emitted when a rocket mission is completed or terminated.
|
|
434
|
+
*
|
|
435
|
+
* @public
|
|
436
|
+
* @since 3.0.0
|
|
437
|
+
*/
|
|
438
|
+
declare class RocketSplashedDown extends DomainEvent {
|
|
439
|
+
readonly rocketId: string;
|
|
440
|
+
constructor(rocketId: string);
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Event emitted when a rocket has finished the refurbishment process.
|
|
444
|
+
*
|
|
445
|
+
* @public
|
|
446
|
+
* @since 3.0.0
|
|
447
|
+
*/
|
|
448
|
+
declare class RefurbishmentCompleted extends DomainEvent {
|
|
449
|
+
readonly rocketId: string;
|
|
450
|
+
constructor(rocketId: string);
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Event emitted when the rocket pool is exhausted.
|
|
454
|
+
*
|
|
455
|
+
* @public
|
|
456
|
+
* @since 1.3.0
|
|
457
|
+
*/
|
|
458
|
+
declare class PoolExhausted extends DomainEvent {
|
|
459
|
+
readonly totalRockets: number;
|
|
460
|
+
readonly maxRockets: number;
|
|
461
|
+
readonly queueLength: number;
|
|
462
|
+
constructor(totalRockets: number, maxRockets: number, queueLength: number);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Event emitted when a mission is queued due to pool exhaustion.
|
|
466
|
+
*
|
|
467
|
+
* @public
|
|
468
|
+
* @since 1.3.0
|
|
469
|
+
*/
|
|
470
|
+
declare class MissionQueued extends DomainEvent {
|
|
471
|
+
readonly missionId: string;
|
|
472
|
+
readonly queuePosition: number;
|
|
473
|
+
constructor(missionId: string, queuePosition: number);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Event emitted when a queued mission times out.
|
|
477
|
+
*
|
|
478
|
+
* @public
|
|
479
|
+
* @since 1.3.0
|
|
480
|
+
*/
|
|
481
|
+
declare class MissionQueueTimeout extends DomainEvent {
|
|
482
|
+
readonly missionId: string;
|
|
483
|
+
readonly waitTimeMs: number;
|
|
484
|
+
constructor(missionId: string, waitTimeMs: number);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* LaunchpadOrbit provides automated deployment and preview capabilities for Gravito.
|
|
489
|
+
* It integrates with Docker and GitHub to provide "Preview Deployments" for Pull Requests.
|
|
490
|
+
*
|
|
491
|
+
* @public
|
|
190
492
|
*/
|
|
191
493
|
declare class LaunchpadOrbit implements GravitoOrbit {
|
|
192
494
|
private ripple;
|
|
495
|
+
/**
|
|
496
|
+
* Create a new LaunchpadOrbit instance.
|
|
497
|
+
* @param ripple - Ripple instance for real-time telemetry communication.
|
|
498
|
+
*/
|
|
193
499
|
constructor(ripple: OrbitRipple);
|
|
500
|
+
/**
|
|
501
|
+
* Install the Launchpad orbit into PlanetCore.
|
|
502
|
+
* Registers the service provider and sets up webhook routes for deployment.
|
|
503
|
+
*
|
|
504
|
+
* @param core - The PlanetCore instance.
|
|
505
|
+
*/
|
|
194
506
|
install(core: PlanetCore): Promise<void>;
|
|
195
507
|
}
|
|
196
508
|
/**
|
|
197
|
-
*
|
|
509
|
+
* Bootstrap the Launchpad application.
|
|
510
|
+
* Initializes PlanetCore with necessary orbits (Cache, Ripple, Launchpad)
|
|
511
|
+
* and returns the configuration for the server entry point.
|
|
512
|
+
*
|
|
513
|
+
* @returns Server configuration object including port and fetch handler.
|
|
514
|
+
* @public
|
|
198
515
|
*/
|
|
199
516
|
declare function bootstrapLaunchpad(): Promise<{
|
|
200
517
|
port: number;
|
|
@@ -202,4 +519,4 @@ declare function bootstrapLaunchpad(): Promise<{
|
|
|
202
519
|
fetch: (req: Request, server: any) => Response | Promise<Response> | undefined;
|
|
203
520
|
}>;
|
|
204
521
|
|
|
205
|
-
export { LaunchpadOrbit, Mission, MissionControl, PayloadInjector, PoolManager, RefurbishUnit, Rocket, RocketStatus, bootstrapLaunchpad };
|
|
522
|
+
export { DEFAULT_POOL_CONFIG, DEFAULT_REFURBISH_CONFIG, type IDockerAdapter, type IGitAdapter, type IGitHubAdapter, type IRocketRepository, type IRouterAdapter, LaunchpadOrbit, Mission, MissionAssigned, MissionControl, MissionQueue, MissionQueueTimeout, MissionQueued, PayloadInjector, type PoolConfig, PoolExhausted, PoolExhaustedException, PoolManager, QueueTimeoutException, type RefurbishConfig, RefurbishUnit, RefurbishmentCompleted, Rocket, RocketIgnited, RocketSplashedDown, RocketStatus, bootstrapLaunchpad };
|