deploy.sh 2.0.0 → 3.0.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.
Files changed (135) hide show
  1. package/.claude/settings.local.json +36 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.yml +105 -0
  3. package/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.yml +56 -0
  5. package/.github/workflows/ci.yml +15 -34
  6. package/.github/workflows/pages.yml +48 -0
  7. package/.oxfmtrc.json +7 -0
  8. package/.oxlintrc.json +11 -0
  9. package/LICENSE +183 -183
  10. package/README.md +99 -10
  11. package/app/actions/deployments.ts +82 -0
  12. package/app/actions/metrics.ts +13 -0
  13. package/app/root.tsx +60 -0
  14. package/app/routes/dashboard/detail/history.tsx +73 -0
  15. package/app/routes/dashboard/detail/layout.tsx +125 -0
  16. package/app/routes/dashboard/detail/logs.tsx +85 -0
  17. package/app/routes/dashboard/detail/overview.tsx +119 -0
  18. package/app/routes/dashboard/detail/requests.tsx +163 -0
  19. package/app/routes/dashboard/detail/resources.tsx +268 -0
  20. package/app/routes/dashboard/detail/shared.tsx +59 -0
  21. package/app/routes/dashboard/index.tsx +360 -0
  22. package/app/routes/dashboard/layout.tsx +30 -0
  23. package/app/routes/docs/architecture.tsx +155 -0
  24. package/app/routes/docs/cli.tsx +122 -0
  25. package/app/routes/docs/deploying.tsx +105 -0
  26. package/app/routes/docs/index.tsx +104 -0
  27. package/app/routes/docs/layout.tsx +58 -0
  28. package/app/routes/home.tsx +134 -0
  29. package/app/routes/root.client.tsx +46 -0
  30. package/app/routes.ts +21 -0
  31. package/app/styles.css +15 -0
  32. package/app/theme.css +134 -0
  33. package/bin/deploy.js +362 -138
  34. package/docs-site/404.html +33 -0
  35. package/docs-site/home.tsx +130 -0
  36. package/docs-site/index.html +35 -0
  37. package/docs-site/layout.tsx +57 -0
  38. package/docs-site/main.tsx +41 -0
  39. package/docs-site/shell.tsx +34 -0
  40. package/docs-site/styles.css +4 -0
  41. package/drizzle.config.js +8 -0
  42. package/examples/docker/Dockerfile +5 -5
  43. package/examples/docker/server.js +18 -0
  44. package/examples/node/package.json +3 -11
  45. package/examples/node/pnpm-lock.yaml +9 -0
  46. package/examples/node/server.js +12 -0
  47. package/examples/static/index.html +41 -15
  48. package/package.json +40 -64
  49. package/public/favicon.ico +0 -0
  50. package/react-router-vite/entry.browser.tsx +49 -0
  51. package/react-router-vite/entry.rsc.single.tsx +7 -0
  52. package/react-router-vite/entry.rsc.tsx +36 -0
  53. package/react-router-vite/entry.ssr.tsx +29 -0
  54. package/react-router-vite/plugin.ts +114 -0
  55. package/react-router-vite/types.d.ts +11 -0
  56. package/react-router.config.ts +5 -0
  57. package/server/api.test.ts +344 -0
  58. package/server/api.ts +445 -0
  59. package/server/docker.ts +268 -0
  60. package/server/index.ts +17 -0
  61. package/server/metrics-collector.ts +29 -0
  62. package/server/schema.ts +56 -0
  63. package/server/store.test.ts +278 -0
  64. package/server/store.ts +398 -0
  65. package/tsconfig.json +21 -0
  66. package/vite.config.ts +45 -0
  67. package/vite.docs.config.ts +31 -0
  68. package/.eslintignore +0 -6
  69. package/.eslintrc +0 -12
  70. package/.husky/pre-commit +0 -5
  71. package/.prettierrc +0 -0
  72. package/.release-it.json +0 -5
  73. package/CHANGELOG.md +0 -56
  74. package/__tests__/fixtures/unknown/.gitkeep +0 -0
  75. package/__tests__/lib/classifier.test.js +0 -49
  76. package/__tests__/lib/helpers/util.test.js +0 -57
  77. package/bin/deploy-delete.js +0 -14
  78. package/bin/deploy-deploy.js +0 -36
  79. package/bin/deploy-list.js +0 -40
  80. package/bin/deploy-login.js +0 -43
  81. package/bin/deploy-logout.js +0 -16
  82. package/bin/deploy-logs.js +0 -26
  83. package/bin/deploy-open.js +0 -26
  84. package/bin/deploy-register.js +0 -45
  85. package/bin/deploy-server.js +0 -11
  86. package/bin/deploy-whoami.js +0 -14
  87. package/examples/docker/index.js +0 -12
  88. package/examples/node/index.js +0 -8
  89. package/examples/static/main.css +0 -9
  90. package/examples/static/out.gifcd +0 -0
  91. package/generate-docs.js +0 -55
  92. package/index.js +0 -69
  93. package/jsdoc.json +0 -27
  94. package/lib/classifier.js +0 -63
  95. package/lib/deploy.js +0 -70
  96. package/lib/helpers/cli.js +0 -262
  97. package/lib/helpers/util.js +0 -140
  98. package/lib/models/deployment.js +0 -474
  99. package/lib/models/request.js +0 -101
  100. package/lib/models/user.js +0 -147
  101. package/lib/server.js +0 -211
  102. package/lib/static/not-found.html +0 -30
  103. package/lib/static/page-could-not-load.html +0 -30
  104. package/lib/static/static-server.js +0 -70
  105. package/website/README.md +0 -41
  106. package/website/babel.config.js +0 -3
  107. package/website/docs/api/_category_.yml +0 -1
  108. package/website/docs/api/lib/classifier.js.md +0 -11
  109. package/website/docs/api/lib/deploy.js.md +0 -13
  110. package/website/docs/api/lib/helpers/cli.js.md +0 -193
  111. package/website/docs/api/lib/helpers/util.js.md +0 -65
  112. package/website/docs/api/lib/models/deployment.js.md +0 -171
  113. package/website/docs/api/lib/models/request.js.md +0 -67
  114. package/website/docs/api/lib/models/user.js.md +0 -92
  115. package/website/docs/api/lib/server.js.md +0 -0
  116. package/website/docs/api/lib/static/static-server.js.md +0 -0
  117. package/website/docs/intro.md +0 -57
  118. package/website/docusaurus.config.js +0 -82
  119. package/website/package-lock.json +0 -25218
  120. package/website/package.json +0 -39
  121. package/website/sidebars.js +0 -31
  122. package/website/src/components/HomepageFeatures/index.js +0 -79
  123. package/website/src/components/HomepageFeatures/styles.module.css +0 -11
  124. package/website/src/css/custom.css +0 -39
  125. package/website/src/pages/index.js +0 -57
  126. package/website/src/pages/index.module.css +0 -23
  127. package/website/static/.nojekyll +0 -0
  128. package/website/static/example.gif +0 -0
  129. package/website/static/example.mov +0 -0
  130. package/website/static/img/favicon.ico +0 -0
  131. package/website/static/img/intro/deploy.png +0 -0
  132. package/website/static/img/intro/logs.png +0 -0
  133. package/website/static/img/logo.png +0 -0
  134. package/website/static/img/logo.pxm +0 -0
  135. package/website/static/img/logo@2x.png +0 -0
