servyx-cli 0.1.7 → 0.1.9
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/README.md +20 -2
- package/package.json +7 -2
- package/src/commands/deploy.js +36 -3
- package/src/index.js +2 -1
- package/src/lib/api.js +7 -0
- package/src/lib/version.js +9 -0
package/README.md
CHANGED
|
@@ -13,14 +13,32 @@ Deploy local projects to Servyx by rsyncing files over SSH, then triggering the
|
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
npm install -g servyx-cli
|
|
16
|
-
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This installs the `latest` published version (no version pin needed). To upgrade an existing install:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm update -g servyx-cli
|
|
22
|
+
# or explicitly:
|
|
23
|
+
npm install -g servyx-cli@latest
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Check your installed version:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
servyx --version
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
From source:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
17
35
|
cd servyx-cli && npm install && npm link
|
|
18
36
|
```
|
|
19
37
|
|
|
20
38
|
## Quick start
|
|
21
39
|
|
|
22
40
|
```bash
|
|
23
|
-
# 1. Authenticate
|
|
41
|
+
# 1. Authenticate
|
|
24
42
|
servyx login
|
|
25
43
|
|
|
26
44
|
# 2. Link your project directory to an app
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "servyx-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Servyx CLI — Upload local projects and deploy via the Servyx platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,12 @@
|
|
|
15
15
|
"node": ">=18.0.0"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
|
-
"start": "node bin/servyx.js"
|
|
18
|
+
"start": "node bin/servyx.js",
|
|
19
|
+
"prepublishOnly": "node -e \"const p=require('./package.json');const l=require('./package-lock.json');if(l.version!==p.version||l.packages[''].version!==p.version){console.error('package-lock.json version mismatch — run npm install');process.exit(1)}\""
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"tag": "latest",
|
|
23
|
+
"access": "public"
|
|
19
24
|
},
|
|
20
25
|
"keywords": [
|
|
21
26
|
"servyx",
|
package/src/commands/deploy.js
CHANGED
|
@@ -5,11 +5,23 @@ import { loadProjectConfig, loadServyxIgnore } from '../lib/project.js';
|
|
|
5
5
|
import { rsyncDeploy, sshPreflight, DEFAULT_RSYNC_EXCLUDES, preserveServerDotfiles, ensureRemoteDir, verifyRemoteStagingDir } from '../lib/rsync.js';
|
|
6
6
|
|
|
7
7
|
const TERMINAL_STATUSES = new Set(['success', 'failure', 'cancelled']);
|
|
8
|
+
const MIN_POLL_MS = 3000;
|
|
9
|
+
const MAX_POLL_MS = 10000;
|
|
8
10
|
|
|
9
11
|
function sleep(ms) {
|
|
10
12
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11
13
|
}
|
|
12
14
|
|
|
15
|
+
function deploymentSnapshot(deployment) {
|
|
16
|
+
return [
|
|
17
|
+
deployment.status,
|
|
18
|
+
deployment.phase,
|
|
19
|
+
deployment.percent,
|
|
20
|
+
deployment.message,
|
|
21
|
+
deployment.error_message,
|
|
22
|
+
].join('|');
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
function formatDeploymentLine(deployment) {
|
|
14
26
|
const pct = deployment.percent != null ? `${deployment.percent}%` : '';
|
|
15
27
|
const phase = deployment.phase || deployment.status || 'unknown';
|
|
@@ -149,15 +161,36 @@ export async function runDeploy(options = {}) {
|
|
|
149
161
|
|
|
150
162
|
console.log(pc.dim(`Deployment ID: ${deploymentId}`));
|
|
151
163
|
|
|
164
|
+
let pollIntervalMs = MIN_POLL_MS;
|
|
165
|
+
let lastSnapshot = null;
|
|
166
|
+
|
|
152
167
|
while (true) {
|
|
153
|
-
|
|
168
|
+
let statusResult;
|
|
169
|
+
try {
|
|
170
|
+
statusResult = await getDeployment(deploymentId);
|
|
171
|
+
pollIntervalMs = Math.min(MAX_POLL_MS, pollIntervalMs + 500);
|
|
172
|
+
} catch (err) {
|
|
173
|
+
if (err.status === 429 || err.code === 'RATE_LIMITED') {
|
|
174
|
+
const retryMs = (err.retryAfterSeconds || Math.ceil(pollIntervalMs / 1000)) * 1000;
|
|
175
|
+
pollIntervalMs = Math.min(MAX_POLL_MS * 2, Math.max(retryMs, pollIntervalMs * 2));
|
|
176
|
+
console.log(pc.yellow(`Rate limited — retrying in ${Math.ceil(pollIntervalMs / 1000)}s...`));
|
|
177
|
+
await sleep(pollIntervalMs);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
throw err;
|
|
181
|
+
}
|
|
182
|
+
|
|
154
183
|
const current = statusResult?.data?.deployment;
|
|
155
184
|
if (!current) {
|
|
156
185
|
console.error(pc.red('Deployment not found while polling.'));
|
|
157
186
|
process.exit(1);
|
|
158
187
|
}
|
|
159
188
|
|
|
160
|
-
|
|
189
|
+
const snapshot = deploymentSnapshot(current);
|
|
190
|
+
if (snapshot !== lastSnapshot) {
|
|
191
|
+
console.log(formatDeploymentLine(current));
|
|
192
|
+
lastSnapshot = snapshot;
|
|
193
|
+
}
|
|
161
194
|
|
|
162
195
|
if (TERMINAL_STATUSES.has(current.status)) {
|
|
163
196
|
if (current.status === 'success') {
|
|
@@ -172,6 +205,6 @@ export async function runDeploy(options = {}) {
|
|
|
172
205
|
process.exit(1);
|
|
173
206
|
}
|
|
174
207
|
|
|
175
|
-
await sleep(
|
|
208
|
+
await sleep(pollIntervalMs);
|
|
176
209
|
}
|
|
177
210
|
}
|
package/src/index.js
CHANGED
|
@@ -4,13 +4,14 @@ import { runLogin } from './commands/login.js';
|
|
|
4
4
|
import { runLink } from './commands/link.js';
|
|
5
5
|
import { runDeploy } from './commands/deploy.js';
|
|
6
6
|
import { runStatus } from './commands/status.js';
|
|
7
|
+
import { getCliVersion } from './lib/version.js';
|
|
7
8
|
|
|
8
9
|
const program = new Command();
|
|
9
10
|
|
|
10
11
|
program
|
|
11
12
|
.name('servyx')
|
|
12
13
|
.description('Servyx CLI — rsync and deploy applications')
|
|
13
|
-
.version(
|
|
14
|
+
.version(getCliVersion());
|
|
14
15
|
|
|
15
16
|
program
|
|
16
17
|
.command('login')
|
package/src/lib/api.js
CHANGED
|
@@ -34,6 +34,13 @@ function unwrap(response) {
|
|
|
34
34
|
err.status = response.status;
|
|
35
35
|
err.code = response.data?.code;
|
|
36
36
|
err.data = response.data;
|
|
37
|
+
const retryAfter = response.headers?.['retry-after'];
|
|
38
|
+
if (retryAfter) {
|
|
39
|
+
const seconds = Number.parseInt(retryAfter, 10);
|
|
40
|
+
if (!Number.isNaN(seconds)) {
|
|
41
|
+
err.retryAfterSeconds = seconds;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
37
44
|
throw err;
|
|
38
45
|
}
|
|
39
46
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { dirname, join } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const packagePath = join(dirname(fileURLToPath(import.meta.url)), '../../package.json');
|
|
6
|
+
|
|
7
|
+
export function getCliVersion() {
|
|
8
|
+
return JSON.parse(readFileSync(packagePath, 'utf8')).version;
|
|
9
|
+
}
|