vyft 0.1.0-alpha
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/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/build.d.ts +11 -0
- package/dist/build.js +39 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +200 -0
- package/dist/docker.d.ts +48 -0
- package/dist/docker.js +855 -0
- package/dist/exec.d.ts +2 -0
- package/dist/exec.js +28 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/init.d.ts +1 -0
- package/dist/init.js +100 -0
- package/dist/interpolate.d.ts +2 -0
- package/dist/interpolate.js +8 -0
- package/dist/logger.d.ts +2 -0
- package/dist/logger.js +10 -0
- package/dist/resource.d.ts +78 -0
- package/dist/resource.js +35 -0
- package/dist/runtime.d.ts +6 -0
- package/dist/runtime.js +0 -0
- package/dist/swarm/factories.d.ts +8 -0
- package/dist/swarm/factories.js +50 -0
- package/dist/swarm/index.d.ts +10 -0
- package/dist/swarm/index.js +5 -0
- package/dist/swarm/types.d.ts +25 -0
- package/dist/swarm/types.js +0 -0
- package/dist/symbols.d.ts +8 -0
- package/dist/symbols.js +1 -0
- package/package.json +68 -0
- package/templates/fullstack/apps/api/Dockerfile +21 -0
- package/templates/fullstack/apps/api/package.json +26 -0
- package/templates/fullstack/apps/api/src/auth.ts +21 -0
- package/templates/fullstack/apps/api/src/db.ts +16 -0
- package/templates/fullstack/apps/api/src/index.ts +17 -0
- package/templates/fullstack/apps/api/src/router.ts +11 -0
- package/templates/fullstack/apps/api/src/schema.ts +11 -0
- package/templates/fullstack/apps/api/tsconfig.json +8 -0
- package/templates/fullstack/apps/web/index.html +12 -0
- package/templates/fullstack/apps/web/package.json +21 -0
- package/templates/fullstack/apps/web/src/app.tsx +8 -0
- package/templates/fullstack/apps/web/src/main.tsx +9 -0
- package/templates/fullstack/apps/web/tsconfig.json +7 -0
- package/templates/fullstack/apps/web/vite.config.ts +14 -0
- package/templates/fullstack/compose.yaml +14 -0
- package/templates/fullstack/dockerignore +7 -0
- package/templates/fullstack/gitignore +3 -0
- package/templates/fullstack/package.json +17 -0
- package/templates/fullstack/pnpm-workspace.yaml +2 -0
- package/templates/fullstack/tsconfig.json +11 -0
- package/templates/fullstack/vyft.config.ts +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Vyft
|
|
2
|
+
|
|
3
|
+
Deploy apps with TypeScript.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install vyft
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
Create `vyft.config.ts`:
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { interpolate } from 'vyft';
|
|
17
|
+
import { swarm } from 'vyft/swarm';
|
|
18
|
+
|
|
19
|
+
const { secret, volume, service, site } = swarm();
|
|
20
|
+
|
|
21
|
+
export const dbPassword = secret('db-password');
|
|
22
|
+
export const dbData = volume('db-data', { size: '20GB' });
|
|
23
|
+
|
|
24
|
+
export const db = service('db', {
|
|
25
|
+
image: 'postgres:16',
|
|
26
|
+
env: {
|
|
27
|
+
POSTGRES_PASSWORD: dbPassword,
|
|
28
|
+
POSTGRES_DB: 'myapp'
|
|
29
|
+
},
|
|
30
|
+
volumes: [
|
|
31
|
+
{ volume: dbData, mount: '/var/lib/postgresql/data' }
|
|
32
|
+
]
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const api = service('api', {
|
|
36
|
+
route: 'api.example.com',
|
|
37
|
+
image: { context: './api' },
|
|
38
|
+
env: {
|
|
39
|
+
DATABASE_URL: interpolate`postgres://postgres:${dbPassword}@${db.host}:5432/myapp`
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const app = site('app', {
|
|
44
|
+
route: 'example.com',
|
|
45
|
+
spa: true,
|
|
46
|
+
build: { context: './frontend' }
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Deploy:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
vyft deploy
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Tear down:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
vyft destroy
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Runtime
|
|
63
|
+
|
|
64
|
+
Resources are created through a runtime. The `swarm` runtime manages containers, networking, and routing.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { swarm } from 'vyft/swarm';
|
|
68
|
+
|
|
69
|
+
const { secret, volume, service, site } = swarm();
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Volumes support size limits (requires xfs with project quotas on the host):
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
const { volume } = swarm();
|
|
76
|
+
|
|
77
|
+
export const data = volume('data', { size: '50GB' });
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Primitives
|
|
81
|
+
|
|
82
|
+
### volume
|
|
83
|
+
|
|
84
|
+
Persistent storage.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
volume('data');
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### secret
|
|
91
|
+
|
|
92
|
+
Auto-generated secure values. Mounted as files at `/run/secrets/<id>`.
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const apiKey = secret('api-key');
|
|
96
|
+
const jwtSecret = secret('jwt-secret', { length: 64 });
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Pass a secret directly as an env value to mount it. The `_FILE` suffix is added automatically:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
env: {
|
|
103
|
+
API_KEY: apiKey
|
|
104
|
+
}
|
|
105
|
+
// → sets API_KEY_FILE=/run/secrets/api-key
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### interpolate
|
|
109
|
+
|
|
110
|
+
Compose env values that contain secrets. Imported from `vyft`.
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { interpolate } from 'vyft';
|
|
114
|
+
|
|
115
|
+
env: {
|
|
116
|
+
DATABASE_URL: interpolate`postgres://user:${dbPassword}@${db.host}:5432/mydb`
|
|
117
|
+
}
|
|
118
|
+
// → sets DATABASE_URL_FILE=/run/secrets/<derived-secret>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The result is stored as a derived secret and mounted as a file.
|
|
122
|
+
|
|
123
|
+
### service
|
|
124
|
+
|
|
125
|
+
Run containers.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const api = service('api', {
|
|
129
|
+
image: { context: './api' }, // Build from directory
|
|
130
|
+
// or: image: 'node:20', // Use existing image
|
|
131
|
+
route: 'api.example.com',
|
|
132
|
+
port: 3000,
|
|
133
|
+
env: { NODE_ENV: 'production' },
|
|
134
|
+
command: ['node', 'server.js'],
|
|
135
|
+
volumes: [{ volume: data, mount: '/app/data' }],
|
|
136
|
+
replicas: 3,
|
|
137
|
+
healthCheck: {
|
|
138
|
+
command: ['curl', '-f', 'http://localhost:3000/health'],
|
|
139
|
+
interval: '30s',
|
|
140
|
+
timeout: '10s',
|
|
141
|
+
retries: 3
|
|
142
|
+
},
|
|
143
|
+
resources: {
|
|
144
|
+
memory: '512MB',
|
|
145
|
+
cpus: 0.5
|
|
146
|
+
},
|
|
147
|
+
restartPolicy: 'on-failure'
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Services expose output properties for referencing elsewhere:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
api.host // internal hostname
|
|
155
|
+
api.port // internal port (default 3000)
|
|
156
|
+
api.url // full URL (https if routed, http otherwise)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### site
|
|
160
|
+
|
|
161
|
+
Serve static sites with automatic builds.
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
const app = site('app', {
|
|
165
|
+
route: 'example.com',
|
|
166
|
+
spa: true,
|
|
167
|
+
build: {
|
|
168
|
+
context: './frontend',
|
|
169
|
+
output: './dist',
|
|
170
|
+
command: 'npm run build'
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
app.url // https://example.com
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Routing
|
|
178
|
+
|
|
179
|
+
Routes go through a reverse proxy with automatic SSL.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
'example.com' // Root domain
|
|
183
|
+
'example.com/api/*' // Path prefix
|
|
184
|
+
'api.example.com' // Subdomain
|
|
185
|
+
'*.example.com' // Wildcard subdomain
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Requirements
|
|
189
|
+
|
|
190
|
+
- Node.js >= 22
|
|
191
|
+
- Docker
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT
|
package/dist/build.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Logger } from "pino";
|
|
2
|
+
export interface BuildResult {
|
|
3
|
+
outputPath: string;
|
|
4
|
+
}
|
|
5
|
+
type BuildOptions = {
|
|
6
|
+
output?: string;
|
|
7
|
+
command?: string;
|
|
8
|
+
env?: Record<string, string>;
|
|
9
|
+
};
|
|
10
|
+
export declare function buildStatic(context: string, options: BuildOptions, log?: Logger): Promise<BuildResult>;
|
|
11
|
+
export {};
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spinner } from "@clack/prompts";
|
|
4
|
+
import { exec } from "./exec.js";
|
|
5
|
+
function resolveBuildCommand(context, optionCommand) {
|
|
6
|
+
if (optionCommand)
|
|
7
|
+
return optionCommand;
|
|
8
|
+
const packageJsonPath = path.join(context, "package.json");
|
|
9
|
+
if (existsSync(packageJsonPath)) {
|
|
10
|
+
const hasYarn = existsSync(path.join(context, "yarn.lock"));
|
|
11
|
+
const hasPnpm = existsSync(path.join(context, "pnpm-lock.yaml"));
|
|
12
|
+
if (hasYarn)
|
|
13
|
+
return "yarn build";
|
|
14
|
+
if (hasPnpm)
|
|
15
|
+
return "pnpm build";
|
|
16
|
+
return "npm run build";
|
|
17
|
+
}
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
export async function buildStatic(context, options, log) {
|
|
21
|
+
const buildContext = path.resolve(context);
|
|
22
|
+
const outputDir = options.output ?? "./dist";
|
|
23
|
+
const outputPath = path.join(buildContext, outputDir);
|
|
24
|
+
const buildCommand = resolveBuildCommand(buildContext, options.command);
|
|
25
|
+
if (buildCommand) {
|
|
26
|
+
const start = performance.now();
|
|
27
|
+
log?.info({ command: buildCommand, context: buildContext }, "static build started");
|
|
28
|
+
const s = spinner();
|
|
29
|
+
s.start(`Building static site (${buildCommand})`);
|
|
30
|
+
await exec(buildCommand, buildContext, options.env, log);
|
|
31
|
+
s.stop("Build complete");
|
|
32
|
+
const durationMs = Math.round(performance.now() - start);
|
|
33
|
+
log?.info({ command: buildCommand, context: buildContext, durationMs }, "static build completed");
|
|
34
|
+
}
|
|
35
|
+
if (!existsSync(outputPath)) {
|
|
36
|
+
throw new Error(`Output directory not found: ${outputPath}`);
|
|
37
|
+
}
|
|
38
|
+
return { outputPath };
|
|
39
|
+
}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { access, readFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { intro, log, outro } from "@clack/prompts";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { DockerClient } from "./docker.js";
|
|
8
|
+
import { init } from "./init.js";
|
|
9
|
+
import { logger } from "./logger.js";
|
|
10
|
+
import { VYFT_RUNTIME } from "./symbols.js";
|
|
11
|
+
const DEPLOY_ORDER = {
|
|
12
|
+
secret: 0,
|
|
13
|
+
volume: 1,
|
|
14
|
+
service: 2,
|
|
15
|
+
site: 3,
|
|
16
|
+
};
|
|
17
|
+
const DESTROY_ORDER = {
|
|
18
|
+
site: 0,
|
|
19
|
+
service: 1,
|
|
20
|
+
volume: 2,
|
|
21
|
+
secret: 3,
|
|
22
|
+
};
|
|
23
|
+
async function findProjectName(configPath) {
|
|
24
|
+
let dir = path.dirname(configPath);
|
|
25
|
+
while (dir !== path.dirname(dir)) {
|
|
26
|
+
try {
|
|
27
|
+
const pkgPath = path.join(dir, "package.json");
|
|
28
|
+
const content = await readFile(pkgPath, "utf-8");
|
|
29
|
+
const pkg = JSON.parse(content);
|
|
30
|
+
if (pkg.name) {
|
|
31
|
+
return pkg.name;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// No package.json here, keep walking up
|
|
36
|
+
}
|
|
37
|
+
dir = path.dirname(dir);
|
|
38
|
+
}
|
|
39
|
+
throw new Error("Could not find package.json with a name field");
|
|
40
|
+
}
|
|
41
|
+
function isResource(value) {
|
|
42
|
+
return (typeof value === "object" &&
|
|
43
|
+
value !== null &&
|
|
44
|
+
"type" in value &&
|
|
45
|
+
"id" in value &&
|
|
46
|
+
typeof value.type === "string" &&
|
|
47
|
+
typeof value.id === "string");
|
|
48
|
+
}
|
|
49
|
+
function hasRuntime(value) {
|
|
50
|
+
return (typeof value === "object" &&
|
|
51
|
+
value !== null &&
|
|
52
|
+
VYFT_RUNTIME in value);
|
|
53
|
+
}
|
|
54
|
+
function createRuntime(resources, project, sessionLogger) {
|
|
55
|
+
const ref = resources.find((r) => hasRuntime(r));
|
|
56
|
+
if (ref && hasRuntime(ref)) {
|
|
57
|
+
const meta = ref[VYFT_RUNTIME];
|
|
58
|
+
switch (meta.name) {
|
|
59
|
+
case "swarm":
|
|
60
|
+
return new DockerClient(project, sessionLogger);
|
|
61
|
+
default:
|
|
62
|
+
throw new Error(`Unknown runtime: ${meta.name}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return new DockerClient(project, sessionLogger);
|
|
66
|
+
}
|
|
67
|
+
async function deploy(configFile, verbose) {
|
|
68
|
+
const sessionId = randomBytes(4).toString("hex");
|
|
69
|
+
const sessionLog = logger.child({ sessionId, command: "deploy" });
|
|
70
|
+
const start = performance.now();
|
|
71
|
+
const project = await findProjectName(configFile);
|
|
72
|
+
intro(`Deploying ${project}`);
|
|
73
|
+
const config = await import(configFile);
|
|
74
|
+
const resources = Object.values(config).filter(isResource);
|
|
75
|
+
if (resources.length === 0) {
|
|
76
|
+
log.warn("No resources found");
|
|
77
|
+
outro();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
sessionLog.info({ project, resourceCount: resources.length }, "deploy started");
|
|
81
|
+
resources.sort((a, b) => DEPLOY_ORDER[a.type] - DEPLOY_ORDER[b.type]);
|
|
82
|
+
const docker = createRuntime(resources, project, sessionLog);
|
|
83
|
+
docker.verbose = verbose;
|
|
84
|
+
await docker.ensureInfrastructure();
|
|
85
|
+
const currentResources = await docker.listManagedResources();
|
|
86
|
+
let created = 0;
|
|
87
|
+
let skipped = 0;
|
|
88
|
+
for (const resource of resources) {
|
|
89
|
+
const exists = await docker.exists(resource);
|
|
90
|
+
if (exists) {
|
|
91
|
+
log.success(resource.id);
|
|
92
|
+
skipped++;
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
await docker.create(resource);
|
|
96
|
+
created++;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const desiredIds = new Set(resources.map((r) => r.id));
|
|
100
|
+
// Keep derived secrets whose parent service is still desired
|
|
101
|
+
for (const r of currentResources) {
|
|
102
|
+
if (r.derived && r.parentService && desiredIds.has(r.parentService)) {
|
|
103
|
+
desiredIds.add(r.id);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const toRemove = currentResources.filter((r) => !desiredIds.has(r.id));
|
|
107
|
+
let removed = 0;
|
|
108
|
+
if (toRemove.length > 0) {
|
|
109
|
+
toRemove.sort((a, b) => DESTROY_ORDER[a.type] - DESTROY_ORDER[b.type]);
|
|
110
|
+
for (const { id, type } of toRemove) {
|
|
111
|
+
const resource = { id, type };
|
|
112
|
+
if (await docker.exists(resource)) {
|
|
113
|
+
await docker.remove(resource);
|
|
114
|
+
removed++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const durationMs = Math.round(performance.now() - start);
|
|
119
|
+
sessionLog.info({ project, created, removed, skipped, durationMs }, "deploy completed");
|
|
120
|
+
outro("Deploy complete");
|
|
121
|
+
}
|
|
122
|
+
async function destroy(searchDir) {
|
|
123
|
+
const sessionId = randomBytes(4).toString("hex");
|
|
124
|
+
const sessionLog = logger.child({ sessionId, command: "destroy" });
|
|
125
|
+
const start = performance.now();
|
|
126
|
+
const project = await findProjectName(path.join(searchDir, "dummy"));
|
|
127
|
+
intro(`Destroying ${project}`);
|
|
128
|
+
const docker = new DockerClient(project, sessionLog);
|
|
129
|
+
const currentResources = await docker.listManagedResources();
|
|
130
|
+
if (currentResources.length === 0) {
|
|
131
|
+
log.warn("No resources found");
|
|
132
|
+
outro();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
sessionLog.info({ project, resourceCount: currentResources.length }, "destroy started");
|
|
136
|
+
const sorted = [...currentResources].sort((a, b) => DESTROY_ORDER[a.type] - DESTROY_ORDER[b.type]);
|
|
137
|
+
let removed = 0;
|
|
138
|
+
for (const { id, type } of sorted) {
|
|
139
|
+
const resource = { id, type };
|
|
140
|
+
if (await docker.exists(resource)) {
|
|
141
|
+
await docker.remove(resource);
|
|
142
|
+
removed++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const durationMs = Math.round(performance.now() - start);
|
|
146
|
+
sessionLog.info({ project, removed, durationMs }, "destroy completed");
|
|
147
|
+
outro("Destroy complete");
|
|
148
|
+
}
|
|
149
|
+
const program = new Command();
|
|
150
|
+
program
|
|
151
|
+
.name("vyft")
|
|
152
|
+
.description("Deploy apps to Docker Swarm with TypeScript")
|
|
153
|
+
.version("0.1.0");
|
|
154
|
+
program
|
|
155
|
+
.command("init")
|
|
156
|
+
.description("Create a new project")
|
|
157
|
+
.argument("[directory]", "directory to create the project in")
|
|
158
|
+
.action(async (directory) => {
|
|
159
|
+
await init(directory);
|
|
160
|
+
});
|
|
161
|
+
program
|
|
162
|
+
.command("deploy")
|
|
163
|
+
.description("Deploy resources from a config file")
|
|
164
|
+
.argument("[config-file]", "path to config file", "vyft.config.ts")
|
|
165
|
+
.option("--verbose", "show build output", false)
|
|
166
|
+
.action(async (configFile, opts) => {
|
|
167
|
+
const absolutePath = path.resolve(process.cwd(), configFile);
|
|
168
|
+
if (configFile === "vyft.config.ts") {
|
|
169
|
+
try {
|
|
170
|
+
await access(absolutePath);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
program.error("No config file specified and vyft.config.ts not found");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
await deploy(absolutePath, opts.verbose);
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
logger.fatal({ err }, "deploy failed");
|
|
181
|
+
throw err;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
program
|
|
185
|
+
.command("destroy")
|
|
186
|
+
.description("Destroy all deployed resources")
|
|
187
|
+
.argument("[config-file]", "path to config file (used to locate project)")
|
|
188
|
+
.action(async (configFile) => {
|
|
189
|
+
const absolutePath = configFile
|
|
190
|
+
? path.resolve(process.cwd(), configFile)
|
|
191
|
+
: process.cwd();
|
|
192
|
+
try {
|
|
193
|
+
await destroy(absolutePath);
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
logger.fatal({ err }, "destroy failed");
|
|
197
|
+
throw err;
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
await program.parseAsync();
|
package/dist/docker.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Logger } from "pino";
|
|
2
|
+
import type { Resource, ResourceType } from "./resource.js";
|
|
3
|
+
import type { Runtime } from "./runtime.js";
|
|
4
|
+
export declare function parseRoute(route: string): {
|
|
5
|
+
host: string;
|
|
6
|
+
path?: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function buildCaddyRoute(project: string, resourceId: string, route: string, handler: Record<string, unknown>): Record<string, unknown>;
|
|
9
|
+
export interface ManagedResource {
|
|
10
|
+
id: string;
|
|
11
|
+
type: ResourceType;
|
|
12
|
+
route?: string;
|
|
13
|
+
derived?: boolean;
|
|
14
|
+
parentService?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class DockerClient implements Runtime {
|
|
17
|
+
private docker;
|
|
18
|
+
private project;
|
|
19
|
+
private secretValues;
|
|
20
|
+
private log;
|
|
21
|
+
verbose: boolean;
|
|
22
|
+
constructor(project: string, parentLogger?: Logger);
|
|
23
|
+
ensureInfrastructure(): Promise<void>;
|
|
24
|
+
private findProxyContainer;
|
|
25
|
+
private caddyApiRequest;
|
|
26
|
+
private seedCaddyConfig;
|
|
27
|
+
private addRoute;
|
|
28
|
+
private removeRoute;
|
|
29
|
+
listManagedResources(): Promise<ManagedResource[]>;
|
|
30
|
+
create(resource: Resource): Promise<void>;
|
|
31
|
+
exists(resource: Resource): Promise<boolean>;
|
|
32
|
+
remove(resource: Resource): Promise<void>;
|
|
33
|
+
private createVolume;
|
|
34
|
+
private volumeExists;
|
|
35
|
+
private removeVolume;
|
|
36
|
+
private createSecret;
|
|
37
|
+
private secretExists;
|
|
38
|
+
private removeSecret;
|
|
39
|
+
private storeSecretData;
|
|
40
|
+
private lookupSecretId;
|
|
41
|
+
private createDerivedSecret;
|
|
42
|
+
private resolveEnv;
|
|
43
|
+
private createService;
|
|
44
|
+
private serviceExists;
|
|
45
|
+
private removeService;
|
|
46
|
+
private createStatic;
|
|
47
|
+
private removeStatic;
|
|
48
|
+
}
|