@@ -0,0 +1,398 @@
1
+ import { mkdirSync, existsSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { randomBytes, createHash } from 'node:crypto';
4
+ import Database from 'better-sqlite3';
5
+ import { drizzle } from 'drizzle-orm/better-sqlite3';
6
+ import { eq } from 'drizzle-orm';
7
+ import { users, deployments, history, requestLogs, resourceMetrics } from './schema.ts';
8
+ import type { RawContainerStats } from './docker.ts';
9
+
10
+ const DATA_DIR = resolve(process.cwd(), '.deploy-data');
11
+ const DB_FILE = resolve(DATA_DIR, 'deploy.db');
12
+ const UPLOADS_DIR = resolve(DATA_DIR, 'uploads');
13
+
14
+ let _sqlite: InstanceType<typeof Database> | null = null;
15
+ let _db: ReturnType<typeof drizzle> | null = null;
16
+
17
+ function getDb() {
18
+ if (_db) return _db;
19
+
20
+ if (!existsSync(DATA_DIR)) mkdirSync(DATA_DIR, { recursive: true });
21
+ if (!existsSync(UPLOADS_DIR)) mkdirSync(UPLOADS_DIR, { recursive: true });
22
+
23
+ _sqlite = new Database(DB_FILE);
24
+ _sqlite.pragma('journal_mode = WAL');
25
+
26
+ _sqlite.exec(`
27
+ CREATE TABLE IF NOT EXISTS users (
28
+ username TEXT PRIMARY KEY,
29
+ password TEXT NOT NULL,
30
+ token TEXT,
31
+ created_at TEXT NOT NULL
32
+ );
33
+ CREATE TABLE IF NOT EXISTS deployments (
34
+ name TEXT PRIMARY KEY,
35
+ type TEXT,
36
+ username TEXT NOT NULL,
37
+ port INTEGER,
38
+ container_id TEXT,
39
+ container_name TEXT,
40
+ directory TEXT,
41
+ created_at TEXT,
42
+ updated_at TEXT
43
+ );
44
+ CREATE TABLE IF NOT EXISTS history (
45
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
46
+ deployment_name TEXT NOT NULL,
47
+ action TEXT NOT NULL,
48
+ username TEXT,
49
+ type TEXT,
50
+ port INTEGER,
51
+ container_id TEXT,
52
+ timestamp TEXT NOT NULL
53
+ );
54
+ CREATE TABLE IF NOT EXISTS request_logs (
55
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
56
+ deployment_name TEXT NOT NULL,
57
+ method TEXT NOT NULL,
58
+ path TEXT NOT NULL,
59
+ status INTEGER NOT NULL,
60
+ duration INTEGER NOT NULL,
61
+ timestamp INTEGER NOT NULL
62
+ );
63
+ CREATE INDEX IF NOT EXISTS idx_request_logs_deployment
64
+ ON request_logs(deployment_name);
65
+ CREATE INDEX IF NOT EXISTS idx_request_logs_timestamp
66
+ ON request_logs(deployment_name, timestamp);
67
+ CREATE INDEX IF NOT EXISTS idx_history_deployment
68
+ ON history(deployment_name);
69
+ CREATE INDEX IF NOT EXISTS idx_deployments_username
70
+ ON deployments(username);
71
+ CREATE TABLE IF NOT EXISTS resource_metrics (
72
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
73
+ deployment_name TEXT NOT NULL,
74
+ cpu_percent REAL NOT NULL,
75
+ mem_usage_bytes INTEGER NOT NULL,
76
+ mem_limit_bytes INTEGER NOT NULL,
77
+ mem_percent REAL NOT NULL,
78
+ net_rx_bytes INTEGER NOT NULL,
79
+ net_tx_bytes INTEGER NOT NULL,
80
+ block_read_bytes INTEGER NOT NULL,
81
+ block_write_bytes INTEGER NOT NULL,
82
+ pids INTEGER NOT NULL,
83
+ timestamp INTEGER NOT NULL
84
+ );
85
+ CREATE INDEX IF NOT EXISTS idx_resource_metrics_deployment
86
+ ON resource_metrics(deployment_name);
87
+ CREATE INDEX IF NOT EXISTS idx_resource_metrics_timestamp
88
+ ON resource_metrics(deployment_name, timestamp);
89
+ `);
90
+
91
+ _db = drizzle(_sqlite);
92
+ return _db;
93
+ }
94
+
95
+ export function _resetDb() {
96
+ if (_sqlite) _sqlite.close();
97
+ _sqlite = null;
98
+ _db = null;
99
+ }
100
+
101
+ export function getUploadsDir() {
102
+ return UPLOADS_DIR;
103
+ }
104
+
105
+ function hashPassword(password: string) {
106
+ return createHash('sha256').update(password).digest('hex');
107
+ }
108
+
109
+ function generateToken() {
110
+ return randomBytes(32).toString('hex');
111
+ }
112
+
113
+ // ── Users ───────────────────────────────────────────────────────────────────
114
+
115
+ export function registerUser(username: string, password: string) {
116
+ const db = getDb();
117
+ const existing = db.select().from(users).where(eq(users.username, username)).get();
118
+ if (existing) {
119
+ return { error: 'User already exists' as const, status: 409 as const };
120
+ }
121
+ const token = generateToken();
122
+ db.insert(users)
123
+ .values({
124
+ username,
125
+ password: hashPassword(password),
126
+ token,
127
+ createdAt: new Date().toISOString(),
128
+ })
129
+ .run();
130
+ return { token };
131
+ }
132
+
133
+ export function loginUser(username: string, password: string) {
134
+ const db = getDb();
135
+ const user = db.select().from(users).where(eq(users.username, username)).get();
136
+ if (!user || user.password !== hashPassword(password)) {
137
+ return { error: 'Invalid credentials' as const, status: 401 as const };
138
+ }
139
+ const token = generateToken();
140
+ db.update(users).set({ token }).where(eq(users.username, username)).run();
141
+ return { token };
142
+ }
143
+
144
+ export function authenticate(
145
+ username: string | null | undefined,
146
+ token: string | null | undefined,
147
+ ) {
148
+ if (!username || !token) return false;
149
+ const db = getDb();
150
+ const user = db.select().from(users).where(eq(users.username, username)).get();
151
+ return user != null && user.token === token;
152
+ }
153
+
154
+ export function logoutUser(username: string) {
155
+ const db = getDb();
156
+ db.update(users).set({ token: null }).where(eq(users.username, username)).run();
157
+ }
158
+
159
+ export function getUser(username: string) {
160
+ const db = getDb();
161
+ const user = db
162
+ .select({ username: users.username, createdAt: users.createdAt })
163
+ .from(users)
164
+ .where(eq(users.username, username))
165
+ .get();
166
+ return user || null;
167
+ }
168
+
169
+ // ── Deployments ─────────────────────────────────────────────────────────────
170
+
171
+ interface DeploymentInput {
172
+ name: string;
173
+ type?: string;
174
+ username: string;
175
+ port?: number;
176
+ containerId?: string;
177
+ containerName?: string;
178
+ directory?: string;
179
+ createdAt?: string;
180
+ }
181
+
182
+ export function saveDeployment(deployment: DeploymentInput) {
183
+ const db = getDb();
184
+ const now = new Date().toISOString();
185
+ db.insert(deployments)
186
+ .values({
187
+ name: deployment.name,
188
+ type: deployment.type || null,
189
+ username: deployment.username,
190
+ port: deployment.port || null,
191
+ containerId: deployment.containerId || null,
192
+ containerName: deployment.containerName || null,
193
+ directory: deployment.directory || null,
194
+ createdAt: deployment.createdAt || now,
195
+ updatedAt: now,
196
+ })
197
+ .onConflictDoUpdate({
198
+ target: deployments.name,
199
+ set: {
200
+ type: deployment.type || null,
201
+ username: deployment.username,
202
+ port: deployment.port || null,
203
+ containerId: deployment.containerId || null,
204
+ containerName: deployment.containerName || null,
205
+ directory: deployment.directory || null,
206
+ updatedAt: now,
207
+ },
208
+ })
209
+ .run();
210
+ }
211
+
212
+ export function getDeployment(name: string) {
213
+ const db = getDb();
214
+ return db.select().from(deployments).where(eq(deployments.name, name)).get() || null;
215
+ }
216
+
217
+ export function getDeployments(username: string) {
218
+ const db = getDb();
219
+ return db.select().from(deployments).where(eq(deployments.username, username)).all();
220
+ }
221
+
222
+ export function deleteDeployment(name: string) {
223
+ const db = getDb();
224
+ db.delete(deployments).where(eq(deployments.name, name)).run();
225
+ }
226
+
227
+ export function getAllDeployments() {
228
+ const db = getDb();
229
+ return db.select().from(deployments).all();
230
+ }
231
+
232
+ // ── Deployment history ───────────────────────────────────────────────────────
233
+
234
+ interface DeployEvent {
235
+ action: string;
236
+ username?: string;
237
+ type?: string;
238
+ port?: number;
239
+ containerId?: string;
240
+ }
241
+
242
+ export function addDeployEvent(name: string, event: DeployEvent) {
243
+ const db = getDb();
244
+ db.insert(history)
245
+ .values({
246
+ deploymentName: name,
247
+ action: event.action,
248
+ username: event.username || null,
249
+ type: event.type || null,
250
+ port: event.port || null,
251
+ containerId: event.containerId || null,
252
+ timestamp: new Date().toISOString(),
253
+ })
254
+ .run();
255
+ }
256
+
257
+ export function getDeployHistory(name: string) {
258
+ const db = getDb();
259
+ return db.select().from(history).where(eq(history.deploymentName, name)).all();
260
+ }
261
+
262
+ // ── Request logs ────────────────────────────────────────────────────────────
263
+
264
+ const MAX_LOGS_PER_APP = 500;
265
+
266
+ interface RequestEntry {
267
+ method: string;
268
+ path: string;
269
+ status: number;
270
+ duration: number;
271
+ timestamp: number;
272
+ }
273
+
274
+ export function logRequest(name: string, entry: RequestEntry) {
275
+ const db = getDb();
276
+ db.insert(requestLogs)
277
+ .values({
278
+ deploymentName: name,
279
+ method: entry.method,
280
+ path: entry.path,
281
+ status: entry.status,
282
+ duration: entry.duration,
283
+ timestamp: entry.timestamp,
284
+ })
285
+ .run();
286
+
287
+ // Enforce ring buffer: keep only the most recent MAX_LOGS_PER_APP entries
288
+ _sqlite!
289
+ .prepare(
290
+ `
291
+ DELETE FROM request_logs
292
+ WHERE deployment_name = ? AND id NOT IN (
293
+ SELECT id FROM request_logs
294
+ WHERE deployment_name = ?
295
+ ORDER BY id DESC
296
+ LIMIT ?
297
+ )
298
+ `,
299
+ )
300
+ .run(name, name, MAX_LOGS_PER_APP);
301
+ }
302
+
303
+ export function getRequestLogs(name: string) {
304
+ const db = getDb();
305
+ return db
306
+ .select({
307
+ method: requestLogs.method,
308
+ path: requestLogs.path,
309
+ status: requestLogs.status,
310
+ duration: requestLogs.duration,
311
+ timestamp: requestLogs.timestamp,
312
+ })
313
+ .from(requestLogs)
314
+ .where(eq(requestLogs.deploymentName, name))
315
+ .all();
316
+ }
317
+
318
+ export function getRequestSummary(name: string) {
319
+ const logs = getRequestLogs(name);
320
+ if (logs.length === 0)
321
+ return { total: 0, statusCodes: {} as Record<string, number>, avgDuration: 0, recentRpm: 0 };
322
+
323
+ const statusCodes: Record<string, number> = {};
324
+ let totalDuration = 0;
325
+ for (const log of logs) {
326
+ const group = `${Math.floor(log.status / 100)}xx`;
327
+ statusCodes[group] = (statusCodes[group] || 0) + 1;
328
+ totalDuration += log.duration;
329
+ }
330
+
331
+ const oneMinAgo = Date.now() - 60_000;
332
+ const recentCount = logs.filter((l) => l.timestamp > oneMinAgo).length;
333
+
334
+ return {
335
+ total: logs.length,
336
+ statusCodes,
337
+ avgDuration: Math.round(totalDuration / logs.length),
338
+ recentRpm: recentCount,
339
+ };
340
+ }
341
+
342
+ // ── Resource metrics ───────────────────────────────────────────────────────
343
+
344
+ const MAX_METRICS_PER_APP = 2880; // ~24h at 30s intervals
345
+
346
+ export function logMetrics(name: string, metrics: RawContainerStats) {
347
+ const db = getDb();
348
+ db.insert(resourceMetrics)
349
+ .values({
350
+ deploymentName: name,
351
+ cpuPercent: metrics.cpuPercent,
352
+ memUsageBytes: metrics.memUsageBytes,
353
+ memLimitBytes: metrics.memLimitBytes,
354
+ memPercent: metrics.memPercent,
355
+ netRxBytes: metrics.netRxBytes,
356
+ netTxBytes: metrics.netTxBytes,
357
+ blockReadBytes: metrics.blockReadBytes,
358
+ blockWriteBytes: metrics.blockWriteBytes,
359
+ pids: metrics.pids,
360
+ timestamp: metrics.timestamp,
361
+ })
362
+ .run();
363
+
364
+ _sqlite!
365
+ .prepare(
366
+ `
367
+ DELETE FROM resource_metrics
368
+ WHERE deployment_name = ? AND id NOT IN (
369
+ SELECT id FROM resource_metrics
370
+ WHERE deployment_name = ?
371
+ ORDER BY id DESC
372
+ LIMIT ?
373
+ )
374
+ `,
375
+ )
376
+ .run(name, name, MAX_METRICS_PER_APP);
377
+ }
378
+
379
+ export function getMetricsHistory(name: string, since: number) {
380
+ const db = getDb();
381
+ return db
382
+ .select({
383
+ cpuPercent: resourceMetrics.cpuPercent,
384
+ memUsageBytes: resourceMetrics.memUsageBytes,
385
+ memLimitBytes: resourceMetrics.memLimitBytes,
386
+ memPercent: resourceMetrics.memPercent,
387
+ netRxBytes: resourceMetrics.netRxBytes,
388
+ netTxBytes: resourceMetrics.netTxBytes,
389
+ blockReadBytes: resourceMetrics.blockReadBytes,
390
+ blockWriteBytes: resourceMetrics.blockWriteBytes,
391
+ pids: resourceMetrics.pids,
392
+ timestamp: resourceMetrics.timestamp,
393
+ })
394
+ .from(resourceMetrics)
395
+ .where(eq(resourceMetrics.deploymentName, name))
396
+ .all()
397
+ .filter((r) => r.timestamp >= since);
398
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowImportingTsExtensions": true,
4
+ "strict": true,
5
+ "noUnusedLocals": true,
6
+ "noUnusedParameters": true,
7
+ "skipLibCheck": true,
8
+ "verbatimModuleSyntax": true,
9
+ "noEmit": true,
10
+ "moduleResolution": "Bundler",
11
+ "module": "ESNext",
12
+ "target": "ESNext",
13
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
14
+ "jsx": "react-jsx",
15
+ "baseUrl": ".",
16
+ "paths": {
17
+ "@/*": ["./app/*"]
18
+ }
19
+ },
20
+ "include": ["**/*.ts", "**/*.tsx", "app/types/**/*.d.ts"]
21
+ }
package/vite.config.ts ADDED
@@ -0,0 +1,45 @@
1
+ import tailwindcss from '@tailwindcss/vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import rsc from '@vitejs/plugin-rsc';
4
+ import { defineConfig } from 'vite';
5
+ import { resolve } from 'path';
6
+ import { reactRouter } from './react-router-vite/plugin.js';
7
+
8
+ export default defineConfig({
9
+ clearScreen: false,
10
+ build: {
11
+ minify: false,
12
+ },
13
+ plugins: [
14
+ // import("vite-plugin-inspect").then(m => m.default()),
15
+ tailwindcss(),
16
+ react(),
17
+ reactRouter(),
18
+ rsc({
19
+ entries: {
20
+ client: './react-router-vite/entry.browser.tsx',
21
+ ssr: './react-router-vite/entry.ssr.tsx',
22
+ rsc: './react-router-vite/entry.rsc.single.tsx',
23
+ },
24
+ }),
25
+ ],
26
+ resolve: {
27
+ alias: {
28
+ '@': resolve(__dirname, './app'),
29
+ },
30
+ },
31
+ optimizeDeps: {
32
+ include: ['react-router', 'react-router/internal/react-server-client'],
33
+ exclude: ['better-sqlite3'],
34
+ },
35
+ server: {
36
+ // Allow connections from iOS simulator and local network devices
37
+ host: true,
38
+ // Serve static files from data directory
39
+ fs: {
40
+ allow: ['..', './data'],
41
+ },
42
+ },
43
+ // Public directory for static assets
44
+ publicDir: 'public',
45
+ }) as any;
@@ -0,0 +1,31 @@
1
+ import tailwindcss from '@tailwindcss/vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import { defineConfig, type Plugin } from 'vite';
4
+ import { resolve } from 'path';
5
+ import { copyFileSync } from 'fs';
6
+
7
+ function copy404(): Plugin {
8
+ return {
9
+ name: 'copy-404',
10
+ writeBundle(options) {
11
+ copyFileSync(resolve(__dirname, 'docs-site', '404.html'), resolve(options.dir!, '404.html'));
12
+ },
13
+ };
14
+ }
15
+
16
+ export default defineConfig({
17
+ root: 'docs-site',
18
+ base: '/deploy.sh/',
19
+ build: {
20
+ outDir: resolve(__dirname, 'dist-docs'),
21
+ emptyOutDir: true,
22
+ minify: false,
23
+ },
24
+ plugins: [tailwindcss(), react(), copy404()],
25
+ resolve: {
26
+ alias: {
27
+ '@': resolve(__dirname, './app'),
28
+ },
29
+ },
30
+ publicDir: resolve(__dirname, 'public'),
31
+ });
package/.eslintignore DELETED
@@ -1,6 +0,0 @@
1
- node_modules
2
- docs
3
- fixtures
4
- tmp
5
- coverage
6
- website
package/.eslintrc DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "extends": ["eslint:recommended", "plugin:jest/recommended"],
3
- "plugins": ["jest"],
4
- "env": {
5
- "es6": true,
6
- "node": true
7
- },
8
- "parserOptions": {
9
- "ecmaVersion": "latest",
10
- "sourceType": "module"
11
- }
12
- }
package/.husky/pre-commit DELETED
@@ -1,5 +0,0 @@
1
- #!/bin/sh
2
- . "$(dirname "$0")/_/husky.sh"
3
-
4
- ./node_modules/.bin/eslint .
5
- ./node_modules/.bin/pretty-quick --staged
package/.prettierrc DELETED
File without changes
package/.release-it.json DELETED
@@ -1,5 +0,0 @@
1
- {
2
- "github": {
3
- "release": true
4
- }
5
- }
package/CHANGELOG.md DELETED
@@ -1,56 +0,0 @@
1
- # 1.0.0 (01/27/2018)
2
-
3
- - sub directories would cause deploy to fail, now recursively find the strings and add them manually
4
- - delete should delete the current working directories deployment if not specified
5
- - Deployment.del removes the instance metadata from the database using the correct query params
6
- - refactors CLI to be a class
7
- - moves from easy-table to turtler
8
- - fixes login and logout functionality was mixed on cli
9
- - by default the open command will open the current directory if it is deployed
10
- - by default the log command will open the current directory if it is deployed
11
- - logs no longer have a `-` preceding each line
12
- - logs trim white space instead of adding an empty line
13
- - delete API actually works now, instead of continuously hanging
14
- - removes; mkdirp, easy-table, async
15
- - adds tryitout for docs page generation
16
- - config is now stored in `${homedir}/.deployrc`
17
- - getCredentials and cacheCredentials are no longer blocking calls, they will happen async
18
- - all error responses from the server will contain an error object
19
- - not-found (application not deployed) and page-could-not-load (proxy errors) pages are now moved into a static directory
20
- - main landing page is rendered with tryitout
21
-
22
- # 0.2.1 (08/15/2017)
23
-
24
- - adds the ability to delete deployment and its assets
25
- - be able to get container status by querying the container on the get call (add to decorator function) is now visible when running (deploy ls) as a status column
26
-
27
- # 0.2.0 (08/14/2017)
28
-
29
- - deployment model now contains amount of requests
30
- - stops overloading Deployment.get and breaks out functionality into Deployment.get and Deployment.getAll
31
- - the request model now captures the http verb associated with the request
32
- - now captures statusCode for responses in the request model
33
- - startup and shutdown is now coordinated and less prone to breaking
34
-
35
- # 0.1.1 (08/13/2017)
36
-
37
- - now is packaged as a universal binary
38
- - fixes failure when no logs are retrieved from server
39
-
40
- # 0.1.0 (08/13/2017)
41
-
42
- - adds api and cli action to be able retrieve logs
43
- - deals with cleaning up old images
44
- - deletes image and container when application is being redeployed
45
- - further consolidates deployment logic into the deployment model
46
- - starts up containers from cold start
47
- - shuts down containers when process is closing
48
- - adds caching to static-server
49
- - abstract models into their own files and their own collections
50
- - fixes the middleware request logger
51
- - fixes CLI responses
52
- - adds whoami functionality that will show the current logged in user (`deploy whoami`)
53
-
54
- # 0.0.1 (08/08/2017)
55
-
56
- - basic functionality working
File without changes
@@ -1,49 +0,0 @@
1
- const path = require("path");
2
-
3
- const classifier = require("../../lib/classifier");
4
-
5
- describe("@lib/classifier", () => {
6
- const baseDirectory = path.resolve(__dirname, "..", "..", "examples");
7
-
8
- test("should be able to classify static site", async () => {
9
- const directory = path.resolve(baseDirectory, "static");
10
- const output = await classifier(directory);
11
-
12
- expect(output).toEqual({
13
- type: "static",
14
- build: `\n FROM mhart/alpine-node:base-8\n WORKDIR ${directory}\n ADD . .\n\n CMD ["node", "index.js"]\n `,
15
- });
16
- });
17
-
18
- test("should be able to classify node site", async () => {
19
- const directory = path.resolve(baseDirectory, "node");
20
- const output = await classifier(directory);
21
-
22
- expect(output).toEqual({
23
- type: "node",
24
- build: `\n FROM mhart/alpine-node:8\n WORKDIR ${directory}\n ADD . .\n\n RUN npm install\n\n CMD ["npm", "start"]\n `,
25
- });
26
- });
27
-
28
- test("should be able to classify docker site", async () => {
29
- const directory = path.resolve(baseDirectory, "docker");
30
- const output = await classifier(directory);
31
-
32
- expect(output).toEqual({
33
- type: "docker",
34
- build: `FROM mhart/alpine-node:8\nWORKDIR ${directory}\nADD . .\n\nCMD ["node", "index.js"]\n`,
35
- });
36
- });
37
-
38
- test("should be able to classify unknown deploy target", async () => {
39
- const directory = path.resolve(
40
- path.resolve(__dirname, "..", "fixtures"),
41
- "unknown"
42
- );
43
- const output = await classifier(directory);
44
-
45
- expect(output).toEqual({
46
- type: "unknown",
47
- });
48
- });
49
- });
@@ -1,57 +0,0 @@
1
- const os = require("os");
2
- const fs = require("fs");
3
-
4
- const {
5
- getPort,
6
- hash,
7
- contains,
8
- mk,
9
- rm,
10
- } = require("../../../lib/helpers/util");
11
-
12
- describe("@lib/util", () => {
13
- test("@getPort: should be able return a valid port", async () => {
14
- let port = await getPort();
15
- let port1 = await getPort();
16
-
17
- expect(Number.isInteger(port)).toBeTruthy();
18
- expect(Number.isInteger(port1)).toBeTruthy();
19
- expect(port !== port1).toBeTruthy();
20
- });
21
-
22
- test("@hash: should return a proper lowercase hash", () => {
23
- const ret = hash(6);
24
- expect(ret.length).toBeTruthy();
25
- expect(ret.toLowerCase()).toBeTruthy();
26
- });
27
-
28
- test("@contains: should be able to return a proper conditional for truthy and false cases", () => {
29
- expect(
30
- contains(
31
- ["index.html", "main.css"],
32
- ["index.html", "!Dockerfile", "!package.json"]
33
- )
34
- ).toBeTruthy();
35
- expect(
36
- contains(
37
- ["index.html", "main.css", "Dockerfile"],
38
- ["index.html", "!Dockerfile", "!package.json"]
39
- )
40
- ).toBeFalsy();
41
- expect(
42
- contains([], ["index.html", "!Dockerfile", "!package.json"])
43
- ).toBeFalsy();
44
- });
45
-
46
- test("@mk / @rm: should be able to make recursive directory", async () => {
47
- let directory = `${os.tmpdir()}/hello/world/this/is/a/nested/directory`;
48
-
49
- await mk(directory);
50
-
51
- expect(fs.existsSync(directory)).toBeTruthy();
52
-
53
- await rm(directory);
54
-
55
- expect(!fs.existsSync(directory)).toBeTruthy();
56
- });
57
- });