pockethost 4.2.0 → 4.2.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/CHANGELOG.md +16 -0
- package/README.md +13 -11
- package/dist/cli.js +30 -29
- package/package.json +11 -9
- package/LICENSE.md +0 -16
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Formerly published as `phio`. Versions below 3.0.0 are phio's history.
|
|
4
4
|
|
|
5
|
+
## 4.2.2
|
|
6
|
+
|
|
7
|
+
### Patch Changes
|
|
8
|
+
|
|
9
|
+
- The CLI talks to `https://mothership.pockethost.io` by default (PocketHost's API has a new name). The old
|
|
10
|
+
`pockethost-central.pockethost.io` keeps working, so older versions are unaffected. `POCKETHOST_MOTHERSHIP_URL`
|
|
11
|
+
still overrides it.
|
|
12
|
+
|
|
13
|
+
## 4.2.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Removing a package (even the last one) now removes it from the instance. With a `package.json`, `deploy` and `dev`
|
|
18
|
+
tell PocketHost that the deploy owns `node_modules`, so whatever isn't shipped is cleaned up.
|
|
19
|
+
`--no-node-modules` still leaves the instance's `node_modules` alone.
|
|
20
|
+
|
|
5
21
|
## 4.2.0
|
|
6
22
|
|
|
7
23
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -117,10 +117,11 @@ Older project files (`.phioconfig`, `package.json` → `pockethost.instanceName`
|
|
|
117
117
|
3. Commit: the server swaps the file set in atomically (stopping the instance when anything outside `pb_public`
|
|
118
118
|
changes) and starts the instance again.
|
|
119
119
|
|
|
120
|
-
PocketHost doesn't run `npm install`. Instead, when `package.json
|
|
120
|
+
PocketHost doesn't run `npm install`. Instead, when your project has a `package.json`, the CLI ships your production
|
|
121
121
|
`node_modules` with the deploy: the packages in `dependencies` and everything they need, never `devDependencies`.
|
|
122
122
|
It works with npm, pnpm, Bun and workspaces, and follows symlinks, so the instance gets plain files. Install first
|
|
123
|
-
(`npm install` or similar), then deploy.
|
|
123
|
+
(`npm install` or similar), then deploy. The instance's `node_modules` ends up matching what you shipped, so packages
|
|
124
|
+
you remove (even the last one) are removed there too. `--no-node-modules` leaves the instance's `node_modules` alone.
|
|
124
125
|
|
|
125
126
|
Only `pb_*` folders (never `pb_data`), `package.json`, `bun.lock(b)`, `patches/` and the production `node_modules` are
|
|
126
127
|
deployed. Files in those folders on the instance that aren't in your project are removed. `.git*` files are never
|
|
@@ -134,8 +135,9 @@ The CLI signs in with a personal access token (`ph_pat_…`) only. Every command
|
|
|
134
135
|
2. the token saved by `pockethost login` (or `pockethost token set`)
|
|
135
136
|
|
|
136
137
|
Create tokens under [Account → API Tokens](https://pockethost.io/account/tokens). A token sees only the instances
|
|
137
|
-
you give it.
|
|
138
|
-
|
|
138
|
+
you give it. You can change those instances later with the token's **Edit** button; the token itself stays the same.
|
|
139
|
+
`list`, `link`, `show` and `logs` work with any token. `deploy`, `dev` and the instance commands need write access,
|
|
140
|
+
and `create` needs a token for all instances. Email/password login was removed in 4.0.
|
|
139
141
|
|
|
140
142
|
Deploy from GitHub Actions:
|
|
141
143
|
|
|
@@ -160,13 +162,13 @@ Customer docs: https://pockethost.io/docs/cli
|
|
|
160
162
|
|
|
161
163
|
## Environment variables
|
|
162
164
|
|
|
163
|
-
| Variable
|
|
164
|
-
|
|
|
165
|
-
| `POCKETHOST_TOKEN`
|
|
166
|
-
| `POCKETHOST_INSTANCE_NAME`
|
|
167
|
-
| `POCKETHOST_MOTHERSHIP_URL` | Override mothership API URL
|
|
168
|
-
| `POCKETHOST_HOME`
|
|
169
|
-
| `POCKETHOST_PROJECT_DIR`
|
|
165
|
+
| Variable | Purpose |
|
|
166
|
+
| --------------------------- | --------------------------------------------------- |
|
|
167
|
+
| `POCKETHOST_TOKEN` | Personal access token (CI); overrides the saved one |
|
|
168
|
+
| `POCKETHOST_INSTANCE_NAME` | Override linked instance name |
|
|
169
|
+
| `POCKETHOST_MOTHERSHIP_URL` | Override mothership API URL |
|
|
170
|
+
| `POCKETHOST_HOME` | Override config directory |
|
|
171
|
+
| `POCKETHOST_PROJECT_DIR` | Override project root detection |
|
|
170
172
|
|
|
171
173
|
Environment variables take precedence over `.pockethostconfig`. The old `PHIO_*` names still work (with a deprecation
|
|
172
174
|
warning) and will be removed in a future major version.
|
package/dist/cli.js
CHANGED
|
@@ -6444,7 +6444,7 @@ function useColor() {
|
|
|
6444
6444
|
var program = new Command();
|
|
6445
6445
|
|
|
6446
6446
|
// package.json
|
|
6447
|
-
var version = "4.2.
|
|
6447
|
+
var version = "4.2.2";
|
|
6448
6448
|
|
|
6449
6449
|
// src/lib/projectRoot.ts
|
|
6450
6450
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -6608,7 +6608,7 @@ var CLI_HOME = (...paths) => {
|
|
|
6608
6608
|
};
|
|
6609
6609
|
var MOTHERSHIP_URL = (...paths) => {
|
|
6610
6610
|
const url = new URL(
|
|
6611
|
-
envVar("MOTHERSHIP_URL") ?? `https://
|
|
6611
|
+
envVar("MOTHERSHIP_URL") ?? `https://mothership.pockethost.io`
|
|
6612
6612
|
);
|
|
6613
6613
|
url.pathname = join(url.pathname, ...paths);
|
|
6614
6614
|
return url.toString();
|
|
@@ -6755,8 +6755,8 @@ var saveInstanceName = (instanceName) => {
|
|
|
6755
6755
|
|
|
6756
6756
|
// src/lib/deployLocal.ts
|
|
6757
6757
|
import { copyFile, mkdir, readdir } from "fs/promises";
|
|
6758
|
-
import { existsSync as
|
|
6759
|
-
import { dirname as dirname3, join as
|
|
6758
|
+
import { existsSync as existsSync6 } from "fs";
|
|
6759
|
+
import { dirname as dirname3, join as join5, resolve } from "path";
|
|
6760
6760
|
|
|
6761
6761
|
// ../../node_modules/.pnpm/pretty-bytes@7.1.0/node_modules/pretty-bytes/index.js
|
|
6762
6762
|
var BYTE_UNITS = [
|
|
@@ -6900,7 +6900,7 @@ function prettyBytes(number, options2) {
|
|
|
6900
6900
|
|
|
6901
6901
|
// src/lib/deployApi.ts
|
|
6902
6902
|
import { execFileSync } from "child_process";
|
|
6903
|
-
import { lstatSync as lstatSync2 } from "fs";
|
|
6903
|
+
import { existsSync as existsSync5, lstatSync as lstatSync2 } from "fs";
|
|
6904
6904
|
import { readFile } from "fs/promises";
|
|
6905
6905
|
|
|
6906
6906
|
// ../../node_modules/.pnpm/glob@13.0.6/node_modules/glob/dist/esm/index.min.js
|
|
@@ -9888,7 +9888,7 @@ var Ze = Object.assign(Je, { glob: Je, globSync: ts, sync: Ui, globStream: Qe, s
|
|
|
9888
9888
|
Ze.glob = Ze;
|
|
9889
9889
|
|
|
9890
9890
|
// src/lib/deployApi.ts
|
|
9891
|
-
import { sep as sep3 } from "path";
|
|
9891
|
+
import { join as join4, sep as sep3 } from "path";
|
|
9892
9892
|
import { gzipSync } from "zlib";
|
|
9893
9893
|
|
|
9894
9894
|
// vendor/ftp-deploy/sftpDeploy.ts
|
|
@@ -12868,10 +12868,6 @@ var collectNodeModules = (projectDir) => {
|
|
|
12868
12868
|
packages.sort((a, b) => a.path < b.path ? -1 : 1);
|
|
12869
12869
|
return { files, packages, missing };
|
|
12870
12870
|
};
|
|
12871
|
-
var hasRuntimeDependencies = (projectDir) => {
|
|
12872
|
-
const pkg = readPackageJson(projectDir);
|
|
12873
|
-
return Object.keys(pkg.dependencies ?? {}).length + Object.keys(pkg.optionalDependencies ?? {}).length > 0;
|
|
12874
|
-
};
|
|
12875
12871
|
|
|
12876
12872
|
// src/lib/config.ts
|
|
12877
12873
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -13015,7 +13011,7 @@ var collectLocalFiles = async (include, exclude, cwd = process.cwd(), options2 =
|
|
|
13015
13011
|
});
|
|
13016
13012
|
}
|
|
13017
13013
|
let nodeModules = null;
|
|
13018
|
-
if (options2.nodeModules ??
|
|
13014
|
+
if (options2.nodeModules ?? existsSync5(join4(cwd, "package.json"))) {
|
|
13019
13015
|
nodeModules = collectNodeModules(cwd);
|
|
13020
13016
|
for (const f of nodeModules.files) {
|
|
13021
13017
|
const data = await readFile(f.source);
|
|
@@ -13039,7 +13035,7 @@ var assertNodeModulesComplete = (nm) => {
|
|
|
13039
13035
|
var describeNodeModules = (nm) => {
|
|
13040
13036
|
if (!nm) return [];
|
|
13041
13037
|
const lines = [
|
|
13042
|
-
`\u{1F4E6} node_modules: ${nm.packages.length} package(s), ${nm.files.length} file(s)`
|
|
13038
|
+
nm.packages.length === 0 ? `\u{1F4E6} node_modules: no packages (any on the server will be removed)` : `\u{1F4E6} node_modules: ${nm.packages.length} package(s), ${nm.files.length} file(s)`
|
|
13043
13039
|
];
|
|
13044
13040
|
if (nm.missing.length > 0) {
|
|
13045
13041
|
lines.push(
|
|
@@ -13115,7 +13111,7 @@ var createDeployClient = (instanceName, cred) => {
|
|
|
13115
13111
|
base,
|
|
13116
13112
|
headers,
|
|
13117
13113
|
whoami: () => call("GET", "/whoami"),
|
|
13118
|
-
check: (files) => call("POST", "/check", { files }),
|
|
13114
|
+
check: (files, nodeModules = false) => call("POST", "/check", { files, nodeModules }),
|
|
13119
13115
|
upload: (bundle) => call(
|
|
13120
13116
|
"POST",
|
|
13121
13117
|
"/blobs",
|
|
@@ -13170,10 +13166,12 @@ var deployViaApi = async (opts) => {
|
|
|
13170
13166
|
uploadedCount += toSend.length;
|
|
13171
13167
|
uploadedBytes += bytes;
|
|
13172
13168
|
};
|
|
13173
|
-
const
|
|
13169
|
+
const managesNodeModules = nodeModules !== null;
|
|
13170
|
+
const { missing } = await client.check(manifest, managesNodeModules);
|
|
13174
13171
|
await upload(missing);
|
|
13175
13172
|
const commitBody = {
|
|
13176
13173
|
files: manifest,
|
|
13174
|
+
nodeModules: managesNodeModules,
|
|
13177
13175
|
commit: opts.commit ?? detectCommit(cwd),
|
|
13178
13176
|
message: opts.message ?? "",
|
|
13179
13177
|
source: detectSource(),
|
|
@@ -13212,12 +13210,12 @@ var deployViaApi = async (opts) => {
|
|
|
13212
13210
|
// src/lib/deployLocal.ts
|
|
13213
13211
|
var writeDeploySet = async (files, outDir) => {
|
|
13214
13212
|
const root = resolve(outDir);
|
|
13215
|
-
if (
|
|
13213
|
+
if (existsSync6(root) && (await readdir(root)).length > 0) {
|
|
13216
13214
|
throw new Error(`${outDir} must be empty or not exist yet`);
|
|
13217
13215
|
}
|
|
13218
13216
|
await mkdir(root, { recursive: true });
|
|
13219
13217
|
for (const f of files) {
|
|
13220
|
-
const dest =
|
|
13218
|
+
const dest = join5(root, ...f.path.split("/"));
|
|
13221
13219
|
await mkdir(dirname3(dest), { recursive: true });
|
|
13222
13220
|
await copyFile(f.source, dest);
|
|
13223
13221
|
}
|
|
@@ -15029,7 +15027,7 @@ var shellQuote = (value) => {
|
|
|
15029
15027
|
var formatSftpCommand = (connection, sftpBin = "sftp") => [sftpBin, ...buildSftpArgs(connection).map(shellQuote)].join(" ");
|
|
15030
15028
|
|
|
15031
15029
|
// src/lib/deployKey.ts
|
|
15032
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
15030
|
+
import { chmodSync as chmodSync2, existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
15033
15031
|
import { generateKeyPairSync } from "crypto";
|
|
15034
15032
|
|
|
15035
15033
|
// src/lib/sshPublicKey.ts
|
|
@@ -15178,7 +15176,7 @@ var migratePkcs8PrivateKeyIfNeeded = (privateKeyPath) => {
|
|
|
15178
15176
|
};
|
|
15179
15177
|
var ensureLocalKeyPair = () => {
|
|
15180
15178
|
const { privateKeyPath, publicKeyPath } = getDeployKeyPaths();
|
|
15181
|
-
if (
|
|
15179
|
+
if (existsSync7(privateKeyPath) && existsSync7(publicKeyPath)) {
|
|
15182
15180
|
migratePkcs8PrivateKeyIfNeeded(privateKeyPath);
|
|
15183
15181
|
return {
|
|
15184
15182
|
privateKeyPath,
|
|
@@ -15360,8 +15358,11 @@ var DeployCommand = () => {
|
|
|
15360
15358
|
`Commit SHA to record (default: $GITHUB_SHA or git HEAD)`
|
|
15361
15359
|
).option(`-m, --message <message>`, `Note to record with this deploy`).option(
|
|
15362
15360
|
`--node-modules`,
|
|
15363
|
-
`Ship the production node_modules tree (default
|
|
15364
|
-
).option(
|
|
15361
|
+
`Ship the production node_modules tree and remove packages no longer used (default with a package.json)`
|
|
15362
|
+
).option(
|
|
15363
|
+
`--no-node-modules`,
|
|
15364
|
+
`Don't ship node_modules; leave the instance's node_modules alone`
|
|
15365
|
+
).option(
|
|
15365
15366
|
`--out <dir>`,
|
|
15366
15367
|
`Write the deploy set to a local folder instead of deploying`
|
|
15367
15368
|
).option(`--dry-run`, `List what would be deployed, deploy nothing`).option(
|
|
@@ -15409,8 +15410,8 @@ var DeployCommand = () => {
|
|
|
15409
15410
|
};
|
|
15410
15411
|
|
|
15411
15412
|
// src/commands/FilesCommand.ts
|
|
15412
|
-
import { existsSync as
|
|
15413
|
-
import { join as
|
|
15413
|
+
import { existsSync as existsSync8, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
|
|
15414
|
+
import { join as join8, relative as relative4, resolve as resolve4, sep as sep4 } from "path";
|
|
15414
15415
|
|
|
15415
15416
|
// src/lib/filesApi.ts
|
|
15416
15417
|
import { createReadStream, createWriteStream } from "fs";
|
|
@@ -15559,7 +15560,7 @@ var FilesCommand = () => instanceOption(
|
|
|
15559
15560
|
if (out.truncated) console.log(`(list truncated)`);
|
|
15560
15561
|
});
|
|
15561
15562
|
var isCurrent = (local, e) => {
|
|
15562
|
-
if (!
|
|
15563
|
+
if (!existsSync8(local)) return false;
|
|
15563
15564
|
const st2 = statSync2(local);
|
|
15564
15565
|
return st2.size === e.size && Math.abs(st2.mtimeMs - e.mtime) < 1e3;
|
|
15565
15566
|
};
|
|
@@ -15596,21 +15597,21 @@ var PullCommand = () => instanceOption(
|
|
|
15596
15597
|
)
|
|
15597
15598
|
);
|
|
15598
15599
|
}
|
|
15599
|
-
const todo = wanted.filter((e) => !isCurrent(
|
|
15600
|
+
const todo = wanted.filter((e) => !isCurrent(join8(dir, e.path), e));
|
|
15600
15601
|
const bytes = todo.reduce((n7, e) => n7 + e.size, 0);
|
|
15601
15602
|
console.log(
|
|
15602
15603
|
`${options2.dryRun ? "Would download" : "Downloading"} ${todo.length} of ${wanted.length} file(s) (${prettyBytes(bytes)}) from ${instanceName} into ${dir}`
|
|
15603
15604
|
);
|
|
15604
15605
|
for (const e of todo) {
|
|
15605
15606
|
if (options2.verbose || options2.dryRun) console.log(` ${e.path}`);
|
|
15606
|
-
if (!options2.dryRun) await files.download(e.path,
|
|
15607
|
+
if (!options2.dryRun) await files.download(e.path, join8(dir, e.path));
|
|
15607
15608
|
}
|
|
15608
15609
|
if (!options2.dryRun) console.log(`\u2705 Done.`);
|
|
15609
15610
|
});
|
|
15610
15611
|
var walkLocal = (abs) => {
|
|
15611
15612
|
const st2 = statSync2(abs);
|
|
15612
15613
|
if (!st2.isDirectory()) return [abs];
|
|
15613
|
-
return readdirSync3(abs).filter((n7) => !n7.startsWith(".git")).flatMap((n7) => walkLocal(
|
|
15614
|
+
return readdirSync3(abs).filter((n7) => !n7.startsWith(".git")).flatMap((n7) => walkLocal(join8(abs, n7)));
|
|
15614
15615
|
};
|
|
15615
15616
|
var UploadCommand = () => instanceOption(
|
|
15616
15617
|
new Command("upload").description(
|
|
@@ -15622,7 +15623,7 @@ var UploadCommand = () => instanceOption(
|
|
|
15622
15623
|
).action(async (local, remote, options2) => {
|
|
15623
15624
|
const instanceName = requireInstance(options2.instance);
|
|
15624
15625
|
const abs = resolve4(local);
|
|
15625
|
-
if (!
|
|
15626
|
+
if (!existsSync8(abs)) throw new Error(`${local} not found`);
|
|
15626
15627
|
const target = normalizeRemotePath(
|
|
15627
15628
|
remote ?? relative4(process.cwd(), abs).split(sep4).join("/")
|
|
15628
15629
|
);
|
|
@@ -15674,7 +15675,7 @@ var RmCommand = () => instanceOption(
|
|
|
15674
15675
|
});
|
|
15675
15676
|
|
|
15676
15677
|
// src/commands/InfoCommand.ts
|
|
15677
|
-
import { existsSync as
|
|
15678
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6 } from "fs";
|
|
15678
15679
|
|
|
15679
15680
|
// ../../node_modules/.pnpm/@inquirer+core@11.2.1_@types+node@20.19.43/node_modules/@inquirer/core/dist/lib/key.js
|
|
15680
15681
|
var keybindings = ["emacs", "vim"];
|
|
@@ -17569,7 +17570,7 @@ var TokenCommand = () => new Command("token").description(`Show, save or remove
|
|
|
17569
17570
|
// src/commands/InfoCommand.ts
|
|
17570
17571
|
var sshKeyStatus = async () => {
|
|
17571
17572
|
const { publicKeyPath } = getDeployKeyPaths();
|
|
17572
|
-
if (!
|
|
17573
|
+
if (!existsSync9(publicKeyPath))
|
|
17573
17574
|
return `none yet (created the first time you use SFTP)`;
|
|
17574
17575
|
const fingerprint = fingerprintForPublicKey(
|
|
17575
17576
|
readFileSync6(publicKeyPath, "utf8").trim()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pockethost",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.2",
|
|
4
4
|
"description": "The PocketHost CLI: deploy, sync, and tail logs for your PocketHost instances",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,6 +25,15 @@
|
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=20"
|
|
27
27
|
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "node scripts/build.mjs",
|
|
30
|
+
"check:types": "tsc --noEmit",
|
|
31
|
+
"dev": "node --import tsx ./src/cli.ts",
|
|
32
|
+
"start": "node --import tsx ./src/cli.ts",
|
|
33
|
+
"test": "vitest run",
|
|
34
|
+
"prepublishOnly": "pnpm check:types && pnpm test",
|
|
35
|
+
"prepack": "pnpm build"
|
|
36
|
+
},
|
|
28
37
|
"files": [
|
|
29
38
|
"dist",
|
|
30
39
|
"CHANGELOG.md",
|
|
@@ -57,12 +66,5 @@
|
|
|
57
66
|
"semi": false,
|
|
58
67
|
"singleQuote": true,
|
|
59
68
|
"trailingComma": "es5"
|
|
60
|
-
},
|
|
61
|
-
"scripts": {
|
|
62
|
-
"build": "node scripts/build.mjs",
|
|
63
|
-
"check:types": "tsc --noEmit",
|
|
64
|
-
"dev": "node --import tsx ./src/cli.ts",
|
|
65
|
-
"start": "node --import tsx ./src/cli.ts",
|
|
66
|
-
"test": "vitest run"
|
|
67
69
|
}
|
|
68
|
-
}
|
|
70
|
+
}
|
package/LICENSE.md
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
|
|
3
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
|
4
|
-
and associated documentation files (the "Software"), to deal in the Software without restriction,
|
|
5
|
-
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
|
6
|
-
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
|
|
7
|
-
is furnished to do so, subject to the following conditions:
|
|
8
|
-
|
|
9
|
-
The above copyright notice and this permission notice shall be included in all copies or
|
|
10
|
-
substantial portions of the Software.
|
|
11
|
-
|
|
12
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
|
13
|
-
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
14
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
15
|
-
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
16
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|