@gravito/launchpad 1.0.0-beta.1 → 1.1.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 ADDED
@@ -0,0 +1,27 @@
1
+ # @gravito/launchpad
2
+
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Launch standalone high-performance engine and core optimizations.
8
+
9
+ ### Patch Changes
10
+
11
+ - q
12
+ - Updated dependencies
13
+ - Updated dependencies
14
+ - @gravito/core@1.1.0
15
+ - @gravito/enterprise@1.0.1
16
+ - @gravito/ripple@2.0.0
17
+ - @gravito/stasis@2.0.0
18
+
19
+ ## 1.0.0
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies
24
+ - @gravito/core@1.0.0
25
+ - @gravito/enterprise@1.0.0
26
+ - @gravito/ripple@1.0.0
27
+ - @gravito/stasis@1.0.0
package/README.md CHANGED
@@ -1,44 +1,102 @@
1
1
  # @gravito/launchpad
2
2
 
3
- > 🚀 Bun 火箭回收系統:專為 Bun 打造的秒級容器部署與生命週期管理系統。
3
+ > 🚀 Instant Deployment System for Bun. Container lifecycle management and zero-downtime deployment.
4
4
 
5
- ## 核心特性
5
+ **Gravito Launchpad** is a specialized orchestration system built for the Bun runtime. It uses a unique "Rocket Pool" architecture to pre-warm containers, enabling sub-second deployments by injecting code into already running instances instead of building images from scratch.
6
6
 
7
- - **Rocket Pool**: 預熱容器池,消除啟動冷啟動。
8
- - **Payload Injection**: 跳過 Docker Build,透過 `docker cp` 秒級注入代碼。
9
- - **DDD 架構**: 基於 `@gravito/enterprise` 實作,具備嚴謹的狀態機管理。
10
- - **可回收性**: 任務結束後自動翻新容器,資源零浪費。
7
+ ## Core Features
11
8
 
12
- ## 架構概覽
9
+ - **🔥 Rocket Pool**: Pre-warmed container pool eliminates cold start times.
10
+ - **💉 Payload Injection**: Skip `docker build`. Code is injected via `docker cp` in milliseconds.
11
+ - **🏗️ DDD Architecture**: Built on `@gravito/enterprise` with rigorous state machine management.
12
+ - **♻️ Auto-Recycling**: Containers are automatically refurbished and returned to the pool after missions.
13
+ - **🤖 GitHub Integration**: Built-in webhook handler for PR previews and automated comments.
14
+ - **🛡️ Secure Isolation**: Each deployment runs in an isolated container environment.
13
15
 
14
- 本套件遵循 **Clean Architecture** 與 **DDD**:
16
+ ## 🏗️ Architecture Overview
15
17
 
16
- - **Domain**: 定義 `Rocket` 狀態機與 `Mission` 邏輯。
17
- - **Application**: `PoolManager` (調度) 與 `PayloadInjector` (部署)。
18
- - **Infrastructure**: 底層 Docker 與 Git 操作實作。
18
+ Launchpad follows **Clean Architecture** principles:
19
19
 
20
- ## 快速開始
20
+ ### Domain Layer
21
+ - **Rocket**: The aggregate root representing a container instance.
22
+ - **Mission**: Represents a deployment task (repo, commit, branch).
23
+ - **Status Machine**: Strictly manages transitions (Idle -> Assigned -> Deployed -> Recycling).
24
+
25
+ ### Application Layer
26
+ - **PoolManager**: Orchestrates the lifecycle of Rockets (warmup, assignment, recycling).
27
+ - **PayloadInjector**: Handles the git clone and code injection process.
28
+ - **MissionControl**: High-level facade for launching missions.
29
+ - **RefurbishUnit**: Cleans up used containers for reuse.
30
+
31
+ ### Infrastructure Layer
32
+ - **DockerAdapter**: Low-level communication with Docker daemon.
33
+ - **ShellGitAdapter**: Git operations via shell commands.
34
+ - **BunProxyAdapter**: Manages reverse proxying to active containers.
35
+
36
+ ## 🚀 Quick Start
37
+
38
+ ### Installation
39
+
40
+ ```bash
41
+ bun add @gravito/launchpad
42
+ ```
43
+
44
+ ### Basic Usage
21
45
 
