@gravito/launchpad 0.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/.turbo/turbo-test.log +82 -0
- package/.turbo/turbo-typecheck.log +1 -0
- package/Dockerfile +23 -0
- package/README.md +44 -0
- package/debug-launch.ts +66 -0
- package/package.json +26 -0
- package/server.ts +7 -0
- package/simulate-github.ts +55 -0
- package/src/Application/MissionControl.ts +59 -0
- package/src/Application/PayloadInjector.ts +60 -0
- package/src/Application/PoolManager.ts +81 -0
- package/src/Application/RefurbishUnit.ts +40 -0
- package/src/Domain/Events.ts +28 -0
- package/src/Domain/Interfaces.ts +62 -0
- package/src/Domain/Mission.ts +27 -0
- package/src/Domain/Rocket.ts +116 -0
- package/src/Domain/RocketStatus.ts +10 -0
- package/src/Infrastructure/Docker/DockerAdapter.ts +127 -0
- package/src/Infrastructure/Git/ShellGitAdapter.ts +22 -0
- package/src/Infrastructure/GitHub/OctokitGitHubAdapter.ts +42 -0
- package/src/Infrastructure/Persistence/CachedRocketRepository.ts +42 -0
- package/src/Infrastructure/Persistence/InMemoryRocketRepository.ts +32 -0
- package/src/Infrastructure/Router/BunProxyAdapter.ts +54 -0
- package/src/index.ts +223 -0
- package/tests/Deployment.test.ts +50 -0
- package/tests/Integration.test.ts +42 -0
- package/tests/PoolManager.test.ts +60 -0
- package/tests/Refurbishment.test.ts +43 -0
- package/tests/Rocket.test.ts +49 -0
- package/tests/Telemetry.test.ts +50 -0
- package/tests/docker-adapter.test.ts +110 -0
- package/tests/enterprise-coverage.test.ts +62 -0
- package/tests/mission-control.test.ts +79 -0
- package/tests/mission.test.ts +18 -0
- package/tests/payload-injector.test.ts +64 -0
- package/tests/pool-manager.test.ts +93 -0
- package/tests/repository.test.ts +31 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { PoolManager } from '../src/Application/PoolManager'
|
|
3
|
+
import { Mission } from '../src/Domain/Mission'
|
|
4
|
+
import { RocketStatus } from '../src/Domain/RocketStatus'
|
|
5
|
+
import { InMemoryRocketRepository } from '../src/Infrastructure/Persistence/InMemoryRocketRepository'
|
|
6
|
+
|
|
7
|
+
class FakeDocker {
|
|
8
|
+
created = 0
|
|
9
|
+
async createBaseContainer(): Promise<string> {
|
|
10
|
+
this.created += 1
|
|
11
|
+
return `container-${this.created}`
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class FakeRefurbishUnit {
|
|
16
|
+
calls = 0
|
|
17
|
+
async refurbish(rocket: { splashDown: () => void; finishRefurbishment: () => void }) {
|
|
18
|
+
this.calls += 1
|
|
19
|
+
rocket.splashDown()
|
|
20
|
+
rocket.finishRefurbishment()
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
describe('PoolManager', () => {
|
|
25
|
+
test('warms up the pool with base containers', async () => {
|
|
26
|
+
const docker = new FakeDocker()
|
|
27
|
+
const repo = new InMemoryRocketRepository()
|
|
28
|
+
const pool = new PoolManager(docker as any, repo)
|
|
29
|
+
|
|
30
|
+
await pool.warmup(2)
|
|
31
|
+
const rockets = await repo.findAll()
|
|
32
|
+
expect(rockets.length).toBe(2)
|
|
33
|
+
expect(docker.created).toBe(2)
|
|
34
|
+
|
|
35
|
+
await pool.warmup(1)
|
|
36
|
+
expect((await repo.findAll()).length).toBe(2)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('assigns missions to idle rockets or creates new ones', async () => {
|
|
40
|
+
const docker = new FakeDocker()
|
|
41
|
+
const repo = new InMemoryRocketRepository()
|
|
42
|
+
const pool = new PoolManager(docker as any, repo)
|
|
43
|
+
|
|
44
|
+
const mission = Mission.create({
|
|
45
|
+
id: 'mission-2',
|
|
46
|
+
repoUrl: 'https://example.com/repo.git',
|
|
47
|
+
branch: 'dev',
|
|
48
|
+
commitSha: 'def456',
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
await pool.warmup(1)
|
|
52
|
+
const rocket = await pool.assignMission(mission)
|
|
53
|
+
expect(rocket.status).toBe(RocketStatus.PREPARING)
|
|
54
|
+
expect(docker.created).toBe(1)
|
|
55
|
+
|
|
56
|
+
const repoRocket = await repo.findById(rocket.id)
|
|
57
|
+
expect(repoRocket?.currentMission?.id).toBe('mission-2')
|
|
58
|
+
|
|
59
|
+
const missionTwo = Mission.create({
|
|
60
|
+
id: 'mission-3',
|
|
61
|
+
repoUrl: 'https://example.com/repo.git',
|
|
62
|
+
branch: 'main',
|
|
63
|
+
commitSha: '7890',
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
const newRocket = await pool.assignMission(missionTwo)
|
|
67
|
+
expect(newRocket.id).not.toBe(rocket.id)
|
|
68
|
+
expect(docker.created).toBe(2)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('recycles rockets through refurbish flow', async () => {
|
|
72
|
+
const docker = new FakeDocker()
|
|
73
|
+
const repo = new InMemoryRocketRepository()
|
|
74
|
+
const refurbishUnit = new FakeRefurbishUnit()
|
|
75
|
+
const pool = new PoolManager(docker as any, repo, refurbishUnit as any)
|
|
76
|
+
|
|
77
|
+
await pool.warmup(1)
|
|
78
|
+
const mission = Mission.create({
|
|
79
|
+
id: 'mission-4',
|
|
80
|
+
repoUrl: 'https://example.com/repo.git',
|
|
81
|
+
branch: 'main',
|
|
82
|
+
commitSha: 'abcd',
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const rocket = await pool.assignMission(mission)
|
|
86
|
+
rocket.ignite()
|
|
87
|
+
|
|
88
|
+
await pool.recycle('mission-4')
|
|
89
|
+
const stored = await repo.findById(rocket.id)
|
|
90
|
+
expect(stored?.status).toBe(RocketStatus.IDLE)
|
|
91
|
+
expect(refurbishUnit.calls).toBe(1)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { Rocket } from '../src/Domain/Rocket'
|
|
3
|
+
import { RocketStatus } from '../src/Domain/RocketStatus'
|
|
4
|
+
import { InMemoryRocketRepository } from '../src/Infrastructure/Persistence/InMemoryRocketRepository'
|
|
5
|
+
|
|
6
|
+
describe('InMemoryRocketRepository', () => {
|
|
7
|
+
test('stores and retrieves rockets', async () => {
|
|
8
|
+
const repo = new InMemoryRocketRepository()
|
|
9
|
+
const rocket = new Rocket('rocket-20', 'container-20')
|
|
10
|
+
|
|
11
|
+
await repo.save(rocket)
|
|
12
|
+
expect(await repo.findById('rocket-20')).toBe(rocket)
|
|
13
|
+
expect((await repo.findAll()).length).toBe(1)
|
|
14
|
+
|
|
15
|
+
await repo.delete('rocket-20')
|
|
16
|
+
expect(await repo.findById('rocket-20')).toBeNull()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('finds idle rockets', async () => {
|
|
20
|
+
const repo = new InMemoryRocketRepository()
|
|
21
|
+
const idle = new Rocket('rocket-idle', 'container-idle')
|
|
22
|
+
const busy = new Rocket('rocket-busy', 'container-busy')
|
|
23
|
+
busy.assignMission({ id: 'mission', repoUrl: '', branch: '', commitSha: '' } as any)
|
|
24
|
+
|
|
25
|
+
await repo.save(idle)
|
|
26
|
+
await repo.save(busy)
|
|
27
|
+
|
|
28
|
+
const found = await repo.findIdle()
|
|
29
|
+
expect(found?.status).toBe(RocketStatus.IDLE)
|
|
30
|
+
})
|
|
31
|
+
})
|