@pmate/cli 0.24.3 → 0.24.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 +125 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12234,6 +12234,7 @@ var PrCommandHandler = class {
|
|
|
12234
12234
|
return Boolean(context.status.trim()) || Boolean(context.diff.trim()) || Boolean(context.diffCached.trim());
|
|
12235
12235
|
}
|
|
12236
12236
|
async handleNoLocalChanges(context, existingPrUrl) {
|
|
12237
|
+
await this.pushCurrentBranchIfAheadOfRemote(context.currentBranch);
|
|
12237
12238
|
if (existingPrUrl) {
|
|
12238
12239
|
await this.pushBranchIfNeeded(context.currentBranch, context.baseBranch);
|
|
12239
12240
|
ui.info(`PR already exists: ${existingPrUrl}`);
|
|
@@ -12436,6 +12437,51 @@ ${lines}`;
|
|
|
12436
12437
|
ui.info(`Pushing branch ${branchName}`);
|
|
12437
12438
|
await this.run("git", ["push", "-u", "origin", branchName]);
|
|
12438
12439
|
}
|
|
12440
|
+
async pushCurrentBranchIfAheadOfRemote(branchName) {
|
|
12441
|
+
if (!branchName || branchName === "HEAD") {
|
|
12442
|
+
throw new Error("Cannot inspect remote sync from detached HEAD.");
|
|
12443
|
+
}
|
|
12444
|
+
const remoteRef = await this.getRemoteTrackingRef(branchName);
|
|
12445
|
+
if (!remoteRef) {
|
|
12446
|
+
ui.info(`No remote branch found for ${branchName}; pushing branch`);
|
|
12447
|
+
await this.pushCurrentBranch();
|
|
12448
|
+
return;
|
|
12449
|
+
}
|
|
12450
|
+
const counts = (await this.runCapture("git", [
|
|
12451
|
+
"rev-list",
|
|
12452
|
+
"--left-right",
|
|
12453
|
+
"--count",
|
|
12454
|
+
`${remoteRef}...HEAD`
|
|
12455
|
+
])).trim().split(/\s+/).map((item) => Number(item));
|
|
12456
|
+
const behind = counts[0] ?? 0;
|
|
12457
|
+
const ahead = counts[1] ?? 0;
|
|
12458
|
+
if (ahead <= 0) {
|
|
12459
|
+
return;
|
|
12460
|
+
}
|
|
12461
|
+
if (behind > 0) {
|
|
12462
|
+
ui.warn(
|
|
12463
|
+
`Current branch is ${ahead} commit(s) ahead and ${behind} commit(s) behind ${remoteRef}; pushing may require reconciliation.`
|
|
12464
|
+
);
|
|
12465
|
+
}
|
|
12466
|
+
await this.pushCurrentBranch();
|
|
12467
|
+
}
|
|
12468
|
+
async getRemoteTrackingRef(branchName) {
|
|
12469
|
+
const upstream = (await this.runCapture(
|
|
12470
|
+
"git",
|
|
12471
|
+
["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"],
|
|
12472
|
+
true
|
|
12473
|
+
)).trim();
|
|
12474
|
+
if (upstream) {
|
|
12475
|
+
return upstream;
|
|
12476
|
+
}
|
|
12477
|
+
const originBranch = `origin/${branchName}`;
|
|
12478
|
+
const hasOriginBranch = await this.run(
|
|
12479
|
+
"git",
|
|
12480
|
+
["show-ref", "--verify", "--quiet", `refs/remotes/${originBranch}`],
|
|
12481
|
+
true
|
|
12482
|
+
);
|
|
12483
|
+
return hasOriginBranch === 0 ? originBranch : "";
|
|
12484
|
+
}
|
|
12439
12485
|
async pushCurrentBranch() {
|
|
12440
12486
|
const branchName = (await this.runCapture("git", ["rev-parse", "--abbrev-ref", "HEAD"])).trim();
|
|
12441
12487
|
if (!branchName || branchName === "HEAD") {
|
|
@@ -18081,6 +18127,12 @@ var ServiceRegistryNotFoundError = class extends Error {
|
|
|
18081
18127
|
this.name = "ServiceRegistryNotFoundError";
|
|
18082
18128
|
}
|
|
18083
18129
|
};
|
|
18130
|
+
var ServiceEnvNotFoundError = class extends Error {
|
|
18131
|
+
constructor(appId) {
|
|
18132
|
+
super(`Service env not found: ${appId}`);
|
|
18133
|
+
this.name = "ServiceEnvNotFoundError";
|
|
18134
|
+
}
|
|
18135
|
+
};
|
|
18084
18136
|
var ServiceRegistryClient = class {
|
|
18085
18137
|
constructor(baseUrl) {
|
|
18086
18138
|
this.baseUrl = baseUrl;
|
|
@@ -18120,7 +18172,7 @@ var ServiceRegistryClient = class {
|
|
|
18120
18172
|
payload
|
|
18121
18173
|
);
|
|
18122
18174
|
}
|
|
18123
|
-
async listEnvItems(
|
|
18175
|
+
async listEnvItems(serviceId, options = {}) {
|
|
18124
18176
|
const params = new URLSearchParams();
|
|
18125
18177
|
if (options.scope) {
|
|
18126
18178
|
params.set("scope", options.scope);
|
|
@@ -18130,14 +18182,14 @@ var ServiceRegistryClient = class {
|
|
|
18130
18182
|
}
|
|
18131
18183
|
const payload = await this.request(
|
|
18132
18184
|
"GET",
|
|
18133
|
-
`/
|
|
18185
|
+
`/services/by-id/${encodeURIComponent(serviceId)}/env${params.size > 0 ? `?${params.toString()}` : ""}`
|
|
18134
18186
|
);
|
|
18135
18187
|
return payload.items;
|
|
18136
18188
|
}
|
|
18137
|
-
async resolveEnv(
|
|
18189
|
+
async resolveEnv(serviceId, options) {
|
|
18138
18190
|
return this.requestWithBody(
|
|
18139
18191
|
"POST",
|
|
18140
|
-
`/
|
|
18192
|
+
`/services/by-id/${encodeURIComponent(serviceId)}/env/resolve`,
|
|
18141
18193
|
{
|
|
18142
18194
|
stage: options.stage,
|
|
18143
18195
|
...options.branch ? { branch: options.branch } : {},
|
|
@@ -18145,6 +18197,35 @@ var ServiceRegistryClient = class {
|
|
|
18145
18197
|
}
|
|
18146
18198
|
);
|
|
18147
18199
|
}
|
|
18200
|
+
async pullEnv(serviceId, options) {
|
|
18201
|
+
const pathname = `/services/by-id/${encodeURIComponent(serviceId)}/env/pull`;
|
|
18202
|
+
const response = await fetch(`${this.resolveBaseUrl()}${pathname}`, {
|
|
18203
|
+
method: "POST",
|
|
18204
|
+
headers: {
|
|
18205
|
+
Authorization: `Bearer ${this.readToken()}`,
|
|
18206
|
+
Accept: "application/json",
|
|
18207
|
+
"Content-Type": "application/json"
|
|
18208
|
+
},
|
|
18209
|
+
body: JSON.stringify({
|
|
18210
|
+
stage: options.stage,
|
|
18211
|
+
...options.branch ? { branch: options.branch } : {},
|
|
18212
|
+
format: options.format
|
|
18213
|
+
})
|
|
18214
|
+
});
|
|
18215
|
+
let payload = null;
|
|
18216
|
+
try {
|
|
18217
|
+
payload = await response.json();
|
|
18218
|
+
} catch {
|
|
18219
|
+
payload = null;
|
|
18220
|
+
}
|
|
18221
|
+
if (response.status === 404) {
|
|
18222
|
+
throw new ServiceEnvNotFoundError(serviceId);
|
|
18223
|
+
}
|
|
18224
|
+
if (!response.ok || !payload?.success || payload.data === void 0) {
|
|
18225
|
+
throw new Error(payload?.message || `Env pull failed with status ${response.status}`);
|
|
18226
|
+
}
|
|
18227
|
+
return payload.data;
|
|
18228
|
+
}
|
|
18148
18229
|
resolveBaseUrl() {
|
|
18149
18230
|
return (this.baseUrl || process.env.PMATE_APPS_API_BASE_URL || "https://apps-api.pmate.chat").replace(/\/+$/, "");
|
|
18150
18231
|
}
|
|
@@ -18226,6 +18307,9 @@ function normalizeRepository(repository) {
|
|
|
18226
18307
|
|
|
18227
18308
|
// src/services/ServiceRuntimeRunner.ts
|
|
18228
18309
|
var ServiceRuntimeRunner = class {
|
|
18310
|
+
constructor(envProvider) {
|
|
18311
|
+
this.envProvider = envProvider;
|
|
18312
|
+
}
|
|
18229
18313
|
async deploy(service, options = {}) {
|
|
18230
18314
|
switch (this.getType(service)) {
|
|
18231
18315
|
case "vite":
|
|
@@ -18612,6 +18696,7 @@ var ServiceRuntimeRunner = class {
|
|
|
18612
18696
|
`pkg="${appDir}/package.json"`,
|
|
18613
18697
|
`if [ -f "$pkg" ] && node -e 'const fs=require("node:fs"); const p=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); process.exit(p?.scripts?.build ? 0 : 1);' "$pkg"; then (cd "${appDir}" && pnpm run build); fi`
|
|
18614
18698
|
].join("; ");
|
|
18699
|
+
const envSyncCommand = await this.buildNodeEnvSyncCommand(service.id, appDir);
|
|
18615
18700
|
const command = [
|
|
18616
18701
|
`cd "$HOME"`,
|
|
18617
18702
|
`[ -d "${repoDir}/.git" ] || git clone "${gitUrl}" "${repoDir}"`,
|
|
@@ -18621,6 +18706,7 @@ var ServiceRuntimeRunner = class {
|
|
|
18621
18706
|
`if [ -n "$(git status --porcelain)" ]; then git reset --hard HEAD; git clean -fd; fi`,
|
|
18622
18707
|
`git pull`,
|
|
18623
18708
|
`pnpm install`,
|
|
18709
|
+
...envSyncCommand ? [envSyncCommand] : [],
|
|
18624
18710
|
buildIfNeeded,
|
|
18625
18711
|
`if [ -f "${appDir}/ecosystem.config.cjs" ]; then eco="${appDir}/ecosystem.config.cjs"; elif [ -f "${appDir}/ecosystem.config.js" ]; then eco="${appDir}/ecosystem.config.js"; else echo "Missing pm2 config: ${appDir}/ecosystem.config.cjs|.js" 1>&2; exit 2; fi`,
|
|
18626
18712
|
pm2Start,
|
|
@@ -18628,6 +18714,33 @@ var ServiceRuntimeRunner = class {
|
|
|
18628
18714
|
].join(" && ");
|
|
18629
18715
|
await this.runRemoteCommand(server, command);
|
|
18630
18716
|
}
|
|
18717
|
+
async buildNodeEnvSyncCommand(serviceId, appDir) {
|
|
18718
|
+
if (!this.envProvider) {
|
|
18719
|
+
return void 0;
|
|
18720
|
+
}
|
|
18721
|
+
let pulled;
|
|
18722
|
+
try {
|
|
18723
|
+
pulled = await this.envProvider(serviceId, { stage: "prod", format: "dotenv" });
|
|
18724
|
+
} catch (error) {
|
|
18725
|
+
if (error instanceof ServiceEnvNotFoundError) {
|
|
18726
|
+
ui.warn(`No env found for ${serviceId}; skipping .env.local sync.`);
|
|
18727
|
+
return void 0;
|
|
18728
|
+
}
|
|
18729
|
+
throw error;
|
|
18730
|
+
}
|
|
18731
|
+
const content = pulled?.content;
|
|
18732
|
+
if (!content) {
|
|
18733
|
+
ui.warn(`No env found for ${serviceId}; skipping .env.local sync.`);
|
|
18734
|
+
return void 0;
|
|
18735
|
+
}
|
|
18736
|
+
const encodedContent = Buffer.from(content, "utf8").toString("base64");
|
|
18737
|
+
return [
|
|
18738
|
+
`env_local="${appDir}/.env.local"`,
|
|
18739
|
+
`mkdir -p "$(dirname "$env_local")"`,
|
|
18740
|
+
`printf '%s' ${shellQuote2(encodedContent)} | base64 --decode > "$env_local"`,
|
|
18741
|
+
`echo "[pmate] Synced env to $env_local"`
|
|
18742
|
+
].join(" && ");
|
|
18743
|
+
}
|
|
18631
18744
|
async deployScriptManaged(service) {
|
|
18632
18745
|
const network = this.getNetwork(service);
|
|
18633
18746
|
const runner = this.getRunner(service);
|
|
@@ -19473,6 +19586,9 @@ var ServiceRuntimeRunner = class {
|
|
|
19473
19586
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
19474
19587
|
}
|
|
19475
19588
|
};
|
|
19589
|
+
function shellQuote2(value) {
|
|
19590
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
19591
|
+
}
|
|
19476
19592
|
|
|
19477
19593
|
// src/commands/ServiceCommandHandler.ts
|
|
19478
19594
|
init_namespacedRef();
|
|
@@ -19480,7 +19596,9 @@ var ServiceCommandHandler = class {
|
|
|
19480
19596
|
async handle(args) {
|
|
19481
19597
|
const baseUrl = this.resolveBaseUrl(args.baseUrl);
|
|
19482
19598
|
const registry = new ServiceRegistryClient(baseUrl);
|
|
19483
|
-
const runtime = new ServiceRuntimeRunner(
|
|
19599
|
+
const runtime = new ServiceRuntimeRunner(
|
|
19600
|
+
(appId, options) => registry.pullEnv(appId, options)
|
|
19601
|
+
);
|
|
19484
19602
|
switch (args.action) {
|
|
19485
19603
|
case "list": {
|
|
19486
19604
|
const items = await registry.listServices({
|
|
@@ -19682,7 +19800,7 @@ var ServiceCommandHandler = class {
|
|
|
19682
19800
|
"POST",
|
|
19683
19801
|
`${this.buildEnvBasePath(args.targetAppId)}/copy-from`,
|
|
19684
19802
|
{
|
|
19685
|
-
|
|
19803
|
+
sourceServiceId: args.sourceAppId,
|
|
19686
19804
|
...args.scope ? { scope: args.scope } : {},
|
|
19687
19805
|
...args.includeSecret ? { includeSecret: true } : {},
|
|
19688
19806
|
...args.overwrite ? { overwrite: true } : {},
|
|
@@ -19733,7 +19851,7 @@ var ServiceCommandHandler = class {
|
|
|
19733
19851
|
return payload.data;
|
|
19734
19852
|
}
|
|
19735
19853
|
buildEnvBasePath(appId) {
|
|
19736
|
-
return `/
|
|
19854
|
+
return `/services/by-id/${encodeURIComponent(appId)}/env`;
|
|
19737
19855
|
}
|
|
19738
19856
|
buildEnvPath(appId, query) {
|
|
19739
19857
|
const params = new URLSearchParams();
|