22
46
  ```typescript
23
- import { PoolManager, PayloadInjector } from '@gravito/launchpad'
47
+ import { PoolManager, PayloadInjector, Mission } from '@gravito/launchpad'
24
48
  import { DockerAdapter, ShellGitAdapter, InMemoryRocketRepository } from '@gravito/launchpad/infra'
25
49
 
26
- const manager = new PoolManager(new DockerAdapter(), new InMemoryRocketRepository())
27
- const injector = new PayloadInjector(new DockerAdapter(), new ShellGitAdapter())
50
+ // Initialize adapters
51
+ const docker = new DockerAdapter()
52
+ const repo = new InMemoryRocketRepository()
53
+ const manager = new PoolManager(docker, repo)
28
54
 
29
- // 1. 預熱池子
55
+ // 1. Warmup the pool (Create 3 idle containers)
30
56
  await manager.warmup(3)
31
57
 
32
- // 2. 指派任務
33
- const mission = Mission.create({ ... })
34
- const rocket = await manager.assignMission(mission)
58
+ // 2. Create a mission
59
+ const mission = Mission.create({
60
+ id: 'mission-1',
61
+ repoUrl: 'https://github.com/user/repo.git',
62
+ commitSha: 'latest'
63
+ })
35
64
 
36
- // 3. 秒級部署
65
+ // 3. Launch! (Assigns a rocket and injects code)
66
+ const rocket = await manager.assignMission(mission)
67
+ const injector = new PayloadInjector(docker, new ShellGitAdapter())
37
68
  await injector.deploy(rocket)
69
+
70
+ console.log(`Mission deployed to http://localhost:${rocket.port}`)
38
71
  ```
39
72
 
40
- ## 測試
73
+ ### Running as a Service (Orbit)
74
+
75
+ Launchpad can run as a Gravito Orbit, providing a REST API and Webhook support.
76
+
77
+ ```typescript
78
+ import { bootstrapLaunchpad } from '@gravito/launchpad'
79
+
80
+ const { port } = await bootstrapLaunchpad()
81
+ console.log(`Launchpad Server running on port ${port}`)
82
+ ```
83
+
84
+ ## ⚙️ Configuration
85
+
86
+ Launchpad relies on Docker. Ensure Docker is installed and running.
87
+
88
+ | Environment Variable | Description | Default |
89
+ |----------------------|-------------|---------|
90
+ | `GITHUB_TOKEN` | Token for GitHub API access | (Required for PR comments) |
91
+ | `GITHUB_WEBHOOK_SECRET` | Secret for verifying webhooks | (Optional) |
92
+ | `POOL_SIZE` | Target number of pre-warmed containers | `3` |
93
+
94
+ ## 🧪 Testing
41
95
 
42
96
  ```bash
43
97
  bun test
44
98
  ```
99
+
100
+ ## 📄 License
101
+
102
+ MIT © Gravito Framework
package/debug-launch.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { getRuntimeAdapter } from '@gravito/core'
1
2
  import { DockerAdapter } from './src/Infrastructure/Docker/DockerAdapter'
2
3
  import { bootstrapLaunchpad } from './src/index'
3
4
 
@@ -6,23 +7,25 @@ async function run() {
6
7
 
7
8
  // 1. Cleanup
8
9
  const _docker = new DockerAdapter()
10
+ const runtime = getRuntimeAdapter()
9
11
  try {
10
- const containers = await Bun.spawn([
12
+ const listProc = runtime.spawn([
11
13
  'docker',
12
14
  'ps',
13
15
  '-aq',
14
16
  '--filter',
15
17
  'label=gravito-origin=launchpad',
16
- ]).text()
18
+ ])
19
+ const containers = await new Response(listProc.stdout ?? null).text()
17
20
  if (containers.trim()) {
18
21
  console.log('🧹 Cleaning up old containers...')
19
- await Bun.spawn(['docker', 'rm', '-f', ...containers.trim().split('\n')]).exited
22
+ await runtime.spawn(['docker', 'rm', '-f', ...containers.trim().split('\n')]).exited
20
23
  }
21
24
  } catch (_e) {}
22
25
 
23
26
  // 2. Start Server
24
27
  const config = await bootstrapLaunchpad()
25
- const server = Bun.serve(config)
28
+ const server = runtime.serve(config)
26
29
  console.log(`🚀 Server started at http://localhost:${config.port}`)
