@yadurajfleetos/cli 0.17.1 → 0.17.2
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/dist/args.js +1 -1
- package/dist/commands/services.js +19 -10
- package/dist/commands/up.js +13 -21
- package/package.json +1 -1
package/dist/args.js
CHANGED
|
@@ -43,7 +43,7 @@ export function parseArgs(argv) {
|
|
|
43
43
|
export const KNOWN_FLAGS = new Set([
|
|
44
44
|
// global
|
|
45
45
|
'fleet', 'api', 'json', 'yes', 'y', 'help', 'h', 'version', 'v', 'no-wait',
|
|
46
|
-
'plan', 'dry-run', 'force',
|
|
46
|
+
'plan', 'dry-run', 'force', 'dir', 'file', 'manifest',
|
|
47
47
|
// per command
|
|
48
48
|
'ai', 'all', 'apply', 'channel', 'deploy', 'email', 'events', 'f', 'follow', 'limit',
|
|
49
49
|
'name', 'node', 'only', 'out', 'password', 'secret', 'service', 'sha',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFile, writeFile, access } from 'node:fs/promises';
|
|
2
|
-
import { join } from 'node:path';
|
|
2
|
+
import { join, dirname } from 'node:path';
|
|
3
3
|
import { request, requireFleet, CliError, EXIT } from '../api.js';
|
|
4
4
|
import { c, table, statusColour, keyValues, relativeTime, mb } from '../render.js';
|
|
5
5
|
import { task, glyph } from '../ui.js';
|
|
@@ -213,10 +213,15 @@ async function waitUntilRunning(fleetId, name, timeoutMs = 180_000) {
|
|
|
213
213
|
* repository is legitimate for a prebuilt `image:` service, and should not
|
|
214
214
|
* become an error about a file the operator never needed.
|
|
215
215
|
*/
|
|
216
|
-
async function buildContextFor(serviceName) {
|
|
216
|
+
async function buildContextFor(serviceName, manifestPath, baseDir) {
|
|
217
217
|
try {
|
|
218
|
-
const
|
|
219
|
-
|
|
218
|
+
const file = manifestPath ?? (baseDir ? join(baseDir, 'fleet.yaml') : 'fleet.yaml');
|
|
219
|
+
const source = await readFile(file, 'utf8');
|
|
220
|
+
const buildRel = planFromManifest(source).find((s) => s.name === serviceName)?.build;
|
|
221
|
+
if (!buildRel)
|
|
222
|
+
return undefined;
|
|
223
|
+
const dir = baseDir ?? (manifestPath ? dirname(manifestPath) : process.cwd());
|
|
224
|
+
return join(dir, buildRel);
|
|
220
225
|
}
|
|
221
226
|
catch {
|
|
222
227
|
return undefined;
|
|
@@ -227,7 +232,7 @@ export const deployCommand = {
|
|
|
227
232
|
const fleetId = await requireFleet(typeof flags.fleet === 'string' ? flags.fleet : undefined);
|
|
228
233
|
const [name] = args;
|
|
229
234
|
if (!name)
|
|
230
|
-
throw new CliError('usage: fleet deploy <service> [--sha <git-sha>] [--no-wait]', EXIT.usage);
|
|
235
|
+
throw new CliError('usage: fleet deploy <service> [--sha <git-sha>] [--no-wait] [--dir <path>]', EXIT.usage);
|
|
231
236
|
const service = await findService(fleetId, name);
|
|
232
237
|
const gitSha = typeof flags.sha === 'string' ? flags.sha : undefined;
|
|
233
238
|
const plan = await task('checking deployment plan', async () => deployPlan(fleetId, service));
|
|
@@ -252,12 +257,16 @@ export const deployCommand = {
|
|
|
252
257
|
}
|
|
253
258
|
// A service that builds from source needs its directory sent, or the
|
|
254
259
|
// control plane has nothing to build and says the context does not exist.
|
|
255
|
-
// Read from the manifest here rather than from the service row, because
|
|
256
|
-
// the build path is relative to the file the operator is standing in.
|
|
257
260
|
let contextId;
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
+
const baseDir = typeof flags.dir === 'string' ? flags.dir : undefined;
|
|
262
|
+
const manifestFile = typeof flags.file === 'string'
|
|
263
|
+
? flags.file
|
|
264
|
+
: typeof flags.manifest === 'string'
|
|
265
|
+
? flags.manifest
|
|
266
|
+
: undefined;
|
|
267
|
+
const buildPath = await buildContextFor(service.name, manifestFile, baseDir);
|
|
268
|
+
if (buildPath) {
|
|
269
|
+
const uploaded = await task(`packaging ${c.bold(service.name)}`, async () => uploadContext(service.id, buildPath), { done: (r) => `uploaded ${humanBytes(r.bytes)} of build context` });
|
|
261
270
|
contextId = uploaded.contextId;
|
|
262
271
|
}
|
|
263
272
|
const body = await withLadder(DEPLOY_STEPS, async (ladder) => {
|
package/dist/commands/up.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* the operator from the loop between them.
|
|
9
9
|
*/
|
|
10
10
|
import { readFile, writeFile, access } from 'node:fs/promises';
|
|
11
|
-
import { join } from 'node:path';
|
|
11
|
+
import { join, dirname } from 'node:path';
|
|
12
12
|
import { request, requireFleet, CliError, EXIT } from '../api.js';
|
|
13
13
|
import { c } from '../render.js';
|
|
14
14
|
import { task, glyph } from '../ui.js';
|
|
@@ -20,7 +20,12 @@ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
20
20
|
export const upCommand = {
|
|
21
21
|
async run(args, flags) {
|
|
22
22
|
const fleetId = await requireFleet(typeof flags.fleet === 'string' ? flags.fleet : undefined);
|
|
23
|
-
const
|
|
23
|
+
const rootDir = typeof flags.dir === 'string' ? flags.dir : process.cwd();
|
|
24
|
+
const manifestPath = typeof flags.file === 'string'
|
|
25
|
+
? flags.file
|
|
26
|
+
: typeof flags.manifest === 'string'
|
|
27
|
+
? flags.manifest
|
|
28
|
+
: join(rootDir, 'fleet.yaml');
|
|
24
29
|
// ── Step 1: scaffold if needed ────────────────────────────────────
|
|
25
30
|
let needsApply = false;
|
|
26
31
|
try {
|
|
@@ -29,14 +34,14 @@ export const upCommand = {
|
|
|
29
34
|
catch {
|
|
30
35
|
// No fleet.yaml — run the smart init inline.
|
|
31
36
|
const { detect, manifestTemplate } = await import('../detect.js');
|
|
32
|
-
const d = await task('detecting project framework', async () => detect());
|
|
37
|
+
const d = await task('detecting project framework', async () => detect(rootDir));
|
|
33
38
|
const name = (typeof flags.name === 'string' ? flags.name : '') ||
|
|
34
39
|
args[0] ||
|
|
35
|
-
|
|
40
|
+
rootDir.split('/').pop()?.toLowerCase().replace(/[^a-z0-9-]+/g, '-') ||
|
|
36
41
|
'app';
|
|
37
42
|
// Write Dockerfile if generated
|
|
38
43
|
if (d.dockerfile) {
|
|
39
|
-
await writeFile(join(
|
|
44
|
+
await writeFile(join(rootDir, 'Dockerfile'), d.dockerfile);
|
|
40
45
|
console.log(`${glyph.ok} ${c.green('created')} Dockerfile ${c.dim(`(${d.label}, port ${d.port})`)}`);
|
|
41
46
|
}
|
|
42
47
|
// Write manifest
|
|
@@ -47,7 +52,7 @@ export const upCommand = {
|
|
|
47
52
|
// ── Step 2: read and apply the manifest ───────────────────────────
|
|
48
53
|
const manifest = await readFile(manifestPath, 'utf8');
|
|
49
54
|
const applyResult = await task(`applying ${manifestPath}`, async () => (await request('POST', `/fleets/${fleetId}/services`, {
|
|
50
|
-
body: { manifest, project: projectNameFor(
|
|
55
|
+
body: { manifest, project: projectNameFor(rootDir) },
|
|
51
56
|
})).body, {
|
|
52
57
|
done: (b) => b.created.length || b.updated.length
|
|
53
58
|
? `applied ${b.created.length + b.updated.length} service(s) to project ${b.project}`
|
|
@@ -76,13 +81,6 @@ export const upCommand = {
|
|
|
76
81
|
return service;
|
|
77
82
|
});
|
|
78
83
|
// A database that is already serving is left alone.
|
|
79
|
-
//
|
|
80
|
-
// Redeploying one replaces a running container for no reason, and every
|
|
81
|
-
// service that talks to it loses its connections while it restarts. It is
|
|
82
|
-
// in the plan so that a database which is *not* running comes back — which
|
|
83
|
-
// is the case that used to need `fleet up db` by name — not so that every
|
|
84
|
-
// deploy of the stack restarts the database underneath it. Naming it
|
|
85
|
-
// explicitly still redeploys it.
|
|
86
84
|
const skipped = args[0]
|
|
87
85
|
? []
|
|
88
86
|
: resolved.filter((s) => isDatabase.has(s.name) && s.current?.status === 'running');
|
|
@@ -101,6 +99,7 @@ export const upCommand = {
|
|
|
101
99
|
gitSha,
|
|
102
100
|
buildContext: buildContexts.get(service.name),
|
|
103
101
|
wait: !flags['no-wait'],
|
|
102
|
+
rootDir: typeof flags.file === 'string' ? dirname(flags.file) : rootDir,
|
|
104
103
|
});
|
|
105
104
|
deployed.push({ service, url });
|
|
106
105
|
}
|
|
@@ -123,18 +122,11 @@ export const upCommand = {
|
|
|
123
122
|
/**
|
|
124
123
|
* Deploy one service: upload its build context if it has one, run the deploy,
|
|
125
124
|
* and wait for it to report running.
|
|
126
|
-
*
|
|
127
|
-
* Returns the URL the control plane handed back, or null for a service that
|
|
128
|
-
* has none — an internal one, which is reached by name from its neighbours
|
|
129
|
-
* rather than from outside.
|
|
130
125
|
*/
|
|
131
126
|
async function deployOne(service, opts) {
|
|
132
|
-
// A service that builds from source sends its directory first. The control
|
|
133
|
-
// plane then builds it for every architecture the fleet has, which is the
|
|
134
|
-
// part that is easy to get wrong by hand and silent when you do.
|
|
135
127
|
let contextId;
|
|
136
128
|
if (opts.buildContext) {
|
|
137
|
-
const dir = join(
|
|
129
|
+
const dir = join(opts.rootDir, opts.buildContext);
|
|
138
130
|
const uploaded = await task(`packaging ${c.bold(service.name)}`, async () => uploadContext(service.id, dir), { done: (r) => `uploaded ${humanBytes(r.bytes)} of build context` });
|
|
139
131
|
contextId = uploaded.contextId;
|
|
140
132
|
}
|