@launchmatic/cli 0.6.2 → 0.6.3
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/index.js +360 -304
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22240,58 +22240,246 @@ function registerDomains(program3) {
|
|
|
22240
22240
|
});
|
|
22241
22241
|
}
|
|
22242
22242
|
|
|
22243
|
+
// src/monorepo.ts
|
|
22244
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
22245
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
22246
|
+
import { join as join2, relative, sep, posix } from "path";
|
|
22247
|
+
var MANIFEST_FILE = "launchmatic.json";
|
|
22248
|
+
function findRepoRoot(start = process.cwd()) {
|
|
22249
|
+
try {
|
|
22250
|
+
return execFileSync5("git", ["rev-parse", "--show-toplevel"], {
|
|
22251
|
+
cwd: start,
|
|
22252
|
+
encoding: "utf-8",
|
|
22253
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
22254
|
+
}).trim();
|
|
22255
|
+
} catch {
|
|
22256
|
+
return start;
|
|
22257
|
+
}
|
|
22258
|
+
}
|
|
22259
|
+
function manifestPath(repoRoot = findRepoRoot()) {
|
|
22260
|
+
return join2(repoRoot, MANIFEST_FILE);
|
|
22261
|
+
}
|
|
22262
|
+
function readManifest(repoRoot = findRepoRoot()) {
|
|
22263
|
+
const p = manifestPath(repoRoot);
|
|
22264
|
+
if (!existsSync3(p)) return null;
|
|
22265
|
+
try {
|
|
22266
|
+
const parsed = JSON.parse(readFileSync3(p, "utf-8"));
|
|
22267
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.services)) {
|
|
22268
|
+
throw new Error(`${MANIFEST_FILE} has unexpected shape`);
|
|
22269
|
+
}
|
|
22270
|
+
return parsed;
|
|
22271
|
+
} catch (err) {
|
|
22272
|
+
throw new Error(`Could not read ${MANIFEST_FILE}: ${err instanceof Error ? err.message : String(err)}`);
|
|
22273
|
+
}
|
|
22274
|
+
}
|
|
22275
|
+
function writeManifest(manifest, repoRoot = findRepoRoot()) {
|
|
22276
|
+
writeFileSync3(manifestPath(repoRoot), JSON.stringify(manifest, null, 2) + "\n");
|
|
22277
|
+
}
|
|
22278
|
+
function discoverServices(repoRoot = findRepoRoot()) {
|
|
22279
|
+
const globs = readWorkspaceGlobs(repoRoot);
|
|
22280
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
22281
|
+
for (const glob of globs) {
|
|
22282
|
+
for (const dir of expandGlob(repoRoot, glob)) {
|
|
22283
|
+
dirs.add(dir);
|
|
22284
|
+
}
|
|
22285
|
+
}
|
|
22286
|
+
if (globs.length === 0) {
|
|
22287
|
+
for (const conv of ["apps", "services"]) {
|
|
22288
|
+
const base = join2(repoRoot, conv);
|
|
22289
|
+
if (existsSync3(base) && statSync(base).isDirectory()) {
|
|
22290
|
+
for (const entry of readdirSync(base)) {
|
|
22291
|
+
const full = join2(base, entry);
|
|
22292
|
+
if (statSync(full).isDirectory()) dirs.add(full);
|
|
22293
|
+
}
|
|
22294
|
+
}
|
|
22295
|
+
}
|
|
22296
|
+
}
|
|
22297
|
+
const out = [];
|
|
22298
|
+
for (const absDir of dirs) {
|
|
22299
|
+
if (!isLikelyDeployable(absDir)) continue;
|
|
22300
|
+
const detection = detectLocal(absDir);
|
|
22301
|
+
out.push({
|
|
22302
|
+
name: deriveName(repoRoot, absDir),
|
|
22303
|
+
rootDir: toPosix(relative(repoRoot, absDir)),
|
|
22304
|
+
framework: detection.framework,
|
|
22305
|
+
buildCmd: detection.buildCmd,
|
|
22306
|
+
startCmd: detection.startCmd,
|
|
22307
|
+
port: detection.port
|
|
22308
|
+
});
|
|
22309
|
+
}
|
|
22310
|
+
out.sort((a, b) => a.rootDir.localeCompare(b.rootDir));
|
|
22311
|
+
return out;
|
|
22312
|
+
}
|
|
22313
|
+
function readWorkspaceGlobs(repoRoot) {
|
|
22314
|
+
const globs = [];
|
|
22315
|
+
const pnpmFile = join2(repoRoot, "pnpm-workspace.yaml");
|
|
22316
|
+
if (existsSync3(pnpmFile)) {
|
|
22317
|
+
const text = readFileSync3(pnpmFile, "utf-8");
|
|
22318
|
+
let inPackages = false;
|
|
22319
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
22320
|
+
const line = rawLine.replace(/#.*$/, "").trimEnd();
|
|
22321
|
+
if (/^packages\s*:/i.test(line)) {
|
|
22322
|
+
inPackages = true;
|
|
22323
|
+
continue;
|
|
22324
|
+
}
|
|
22325
|
+
if (inPackages) {
|
|
22326
|
+
const m = line.match(/^\s*-\s*['"]?([^'"#]+?)['"]?\s*$/);
|
|
22327
|
+
if (m) {
|
|
22328
|
+
globs.push(m[1].trim());
|
|
22329
|
+
continue;
|
|
22330
|
+
}
|
|
22331
|
+
if (line.trim() && !line.startsWith(" ") && !line.startsWith(" ")) {
|
|
22332
|
+
inPackages = false;
|
|
22333
|
+
}
|
|
22334
|
+
}
|
|
22335
|
+
}
|
|
22336
|
+
}
|
|
22337
|
+
const pkgFile = join2(repoRoot, "package.json");
|
|
22338
|
+
if (existsSync3(pkgFile)) {
|
|
22339
|
+
try {
|
|
22340
|
+
const pkg2 = JSON.parse(readFileSync3(pkgFile, "utf-8"));
|
|
22341
|
+
if (Array.isArray(pkg2.workspaces)) {
|
|
22342
|
+
globs.push(...pkg2.workspaces.filter((g) => typeof g === "string"));
|
|
22343
|
+
} else if (pkg2.workspaces && Array.isArray(pkg2.workspaces.packages)) {
|
|
22344
|
+
globs.push(...pkg2.workspaces.packages.filter((g) => typeof g === "string"));
|
|
22345
|
+
}
|
|
22346
|
+
} catch {
|
|
22347
|
+
}
|
|
22348
|
+
}
|
|
22349
|
+
return globs;
|
|
22350
|
+
}
|
|
22351
|
+
function expandGlob(repoRoot, glob) {
|
|
22352
|
+
const cleaned = glob.replace(/^\.\//, "").replace(/\/$/, "");
|
|
22353
|
+
if (cleaned.endsWith("/*")) {
|
|
22354
|
+
const base = join2(repoRoot, cleaned.slice(0, -2));
|
|
22355
|
+
if (!existsSync3(base) || !statSync(base).isDirectory()) return [];
|
|
22356
|
+
return readdirSync(base).map((entry) => join2(base, entry)).filter((p) => {
|
|
22357
|
+
try {
|
|
22358
|
+
return statSync(p).isDirectory();
|
|
22359
|
+
} catch {
|
|
22360
|
+
return false;
|
|
22361
|
+
}
|
|
22362
|
+
});
|
|
22363
|
+
}
|
|
22364
|
+
const abs = join2(repoRoot, cleaned);
|
|
22365
|
+
if (existsSync3(abs) && statSync(abs).isDirectory()) return [abs];
|
|
22366
|
+
return [];
|
|
22367
|
+
}
|
|
22368
|
+
function isLikelyDeployable(absDir) {
|
|
22369
|
+
if (existsSync3(join2(absDir, "Dockerfile"))) return true;
|
|
22370
|
+
const pkgPath = join2(absDir, "package.json");
|
|
22371
|
+
if (existsSync3(pkgPath)) {
|
|
22372
|
+
try {
|
|
22373
|
+
const pkg2 = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
22374
|
+
if (pkg2.scripts?.start || pkg2.scripts?.dev || pkg2.scripts?.serve) return true;
|
|
22375
|
+
if (pkg2.bin) return true;
|
|
22376
|
+
} catch {
|
|
22377
|
+
}
|
|
22378
|
+
}
|
|
22379
|
+
if (existsSync3(join2(absDir, "next.config.js")) || existsSync3(join2(absDir, "next.config.mjs")) || existsSync3(join2(absDir, "next.config.ts")) || existsSync3(join2(absDir, "go.mod")) || existsSync3(join2(absDir, "Cargo.toml")) || existsSync3(join2(absDir, "manage.py")) || existsSync3(join2(absDir, "pyproject.toml")) || existsSync3(join2(absDir, "Gemfile"))) {
|
|
22380
|
+
return true;
|
|
22381
|
+
}
|
|
22382
|
+
return false;
|
|
22383
|
+
}
|
|
22384
|
+
function deriveName(repoRoot, absDir) {
|
|
22385
|
+
const rel = toPosix(relative(repoRoot, absDir));
|
|
22386
|
+
const parts = rel.split("/");
|
|
22387
|
+
return parts[parts.length - 1].toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
22388
|
+
}
|
|
22389
|
+
function toPosix(p) {
|
|
22390
|
+
return p.split(sep).join(posix.sep);
|
|
22391
|
+
}
|
|
22392
|
+
function changedFilesSince(repoRoot, baseRef) {
|
|
22393
|
+
const ref = baseRef ?? autoBaseRef(repoRoot);
|
|
22394
|
+
if (!ref) return [];
|
|
22395
|
+
try {
|
|
22396
|
+
const out = execFileSync5("git", ["diff", "--name-only", `${ref}..HEAD`], {
|
|
22397
|
+
cwd: repoRoot,
|
|
22398
|
+
encoding: "utf-8",
|
|
22399
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
22400
|
+
});
|
|
22401
|
+
return out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
22402
|
+
} catch {
|
|
22403
|
+
return [];
|
|
22404
|
+
}
|
|
22405
|
+
}
|
|
22406
|
+
function autoBaseRef(repoRoot) {
|
|
22407
|
+
try {
|
|
22408
|
+
const branch = execFileSync5("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
22409
|
+
cwd: repoRoot,
|
|
22410
|
+
encoding: "utf-8",
|
|
22411
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
22412
|
+
}).trim();
|
|
22413
|
+
if (branch && branch !== "HEAD") {
|
|
22414
|
+
try {
|
|
22415
|
+
execFileSync5("git", ["rev-parse", "--verify", `origin/${branch}`], {
|
|
22416
|
+
cwd: repoRoot,
|
|
22417
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
22418
|
+
});
|
|
22419
|
+
return `origin/${branch}`;
|
|
22420
|
+
} catch {
|
|
22421
|
+
}
|
|
22422
|
+
}
|
|
22423
|
+
} catch {
|
|
22424
|
+
}
|
|
22425
|
+
try {
|
|
22426
|
+
execFileSync5("git", ["rev-parse", "--verify", "HEAD~1"], {
|
|
22427
|
+
cwd: repoRoot,
|
|
22428
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
22429
|
+
});
|
|
22430
|
+
return "HEAD~1";
|
|
22431
|
+
} catch {
|
|
22432
|
+
return null;
|
|
22433
|
+
}
|
|
22434
|
+
}
|
|
22435
|
+
function serviceWasChanged(rootDir, changedPaths) {
|
|
22436
|
+
if (changedPaths.length === 0) return true;
|
|
22437
|
+
const normalized = rootDir.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/$/, "");
|
|
22438
|
+
if (normalized === "" || normalized === "." || normalized === "/") return true;
|
|
22439
|
+
const prefix = normalized + "/";
|
|
22440
|
+
return changedPaths.some((p) => p === normalized || p.startsWith(prefix));
|
|
22441
|
+
}
|
|
22442
|
+
|
|
22243
22443
|
// src/commands/status.ts
|
|
22244
22444
|
function registerStatus(program3) {
|
|
22245
|
-
program3.command("status").description("Show service and deployment status").
|
|
22445
|
+
program3.command("status").description("Show service and deployment status").option(
|
|
22446
|
+
"-s, --service <name>",
|
|
22447
|
+
"In a monorepo, show status for a single service (by name)"
|
|
22448
|
+
).action(async (opts) => {
|
|
22246
22449
|
if (!isLoggedIn()) {
|
|
22247
22450
|
console.error(source_default.red('Not logged in. Run "lm login" first.'));
|
|
22248
22451
|
process.exitCode = 1;
|
|
22249
22452
|
return;
|
|
22250
22453
|
}
|
|
22251
|
-
const
|
|
22454
|
+
const manifest = readManifest();
|
|
22455
|
+
if (manifest && manifest.services.length > 0) {
|
|
22456
|
+
await renderMonorepoStatus(manifest.services, opts.service);
|
|
22457
|
+
return;
|
|
22458
|
+
}
|
|
22459
|
+
let ctx;
|
|
22252
22460
|
try {
|
|
22253
|
-
|
|
22254
|
-
|
|
22255
|
-
console.
|
|
22256
|
-
|
|
22257
|
-
|
|
22461
|
+
ctx = readContext();
|
|
22462
|
+
} catch (err) {
|
|
22463
|
+
console.error(
|
|
22464
|
+
source_default.red(
|
|
22465
|
+
err instanceof Error ? err.message : "No Launchmatic context found."
|
|
22466
|
+
)
|
|
22258
22467
|
);
|
|
22259
|
-
|
|
22260
|
-
|
|
22261
|
-
`
|
|
22262
|
-
|
|
22263
|
-
|
|
22264
|
-
|
|
22265
|
-
|
|
22266
|
-
|
|
22267
|
-
|
|
22268
|
-
|
|
22269
|
-
|
|
22270
|
-
|
|
22271
|
-
|
|
22272
|
-
|
|
22273
|
-
|
|
22274
|
-
if (last.branch) {
|
|
22275
|
-
console.log(` Branch: ${last.branch}`);
|
|
22276
|
-
}
|
|
22277
|
-
if (last.commitSha) {
|
|
22278
|
-
console.log(
|
|
22279
|
-
` Commit: ${last.commitSha.slice(0, 7)}`
|
|
22280
|
-
);
|
|
22281
|
-
}
|
|
22282
|
-
console.log(
|
|
22283
|
-
` Time: ${new Date(last.createdAt).toLocaleString()}`
|
|
22284
|
-
);
|
|
22285
|
-
}
|
|
22286
|
-
if (data.domains && data.domains.length > 0) {
|
|
22287
|
-
console.log();
|
|
22288
|
-
console.log(source_default.bold(" Domains:"));
|
|
22289
|
-
for (const d of data.domains) {
|
|
22290
|
-
const ssl = d.sslStatus === "ACTIVE" ? source_default.green("\u2713 SSL") : source_default.yellow("\u23F3 SSL");
|
|
22291
|
-
console.log(` ${d.hostname} ${ssl}`);
|
|
22292
|
-
}
|
|
22293
|
-
}
|
|
22294
|
-
console.log();
|
|
22468
|
+
console.error(
|
|
22469
|
+
source_default.dim(
|
|
22470
|
+
`Run ${source_default.bold("lm init")} to bind a service, or ${source_default.bold(
|
|
22471
|
+
"lm monorepo init"
|
|
22472
|
+
)} to create a ${MANIFEST_FILE} for multi-service repos.`
|
|
22473
|
+
)
|
|
22474
|
+
);
|
|
22475
|
+
process.exitCode = 1;
|
|
22476
|
+
return;
|
|
22477
|
+
}
|
|
22478
|
+
try {
|
|
22479
|
+
const { data } = await api(
|
|
22480
|
+
`/api/services/${ctx.serviceId}`
|
|
22481
|
+
);
|
|
22482
|
+
renderServiceBlock(data, { indent: " " });
|
|
22295
22483
|
} catch (err) {
|
|
22296
22484
|
console.error(
|
|
22297
22485
|
source_default.red(
|
|
@@ -22299,10 +22487,80 @@ function registerStatus(program3) {
|
|
|
22299
22487
|
)
|
|
22300
22488
|
);
|
|
22301
22489
|
process.exitCode = 1;
|
|
22302
|
-
return;
|
|
22303
22490
|
}
|
|
22304
22491
|
});
|
|
22305
22492
|
}
|
|
22493
|
+
async function renderMonorepoStatus(services, filter) {
|
|
22494
|
+
const targets = filter ? services.filter((s) => s.name === filter) : services;
|
|
22495
|
+
if (targets.length === 0) {
|
|
22496
|
+
console.error(
|
|
22497
|
+
source_default.red(
|
|
22498
|
+
filter ? `No service named "${filter}" in ${MANIFEST_FILE}.` : `No services declared in ${MANIFEST_FILE}.`
|
|
22499
|
+
)
|
|
22500
|
+
);
|
|
22501
|
+
process.exitCode = 1;
|
|
22502
|
+
return;
|
|
22503
|
+
}
|
|
22504
|
+
console.log();
|
|
22505
|
+
console.log(
|
|
22506
|
+
source_default.bold(`Monorepo`) + source_default.dim(` \u2014 ${targets.length} of ${services.length} service${services.length === 1 ? "" : "s"}`)
|
|
22507
|
+
);
|
|
22508
|
+
const results = await Promise.allSettled(
|
|
22509
|
+
targets.map(async (s) => {
|
|
22510
|
+
if (!s.serviceId) {
|
|
22511
|
+
return { name: s.name, error: `no serviceId \u2014 re-run ${source_default.bold("lm monorepo init")}` };
|
|
22512
|
+
}
|
|
22513
|
+
const { data } = await api(
|
|
22514
|
+
`/api/services/${s.serviceId}`
|
|
22515
|
+
);
|
|
22516
|
+
return { name: s.name, data };
|
|
22517
|
+
})
|
|
22518
|
+
);
|
|
22519
|
+
for (const r of results) {
|
|
22520
|
+
console.log();
|
|
22521
|
+
if (r.status === "fulfilled") {
|
|
22522
|
+
if ("error" in r.value) {
|
|
22523
|
+
console.log(` ${source_default.cyan(r.value.name)} ${source_default.yellow(r.value.error)}`);
|
|
22524
|
+
} else {
|
|
22525
|
+
renderServiceBlock(r.value.data, { indent: " " });
|
|
22526
|
+
}
|
|
22527
|
+
} else {
|
|
22528
|
+
const msg = r.reason instanceof Error ? r.reason.message : String(r.reason);
|
|
22529
|
+
console.log(` ${source_default.red("\u2717")} ${source_default.dim(msg)}`);
|
|
22530
|
+
}
|
|
22531
|
+
}
|
|
22532
|
+
console.log();
|
|
22533
|
+
}
|
|
22534
|
+
function renderServiceBlock(data, { indent }) {
|
|
22535
|
+
console.log();
|
|
22536
|
+
console.log(`${indent}${source_default.bold("Service:")} ${data.name}`);
|
|
22537
|
+
console.log(`${indent}${source_default.bold("Status:")} ${statusColor(data.status)}`);
|
|
22538
|
+
if (data.subdomain) {
|
|
22539
|
+
console.log(
|
|
22540
|
+
`${indent}${source_default.bold("URL:")} ${source_default.cyan(`https://${data.subdomain}`)}`
|
|
22541
|
+
);
|
|
22542
|
+
}
|
|
22543
|
+
if (data.runtime) {
|
|
22544
|
+
console.log(`${indent}${source_default.bold("Runtime:")} ${data.runtime}`);
|
|
22545
|
+
}
|
|
22546
|
+
if (data.deployments && data.deployments.length > 0) {
|
|
22547
|
+
const last = data.deployments[0];
|
|
22548
|
+
console.log();
|
|
22549
|
+
console.log(`${indent}${source_default.bold("Last deployment:")}`);
|
|
22550
|
+
console.log(`${indent} Status: ${statusColor(last.status)}`);
|
|
22551
|
+
if (last.branch) console.log(`${indent} Branch: ${last.branch}`);
|
|
22552
|
+
if (last.commitSha) console.log(`${indent} Commit: ${last.commitSha.slice(0, 7)}`);
|
|
22553
|
+
console.log(`${indent} Time: ${new Date(last.createdAt).toLocaleString()}`);
|
|
22554
|
+
}
|
|
22555
|
+
if (data.domains && data.domains.length > 0) {
|
|
22556
|
+
console.log();
|
|
22557
|
+
console.log(`${indent}${source_default.bold("Domains:")}`);
|
|
22558
|
+
for (const d of data.domains) {
|
|
22559
|
+
const ssl = d.sslStatus === "ACTIVE" ? source_default.green("\u2713 SSL") : source_default.yellow("\u23F3 SSL");
|
|
22560
|
+
console.log(`${indent} ${d.hostname} ${ssl}`);
|
|
22561
|
+
}
|
|
22562
|
+
}
|
|
22563
|
+
}
|
|
22306
22564
|
function statusColor(status) {
|
|
22307
22565
|
switch (status.toUpperCase()) {
|
|
22308
22566
|
case "ACTIVE":
|
|
@@ -22541,12 +22799,12 @@ function registerLightspeed(program3) {
|
|
|
22541
22799
|
}
|
|
22542
22800
|
|
|
22543
22801
|
// src/commands/quicklaunch.ts
|
|
22544
|
-
import { execFileSync as
|
|
22802
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
22545
22803
|
import { basename } from "path";
|
|
22546
22804
|
|
|
22547
22805
|
// src/connectors.ts
|
|
22548
|
-
import { existsSync as
|
|
22549
|
-
import { join as
|
|
22806
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
22807
|
+
import { join as join3 } from "path";
|
|
22550
22808
|
import { randomBytes } from "crypto";
|
|
22551
22809
|
var CONNECTOR_REGISTRY = [
|
|
22552
22810
|
// Stripe
|
|
@@ -22595,10 +22853,10 @@ function getConnector(envKey, appName) {
|
|
|
22595
22853
|
function detectEnvVars(cwd) {
|
|
22596
22854
|
const envFiles = [".env.example", ".env.local.example", ".env.sample", ".env"];
|
|
22597
22855
|
for (const file of envFiles) {
|
|
22598
|
-
const filePath =
|
|
22599
|
-
if (
|
|
22856
|
+
const filePath = join3(cwd, file);
|
|
22857
|
+
if (existsSync4(filePath)) {
|
|
22600
22858
|
try {
|
|
22601
|
-
const content =
|
|
22859
|
+
const content = readFileSync4(filePath, "utf-8");
|
|
22602
22860
|
const keys = [];
|
|
22603
22861
|
for (const line of content.split("\n")) {
|
|
22604
22862
|
const trimmed = line.trim();
|
|
@@ -22672,25 +22930,25 @@ Detected: ${runtimeLabel} on port ${detection.port}`));
|
|
|
22672
22930
|
const repoSpinner = ora("Creating GitHub repository...").start();
|
|
22673
22931
|
try {
|
|
22674
22932
|
try {
|
|
22675
|
-
|
|
22933
|
+
execFileSync6("git", ["rev-parse", "--git-dir"], {
|
|
22676
22934
|
cwd,
|
|
22677
22935
|
stdio: ["pipe", "pipe", "pipe"]
|
|
22678
22936
|
});
|
|
22679
22937
|
} catch {
|
|
22680
|
-
|
|
22938
|
+
execFileSync6("git", ["init"], { cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
22681
22939
|
}
|
|
22682
22940
|
const { data: repoData } = await api(`/api/services/${ctx.serviceId}/create-repo`, {
|
|
22683
22941
|
method: "POST",
|
|
22684
22942
|
body: JSON.stringify({})
|
|
22685
22943
|
});
|
|
22686
22944
|
try {
|
|
22687
|
-
|
|
22945
|
+
execFileSync6("git", ["remote", "add", "origin", repoData.cloneUrl], {
|
|
22688
22946
|
cwd,
|
|
22689
22947
|
stdio: ["pipe", "pipe", "pipe"]
|
|
22690
22948
|
});
|
|
22691
22949
|
} catch {
|
|
22692
22950
|
try {
|
|
22693
|
-
|
|
22951
|
+
execFileSync6("git", ["remote", "set-url", "origin", repoData.cloneUrl], {
|
|
22694
22952
|
cwd,
|
|
22695
22953
|
stdio: ["pipe", "pipe", "pipe"]
|
|
22696
22954
|
});
|
|
@@ -22784,12 +23042,12 @@ Detected: ${runtimeLabel} on port ${detection.port}`));
|
|
|
22784
23042
|
const repoSpinner = ora("Creating GitHub repository...").start();
|
|
22785
23043
|
try {
|
|
22786
23044
|
try {
|
|
22787
|
-
|
|
23045
|
+
execFileSync6("git", ["rev-parse", "--git-dir"], {
|
|
22788
23046
|
cwd,
|
|
22789
23047
|
stdio: ["pipe", "pipe", "pipe"]
|
|
22790
23048
|
});
|
|
22791
23049
|
} catch {
|
|
22792
|
-
|
|
23050
|
+
execFileSync6("git", ["init"], {
|
|
22793
23051
|
cwd,
|
|
22794
23052
|
stdio: ["pipe", "pipe", "pipe"]
|
|
22795
23053
|
});
|
|
@@ -22799,13 +23057,13 @@ Detected: ${runtimeLabel} on port ${detection.port}`));
|
|
|
22799
23057
|
body: JSON.stringify({})
|
|
22800
23058
|
});
|
|
22801
23059
|
try {
|
|
22802
|
-
|
|
23060
|
+
execFileSync6("git", ["remote", "add", "origin", repoData.cloneUrl], {
|
|
22803
23061
|
cwd,
|
|
22804
23062
|
stdio: ["pipe", "pipe", "pipe"]
|
|
22805
23063
|
});
|
|
22806
23064
|
} catch {
|
|
22807
23065
|
try {
|
|
22808
|
-
|
|
23066
|
+
execFileSync6("git", ["remote", "set-url", "origin", repoData.cloneUrl], {
|
|
22809
23067
|
cwd,
|
|
22810
23068
|
stdio: ["pipe", "pipe", "pipe"]
|
|
22811
23069
|
});
|
|
@@ -22830,8 +23088,8 @@ Detected: ${runtimeLabel} on port ${detection.port}`));
|
|
|
22830
23088
|
if (gitInfo.hasUncommitted) {
|
|
22831
23089
|
const commitSpinner = ora("Committing changes...").start();
|
|
22832
23090
|
try {
|
|
22833
|
-
|
|
22834
|
-
|
|
23091
|
+
execFileSync6("git", ["add", "-A"], { cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
23092
|
+
execFileSync6("git", ["commit", "-m", "quicklaunch: initial deploy"], {
|
|
22835
23093
|
cwd,
|
|
22836
23094
|
stdio: ["pipe", "pipe", "pipe"]
|
|
22837
23095
|
});
|
|
@@ -22844,7 +23102,7 @@ Detected: ${runtimeLabel} on port ${detection.port}`));
|
|
|
22844
23102
|
if (gitInfo.hasUnpushed || !gitInfo.commitSha) {
|
|
22845
23103
|
const pushSpinner = ora("Pushing to remote...").start();
|
|
22846
23104
|
try {
|
|
22847
|
-
|
|
23105
|
+
execFileSync6("git", ["push", "-u", "origin", gitInfo.repoBranch], {
|
|
22848
23106
|
cwd,
|
|
22849
23107
|
stdio: ["pipe", "pipe", "pipe"]
|
|
22850
23108
|
});
|
|
@@ -23024,22 +23282,22 @@ async function pollStatus(deploymentId, spinner) {
|
|
|
23024
23282
|
}
|
|
23025
23283
|
|
|
23026
23284
|
// src/commands/browser.ts
|
|
23027
|
-
import { resolve as
|
|
23028
|
-
import { existsSync as
|
|
23029
|
-
import { execFileSync as
|
|
23285
|
+
import { resolve as resolve3 } from "path";
|
|
23286
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
|
|
23287
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
23030
23288
|
async function ensureChromium() {
|
|
23031
23289
|
const existing = findChromium();
|
|
23032
23290
|
if (existing) return existing;
|
|
23033
23291
|
const spinner = ora("Downloading Chromium (one-time setup)...").start();
|
|
23034
23292
|
try {
|
|
23035
|
-
|
|
23293
|
+
execFileSync7("npx", ["playwright", "install", "chromium"], {
|
|
23036
23294
|
stdio: ["pipe", "pipe", "pipe"],
|
|
23037
23295
|
timeout: 12e4
|
|
23038
23296
|
});
|
|
23039
23297
|
spinner.succeed("Chromium installed");
|
|
23040
23298
|
} catch {
|
|
23041
23299
|
try {
|
|
23042
|
-
|
|
23300
|
+
execFileSync7("npx", ["playwright-core", "install", "chromium"], {
|
|
23043
23301
|
stdio: ["pipe", "pipe", "pipe"],
|
|
23044
23302
|
timeout: 12e4
|
|
23045
23303
|
});
|
|
@@ -23084,27 +23342,27 @@ function findChromium() {
|
|
|
23084
23342
|
}
|
|
23085
23343
|
try {
|
|
23086
23344
|
const home = process.env.HOME || process.env.USERPROFILE || "";
|
|
23087
|
-
const pwBrowsers =
|
|
23088
|
-
if (
|
|
23089
|
-
const dirs =
|
|
23345
|
+
const pwBrowsers = resolve3(home, ".cache", "ms-playwright");
|
|
23346
|
+
if (existsSync5(pwBrowsers)) {
|
|
23347
|
+
const dirs = readdirSync2(pwBrowsers).filter((d) => d.startsWith("chromium-"));
|
|
23090
23348
|
if (dirs.length > 0) {
|
|
23091
23349
|
const latest = dirs.sort().pop();
|
|
23092
|
-
const chromePath = process.platform === "win32" ?
|
|
23093
|
-
if (
|
|
23350
|
+
const chromePath = process.platform === "win32" ? resolve3(pwBrowsers, latest, "chrome-win", "chrome.exe") : process.platform === "darwin" ? resolve3(pwBrowsers, latest, "chrome-mac", "Chromium.app", "Contents", "MacOS", "Chromium") : resolve3(pwBrowsers, latest, "chrome-linux", "chrome");
|
|
23351
|
+
if (existsSync5(chromePath)) return chromePath;
|
|
23094
23352
|
}
|
|
23095
23353
|
}
|
|
23096
23354
|
} catch {
|
|
23097
23355
|
}
|
|
23098
23356
|
for (const c of candidates) {
|
|
23099
|
-
if (
|
|
23357
|
+
if (existsSync5(c)) return c;
|
|
23100
23358
|
}
|
|
23101
23359
|
if (process.platform !== "win32") {
|
|
23102
23360
|
try {
|
|
23103
|
-
return
|
|
23361
|
+
return execFileSync7("which", ["google-chrome"], { encoding: "utf-8" }).trim();
|
|
23104
23362
|
} catch {
|
|
23105
23363
|
}
|
|
23106
23364
|
try {
|
|
23107
|
-
return
|
|
23365
|
+
return execFileSync7("which", ["chromium"], { encoding: "utf-8" }).trim();
|
|
23108
23366
|
} catch {
|
|
23109
23367
|
}
|
|
23110
23368
|
}
|
|
@@ -23143,7 +23401,7 @@ function registerBrowser(program3) {
|
|
|
23143
23401
|
const page = await context.newPage();
|
|
23144
23402
|
await page.goto(targetUrl, { waitUntil: "networkidle" });
|
|
23145
23403
|
if (parseInt(opts.delay) > 0) await page.waitForTimeout(parseInt(opts.delay));
|
|
23146
|
-
const output =
|
|
23404
|
+
const output = resolve3(opts.output);
|
|
23147
23405
|
await page.screenshot({ path: output, fullPage: opts.full });
|
|
23148
23406
|
await b.close();
|
|
23149
23407
|
spinner.succeed(`Screenshot saved \u2192 ${source_default.cyan(output)}`);
|
|
@@ -23159,7 +23417,7 @@ function registerBrowser(program3) {
|
|
|
23159
23417
|
const b = await launchBrowser(true);
|
|
23160
23418
|
const page = await b.newPage();
|
|
23161
23419
|
await page.goto(targetUrl, { waitUntil: "networkidle" });
|
|
23162
|
-
const output =
|
|
23420
|
+
const output = resolve3(opts.output);
|
|
23163
23421
|
await page.pdf({ path: output, format: opts.format, landscape: opts.landscape, printBackground: true });
|
|
23164
23422
|
await b.close();
|
|
23165
23423
|
spinner.succeed(`PDF saved \u2192 ${source_default.cyan(output)}`);
|
|
@@ -23368,7 +23626,7 @@ ${actions.map((a) => ` ${a}`).join("\n")}
|
|
|
23368
23626
|
}
|
|
23369
23627
|
|
|
23370
23628
|
// src/commands/repo.ts
|
|
23371
|
-
import { execFileSync as
|
|
23629
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
23372
23630
|
function requireLogin() {
|
|
23373
23631
|
if (!isLoggedIn()) {
|
|
23374
23632
|
console.error(source_default.red('Not logged in. Run "lm login" first.'));
|
|
@@ -23399,7 +23657,7 @@ function openUrl(url) {
|
|
|
23399
23657
|
const cmd = process.platform === "win32" ? "cmd" : process.platform === "darwin" ? "open" : "xdg-open";
|
|
23400
23658
|
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
23401
23659
|
try {
|
|
23402
|
-
|
|
23660
|
+
execFileSync8(cmd, args, { stdio: "pipe" });
|
|
23403
23661
|
} catch {
|
|
23404
23662
|
}
|
|
23405
23663
|
}
|
|
@@ -23661,7 +23919,7 @@ function registerRepo(program3) {
|
|
|
23661
23919
|
const args = ["clone", "--depth", "1"];
|
|
23662
23920
|
if (opts.branch) args.push("--branch", opts.branch);
|
|
23663
23921
|
args.push(url);
|
|
23664
|
-
|
|
23922
|
+
execFileSync8("git", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
23665
23923
|
spinner.succeed(`Cloned ${source_default.cyan(fullName)}`);
|
|
23666
23924
|
} catch (err) {
|
|
23667
23925
|
spinner.fail(source_default.red(`Clone failed: ${err.message}`));
|
|
@@ -23673,7 +23931,7 @@ function registerRepo(program3) {
|
|
|
23673
23931
|
const args = ["diff"];
|
|
23674
23932
|
if (opts.staged) args.push("--staged");
|
|
23675
23933
|
args.push("--stat");
|
|
23676
|
-
const output =
|
|
23934
|
+
const output = execFileSync8("git", args, { cwd: process.cwd(), encoding: "utf-8" });
|
|
23677
23935
|
if (!output.trim()) {
|
|
23678
23936
|
console.log(source_default.dim("\n No changes.\n"));
|
|
23679
23937
|
} else {
|
|
@@ -23688,7 +23946,7 @@ function registerRepo(program3) {
|
|
|
23688
23946
|
try {
|
|
23689
23947
|
const args = ["stash"];
|
|
23690
23948
|
if (action) args.push(action);
|
|
23691
|
-
const output =
|
|
23949
|
+
const output = execFileSync8("git", args, { cwd: process.cwd(), encoding: "utf-8" });
|
|
23692
23950
|
console.log(output || source_default.dim("Done."));
|
|
23693
23951
|
} catch (err) {
|
|
23694
23952
|
console.error(source_default.red(`Failed: ${err.message}`));
|
|
@@ -24627,8 +24885,8 @@ function validateGeneratedApp(files, ctx = {}) {
|
|
|
24627
24885
|
}
|
|
24628
24886
|
|
|
24629
24887
|
// ../../packages/validator/dist/validate-disk.js
|
|
24630
|
-
import { readdirSync as
|
|
24631
|
-
import { join as
|
|
24888
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
24889
|
+
import { join as join4, relative as relative2 } from "path";
|
|
24632
24890
|
var MAX_FILE_SIZE = 512 * 1024;
|
|
24633
24891
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", "dist", "build", "__pycache__", "target"]);
|
|
24634
24892
|
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -24655,22 +24913,22 @@ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
24655
24913
|
".env.example"
|
|
24656
24914
|
]);
|
|
24657
24915
|
function collectFiles(dir, rootDir, out) {
|
|
24658
|
-
for (const entry of
|
|
24916
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
24659
24917
|
if (entry.name.startsWith(".") && entry.name !== ".env.example")
|
|
24660
24918
|
continue;
|
|
24661
24919
|
if (SKIP_DIRS.has(entry.name))
|
|
24662
24920
|
continue;
|
|
24663
|
-
const fullPath =
|
|
24921
|
+
const fullPath = join4(dir, entry.name);
|
|
24664
24922
|
if (entry.isDirectory()) {
|
|
24665
24923
|
collectFiles(fullPath, rootDir, out);
|
|
24666
24924
|
continue;
|
|
24667
24925
|
}
|
|
24668
24926
|
if (entry.name === "Dockerfile" || entry.name === "docker-compose.yml") {
|
|
24669
24927
|
try {
|
|
24670
|
-
const stat =
|
|
24928
|
+
const stat = statSync2(fullPath);
|
|
24671
24929
|
if (stat.size > MAX_FILE_SIZE)
|
|
24672
24930
|
continue;
|
|
24673
|
-
out.push({ path:
|
|
24931
|
+
out.push({ path: relative2(rootDir, fullPath).replace(/\\/g, "/"), content: readFileSync5(fullPath, "utf-8") });
|
|
24674
24932
|
} catch {
|
|
24675
24933
|
}
|
|
24676
24934
|
continue;
|
|
@@ -24679,10 +24937,10 @@ function collectFiles(dir, rootDir, out) {
|
|
|
24679
24937
|
if (!SOURCE_EXTENSIONS.has(ext))
|
|
24680
24938
|
continue;
|
|
24681
24939
|
try {
|
|
24682
|
-
const stat =
|
|
24940
|
+
const stat = statSync2(fullPath);
|
|
24683
24941
|
if (stat.size > MAX_FILE_SIZE)
|
|
24684
24942
|
continue;
|
|
24685
|
-
out.push({ path:
|
|
24943
|
+
out.push({ path: relative2(rootDir, fullPath).replace(/\\/g, "/"), content: readFileSync5(fullPath, "utf-8") });
|
|
24686
24944
|
} catch {
|
|
24687
24945
|
}
|
|
24688
24946
|
}
|
|
@@ -24694,12 +24952,12 @@ function validateFromDisk(sourceDir, ctx = {}) {
|
|
|
24694
24952
|
}
|
|
24695
24953
|
|
|
24696
24954
|
// src/commands/doctor.ts
|
|
24697
|
-
import { existsSync as
|
|
24698
|
-
import { resolve as
|
|
24955
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
24956
|
+
import { resolve as resolve4 } from "path";
|
|
24699
24957
|
function registerDoctor(program3) {
|
|
24700
24958
|
program3.command("doctor").description("Scan project for common deployment issues").option("-d, --dir <path>", "Project directory to scan", ".").option("--json", "Output results as JSON").action(async (opts) => {
|
|
24701
|
-
const dir =
|
|
24702
|
-
if (!
|
|
24959
|
+
const dir = resolve4(opts.dir);
|
|
24960
|
+
if (!existsSync6(dir)) {
|
|
24703
24961
|
console.error(source_default.red(`Directory not found: ${dir}`));
|
|
24704
24962
|
process.exitCode = 1;
|
|
24705
24963
|
return;
|
|
@@ -24723,18 +24981,18 @@ function registerDoctor(program3) {
|
|
|
24723
24981
|
}
|
|
24724
24982
|
function detectContext(dir) {
|
|
24725
24983
|
const ctx = {};
|
|
24726
|
-
const pkgPath =
|
|
24727
|
-
if (
|
|
24984
|
+
const pkgPath = resolve4(dir, "package.json");
|
|
24985
|
+
if (existsSync6(pkgPath)) {
|
|
24728
24986
|
try {
|
|
24729
|
-
const pkg2 = JSON.parse(
|
|
24987
|
+
const pkg2 = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
24730
24988
|
const deps = { ...pkg2.dependencies, ...pkg2.devDependencies };
|
|
24731
24989
|
if (deps.next) ctx.runtime = "nodejs";
|
|
24732
24990
|
else if (deps.express || deps.fastify || deps.koa || deps.hapi) ctx.runtime = "nodejs";
|
|
24733
24991
|
else if (deps.react || deps.vue || deps.svelte) ctx.runtime = "nodejs";
|
|
24734
24992
|
else if (pkg2.type === "module" || deps.typescript) ctx.runtime = "nodejs";
|
|
24735
|
-
if (
|
|
24736
|
-
else if (
|
|
24737
|
-
else if (
|
|
24993
|
+
if (existsSync6(resolve4(dir, "pnpm-lock.yaml"))) ctx.packageManager = "pnpm";
|
|
24994
|
+
else if (existsSync6(resolve4(dir, "yarn.lock"))) ctx.packageManager = "yarn";
|
|
24995
|
+
else if (existsSync6(resolve4(dir, "package-lock.json"))) ctx.packageManager = "npm";
|
|
24738
24996
|
const startScript = pkg2.scripts?.start || pkg2.scripts?.dev || "";
|
|
24739
24997
|
const portMatch = startScript.match(/--port\s+(\d+)|-p\s+(\d+)/);
|
|
24740
24998
|
if (portMatch) ctx.port = parseInt(portMatch[1] || portMatch[2]);
|
|
@@ -24743,11 +25001,11 @@ function detectContext(dir) {
|
|
|
24743
25001
|
} catch {
|
|
24744
25002
|
}
|
|
24745
25003
|
}
|
|
24746
|
-
if (
|
|
25004
|
+
if (existsSync6(resolve4(dir, "requirements.txt")) || existsSync6(resolve4(dir, "pyproject.toml"))) {
|
|
24747
25005
|
ctx.runtime = "python";
|
|
24748
25006
|
}
|
|
24749
|
-
if (
|
|
24750
|
-
if (
|
|
25007
|
+
if (existsSync6(resolve4(dir, "go.mod"))) ctx.runtime = "go";
|
|
25008
|
+
if (existsSync6(resolve4(dir, "Cargo.toml"))) ctx.runtime = "rust";
|
|
24751
25009
|
return ctx;
|
|
24752
25010
|
}
|
|
24753
25011
|
function severityIcon(severity) {
|
|
@@ -25807,208 +26065,6 @@ import { execFileSync as execFileSync9 } from "child_process";
|
|
|
25807
26065
|
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
25808
26066
|
import { join as join5 } from "path";
|
|
25809
26067
|
import readline7 from "readline";
|
|
25810
|
-
|
|
25811
|
-
// src/monorepo.ts
|
|
25812
|
-
import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
25813
|
-
import { execFileSync as execFileSync8 } from "child_process";
|
|
25814
|
-
import { join as join4, relative as relative2, sep, posix } from "path";
|
|
25815
|
-
var MANIFEST_FILE = "launchmatic.json";
|
|
25816
|
-
function findRepoRoot(start = process.cwd()) {
|
|
25817
|
-
try {
|
|
25818
|
-
return execFileSync8("git", ["rev-parse", "--show-toplevel"], {
|
|
25819
|
-
cwd: start,
|
|
25820
|
-
encoding: "utf-8",
|
|
25821
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
25822
|
-
}).trim();
|
|
25823
|
-
} catch {
|
|
25824
|
-
return start;
|
|
25825
|
-
}
|
|
25826
|
-
}
|
|
25827
|
-
function manifestPath(repoRoot = findRepoRoot()) {
|
|
25828
|
-
return join4(repoRoot, MANIFEST_FILE);
|
|
25829
|
-
}
|
|
25830
|
-
function readManifest(repoRoot = findRepoRoot()) {
|
|
25831
|
-
const p = manifestPath(repoRoot);
|
|
25832
|
-
if (!existsSync6(p)) return null;
|
|
25833
|
-
try {
|
|
25834
|
-
const parsed = JSON.parse(readFileSync6(p, "utf-8"));
|
|
25835
|
-
if (parsed.version !== 1 || !Array.isArray(parsed.services)) {
|
|
25836
|
-
throw new Error(`${MANIFEST_FILE} has unexpected shape`);
|
|
25837
|
-
}
|
|
25838
|
-
return parsed;
|
|
25839
|
-
} catch (err) {
|
|
25840
|
-
throw new Error(`Could not read ${MANIFEST_FILE}: ${err instanceof Error ? err.message : String(err)}`);
|
|
25841
|
-
}
|
|
25842
|
-
}
|
|
25843
|
-
function writeManifest(manifest, repoRoot = findRepoRoot()) {
|
|
25844
|
-
writeFileSync3(manifestPath(repoRoot), JSON.stringify(manifest, null, 2) + "\n");
|
|
25845
|
-
}
|
|
25846
|
-
function discoverServices(repoRoot = findRepoRoot()) {
|
|
25847
|
-
const globs = readWorkspaceGlobs(repoRoot);
|
|
25848
|
-
const dirs = /* @__PURE__ */ new Set();
|
|
25849
|
-
for (const glob of globs) {
|
|
25850
|
-
for (const dir of expandGlob(repoRoot, glob)) {
|
|
25851
|
-
dirs.add(dir);
|
|
25852
|
-
}
|
|
25853
|
-
}
|
|
25854
|
-
if (globs.length === 0) {
|
|
25855
|
-
for (const conv of ["apps", "services"]) {
|
|
25856
|
-
const base = join4(repoRoot, conv);
|
|
25857
|
-
if (existsSync6(base) && statSync2(base).isDirectory()) {
|
|
25858
|
-
for (const entry of readdirSync3(base)) {
|
|
25859
|
-
const full = join4(base, entry);
|
|
25860
|
-
if (statSync2(full).isDirectory()) dirs.add(full);
|
|
25861
|
-
}
|
|
25862
|
-
}
|
|
25863
|
-
}
|
|
25864
|
-
}
|
|
25865
|
-
const out = [];
|
|
25866
|
-
for (const absDir of dirs) {
|
|
25867
|
-
if (!isLikelyDeployable(absDir)) continue;
|
|
25868
|
-
const detection = detectLocal(absDir);
|
|
25869
|
-
out.push({
|
|
25870
|
-
name: deriveName(repoRoot, absDir),
|
|
25871
|
-
rootDir: toPosix(relative2(repoRoot, absDir)),
|
|
25872
|
-
framework: detection.framework,
|
|
25873
|
-
buildCmd: detection.buildCmd,
|
|
25874
|
-
startCmd: detection.startCmd,
|
|
25875
|
-
port: detection.port
|
|
25876
|
-
});
|
|
25877
|
-
}
|
|
25878
|
-
out.sort((a, b) => a.rootDir.localeCompare(b.rootDir));
|
|
25879
|
-
return out;
|
|
25880
|
-
}
|
|
25881
|
-
function readWorkspaceGlobs(repoRoot) {
|
|
25882
|
-
const globs = [];
|
|
25883
|
-
const pnpmFile = join4(repoRoot, "pnpm-workspace.yaml");
|
|
25884
|
-
if (existsSync6(pnpmFile)) {
|
|
25885
|
-
const text = readFileSync6(pnpmFile, "utf-8");
|
|
25886
|
-
let inPackages = false;
|
|
25887
|
-
for (const rawLine of text.split(/\r?\n/)) {
|
|
25888
|
-
const line = rawLine.replace(/#.*$/, "").trimEnd();
|
|
25889
|
-
if (/^packages\s*:/i.test(line)) {
|
|
25890
|
-
inPackages = true;
|
|
25891
|
-
continue;
|
|
25892
|
-
}
|
|
25893
|
-
if (inPackages) {
|
|
25894
|
-
const m = line.match(/^\s*-\s*['"]?([^'"#]+?)['"]?\s*$/);
|
|
25895
|
-
if (m) {
|
|
25896
|
-
globs.push(m[1].trim());
|
|
25897
|
-
continue;
|
|
25898
|
-
}
|
|
25899
|
-
if (line.trim() && !line.startsWith(" ") && !line.startsWith(" ")) {
|
|
25900
|
-
inPackages = false;
|
|
25901
|
-
}
|
|
25902
|
-
}
|
|
25903
|
-
}
|
|
25904
|
-
}
|
|
25905
|
-
const pkgFile = join4(repoRoot, "package.json");
|
|
25906
|
-
if (existsSync6(pkgFile)) {
|
|
25907
|
-
try {
|
|
25908
|
-
const pkg2 = JSON.parse(readFileSync6(pkgFile, "utf-8"));
|
|
25909
|
-
if (Array.isArray(pkg2.workspaces)) {
|
|
25910
|
-
globs.push(...pkg2.workspaces.filter((g) => typeof g === "string"));
|
|
25911
|
-
} else if (pkg2.workspaces && Array.isArray(pkg2.workspaces.packages)) {
|
|
25912
|
-
globs.push(...pkg2.workspaces.packages.filter((g) => typeof g === "string"));
|
|
25913
|
-
}
|
|
25914
|
-
} catch {
|
|
25915
|
-
}
|
|
25916
|
-
}
|
|
25917
|
-
return globs;
|
|
25918
|
-
}
|
|
25919
|
-
function expandGlob(repoRoot, glob) {
|
|
25920
|
-
const cleaned = glob.replace(/^\.\//, "").replace(/\/$/, "");
|
|
25921
|
-
if (cleaned.endsWith("/*")) {
|
|
25922
|
-
const base = join4(repoRoot, cleaned.slice(0, -2));
|
|
25923
|
-
if (!existsSync6(base) || !statSync2(base).isDirectory()) return [];
|
|
25924
|
-
return readdirSync3(base).map((entry) => join4(base, entry)).filter((p) => {
|
|
25925
|
-
try {
|
|
25926
|
-
return statSync2(p).isDirectory();
|
|
25927
|
-
} catch {
|
|
25928
|
-
return false;
|
|
25929
|
-
}
|
|
25930
|
-
});
|
|
25931
|
-
}
|
|
25932
|
-
const abs = join4(repoRoot, cleaned);
|
|
25933
|
-
if (existsSync6(abs) && statSync2(abs).isDirectory()) return [abs];
|
|
25934
|
-
return [];
|
|
25935
|
-
}
|
|
25936
|
-
function isLikelyDeployable(absDir) {
|
|
25937
|
-
if (existsSync6(join4(absDir, "Dockerfile"))) return true;
|
|
25938
|
-
const pkgPath = join4(absDir, "package.json");
|
|
25939
|
-
if (existsSync6(pkgPath)) {
|
|
25940
|
-
try {
|
|
25941
|
-
const pkg2 = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
25942
|
-
if (pkg2.scripts?.start || pkg2.scripts?.dev || pkg2.scripts?.serve) return true;
|
|
25943
|
-
if (pkg2.bin) return true;
|
|
25944
|
-
} catch {
|
|
25945
|
-
}
|
|
25946
|
-
}
|
|
25947
|
-
if (existsSync6(join4(absDir, "next.config.js")) || existsSync6(join4(absDir, "next.config.mjs")) || existsSync6(join4(absDir, "next.config.ts")) || existsSync6(join4(absDir, "go.mod")) || existsSync6(join4(absDir, "Cargo.toml")) || existsSync6(join4(absDir, "manage.py")) || existsSync6(join4(absDir, "pyproject.toml")) || existsSync6(join4(absDir, "Gemfile"))) {
|
|
25948
|
-
return true;
|
|
25949
|
-
}
|
|
25950
|
-
return false;
|
|
25951
|
-
}
|
|
25952
|
-
function deriveName(repoRoot, absDir) {
|
|
25953
|
-
const rel = toPosix(relative2(repoRoot, absDir));
|
|
25954
|
-
const parts = rel.split("/");
|
|
25955
|
-
return parts[parts.length - 1].toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
25956
|
-
}
|
|
25957
|
-
function toPosix(p) {
|
|
25958
|
-
return p.split(sep).join(posix.sep);
|
|
25959
|
-
}
|
|
25960
|
-
function changedFilesSince(repoRoot, baseRef) {
|
|
25961
|
-
const ref = baseRef ?? autoBaseRef(repoRoot);
|
|
25962
|
-
if (!ref) return [];
|
|
25963
|
-
try {
|
|
25964
|
-
const out = execFileSync8("git", ["diff", "--name-only", `${ref}..HEAD`], {
|
|
25965
|
-
cwd: repoRoot,
|
|
25966
|
-
encoding: "utf-8",
|
|
25967
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
25968
|
-
});
|
|
25969
|
-
return out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
25970
|
-
} catch {
|
|
25971
|
-
return [];
|
|
25972
|
-
}
|
|
25973
|
-
}
|
|
25974
|
-
function autoBaseRef(repoRoot) {
|
|
25975
|
-
try {
|
|
25976
|
-
const branch = execFileSync8("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
25977
|
-
cwd: repoRoot,
|
|
25978
|
-
encoding: "utf-8",
|
|
25979
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
25980
|
-
}).trim();
|
|
25981
|
-
if (branch && branch !== "HEAD") {
|
|
25982
|
-
try {
|
|
25983
|
-
execFileSync8("git", ["rev-parse", "--verify", `origin/${branch}`], {
|
|
25984
|
-
cwd: repoRoot,
|
|
25985
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
25986
|
-
});
|
|
25987
|
-
return `origin/${branch}`;
|
|
25988
|
-
} catch {
|
|
25989
|
-
}
|
|
25990
|
-
}
|
|
25991
|
-
} catch {
|
|
25992
|
-
}
|
|
25993
|
-
try {
|
|
25994
|
-
execFileSync8("git", ["rev-parse", "--verify", "HEAD~1"], {
|
|
25995
|
-
cwd: repoRoot,
|
|
25996
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
25997
|
-
});
|
|
25998
|
-
return "HEAD~1";
|
|
25999
|
-
} catch {
|
|
26000
|
-
return null;
|
|
26001
|
-
}
|
|
26002
|
-
}
|
|
26003
|
-
function serviceWasChanged(rootDir, changedPaths) {
|
|
26004
|
-
if (changedPaths.length === 0) return true;
|
|
26005
|
-
const normalized = rootDir.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/$/, "");
|
|
26006
|
-
if (normalized === "" || normalized === "." || normalized === "/") return true;
|
|
26007
|
-
const prefix = normalized + "/";
|
|
26008
|
-
return changedPaths.some((p) => p === normalized || p.startsWith(prefix));
|
|
26009
|
-
}
|
|
26010
|
-
|
|
26011
|
-
// src/commands/monorepo.ts
|
|
26012
26068
|
function requireLogin3() {
|
|
26013
26069
|
if (!isLoggedIn()) {
|
|
26014
26070
|
console.error(source_default.red('Not logged in. Run "lm login" first.'));
|