@launchmatic/cli 0.6.3 → 0.6.5
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 +303 -241
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6592,49 +6592,49 @@ var require_fast_uri = __commonJS({
|
|
|
6592
6592
|
schemelessOptions.skipEscape = true;
|
|
6593
6593
|
return serialize(resolved, schemelessOptions);
|
|
6594
6594
|
}
|
|
6595
|
-
function resolveComponent(base,
|
|
6595
|
+
function resolveComponent(base, relative4, options, skipNormalization) {
|
|
6596
6596
|
const target = {};
|
|
6597
6597
|
if (!skipNormalization) {
|
|
6598
6598
|
base = parse(serialize(base, options), options);
|
|
6599
|
-
|
|
6599
|
+
relative4 = parse(serialize(relative4, options), options);
|
|
6600
6600
|
}
|
|
6601
6601
|
options = options || {};
|
|
6602
|
-
if (!options.tolerant &&
|
|
6603
|
-
target.scheme =
|
|
6604
|
-
target.userinfo =
|
|
6605
|
-
target.host =
|
|
6606
|
-
target.port =
|
|
6607
|
-
target.path = removeDotSegments(
|
|
6608
|
-
target.query =
|
|
6602
|
+
if (!options.tolerant && relative4.scheme) {
|
|
6603
|
+
target.scheme = relative4.scheme;
|
|
6604
|
+
target.userinfo = relative4.userinfo;
|
|
6605
|
+
target.host = relative4.host;
|
|
6606
|
+
target.port = relative4.port;
|
|
6607
|
+
target.path = removeDotSegments(relative4.path || "");
|
|
6608
|
+
target.query = relative4.query;
|
|
6609
6609
|
} else {
|
|
6610
|
-
if (
|
|
6611
|
-
target.userinfo =
|
|
6612
|
-
target.host =
|
|
6613
|
-
target.port =
|
|
6614
|
-
target.path = removeDotSegments(
|
|
6615
|
-
target.query =
|
|
6610
|
+
if (relative4.userinfo !== void 0 || relative4.host !== void 0 || relative4.port !== void 0) {
|
|
6611
|
+
target.userinfo = relative4.userinfo;
|
|
6612
|
+
target.host = relative4.host;
|
|
6613
|
+
target.port = relative4.port;
|
|
6614
|
+
target.path = removeDotSegments(relative4.path || "");
|
|
6615
|
+
target.query = relative4.query;
|
|
6616
6616
|
} else {
|
|
6617
|
-
if (!
|
|
6617
|
+
if (!relative4.path) {
|
|
6618
6618
|
target.path = base.path;
|
|
6619
|
-
if (
|
|
6620
|
-
target.query =
|
|
6619
|
+
if (relative4.query !== void 0) {
|
|
6620
|
+
target.query = relative4.query;
|
|
6621
6621
|
} else {
|
|
6622
6622
|
target.query = base.query;
|
|
6623
6623
|
}
|
|
6624
6624
|
} else {
|
|
6625
|
-
if (
|
|
6626
|
-
target.path = removeDotSegments(
|
|
6625
|
+
if (relative4.path[0] === "/") {
|
|
6626
|
+
target.path = removeDotSegments(relative4.path);
|
|
6627
6627
|
} else {
|
|
6628
6628
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
6629
|
-
target.path = "/" +
|
|
6629
|
+
target.path = "/" + relative4.path;
|
|
6630
6630
|
} else if (!base.path) {
|
|
6631
|
-
target.path =
|
|
6631
|
+
target.path = relative4.path;
|
|
6632
6632
|
} else {
|
|
6633
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
6633
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative4.path;
|
|
6634
6634
|
}
|
|
6635
6635
|
target.path = removeDotSegments(target.path);
|
|
6636
6636
|
}
|
|
6637
|
-
target.query =
|
|
6637
|
+
target.query = relative4.query;
|
|
6638
6638
|
}
|
|
6639
6639
|
target.userinfo = base.userinfo;
|
|
6640
6640
|
target.host = base.host;
|
|
@@ -6642,7 +6642,7 @@ var require_fast_uri = __commonJS({
|
|
|
6642
6642
|
}
|
|
6643
6643
|
target.scheme = base.scheme;
|
|
6644
6644
|
}
|
|
6645
|
-
target.fragment =
|
|
6645
|
+
target.fragment = relative4.fragment;
|
|
6646
6646
|
return target;
|
|
6647
6647
|
}
|
|
6648
6648
|
function equal(uriA, uriB, options) {
|
|
@@ -21817,7 +21817,8 @@ function prompt(question) {
|
|
|
21817
21817
|
}
|
|
21818
21818
|
|
|
21819
21819
|
// src/commands/deploy.ts
|
|
21820
|
-
import { execFileSync as
|
|
21820
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
21821
|
+
import { relative as relative2 } from "path";
|
|
21821
21822
|
|
|
21822
21823
|
// ../../node_modules/.pnpm/ws@8.19.0/node_modules/ws/wrapper.mjs
|
|
21823
21824
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -21827,7 +21828,253 @@ var import_websocket = __toESM(require_websocket(), 1);
|
|
|
21827
21828
|
var import_websocket_server = __toESM(require_websocket_server(), 1);
|
|
21828
21829
|
var wrapper_default = import_websocket.default;
|
|
21829
21830
|
|
|
21831
|
+
// src/monorepo.ts
|
|
21832
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync, statSync, writeFileSync as writeFileSync3 } from "fs";
|
|
21833
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
21834
|
+
import { join as join2, relative, sep, posix } from "path";
|
|
21835
|
+
var MANIFEST_FILE = "launchmatic.json";
|
|
21836
|
+
function findRepoRoot(start = process.cwd()) {
|
|
21837
|
+
try {
|
|
21838
|
+
return execFileSync4("git", ["rev-parse", "--show-toplevel"], {
|
|
21839
|
+
cwd: start,
|
|
21840
|
+
encoding: "utf-8",
|
|
21841
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
21842
|
+
}).trim();
|
|
21843
|
+
} catch {
|
|
21844
|
+
return start;
|
|
21845
|
+
}
|
|
21846
|
+
}
|
|
21847
|
+
function manifestPath(repoRoot = findRepoRoot()) {
|
|
21848
|
+
return join2(repoRoot, MANIFEST_FILE);
|
|
21849
|
+
}
|
|
21850
|
+
function readManifest(repoRoot = findRepoRoot()) {
|
|
21851
|
+
const p = manifestPath(repoRoot);
|
|
21852
|
+
if (!existsSync3(p)) return null;
|
|
21853
|
+
try {
|
|
21854
|
+
const parsed = JSON.parse(readFileSync3(p, "utf-8"));
|
|
21855
|
+
if (parsed.version !== 1 || !Array.isArray(parsed.services)) {
|
|
21856
|
+
throw new Error(`${MANIFEST_FILE} has unexpected shape`);
|
|
21857
|
+
}
|
|
21858
|
+
return parsed;
|
|
21859
|
+
} catch (err) {
|
|
21860
|
+
throw new Error(`Could not read ${MANIFEST_FILE}: ${err instanceof Error ? err.message : String(err)}`);
|
|
21861
|
+
}
|
|
21862
|
+
}
|
|
21863
|
+
function writeManifest(manifest, repoRoot = findRepoRoot()) {
|
|
21864
|
+
writeFileSync3(manifestPath(repoRoot), JSON.stringify(manifest, null, 2) + "\n");
|
|
21865
|
+
}
|
|
21866
|
+
function discoverServices(repoRoot = findRepoRoot()) {
|
|
21867
|
+
const globs = readWorkspaceGlobs(repoRoot);
|
|
21868
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
21869
|
+
for (const glob of globs) {
|
|
21870
|
+
for (const dir of expandGlob(repoRoot, glob)) {
|
|
21871
|
+
dirs.add(dir);
|
|
21872
|
+
}
|
|
21873
|
+
}
|
|
21874
|
+
if (globs.length === 0) {
|
|
21875
|
+
for (const conv of ["apps", "services"]) {
|
|
21876
|
+
const base = join2(repoRoot, conv);
|
|
21877
|
+
if (existsSync3(base) && statSync(base).isDirectory()) {
|
|
21878
|
+
for (const entry of readdirSync(base)) {
|
|
21879
|
+
const full = join2(base, entry);
|
|
21880
|
+
if (statSync(full).isDirectory()) dirs.add(full);
|
|
21881
|
+
}
|
|
21882
|
+
}
|
|
21883
|
+
}
|
|
21884
|
+
}
|
|
21885
|
+
const out = [];
|
|
21886
|
+
for (const absDir of dirs) {
|
|
21887
|
+
if (!isLikelyDeployable(absDir)) continue;
|
|
21888
|
+
const detection = detectLocal(absDir);
|
|
21889
|
+
out.push({
|
|
21890
|
+
name: deriveName(repoRoot, absDir),
|
|
21891
|
+
rootDir: toPosix(relative(repoRoot, absDir)),
|
|
21892
|
+
framework: detection.framework,
|
|
21893
|
+
buildCmd: detection.buildCmd,
|
|
21894
|
+
startCmd: detection.startCmd,
|
|
21895
|
+
port: detection.port
|
|
21896
|
+
});
|
|
21897
|
+
}
|
|
21898
|
+
out.sort((a, b) => a.rootDir.localeCompare(b.rootDir));
|
|
21899
|
+
return out;
|
|
21900
|
+
}
|
|
21901
|
+
function readWorkspaceGlobs(repoRoot) {
|
|
21902
|
+
const globs = [];
|
|
21903
|
+
const pnpmFile = join2(repoRoot, "pnpm-workspace.yaml");
|
|
21904
|
+
if (existsSync3(pnpmFile)) {
|
|
21905
|
+
const text = readFileSync3(pnpmFile, "utf-8");
|
|
21906
|
+
let inPackages = false;
|
|
21907
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
21908
|
+
const line = rawLine.replace(/#.*$/, "").trimEnd();
|
|
21909
|
+
if (/^packages\s*:/i.test(line)) {
|
|
21910
|
+
inPackages = true;
|
|
21911
|
+
continue;
|
|
21912
|
+
}
|
|
21913
|
+
if (inPackages) {
|
|
21914
|
+
const m = line.match(/^\s*-\s*['"]?([^'"#]+?)['"]?\s*$/);
|
|
21915
|
+
if (m) {
|
|
21916
|
+
globs.push(m[1].trim());
|
|
21917
|
+
continue;
|
|
21918
|
+
}
|
|
21919
|
+
if (line.trim() && !line.startsWith(" ") && !line.startsWith(" ")) {
|
|
21920
|
+
inPackages = false;
|
|
21921
|
+
}
|
|
21922
|
+
}
|
|
21923
|
+
}
|
|
21924
|
+
}
|
|
21925
|
+
const pkgFile = join2(repoRoot, "package.json");
|
|
21926
|
+
if (existsSync3(pkgFile)) {
|
|
21927
|
+
try {
|
|
21928
|
+
const pkg2 = JSON.parse(readFileSync3(pkgFile, "utf-8"));
|
|
21929
|
+
if (Array.isArray(pkg2.workspaces)) {
|
|
21930
|
+
globs.push(...pkg2.workspaces.filter((g) => typeof g === "string"));
|
|
21931
|
+
} else if (pkg2.workspaces && Array.isArray(pkg2.workspaces.packages)) {
|
|
21932
|
+
globs.push(...pkg2.workspaces.packages.filter((g) => typeof g === "string"));
|
|
21933
|
+
}
|
|
21934
|
+
} catch {
|
|
21935
|
+
}
|
|
21936
|
+
}
|
|
21937
|
+
return globs;
|
|
21938
|
+
}
|
|
21939
|
+
function expandGlob(repoRoot, glob) {
|
|
21940
|
+
const cleaned = glob.replace(/^\.\//, "").replace(/\/$/, "");
|
|
21941
|
+
if (cleaned.endsWith("/*")) {
|
|
21942
|
+
const base = join2(repoRoot, cleaned.slice(0, -2));
|
|
21943
|
+
if (!existsSync3(base) || !statSync(base).isDirectory()) return [];
|
|
21944
|
+
return readdirSync(base).map((entry) => join2(base, entry)).filter((p) => {
|
|
21945
|
+
try {
|
|
21946
|
+
return statSync(p).isDirectory();
|
|
21947
|
+
} catch {
|
|
21948
|
+
return false;
|
|
21949
|
+
}
|
|
21950
|
+
});
|
|
21951
|
+
}
|
|
21952
|
+
const abs = join2(repoRoot, cleaned);
|
|
21953
|
+
if (existsSync3(abs) && statSync(abs).isDirectory()) return [abs];
|
|
21954
|
+
return [];
|
|
21955
|
+
}
|
|
21956
|
+
function isLikelyDeployable(absDir) {
|
|
21957
|
+
if (existsSync3(join2(absDir, "Dockerfile"))) return true;
|
|
21958
|
+
const pkgPath = join2(absDir, "package.json");
|
|
21959
|
+
if (existsSync3(pkgPath)) {
|
|
21960
|
+
try {
|
|
21961
|
+
const pkg2 = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
21962
|
+
if (pkg2.scripts?.start || pkg2.scripts?.dev || pkg2.scripts?.serve) return true;
|
|
21963
|
+
if (pkg2.bin) return true;
|
|
21964
|
+
} catch {
|
|
21965
|
+
}
|
|
21966
|
+
}
|
|
21967
|
+
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"))) {
|
|
21968
|
+
return true;
|
|
21969
|
+
}
|
|
21970
|
+
return false;
|
|
21971
|
+
}
|
|
21972
|
+
function deriveName(repoRoot, absDir) {
|
|
21973
|
+
const rel = toPosix(relative(repoRoot, absDir));
|
|
21974
|
+
const parts = rel.split("/");
|
|
21975
|
+
return parts[parts.length - 1].toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
21976
|
+
}
|
|
21977
|
+
function toPosix(p) {
|
|
21978
|
+
return p.split(sep).join(posix.sep);
|
|
21979
|
+
}
|
|
21980
|
+
function changedFilesSince(repoRoot, baseRef) {
|
|
21981
|
+
const ref = baseRef ?? autoBaseRef(repoRoot);
|
|
21982
|
+
if (!ref) return [];
|
|
21983
|
+
try {
|
|
21984
|
+
const out = execFileSync4("git", ["diff", "--name-only", `${ref}..HEAD`], {
|
|
21985
|
+
cwd: repoRoot,
|
|
21986
|
+
encoding: "utf-8",
|
|
21987
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
21988
|
+
});
|
|
21989
|
+
return out.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
21990
|
+
} catch {
|
|
21991
|
+
return [];
|
|
21992
|
+
}
|
|
21993
|
+
}
|
|
21994
|
+
function autoBaseRef(repoRoot) {
|
|
21995
|
+
try {
|
|
21996
|
+
const branch = execFileSync4("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
21997
|
+
cwd: repoRoot,
|
|
21998
|
+
encoding: "utf-8",
|
|
21999
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
22000
|
+
}).trim();
|
|
22001
|
+
if (branch && branch !== "HEAD") {
|
|
22002
|
+
try {
|
|
22003
|
+
execFileSync4("git", ["rev-parse", "--verify", `origin/${branch}`], {
|
|
22004
|
+
cwd: repoRoot,
|
|
22005
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
22006
|
+
});
|
|
22007
|
+
return `origin/${branch}`;
|
|
22008
|
+
} catch {
|
|
22009
|
+
}
|
|
22010
|
+
}
|
|
22011
|
+
} catch {
|
|
22012
|
+
}
|
|
22013
|
+
try {
|
|
22014
|
+
execFileSync4("git", ["rev-parse", "--verify", "HEAD~1"], {
|
|
22015
|
+
cwd: repoRoot,
|
|
22016
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
22017
|
+
});
|
|
22018
|
+
return "HEAD~1";
|
|
22019
|
+
} catch {
|
|
22020
|
+
return null;
|
|
22021
|
+
}
|
|
22022
|
+
}
|
|
22023
|
+
function serviceWasChanged(rootDir, changedPaths) {
|
|
22024
|
+
if (changedPaths.length === 0) return true;
|
|
22025
|
+
const normalized = rootDir.replace(/\\/g, "/").replace(/^\.?\//, "").replace(/\/$/, "");
|
|
22026
|
+
if (normalized === "" || normalized === "." || normalized === "/") return true;
|
|
22027
|
+
const prefix = normalized + "/";
|
|
22028
|
+
return changedPaths.some((p) => p === normalized || p.startsWith(prefix));
|
|
22029
|
+
}
|
|
22030
|
+
|
|
21830
22031
|
// src/commands/deploy.ts
|
|
22032
|
+
function resolveDeployTarget(cwd, opts) {
|
|
22033
|
+
try {
|
|
22034
|
+
const ctx = readContext();
|
|
22035
|
+
return { serviceId: ctx.serviceId };
|
|
22036
|
+
} catch {
|
|
22037
|
+
}
|
|
22038
|
+
const repoRoot = findRepoRoot(cwd);
|
|
22039
|
+
const manifest = readManifest(repoRoot);
|
|
22040
|
+
if (!manifest || manifest.services.length === 0) {
|
|
22041
|
+
throw new Error(
|
|
22042
|
+
`No .launchmatic.json in this directory and no ${MANIFEST_FILE} at the repo root.
|
|
22043
|
+
Run ${source_default.bold("lm init")} to bind a service, or ${source_default.bold("lm monorepo init")} for a multi-service repo.`
|
|
22044
|
+
);
|
|
22045
|
+
}
|
|
22046
|
+
const findByName = (name) => manifest.services.find((s) => s.name === name);
|
|
22047
|
+
if (opts.service) {
|
|
22048
|
+
const match = findByName(opts.service);
|
|
22049
|
+
if (!match) {
|
|
22050
|
+
const names2 = manifest.services.map((s) => s.name).join(", ");
|
|
22051
|
+
throw new Error(
|
|
22052
|
+
`No service named "${opts.service}" in ${MANIFEST_FILE}. Known services: ${names2}`
|
|
22053
|
+
);
|
|
22054
|
+
}
|
|
22055
|
+
if (!match.serviceId) {
|
|
22056
|
+
throw new Error(
|
|
22057
|
+
`Service "${match.name}" has no serviceId \u2014 re-run ${source_default.bold("lm monorepo init")}.`
|
|
22058
|
+
);
|
|
22059
|
+
}
|
|
22060
|
+
return { serviceId: match.serviceId, entryName: match.name };
|
|
22061
|
+
}
|
|
22062
|
+
const relCwd = relative2(repoRoot, cwd).split(/[\\/]/).filter(Boolean).join("/");
|
|
22063
|
+
const auto = manifest.services.find((s) => {
|
|
22064
|
+
const rd = s.rootDir.replace(/^\/+|\/+$/g, "");
|
|
22065
|
+
return rd === relCwd;
|
|
22066
|
+
});
|
|
22067
|
+
if (auto?.serviceId) {
|
|
22068
|
+
return { serviceId: auto.serviceId, entryName: auto.name };
|
|
22069
|
+
}
|
|
22070
|
+
const names = manifest.services.map((s) => s.name).join(", ");
|
|
22071
|
+
throw new Error(
|
|
22072
|
+
`Couldn't auto-detect which service to deploy from this directory.
|
|
22073
|
+
cwd: ${cwd}
|
|
22074
|
+
repo root: ${repoRoot}
|
|
22075
|
+
Try ${source_default.bold(`lm deploy --service <name>`)} (${names}) or ${source_default.bold("lm up")} to deploy everything.`
|
|
22076
|
+
);
|
|
22077
|
+
}
|
|
21831
22078
|
function registerDeploy(program3) {
|
|
21832
22079
|
program3.command("deploy").description("Build and deploy the current service").option(
|
|
21833
22080
|
"--dockerfile <path>",
|
|
@@ -21835,14 +22082,28 @@ function registerDeploy(program3) {
|
|
|
21835
22082
|
).option(
|
|
21836
22083
|
"--root-dir <path>",
|
|
21837
22084
|
"Root directory for the build (default: /)"
|
|
21838
|
-
).option("--build-cmd <cmd>", "Override the build command").option("--start-cmd <cmd>", "Override the start command").
|
|
22085
|
+
).option("--build-cmd <cmd>", "Override the build command").option("--start-cmd <cmd>", "Override the start command").option(
|
|
22086
|
+
"-s, --service <name>",
|
|
22087
|
+
"In a monorepo, deploy a service by name from anywhere in the repo (reads launchmatic.json at the repo root)"
|
|
22088
|
+
).action(async (opts) => {
|
|
21839
22089
|
if (!isLoggedIn()) {
|
|
21840
22090
|
console.error(source_default.red('Not logged in. Run "lm login" first.'));
|
|
21841
22091
|
process.exitCode = 1;
|
|
21842
22092
|
return;
|
|
21843
22093
|
}
|
|
21844
|
-
const ctx = readContext();
|
|
21845
22094
|
const cwd = process.cwd();
|
|
22095
|
+
let target;
|
|
22096
|
+
try {
|
|
22097
|
+
target = resolveDeployTarget(cwd, { service: opts.service });
|
|
22098
|
+
} catch (err) {
|
|
22099
|
+
console.error(source_default.red(err instanceof Error ? err.message : String(err)));
|
|
22100
|
+
process.exitCode = 1;
|
|
22101
|
+
return;
|
|
22102
|
+
}
|
|
22103
|
+
const ctx = { serviceId: target.serviceId, projectId: "", teamId: "" };
|
|
22104
|
+
if (target.entryName) {
|
|
22105
|
+
console.log(source_default.dim(`Service: ${target.entryName} (from ${MANIFEST_FILE})`));
|
|
22106
|
+
}
|
|
21846
22107
|
let gitInfo = getGitInfo(cwd);
|
|
21847
22108
|
const branch = gitInfo.repoBranch;
|
|
21848
22109
|
if (gitInfo.hasUncommitted) {
|
|
@@ -21853,7 +22114,7 @@ function registerDeploy(program3) {
|
|
|
21853
22114
|
if (gitInfo.hasUnpushed && gitInfo.hasRemote) {
|
|
21854
22115
|
const pushSpinner = ora("Pushing commits to remote...").start();
|
|
21855
22116
|
try {
|
|
21856
|
-
|
|
22117
|
+
execFileSync5("git", ["push", "origin", branch], {
|
|
21857
22118
|
cwd,
|
|
21858
22119
|
encoding: "utf-8",
|
|
21859
22120
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -21877,19 +22138,19 @@ function registerDeploy(program3) {
|
|
|
21877
22138
|
const repoSpinner = ora("No git remote \u2014 creating GitHub repository...").start();
|
|
21878
22139
|
try {
|
|
21879
22140
|
try {
|
|
21880
|
-
|
|
22141
|
+
execFileSync5("git", ["rev-parse", "--git-dir"], { cwd, stdio: "pipe" });
|
|
21881
22142
|
} catch {
|
|
21882
|
-
|
|
22143
|
+
execFileSync5("git", ["init"], { cwd, stdio: "pipe" });
|
|
21883
22144
|
}
|
|
21884
22145
|
try {
|
|
21885
|
-
const status =
|
|
22146
|
+
const status = execFileSync5("git", ["status", "--porcelain"], {
|
|
21886
22147
|
cwd,
|
|
21887
22148
|
encoding: "utf-8",
|
|
21888
22149
|
stdio: ["pipe", "pipe", "pipe"]
|
|
21889
22150
|
});
|
|
21890
22151
|
if (status.trim().length > 0) {
|
|
21891
|
-
|
|
21892
|
-
|
|
22152
|
+
execFileSync5("git", ["add", "-A"], { cwd, stdio: "pipe" });
|
|
22153
|
+
execFileSync5(
|
|
21893
22154
|
"git",
|
|
21894
22155
|
["commit", "-m", "Initial commit"],
|
|
21895
22156
|
{ cwd, stdio: "pipe" }
|
|
@@ -21902,18 +22163,18 @@ function registerDeploy(program3) {
|
|
|
21902
22163
|
body: JSON.stringify({})
|
|
21903
22164
|
});
|
|
21904
22165
|
try {
|
|
21905
|
-
|
|
22166
|
+
execFileSync5("git", ["remote", "add", "origin", repoData.cloneUrl], { cwd, stdio: "pipe" });
|
|
21906
22167
|
} catch {
|
|
21907
|
-
|
|
22168
|
+
execFileSync5("git", ["remote", "set-url", "origin", repoData.cloneUrl], { cwd, stdio: "pipe" });
|
|
21908
22169
|
}
|
|
21909
22170
|
const localBranch = (() => {
|
|
21910
22171
|
try {
|
|
21911
|
-
return
|
|
22172
|
+
return execFileSync5("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd, encoding: "utf-8" }).trim();
|
|
21912
22173
|
} catch {
|
|
21913
22174
|
return "main";
|
|
21914
22175
|
}
|
|
21915
22176
|
})();
|
|
21916
|
-
|
|
22177
|
+
execFileSync5("git", ["push", "-u", "origin", localBranch], { cwd, stdio: ["pipe", "pipe", "pipe"] });
|
|
21917
22178
|
repoSpinner.succeed(`Repository created: ${source_default.dim(repoData.repoFullName)} (private)`);
|
|
21918
22179
|
gitInfo = getGitInfo(cwd);
|
|
21919
22180
|
} catch (err) {
|
|
@@ -22240,206 +22501,6 @@ function registerDomains(program3) {
|
|
|
22240
22501
|
});
|
|
22241
22502
|
}
|
|
22242
22503
|
|
|
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
|
-
|
|
22443
22504
|
// src/commands/status.ts
|
|
22444
22505
|
function registerStatus(program3) {
|
|
22445
22506
|
program3.command("status").description("Show service and deployment status").option(
|
|
@@ -22562,6 +22623,7 @@ function renderServiceBlock(data, { indent }) {
|
|
|
22562
22623
|
}
|
|
22563
22624
|
}
|
|
22564
22625
|
function statusColor(status) {
|
|
22626
|
+
if (!status) return source_default.dim("unknown");
|
|
22565
22627
|
switch (status.toUpperCase()) {
|
|
22566
22628
|
case "ACTIVE":
|
|
22567
22629
|
case "RUNNING":
|
|
@@ -24886,7 +24948,7 @@ function validateGeneratedApp(files, ctx = {}) {
|
|
|
24886
24948
|
|
|
24887
24949
|
// ../../packages/validator/dist/validate-disk.js
|
|
24888
24950
|
import { readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
|
|
24889
|
-
import { join as join4, relative as
|
|
24951
|
+
import { join as join4, relative as relative3 } from "path";
|
|
24890
24952
|
var MAX_FILE_SIZE = 512 * 1024;
|
|
24891
24953
|
var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".next", "dist", "build", "__pycache__", "target"]);
|
|
24892
24954
|
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
@@ -24928,7 +24990,7 @@ function collectFiles(dir, rootDir, out) {
|
|
|
24928
24990
|
const stat = statSync2(fullPath);
|
|
24929
24991
|
if (stat.size > MAX_FILE_SIZE)
|
|
24930
24992
|
continue;
|
|
24931
|
-
out.push({ path:
|
|
24993
|
+
out.push({ path: relative3(rootDir, fullPath).replace(/\\/g, "/"), content: readFileSync5(fullPath, "utf-8") });
|
|
24932
24994
|
} catch {
|
|
24933
24995
|
}
|
|
24934
24996
|
continue;
|
|
@@ -24940,7 +25002,7 @@ function collectFiles(dir, rootDir, out) {
|
|
|
24940
25002
|
const stat = statSync2(fullPath);
|
|
24941
25003
|
if (stat.size > MAX_FILE_SIZE)
|
|
24942
25004
|
continue;
|
|
24943
|
-
out.push({ path:
|
|
25005
|
+
out.push({ path: relative3(rootDir, fullPath).replace(/\\/g, "/"), content: readFileSync5(fullPath, "utf-8") });
|
|
24944
25006
|
} catch {
|
|
24945
25007
|
}
|
|
24946
25008
|
}
|