27
30
 
28
31
  // 3. Launch Mission
@@ -0,0 +1,205 @@
1
+ import * as _gravito_ripple from '@gravito/ripple';
2
+ import { OrbitRipple } from '@gravito/ripple';
3
+ import { GravitoOrbit, PlanetCore } from '@gravito/core';
4
+ import { ValueObject, AggregateRoot } from '@gravito/enterprise';
5
+
6
+ interface MissionProps {
7
+ id: string;
8
+ repoUrl: string;
9
+ branch: string;
10
+ commitSha: string;
11
+ }
12
+ declare class Mission extends ValueObject<MissionProps> {
13
+ get id(): string;
14
+ get repoUrl(): string;
15
+ get branch(): string;
16
+ get commitSha(): string;
17
+ static create(props: MissionProps): Mission;
18
+ }
19
+
20
+ /**
21
+ * 火箭生命週期狀態
22
+ */
23
+ declare enum RocketStatus {
24
+ IDLE = "IDLE",// 待命:容器運行中,無任務
25
+ PREPARING = "PREPARING",// 裝填:正在注入代碼 (Locked)
26
+ ORBITING = "ORBITING",// 運行:應用服務中
27
+ REFURBISHING = "REFURBISHING",// 翻新:正在清理環境
28
+ DECOMMISSIONED = "DECOMMISSIONED"
29
+ }
30
+
31
+ declare class Rocket extends AggregateRoot<string> {
32
+ private _status;
33
+ private _currentMission;
34
+ private _containerId;
35
+ private _assignedDomain;
36
+ constructor(id: string, containerId: string);
37
+ get status(): RocketStatus;
38
+ get currentMission(): Mission | null;
39
+ get containerId(): string;
40
+ get assignedDomain(): string | null;
41
+ /**
42
+ * 分配域名
43
+ */
44
+ assignDomain(domain: string): void;
45
+ /**
46
+ * 分配任務 (指派任務給 IDLE 的火箭)
47
+ */
48
+ assignMission(mission: Mission): void;
49
+ /**
50
+ * 點火啟動 (應用程式啟動成功)
51
+ */
52
+ ignite(): void;
53
+ /**
54
+ * 任務降落 (PR 合併或關閉,暫停服務)
55
+ */
56
+ splashDown(): void;
57
+ /**
58
+ * 翻新完成 (清理完畢,回歸池中)
59
+ */
60
+ finishRefurbishment(): void;
61
+ /**
62
+ * 火箭除役 (移除容器)
63
+ */
64
+ decommission(): void;
65
+ toJSON(): {
66
+ id: string;
67
+ containerId: string;
68
+ status: RocketStatus;
69
+ currentMission: Mission | null;
70
+ assignedDomain: string | null;
71
+ };
72
+ static fromJSON(data: any): Rocket;
73
+ }
74
+
75
+ /**
76
+ * 負責與底層容器引擎(如 Docker)溝通的轉接器介面
77
+ */
78
+ interface IDockerAdapter {
79
+ createBaseContainer(): Promise<string>;
80
+ copyFiles(containerId: string, sourcePath: string, targetPath: string): Promise<void>;
81
+ executeCommand(containerId: string, command: string[]): Promise<{
82
+ stdout: string;
83
+ stderr: string;
84
+ exitCode: number;
85
+ }>;
86
+ /**
87
+ * 獲取容器映射到宿主機的真實端口
88
+ */
89
+ getExposedPort(containerId: string, containerPort?: number): Promise<number>;
90
+ removeContainer(containerId: string): Promise<void>;
91
+ /**
92
+ * 根據 Label 批量移除容器
93
+ */
94
+ removeContainerByLabel(label: string): Promise<void>;
95
+ streamLogs(containerId: string, onData: (data: string) => void): void;
96
+ getStats(containerId: string): Promise<{
97
+ cpu: string;
98
+ memory: string;
99
+ }>;
100
+ }
101
+ /**
102
+ * 負責代碼獲取
103
+ */
104
+ interface IGitAdapter {
105
+ clone(repoUrl: string, branch: string): Promise<string>;
106
+ }
107
+ /**
108
+ * 負責動態反向代理與域名路由的轉接器
109
+ */
110
+ interface IRouterAdapter {
111
+ /**
112
+ * 註冊一個域名映射到指定的目標 (URL)
113
+ */
114
+ register(domain: string, targetUrl: string): void;
115
+ /**
116
+ * 註銷域名
117
+ */
118
+ unregister(domain: string): void;
119
+ /**
120
+ * 啟動代理伺服器
121
+ */
122
+ start(port: number): void;
123
+ }
124
+ /**
125
+ * 負責動態反向代理與域名路由的轉接器
126
+ */
127
+ interface IRouterAdapter {
128
+ register(domain: string, targetUrl: string): void;
129
+ unregister(domain: string): void;
130
+ start(port: number): void;
131
+ }
132
+ /**
133
+ * 負責持久化火箭狀態的儲存庫介面
134
+ */
135
+ interface IRocketRepository {
136
+ save(rocket: Rocket): Promise<void>;
137
+ findById(id: string): Promise<Rocket | null>;
138
+ findIdle(): Promise<Rocket | null>;
139
+ findAll(): Promise<Rocket[]>;
140
+ delete(id: string): Promise<void>;
141
+ }
142
+
143
+ declare class PayloadInjector {
144
+ private docker;
145
+ private git;
146
+ constructor(docker: IDockerAdapter, git: IGitAdapter);
147
+ deploy(rocket: Rocket): Promise<void>;
148
+ }
149
+
150
+ declare class RefurbishUnit {
151
+ private docker;
152
+ constructor(docker: IDockerAdapter);
153
+ /**
154
+ * 執行火箭翻新邏輯
155
+ */
156
+ refurbish(rocket: Rocket): Promise<void>;
157
+ }
158
+
159
+ declare class PoolManager {
160
+ private dockerAdapter;
161
+ private rocketRepository;
162
+ private refurbishUnit?;
163
+ private router?;
164
+ constructor(dockerAdapter: IDockerAdapter, rocketRepository: IRocketRepository, refurbishUnit?: RefurbishUnit | undefined, router?: IRouterAdapter | undefined);
165
+ /**
166
+ * 初始化發射場:預先準備指定數量的火箭
167
+ */
168
+ warmup(count: number): Promise<void>;
169
+ /**
170
+ * 獲取一架可用的火箭並分配任務
171
+ */
172
+ assignMission(mission: Mission): Promise<Rocket>;
173
+ /**
174
+ * 回收指定任務的火箭
175
+ */
176
+ recycle(missionId: string): Promise<void>;
177
+ }
178
+
179
+ declare class MissionControl {
180
+ private poolManager;
181
+ private injector;
182
+ private docker;
183
+ private router?;
184
+ constructor(poolManager: PoolManager, injector: PayloadInjector, docker: IDockerAdapter, router?: IRouterAdapter | undefined);
185
+ launch(mission: Mission, onTelemetry: (type: string, data: any) => void): Promise<string>;
186
+ }
187
+
188
+ /**
189
+ * Gravito Launchpad Orbit
190
+ */
191
+ declare class LaunchpadOrbit implements GravitoOrbit {
192
+ private ripple;
193
+ constructor(ripple: OrbitRipple);
194
+ install(core: PlanetCore): Promise<void>;
195
+ }
196
+ /**
197
+ * 一鍵啟動 Launchpad 應用程式
198
+ */
199
+ declare function bootstrapLaunchpad(): Promise<{
200
+ port: number;
201
+ websocket: _gravito_ripple.WebSocketHandlerConfig;
202
+ fetch: (req: Request, server: any) => Response | Promise<Response> | undefined;
203
+ }>;
204
+
205
+ export { LaunchpadOrbit, Mission, MissionControl, PayloadInjector, PoolManager, RefurbishUnit, Rocket, RocketStatus, bootstrapLaunchpad };
@@ -0,0 +1,205 @@
1
+ import * as _gravito_ripple from '@gravito/ripple';
2
+ import { OrbitRipple } from '@gravito/ripple';
3
+ import { GravitoOrbit, PlanetCore } from '@gravito/core';
4
+ import { ValueObject, AggregateRoot } from '@gravito/enterprise';
5
+
6
+ interface MissionProps {
7
+ id: string;
8
+ repoUrl: string;
9
+ branch: string;
10
+ commitSha: string;
11
+ }
12
+ declare class Mission extends ValueObject<MissionProps> {
13
+ get id(): string;
14
+ get repoUrl(): string;
15
+ get branch(): string;
16
+ get commitSha(): string;
17
+ static create(props: MissionProps): Mission;
18
+ }
19
+
20
+ /**
21
+ * 火箭生命週期狀態
22
+ */
23
+ declare enum RocketStatus {
24
+ IDLE = "IDLE",// 待命:容器運行中,無任務
25
+ PREPARING = "PREPARING",// 裝填:正在注入代碼 (Locked)
26
+ ORBITING = "ORBITING",// 運行:應用服務中
27
+ REFURBISHING = "REFURBISHING",// 翻新:正在清理環境
28
+ DECOMMISSIONED = "DECOMMISSIONED"
29
+ }
30
+
31
+ declare class Rocket extends AggregateRoot<string> {
32
+ private _status;
33
+ private _currentMission;
34
+ private _containerId;
35
+ private _assignedDomain;
36
+ constructor(id: string, containerId: string);
37
+ get status(): RocketStatus;
38
+ get currentMission(): Mission | null;
39
+ get containerId(): string;
40
+ get assignedDomain(): string | null;
41
+ /**
42
+ * 分配域名
43
+ */
44
+ assignDomain(domain: string): void;
45
+ /**
46
+ * 分配任務 (指派任務給 IDLE 的火箭)
47
+ */
48
+ assignMission(mission: Mission): void;
49
+ /**
50
+ * 點火啟動 (應用程式啟動成功)
51
+ */
52
+ ignite(): void;
53
+ /**
54
+ * 任務降落 (PR 合併或關閉,暫停服務)
55
+ */
56
+ splashDown(): void;
57
+ /**
58
+ * 翻新完成 (清理完畢,回歸池中)
59
+ */
60
+ finishRefurbishment(): void;
61
+ /**
62
+ * 火箭除役 (移除容器)
63
+ */
64
+ decommission(): void;
65
+ toJSON(): {
66
+ id: string;
67
+ containerId: string;
68
+ status: RocketStatus;
69
+ currentMission: Mission | null;
70
+ assignedDomain: string | null;
71
+ };
72
+ static fromJSON(data: any): Rocket;
73
+ }
74
+
75
+ /**
76
+ * 負責與底層容器引擎(如 Docker)溝通的轉接器介面
77
+ */
78
+ interface IDockerAdapter {
79
+ createBaseContainer(): Promise<string>;
80
+ copyFiles(containerId: string, sourcePath: string, targetPath: string): Promise<void>;
81
+ executeCommand(containerId: string, command: string[]): Promise<{
82
+ stdout: string;
83
+ stderr: string;
84
+ exitCode: number;
85
+ }>;
86
+ /**
87
+ * 獲取容器映射到宿主機的真實端口
88
+ */
89
+ getExposedPort(containerId: string, containerPort?: number): Promise<number>;
90
+ removeContainer(containerId: string): Promise<void>;
91
+ /**
92
+ * 根據 Label 批量移除容器
93
+ */
94
+ removeContainerByLabel(label: string): Promise<void>;
95
+ streamLogs(containerId: string, onData: (data: string) => void): void;
96
+ getStats(containerId: string): Promise<{
97
+ cpu: string;
98
+ memory: string;
99
+ }>;
100
+ }
101
+ /**
102
+ * 負責代碼獲取
103
+ */
104
+ interface IGitAdapter {
105
+ clone(repoUrl: string, branch: string): Promise<string>;
106
+ }
107
+ /**
108
+ * 負責動態反向代理與域名路由的轉接器
109
+ */
110
+ interface IRouterAdapter {
111
+ /**
112
+ * 註冊一個域名映射到指定的目標 (URL)
113
+ */
114
+ register(domain: string, targetUrl: string): void;
115
+ /**
116
+ * 註銷域名
117
+ */
118
+ unregister(domain: string): void;
119
+ /**
120
+ * 啟動代理伺服器
121
+ */
122
+ start(port: number): void;
123
+ }
124
+ /**
125
+ * 負責動態反向代理與域名路由的轉接器
126
+ */
127
+ interface IRouterAdapter {
128
+ register(domain: string, targetUrl: string): void;
129
+ unregister(domain: string): void;
130
+ start(port: number): void;
131
+ }
132
+ /**
133
+ * 負責持久化火箭狀態的儲存庫介面
134
+ */
135
+ interface IRocketRepository {
136
+ save(rocket: Rocket): Promise<void>;
137
+ findById(id: string): Promise<Rocket | null>;
138
+ findIdle(): Promise<Rocket | null>;
139
+ findAll(): Promise<Rocket[]>;
140
+ delete(id: string): Promise<void>;
141
+ }
142
+
143
+ declare class PayloadInjector {
144
+ private docker;
145
+ private git;
146
+ constructor(docker: IDockerAdapter, git: IGitAdapter);
147
+ deploy(rocket: Rocket): Promise<void>;
148
+ }
149
+
150
+ declare class RefurbishUnit {
151
+ private docker;
152
+ constructor(docker: IDockerAdapter);
153
+ /**
154
+ * 執行火箭翻新邏輯
155
+ */
156
+ refurbish(rocket: Rocket): Promise<void>;
157
+ }
158
+
159
+ declare class PoolManager {
160
+ private dockerAdapter;
161
+ private rocketRepository;
162
+ private refurbishUnit?;
163
+ private router?;
164
+ constructor(dockerAdapter: IDockerAdapter, rocketRepository: IRocketRepository, refurbishUnit?: RefurbishUnit | undefined, router?: IRouterAdapter | undefined);
165
+ /**
166
+ * 初始化發射場:預先準備指定數量的火箭
167
+ */
168
+ warmup(count: number): Promise<void>;
169
+ /**
170
+ * 獲取一架可用的火箭並分配任務
171
+ */
172
+ assignMission(mission: Mission): Promise<Rocket>;
173
+ /**
174
+ * 回收指定任務的火箭
175
+ */
176
+ recycle(missionId: string): Promise<void>;
177
+ }
178
+
179
+ declare class MissionControl {
180
+ private poolManager;
181
+ private injector;
182
+ private docker;
183
+ private router?;
184
+ constructor(poolManager: PoolManager, injector: PayloadInjector, docker: IDockerAdapter, router?: IRouterAdapter | undefined);
185
+ launch(mission: Mission, onTelemetry: (type: string, data: any) => void): Promise<string>;
186
+ }
187
+
188
+ /**
189
+ * Gravito Launchpad Orbit
190
+ */
191
+ declare class LaunchpadOrbit implements GravitoOrbit {
192
+ private ripple;
193
+ constructor(ripple: OrbitRipple);
194
+ install(core: PlanetCore): Promise<void>;
195
+ }
196
+ /**
197
+ * 一鍵啟動 Launchpad 應用程式
198
+ */
199
+ declare function bootstrapLaunchpad(): Promise<{
200
+ port: number;
201
+ websocket: _gravito_ripple.WebSocketHandlerConfig;
202
+ fetch: (req: Request, server: any) => Response | Promise<Response> | undefined;
203
+ }>;
204
+
205
+ export { LaunchpadOrbit, Mission, MissionControl, PayloadInjector, PoolManager, RefurbishUnit, Rocket, RocketStatus, bootstrapLaunchpad };