@startanaicompany/cli 1.4.16 → 1.4.17
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/CLAUDE.md +118 -9
- package/package.json +1 -1
- package/src/commands/create.js +27 -6
- package/src/commands/deploy.js +41 -8
- package/src/commands/keys.js +0 -0
- package/src/lib/api.js +9 -4
- package/src/lib/errorDisplay.js +170 -0
package/CLAUDE.md
CHANGED
|
@@ -275,6 +275,71 @@ saac create web -s web -r git@git... -t abc123 \
|
|
|
275
275
|
- Saves project config to `.saac/config.json` after successful creation
|
|
276
276
|
- Displays next steps and useful commands
|
|
277
277
|
|
|
278
|
+
**Deployment Behavior (NEW):**
|
|
279
|
+
|
|
280
|
+
The `create` command now **waits for the initial deployment to complete** (up to 5 minutes) before returning.
|
|
281
|
+
|
|
282
|
+
**Response Time:**
|
|
283
|
+
- Typical: 30-120 seconds
|
|
284
|
+
- Maximum: 5 minutes (timeout)
|
|
285
|
+
|
|
286
|
+
**Success Response:**
|
|
287
|
+
```json
|
|
288
|
+
{
|
|
289
|
+
"success": true,
|
|
290
|
+
"coolify_app_uuid": "...",
|
|
291
|
+
"app_name": "my-app",
|
|
292
|
+
"domain": "https://myapp.startanaicompany.com",
|
|
293
|
+
"deployment_status": "finished",
|
|
294
|
+
"deployment_uuid": "...",
|
|
295
|
+
"git_branch": "master"
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Failure Response (HTTP 200 with success: false):**
|
|
300
|
+
```json
|
|
301
|
+
{
|
|
302
|
+
"success": false,
|
|
303
|
+
"coolify_app_uuid": "...",
|
|
304
|
+
"app_name": "my-app",
|
|
305
|
+
"deployment_status": "failed",
|
|
306
|
+
"message": "Port 8080 is already in use. Remove host port bindings...",
|
|
307
|
+
"errors": [
|
|
308
|
+
{
|
|
309
|
+
"type": "PORT_CONFLICT",
|
|
310
|
+
"message": "Port 8080 is already in use...",
|
|
311
|
+
"detail": "Bind for 0.0.0.0:8080 failed..."
|
|
312
|
+
}
|
|
313
|
+
],
|
|
314
|
+
"relevant_logs": [...],
|
|
315
|
+
"last_logs": [...]
|
|
316
|
+
}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**Error Types:**
|
|
320
|
+
- `PORT_CONFLICT` - Host port binding conflict in docker-compose.yml
|
|
321
|
+
- `BUILD_FAILED` - Build process returned non-zero exit code
|
|
322
|
+
- `TIMEOUT` - Deployment didn't complete in 5 minutes
|
|
323
|
+
- `UNKNOWN` - Generic deployment failure
|
|
324
|
+
|
|
325
|
+
**Important Notes:**
|
|
326
|
+
1. **Application is created even if deployment fails** - the UUID is saved to `.saac/config.json`
|
|
327
|
+
2. Failed deployments return HTTP 200 (not 4xx) with `success: false`
|
|
328
|
+
3. CLI must check the `success` field, not just HTTP status code
|
|
329
|
+
4. Detailed error information is displayed with actionable advice
|
|
330
|
+
5. User can fix the issue and run `saac deploy` to retry
|
|
331
|
+
|
|
332
|
+
**Error Display:**
|
|
333
|
+
The CLI displays comprehensive error information:
|
|
334
|
+
- Error summary message
|
|
335
|
+
- Structured error details with types
|
|
336
|
+
- Relevant error logs (filtered)
|
|
337
|
+
- Last log lines for context
|
|
338
|
+
- Actionable advice based on error type:
|
|
339
|
+
- `PORT_CONFLICT`: Remove host port bindings from docker-compose.yml
|
|
340
|
+
- `BUILD_FAILED`: Check Dockerfile, run `docker build .` locally
|
|
341
|
+
- `TIMEOUT`: Check `saac status` and `saac logs`, may still be running
|
|
342
|
+
|
|
278
343
|
### Update Command Implementation
|
|
279
344
|
|
|
280
345
|
The `update` command allows modifying application configuration after deployment using `PATCH /api/v1/applications/:uuid`.
|
|
@@ -373,7 +438,7 @@ saac git disconnect git.startanaicompany.com
|
|
|
373
438
|
|
|
374
439
|
### Deploy Command Implementation
|
|
375
440
|
|
|
376
|
-
The `deploy` command triggers deployment for
|
|
441
|
+
The `deploy` command triggers deployment and **waits for completion** (up to 5 minutes).
|
|
377
442
|
|
|
378
443
|
**Usage:**
|
|
379
444
|
```bash
|
|
@@ -385,13 +450,57 @@ saac deploy --force
|
|
|
385
450
|
1. Validates authentication (session token not expired)
|
|
386
451
|
2. Checks for project config (`.saac/config.json`)
|
|
387
452
|
3. Makes POST request to `/api/v1/applications/:uuid/deploy`
|
|
388
|
-
4.
|
|
389
|
-
5.
|
|
453
|
+
4. **Waits for deployment to complete** (up to 5 minutes)
|
|
454
|
+
5. Displays deployment status with detailed error information on failure
|
|
455
|
+
|
|
456
|
+
**Response Time:**
|
|
457
|
+
- Typical: 30-120 seconds
|
|
458
|
+
- Maximum: 5 minutes (timeout)
|
|
459
|
+
|
|
460
|
+
**Success Response:**
|
|
461
|
+
```json
|
|
462
|
+
{
|
|
463
|
+
"success": true,
|
|
464
|
+
"status": "finished",
|
|
465
|
+
"deployment_uuid": "...",
|
|
466
|
+
"git_branch": "master",
|
|
467
|
+
"domain": "https://myapp.startanaicompany.com",
|
|
468
|
+
"traefik_status": "queued"
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
**Failure Response:**
|
|
473
|
+
```json
|
|
474
|
+
{
|
|
475
|
+
"success": false,
|
|
476
|
+
"status": "failed",
|
|
477
|
+
"message": "Build failed with exit code 1",
|
|
478
|
+
"errors": [
|
|
479
|
+
{
|
|
480
|
+
"type": "BUILD_FAILED",
|
|
481
|
+
"message": "Build failed with exit code 1",
|
|
482
|
+
"detail": "npm ERR! code ELIFECYCLE..."
|
|
483
|
+
}
|
|
484
|
+
],
|
|
485
|
+
"relevant_logs": [...],
|
|
486
|
+
"last_logs": [...]
|
|
487
|
+
}
|
|
488
|
+
```
|
|
390
489
|
|
|
391
|
-
**
|
|
392
|
-
- `
|
|
393
|
-
- `
|
|
394
|
-
- `
|
|
490
|
+
**Error Types:**
|
|
491
|
+
- `PORT_CONFLICT` - Host port binding conflict
|
|
492
|
+
- `BUILD_FAILED` - Build process failed
|
|
493
|
+
- `TIMEOUT` - Deployment didn't complete in 5 minutes
|
|
494
|
+
- `UNKNOWN` - Generic failure
|
|
495
|
+
|
|
496
|
+
**Error Display:**
|
|
497
|
+
The CLI displays:
|
|
498
|
+
1. Error summary message
|
|
499
|
+
2. Structured error details with types
|
|
500
|
+
3. Relevant logs (filtered error logs)
|
|
501
|
+
4. Last 5 log lines for context
|
|
502
|
+
5. Actionable advice based on error type
|
|
503
|
+
6. Suggestion to view full logs with `saac logs --follow`
|
|
395
504
|
|
|
396
505
|
**Note:** The `--force` flag is defined in the CLI but not currently used by the API.
|
|
397
506
|
|
|
@@ -588,7 +697,7 @@ The wrapper API expects Git repositories to be hosted on the StartAnAiCompany Gi
|
|
|
588
697
|
- During registration, Gitea username can be auto-detected or manually provided
|
|
589
698
|
- Applications reference repositories in the format: `git@git.startanaicompany.com:user/repo.git`
|
|
590
699
|
|
|
591
|
-
## Current Status - Version 1.4.
|
|
700
|
+
## Current Status - Version 1.4.17
|
|
592
701
|
|
|
593
702
|
### Completed Features
|
|
594
703
|
|
|
@@ -808,4 +917,4 @@ Before publishing to npm:
|
|
|
808
917
|
- `dotenv` - Environment variables
|
|
809
918
|
- `open` - Open browser for OAuth (v8.4.2 for compatibility with chalk v4)
|
|
810
919
|
|
|
811
|
-
**Version:** 1.4.
|
|
920
|
+
**Version:** 1.4.17 (current)
|
package/package.json
CHANGED
package/src/commands/create.js
CHANGED
|
@@ -8,6 +8,7 @@ const logger = require('../lib/logger');
|
|
|
8
8
|
const oauth = require('../lib/oauth');
|
|
9
9
|
const inquirer = require('inquirer');
|
|
10
10
|
const { execSync } = require('child_process');
|
|
11
|
+
const errorDisplay = require('../lib/errorDisplay');
|
|
11
12
|
|
|
12
13
|
async function create(name, options) {
|
|
13
14
|
try {
|
|
@@ -281,14 +282,12 @@ async function create(name, options) {
|
|
|
281
282
|
|
|
282
283
|
logger.newline();
|
|
283
284
|
|
|
284
|
-
const spin = logger.spinner('Creating application...').start();
|
|
285
|
+
const spin = logger.spinner('Creating application and deploying (this may take up to 5 minutes)...').start();
|
|
285
286
|
|
|
286
287
|
try {
|
|
287
288
|
const result = await api.createApplication(appData);
|
|
288
289
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
// Save project configuration
|
|
290
|
+
// Always save project configuration (even if deployment failed)
|
|
292
291
|
saveProjectConfig({
|
|
293
292
|
applicationUuid: result.coolify_app_uuid,
|
|
294
293
|
applicationName: result.app_name,
|
|
@@ -297,13 +296,35 @@ async function create(name, options) {
|
|
|
297
296
|
gitRepository: appData.git_repository,
|
|
298
297
|
});
|
|
299
298
|
|
|
299
|
+
// Check if deployment failed
|
|
300
|
+
if (result.success === false) {
|
|
301
|
+
spin.fail('Deployment failed');
|
|
302
|
+
|
|
303
|
+
// Display detailed error information
|
|
304
|
+
errorDisplay.displayDeploymentError(result, logger);
|
|
305
|
+
|
|
306
|
+
// Show recovery instructions
|
|
307
|
+
errorDisplay.displayCreateRecoveryInstructions(result, logger);
|
|
308
|
+
|
|
309
|
+
process.exit(1);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// SUCCESS: Application created and deployed
|
|
313
|
+
spin.succeed('Application created and deployed successfully!');
|
|
314
|
+
|
|
300
315
|
logger.newline();
|
|
301
|
-
logger.success('
|
|
316
|
+
logger.success('Your application is live!');
|
|
302
317
|
logger.newline();
|
|
303
318
|
logger.field('Name', result.app_name);
|
|
304
319
|
logger.field('Domain', result.domain);
|
|
305
320
|
logger.field('UUID', result.coolify_app_uuid);
|
|
306
|
-
logger.field('Status', result.deployment_status);
|
|
321
|
+
logger.field('Status', result.deployment_status || 'finished');
|
|
322
|
+
if (result.git_branch) {
|
|
323
|
+
logger.field('Branch', result.git_branch);
|
|
324
|
+
}
|
|
325
|
+
if (result.deployment_uuid) {
|
|
326
|
+
logger.field('Deployment ID', result.deployment_uuid);
|
|
327
|
+
}
|
|
307
328
|
logger.newline();
|
|
308
329
|
|
|
309
330
|
// Show next steps
|
package/src/commands/deploy.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
const api = require('../lib/api');
|
|
6
6
|
const { getProjectConfig, isAuthenticated } = require('../lib/config');
|
|
7
7
|
const logger = require('../lib/logger');
|
|
8
|
+
const errorDisplay = require('../lib/errorDisplay');
|
|
8
9
|
|
|
9
10
|
async function deploy(options) {
|
|
10
11
|
try {
|
|
@@ -25,30 +26,62 @@ async function deploy(options) {
|
|
|
25
26
|
const { applicationUuid, applicationName } = projectConfig;
|
|
26
27
|
|
|
27
28
|
logger.section(`Deploying ${applicationName}`);
|
|
29
|
+
logger.newline();
|
|
28
30
|
|
|
29
|
-
const spin = logger.spinner('
|
|
31
|
+
const spin = logger.spinner('Deploying application (waiting for completion, up to 5 minutes)...').start();
|
|
30
32
|
|
|
31
33
|
try {
|
|
32
34
|
const result = await api.deployApplication(applicationUuid);
|
|
33
35
|
|
|
34
|
-
|
|
36
|
+
// Check if deployment failed
|
|
37
|
+
if (result.success === false) {
|
|
38
|
+
spin.fail('Deployment failed');
|
|
39
|
+
|
|
40
|
+
// Display detailed error information
|
|
41
|
+
errorDisplay.displayDeploymentError(result, logger);
|
|
42
|
+
|
|
43
|
+
// Handle timeout specifically
|
|
44
|
+
if (result.status === 'timeout') {
|
|
45
|
+
errorDisplay.displayTimeoutInstructions(logger);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// SUCCESS: Deployment completed
|
|
52
|
+
spin.succeed('Deployment completed successfully!');
|
|
35
53
|
|
|
36
54
|
logger.newline();
|
|
37
|
-
logger.success('
|
|
55
|
+
logger.success('Your application has been deployed!');
|
|
38
56
|
logger.newline();
|
|
39
57
|
logger.field('Application', applicationName);
|
|
40
58
|
logger.field('Status', result.status);
|
|
59
|
+
if (result.git_branch) {
|
|
60
|
+
logger.field('Branch', result.git_branch);
|
|
61
|
+
}
|
|
41
62
|
if (result.domain) {
|
|
42
63
|
logger.field('Domain', result.domain);
|
|
43
64
|
}
|
|
44
|
-
|
|
65
|
+
if (result.deployment_uuid || result.deployment_id) {
|
|
66
|
+
logger.field('Deployment ID', result.deployment_uuid || result.deployment_id);
|
|
67
|
+
}
|
|
45
68
|
logger.newline();
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
)
|
|
69
|
+
|
|
70
|
+
// Show Traefik status if present
|
|
71
|
+
if (result.traefik_status === 'queued') {
|
|
72
|
+
logger.info('Routing configuration is being applied (may take a few seconds)');
|
|
73
|
+
logger.newline();
|
|
74
|
+
} else if (result.traefik_status === 'failed') {
|
|
75
|
+
logger.warn('Routing configuration failed - application may not be accessible');
|
|
76
|
+
logger.newline();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
logger.info('Useful commands:');
|
|
80
|
+
logger.log(` saac logs --follow View live deployment logs`);
|
|
81
|
+
logger.log(` saac status Check application status`);
|
|
49
82
|
|
|
50
83
|
} catch (error) {
|
|
51
|
-
spin.fail('Deployment failed');
|
|
84
|
+
spin.fail('Deployment request failed');
|
|
52
85
|
throw error;
|
|
53
86
|
}
|
|
54
87
|
} catch (error) {
|
package/src/commands/keys.js
CHANGED
|
File without changes
|
package/src/lib/api.js
CHANGED
|
@@ -9,8 +9,9 @@ const pkg = require('../../package.json');
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Create axios instance with base configuration
|
|
12
|
+
* @param {number} timeout - Timeout in milliseconds (default: 30000)
|
|
12
13
|
*/
|
|
13
|
-
function createClient() {
|
|
14
|
+
function createClient(timeout = 30000) {
|
|
14
15
|
const user = getUser();
|
|
15
16
|
const envApiKey = process.env.SAAC_API_KEY; // For CI/CD
|
|
16
17
|
|
|
@@ -33,7 +34,7 @@ function createClient() {
|
|
|
33
34
|
|
|
34
35
|
return axios.create({
|
|
35
36
|
baseURL: getApiUrl(),
|
|
36
|
-
timeout:
|
|
37
|
+
timeout: timeout,
|
|
37
38
|
headers,
|
|
38
39
|
});
|
|
39
40
|
}
|
|
@@ -88,9 +89,11 @@ async function getUserInfo() {
|
|
|
88
89
|
|
|
89
90
|
/**
|
|
90
91
|
* Create a new application
|
|
92
|
+
* Note: This waits for deployment to complete (up to 5 minutes)
|
|
91
93
|
*/
|
|
92
94
|
async function createApplication(appData) {
|
|
93
|
-
|
|
95
|
+
// Use 5-minute timeout for deployment waiting
|
|
96
|
+
const client = createClient(300000); // 5 minutes
|
|
94
97
|
const response = await client.post('/applications', appData);
|
|
95
98
|
return response.data;
|
|
96
99
|
}
|
|
@@ -115,9 +118,11 @@ async function getApplication(uuid) {
|
|
|
115
118
|
|
|
116
119
|
/**
|
|
117
120
|
* Deploy application
|
|
121
|
+
* Note: This waits for deployment to complete (up to 5 minutes)
|
|
118
122
|
*/
|
|
119
123
|
async function deployApplication(uuid) {
|
|
120
|
-
|
|
124
|
+
// Use 5-minute timeout for deployment waiting
|
|
125
|
+
const client = createClient(300000); // 5 minutes
|
|
121
126
|
const response = await client.post(`/applications/${uuid}/deploy`);
|
|
122
127
|
return response.data;
|
|
123
128
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Display Utilities
|
|
3
|
+
* Formats and displays deployment errors with actionable advice
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get actionable advice for specific error types
|
|
10
|
+
*/
|
|
11
|
+
function getErrorAdvice(errorType) {
|
|
12
|
+
const advice = {
|
|
13
|
+
PORT_CONFLICT: [
|
|
14
|
+
'Fix: Remove host port bindings from your docker-compose.yml',
|
|
15
|
+
' Change "8080:8080" to just "8080"',
|
|
16
|
+
' Traefik will handle external routing automatically',
|
|
17
|
+
],
|
|
18
|
+
BUILD_FAILED: [
|
|
19
|
+
'Fix: Check your Dockerfile and build configuration',
|
|
20
|
+
' Run "docker build ." locally to debug',
|
|
21
|
+
' Verify all dependencies are properly specified',
|
|
22
|
+
],
|
|
23
|
+
TIMEOUT: [
|
|
24
|
+
'Note: Deployment may still be running in the background',
|
|
25
|
+
' Check status with: saac status',
|
|
26
|
+
' View logs with: saac logs --follow',
|
|
27
|
+
],
|
|
28
|
+
UNKNOWN: [
|
|
29
|
+
'Tip: Check the deployment logs for more details',
|
|
30
|
+
' Run: saac logs --follow',
|
|
31
|
+
],
|
|
32
|
+
PARSE_ERROR: [
|
|
33
|
+
'Note: Could not parse deployment logs',
|
|
34
|
+
' Contact support if this persists',
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
return advice[errorType] || advice.UNKNOWN;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Format log lines for display
|
|
43
|
+
*/
|
|
44
|
+
function formatLogs(logs, maxLines = 10) {
|
|
45
|
+
if (!logs || logs.length === 0) {
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// If logs is an array of objects with 'output' field
|
|
50
|
+
if (logs[0] && typeof logs[0] === 'object' && logs[0].output) {
|
|
51
|
+
return logs.slice(-maxLines).map(log => log.output);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// If logs is an array of strings
|
|
55
|
+
return logs.slice(-maxLines);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Display detailed deployment error information
|
|
60
|
+
*/
|
|
61
|
+
function displayDeploymentError(result, logger) {
|
|
62
|
+
logger.newline();
|
|
63
|
+
logger.error(`Deployment Error: ${result.message || 'Deployment failed'}`);
|
|
64
|
+
logger.newline();
|
|
65
|
+
|
|
66
|
+
// Display deployment info
|
|
67
|
+
if (result.deployment_status || result.status) {
|
|
68
|
+
logger.field('Status', result.deployment_status || result.status);
|
|
69
|
+
}
|
|
70
|
+
if (result.git_branch) {
|
|
71
|
+
logger.field('Branch', result.git_branch);
|
|
72
|
+
}
|
|
73
|
+
if (result.deployment_uuid) {
|
|
74
|
+
logger.field('Deployment ID', result.deployment_uuid);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
logger.newline();
|
|
78
|
+
|
|
79
|
+
// Display structured errors
|
|
80
|
+
if (result.errors && result.errors.length > 0) {
|
|
81
|
+
logger.info('Error Details:');
|
|
82
|
+
result.errors.forEach(err => {
|
|
83
|
+
logger.log(` ${chalk.yellow(`[${err.type}]`)} ${err.message}`);
|
|
84
|
+
if (err.detail) {
|
|
85
|
+
const detailLines = err.detail.split('\n').slice(0, 3); // First 3 lines
|
|
86
|
+
detailLines.forEach(line => {
|
|
87
|
+
logger.log(` ${chalk.gray(line)}`);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
logger.newline();
|
|
92
|
+
|
|
93
|
+
// Display actionable advice for the first error
|
|
94
|
+
const firstError = result.errors[0];
|
|
95
|
+
if (firstError && firstError.type) {
|
|
96
|
+
const advice = getErrorAdvice(firstError.type);
|
|
97
|
+
if (advice && advice.length > 0) {
|
|
98
|
+
logger.info('Suggested Fix:');
|
|
99
|
+
advice.forEach(line => {
|
|
100
|
+
logger.log(` ${chalk.cyan(line)}`);
|
|
101
|
+
});
|
|
102
|
+
logger.newline();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Display relevant logs (filtered error logs)
|
|
108
|
+
if (result.relevant_logs && result.relevant_logs.length > 0) {
|
|
109
|
+
logger.info('Relevant Logs:');
|
|
110
|
+
const logLines = formatLogs(result.relevant_logs, 5);
|
|
111
|
+
logLines.forEach(line => {
|
|
112
|
+
logger.log(` ${chalk.gray(line)}`);
|
|
113
|
+
});
|
|
114
|
+
logger.newline();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Display last logs (context)
|
|
118
|
+
if (result.last_logs && result.last_logs.length > 0) {
|
|
119
|
+
logger.info('Recent Log Output:');
|
|
120
|
+
const logLines = formatLogs(result.last_logs, 5);
|
|
121
|
+
logLines.forEach(line => {
|
|
122
|
+
logger.log(` ${chalk.gray(line)}`);
|
|
123
|
+
});
|
|
124
|
+
logger.newline();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Suggest viewing full logs
|
|
128
|
+
if (result.coolify_app_uuid || result.deployment_uuid) {
|
|
129
|
+
const uuid = result.coolify_app_uuid || result.deployment_uuid;
|
|
130
|
+
logger.info('View full logs:');
|
|
131
|
+
logger.log(` ${chalk.yellow('saac logs --follow')}`);
|
|
132
|
+
logger.newline();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Display recovery instructions after failed create
|
|
138
|
+
*/
|
|
139
|
+
function displayCreateRecoveryInstructions(result, logger) {
|
|
140
|
+
if (result.coolify_app_uuid) {
|
|
141
|
+
logger.warn(`Application "${result.app_name}" was created but deployment failed.`);
|
|
142
|
+
logger.info('Fix the issue in your repository, then redeploy:');
|
|
143
|
+
logger.log(` ${chalk.yellow('saac deploy')}`);
|
|
144
|
+
logger.newline();
|
|
145
|
+
logger.info('Or delete and recreate:');
|
|
146
|
+
logger.log(` ${chalk.yellow(`saac delete ${result.coolify_app_uuid}`)}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Display timeout-specific instructions
|
|
152
|
+
*/
|
|
153
|
+
function displayTimeoutInstructions(logger) {
|
|
154
|
+
logger.warn('Deployment timed out after 5 minutes');
|
|
155
|
+
logger.newline();
|
|
156
|
+
logger.info('The deployment may still be running in the background.');
|
|
157
|
+
logger.info('Check the status:');
|
|
158
|
+
logger.log(` ${chalk.yellow('saac status')}`);
|
|
159
|
+
logger.newline();
|
|
160
|
+
logger.info('Or view live logs:');
|
|
161
|
+
logger.log(` ${chalk.yellow('saac logs --follow')}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = {
|
|
165
|
+
getErrorAdvice,
|
|
166
|
+
formatLogs,
|
|
167
|
+
displayDeploymentError,
|
|
168
|
+
displayCreateRecoveryInstructions,
|
|
169
|
+
displayTimeoutInstructions,
|
|
170
|
+
};
|