dockup-cli 1.0.0
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/commands/auth.d.ts +5 -0
- package/dist/commands/auth.js +140 -0
- package/dist/commands/database.d.ts +16 -0
- package/dist/commands/database.js +584 -0
- package/dist/commands/env.d.ts +5 -0
- package/dist/commands/env.js +103 -0
- package/dist/commands/link.d.ts +2 -0
- package/dist/commands/link.js +135 -0
- package/dist/commands/logs.d.ts +4 -0
- package/dist/commands/logs.js +91 -0
- package/dist/commands/open.d.ts +3 -0
- package/dist/commands/open.js +46 -0
- package/dist/commands/projects.d.ts +2 -0
- package/dist/commands/projects.js +105 -0
- package/dist/commands/push.d.ts +3 -0
- package/dist/commands/push.js +135 -0
- package/dist/commands/service.d.ts +8 -0
- package/dist/commands/service.js +133 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +96 -0
- package/dist/commands/workspace.d.ts +10 -0
- package/dist/commands/workspace.js +240 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +186 -0
- package/dist/lib/api.d.ts +39 -0
- package/dist/lib/api.js +196 -0
- package/dist/lib/config.d.ts +51 -0
- package/dist/lib/config.js +176 -0
- package/package.json +52 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.link = link;
|
|
7
|
+
exports.unlink = unlink;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
10
|
+
const ora_1 = __importDefault(require("ora"));
|
|
11
|
+
const config_1 = require("../lib/config");
|
|
12
|
+
const api_1 = require("../lib/api");
|
|
13
|
+
async function link(target) {
|
|
14
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
15
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const existing = (0, config_1.getProjectConfig)();
|
|
19
|
+
if (existing) {
|
|
20
|
+
console.log(chalk_1.default.yellow(`Already linked to ${existing.projectName}/${existing.serviceName}`));
|
|
21
|
+
const { confirm } = await inquirer_1.default.prompt([{
|
|
22
|
+
type: 'confirm',
|
|
23
|
+
name: 'confirm',
|
|
24
|
+
message: 'Do you want to relink to a different service?',
|
|
25
|
+
default: false
|
|
26
|
+
}]);
|
|
27
|
+
if (!confirm) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
let projectSlug;
|
|
32
|
+
let serviceSlug;
|
|
33
|
+
let projectName;
|
|
34
|
+
let serviceName;
|
|
35
|
+
if (target) {
|
|
36
|
+
// Parse target: project/service
|
|
37
|
+
const parts = target.split('/');
|
|
38
|
+
if (parts.length !== 2) {
|
|
39
|
+
console.log(chalk_1.default.red('Invalid format. Use: dockup link <project>/<service>'));
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
[projectSlug, serviceSlug] = parts;
|
|
43
|
+
// Verify the service exists
|
|
44
|
+
const spinner = (0, ora_1.default)('Verifying service...').start();
|
|
45
|
+
try {
|
|
46
|
+
const service = await api_1.api.getServiceStatus(projectSlug, serviceSlug);
|
|
47
|
+
projectName = service.project?.name || projectSlug;
|
|
48
|
+
serviceName = service.name || serviceSlug;
|
|
49
|
+
spinner.succeed(`Found service: ${serviceName}`);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
spinner.fail(`Service not found: ${error.message}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// Interactive selection
|
|
58
|
+
const spinner = (0, ora_1.default)('Loading projects...').start();
|
|
59
|
+
try {
|
|
60
|
+
const projects = await api_1.api.getProjects();
|
|
61
|
+
spinner.stop();
|
|
62
|
+
if (projects.length === 0) {
|
|
63
|
+
console.log(chalk_1.default.yellow('No projects found. Create a project first at https://app.dockup.ai'));
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
// Select project
|
|
67
|
+
const { selectedProject } = await inquirer_1.default.prompt([{
|
|
68
|
+
type: 'list',
|
|
69
|
+
name: 'selectedProject',
|
|
70
|
+
message: 'Select a project:',
|
|
71
|
+
choices: projects.map(p => ({
|
|
72
|
+
name: `${p.name} ${chalk_1.default.dim(`(${p.slug})`)}`,
|
|
73
|
+
value: p
|
|
74
|
+
}))
|
|
75
|
+
}]);
|
|
76
|
+
projectSlug = selectedProject.slug;
|
|
77
|
+
projectName = selectedProject.name;
|
|
78
|
+
// Get services for the project
|
|
79
|
+
spinner.start('Loading services...');
|
|
80
|
+
const services = await api_1.api.getServices(projectSlug);
|
|
81
|
+
spinner.stop();
|
|
82
|
+
if (services.length === 0) {
|
|
83
|
+
console.log(chalk_1.default.yellow('No services found in this project.'));
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
// Select service
|
|
87
|
+
const { selectedService } = await inquirer_1.default.prompt([{
|
|
88
|
+
type: 'list',
|
|
89
|
+
name: 'selectedService',
|
|
90
|
+
message: 'Select a service:',
|
|
91
|
+
choices: services.map(s => ({
|
|
92
|
+
name: `${s.name} ${chalk_1.default.dim(`(${s.slug})`)} ${s.status === 'running' ? chalk_1.default.green('●') : chalk_1.default.red('●')}`,
|
|
93
|
+
value: s
|
|
94
|
+
}))
|
|
95
|
+
}]);
|
|
96
|
+
serviceSlug = selectedService.slug;
|
|
97
|
+
serviceName = selectedService.name;
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
spinner.fail(`Failed to load projects: ${error.message}`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Save config
|
|
105
|
+
(0, config_1.saveProjectConfig)({
|
|
106
|
+
projectSlug,
|
|
107
|
+
serviceSlug,
|
|
108
|
+
projectName,
|
|
109
|
+
serviceName
|
|
110
|
+
});
|
|
111
|
+
console.log(chalk_1.default.green(`\n✓ Linked to ${chalk_1.default.bold(projectName)}/${chalk_1.default.bold(serviceName)}`));
|
|
112
|
+
console.log(chalk_1.default.dim('\nYou can now run:'));
|
|
113
|
+
console.log(chalk_1.default.cyan(' dockup push ') + chalk_1.default.dim('- Deploy your code'));
|
|
114
|
+
console.log(chalk_1.default.cyan(' dockup logs ') + chalk_1.default.dim('- View live logs'));
|
|
115
|
+
console.log(chalk_1.default.cyan(' dockup status ') + chalk_1.default.dim('- Check service status'));
|
|
116
|
+
console.log();
|
|
117
|
+
}
|
|
118
|
+
async function unlink() {
|
|
119
|
+
const config = (0, config_1.getProjectConfig)();
|
|
120
|
+
if (!config) {
|
|
121
|
+
console.log(chalk_1.default.yellow('Not linked to any service'));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const { confirm } = await inquirer_1.default.prompt([{
|
|
125
|
+
type: 'confirm',
|
|
126
|
+
name: 'confirm',
|
|
127
|
+
message: `Unlink from ${config.projectName}/${config.serviceName}?`,
|
|
128
|
+
default: false
|
|
129
|
+
}]);
|
|
130
|
+
if (!confirm) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
(0, config_1.removeProjectConfig)();
|
|
134
|
+
console.log(chalk_1.default.green('✓ Unlinked successfully'));
|
|
135
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.logs = logs;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const config_1 = require("../lib/config");
|
|
10
|
+
const api_1 = require("../lib/api");
|
|
11
|
+
async function logs(options) {
|
|
12
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
13
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const config = (0, config_1.getProjectConfig)();
|
|
17
|
+
if (!config) {
|
|
18
|
+
console.log(chalk_1.default.red('Not linked to any service. Run `dockup link` first.'));
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
console.log(chalk_1.default.cyan(`\n Logs for ${chalk_1.default.bold(config.projectName)}/${chalk_1.default.bold(config.serviceName)}\n`));
|
|
22
|
+
const spinner = (0, ora_1.default)('Connecting to log stream...').start();
|
|
23
|
+
try {
|
|
24
|
+
// For now, just fetch recent logs via API
|
|
25
|
+
// In the future, we can add WebSocket support for real-time streaming
|
|
26
|
+
const service = await api_1.api.getServiceStatus(config.projectSlug, config.serviceSlug);
|
|
27
|
+
spinner.stop();
|
|
28
|
+
// Get recent deployments and their logs
|
|
29
|
+
const deployments = service.deployments || [];
|
|
30
|
+
if (deployments.length === 0) {
|
|
31
|
+
console.log(chalk_1.default.yellow(' No deployments found'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const latest = deployments[0];
|
|
35
|
+
console.log(chalk_1.default.dim(` Latest deployment: ${latest.id}`));
|
|
36
|
+
console.log(chalk_1.default.dim(` Status: ${latest.status}`));
|
|
37
|
+
console.log(chalk_1.default.dim(` Started: ${new Date(latest.startedAt || latest.createdAt).toLocaleString()}`));
|
|
38
|
+
if (latest.completedAt) {
|
|
39
|
+
console.log(chalk_1.default.dim(` Completed: ${new Date(latest.completedAt).toLocaleString()}`));
|
|
40
|
+
}
|
|
41
|
+
console.log(chalk_1.default.dim('\n ─────────────────────────────────────────\n'));
|
|
42
|
+
// If there's a build log available
|
|
43
|
+
if (latest.buildLog) {
|
|
44
|
+
const lines = latest.buildLog.split('\n');
|
|
45
|
+
const tailLines = parseInt(options.tail || '100', 10);
|
|
46
|
+
const displayLines = lines.slice(-tailLines);
|
|
47
|
+
for (const line of displayLines) {
|
|
48
|
+
// Colorize common log patterns
|
|
49
|
+
if (line.includes('ERROR') || line.includes('error')) {
|
|
50
|
+
console.log(chalk_1.default.red(` ${line}`));
|
|
51
|
+
}
|
|
52
|
+
else if (line.includes('WARN') || line.includes('warn')) {
|
|
53
|
+
console.log(chalk_1.default.yellow(` ${line}`));
|
|
54
|
+
}
|
|
55
|
+
else if (line.includes('SUCCESS') || line.includes('success') || line.includes('✓')) {
|
|
56
|
+
console.log(chalk_1.default.green(` ${line}`));
|
|
57
|
+
}
|
|
58
|
+
else if (line.startsWith('#') || line.includes('Step')) {
|
|
59
|
+
console.log(chalk_1.default.cyan(` ${line}`));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(chalk_1.default.dim(` ${line}`));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (lines.length > tailLines) {
|
|
66
|
+
console.log(chalk_1.default.dim(`\n ... ${lines.length - tailLines} more lines (use --tail to see more)`));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
console.log(chalk_1.default.yellow(' No build logs available yet'));
|
|
71
|
+
}
|
|
72
|
+
console.log();
|
|
73
|
+
if (options.follow) {
|
|
74
|
+
console.log(chalk_1.default.dim(' Live log streaming coming soon...'));
|
|
75
|
+
console.log(chalk_1.default.dim(' Press Ctrl+C to exit\n'));
|
|
76
|
+
// Keep the process running for future WebSocket implementation
|
|
77
|
+
process.on('SIGINT', () => {
|
|
78
|
+
console.log(chalk_1.default.dim('\n Disconnected'));
|
|
79
|
+
process.exit(0);
|
|
80
|
+
});
|
|
81
|
+
// For now, just poll every 5 seconds
|
|
82
|
+
// setInterval(async () => {
|
|
83
|
+
// // Poll for new logs
|
|
84
|
+
// }, 5000);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
spinner.fail(`Failed to fetch logs: ${error.message}`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.open = open;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const open_1 = __importDefault(require("open"));
|
|
10
|
+
const config_1 = require("../lib/config");
|
|
11
|
+
const api_1 = require("../lib/api");
|
|
12
|
+
async function open(options) {
|
|
13
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
14
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const config = (0, config_1.getProjectConfig)();
|
|
18
|
+
if (!config) {
|
|
19
|
+
console.log(chalk_1.default.red('Not linked to any service. Run `dockup link` first.'));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
if (options.dashboard) {
|
|
23
|
+
// Open dashboard
|
|
24
|
+
const dashboardUrl = `${(0, config_1.getApiUrl)().replace('deploy-api', 'app')}/projects/${config.projectSlug}/services/${config.serviceSlug}`;
|
|
25
|
+
console.log(chalk_1.default.dim(`Opening dashboard: ${dashboardUrl}`));
|
|
26
|
+
await (0, open_1.default)(dashboardUrl);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Open live URL
|
|
30
|
+
const spinner = (0, ora_1.default)('Fetching service URL...').start();
|
|
31
|
+
try {
|
|
32
|
+
const service = await api_1.api.getServiceStatus(config.projectSlug, config.serviceSlug);
|
|
33
|
+
spinner.stop();
|
|
34
|
+
if (!service.domain) {
|
|
35
|
+
console.log(chalk_1.default.yellow('Service has no domain yet'));
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const url = `https://${service.domain}`;
|
|
39
|
+
console.log(chalk_1.default.dim(`Opening: ${url}`));
|
|
40
|
+
await (0, open_1.default)(url);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
spinner.fail(`Failed to fetch service: ${error.message}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.listProjects = listProjects;
|
|
7
|
+
exports.listServices = listServices;
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const ora_1 = __importDefault(require("ora"));
|
|
10
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
11
|
+
const config_1 = require("../lib/config");
|
|
12
|
+
const api_1 = require("../lib/api");
|
|
13
|
+
async function listProjects() {
|
|
14
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
15
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const spinner = (0, ora_1.default)('Loading projects...').start();
|
|
19
|
+
try {
|
|
20
|
+
const projects = await api_1.api.getProjects();
|
|
21
|
+
spinner.stop();
|
|
22
|
+
if (projects.length === 0) {
|
|
23
|
+
console.log(chalk_1.default.yellow('\n No projects found.'));
|
|
24
|
+
console.log(chalk_1.default.dim(' Create one at https://app.dockup.ai\n'));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
console.log(chalk_1.default.cyan('\n Your Projects:\n'));
|
|
28
|
+
for (const project of projects) {
|
|
29
|
+
const serviceCount = project.serviceCount || 0;
|
|
30
|
+
const dbCount = project.databaseCount || 0;
|
|
31
|
+
console.log(` ${chalk_1.default.bold(project.name)} ${chalk_1.default.dim(`(${project.slug})`)}`);
|
|
32
|
+
console.log(chalk_1.default.dim(` Services: ${serviceCount} | Databases: ${dbCount}`));
|
|
33
|
+
console.log();
|
|
34
|
+
}
|
|
35
|
+
console.log(chalk_1.default.dim(` Total: ${projects.length} project(s)\n`));
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
spinner.fail(`Failed to load projects: ${error.message}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function listServices(projectSlug) {
|
|
43
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
44
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
const spinner = (0, ora_1.default)('Loading...').start();
|
|
48
|
+
try {
|
|
49
|
+
// If no project specified, ask user to select
|
|
50
|
+
if (!projectSlug) {
|
|
51
|
+
const projects = await api_1.api.getProjects();
|
|
52
|
+
spinner.stop();
|
|
53
|
+
if (projects.length === 0) {
|
|
54
|
+
console.log(chalk_1.default.yellow('No projects found.'));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const { selected } = await inquirer_1.default.prompt([{
|
|
58
|
+
type: 'list',
|
|
59
|
+
name: 'selected',
|
|
60
|
+
message: 'Select a project:',
|
|
61
|
+
choices: projects.map(p => ({
|
|
62
|
+
name: `${p.name} ${chalk_1.default.dim(`(${p.slug})`)}`,
|
|
63
|
+
value: p.slug
|
|
64
|
+
}))
|
|
65
|
+
}]);
|
|
66
|
+
projectSlug = selected;
|
|
67
|
+
spinner.start('Loading services...');
|
|
68
|
+
}
|
|
69
|
+
const services = await api_1.api.getServices(projectSlug);
|
|
70
|
+
spinner.stop();
|
|
71
|
+
if (services.length === 0) {
|
|
72
|
+
console.log(chalk_1.default.yellow(`\n No services in ${projectSlug}`));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(chalk_1.default.cyan(`\n Services in ${chalk_1.default.bold(projectSlug)}:\n`));
|
|
76
|
+
for (const service of services) {
|
|
77
|
+
const statusColors = {
|
|
78
|
+
running: chalk_1.default.green,
|
|
79
|
+
building: chalk_1.default.yellow,
|
|
80
|
+
deploying: chalk_1.default.blue,
|
|
81
|
+
stopped: chalk_1.default.red,
|
|
82
|
+
error: chalk_1.default.red,
|
|
83
|
+
pending: chalk_1.default.gray
|
|
84
|
+
};
|
|
85
|
+
const statusColor = statusColors[service.status] || chalk_1.default.gray;
|
|
86
|
+
const statusIcons = {
|
|
87
|
+
running: '●',
|
|
88
|
+
building: '◐',
|
|
89
|
+
deploying: '◐',
|
|
90
|
+
stopped: '○',
|
|
91
|
+
error: '✗',
|
|
92
|
+
pending: '○'
|
|
93
|
+
};
|
|
94
|
+
const statusIcon = statusIcons[service.status] || '?';
|
|
95
|
+
console.log(` ${statusColor(statusIcon)} ${chalk_1.default.bold(service.name)} ${chalk_1.default.dim(`(${service.slug})`)}`);
|
|
96
|
+
console.log(chalk_1.default.dim(` Status: ${service.status} | Domain: ${service.domain || 'N/A'}`));
|
|
97
|
+
console.log();
|
|
98
|
+
}
|
|
99
|
+
console.log(chalk_1.default.dim(` Total: ${services.length} service(s)\n`));
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
spinner.fail(`Failed to load services: ${error.message}`);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.push = push;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const config_1 = require("../lib/config");
|
|
11
|
+
const api_1 = require("../lib/api");
|
|
12
|
+
async function push(branch, options) {
|
|
13
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
14
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const config = (0, config_1.getProjectConfig)();
|
|
18
|
+
if (!config) {
|
|
19
|
+
console.log(chalk_1.default.red('Not linked to any service. Run `dockup link` first.'));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
console.log(chalk_1.default.cyan(`\n Deploying to ${chalk_1.default.bold(config.projectName)}/${chalk_1.default.bold(config.serviceName)}\n`));
|
|
23
|
+
// Get current branch if not specified
|
|
24
|
+
if (!branch) {
|
|
25
|
+
try {
|
|
26
|
+
branch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', { encoding: 'utf-8' }).trim();
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
branch = 'main';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Check for uncommitted changes
|
|
33
|
+
try {
|
|
34
|
+
const status = (0, child_process_1.execSync)('git status --porcelain', { encoding: 'utf-8' }).trim();
|
|
35
|
+
if (status) {
|
|
36
|
+
console.log(chalk_1.default.yellow('⚠ You have uncommitted changes:'));
|
|
37
|
+
console.log(chalk_1.default.dim(status.split('\n').slice(0, 5).join('\n')));
|
|
38
|
+
if (status.split('\n').length > 5) {
|
|
39
|
+
console.log(chalk_1.default.dim(` ... and ${status.split('\n').length - 5} more files`));
|
|
40
|
+
}
|
|
41
|
+
console.log();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Not a git repo, that's fine
|
|
46
|
+
}
|
|
47
|
+
// Git push first (unless --force)
|
|
48
|
+
if (!options?.force) {
|
|
49
|
+
const gitSpinner = (0, ora_1.default)(`Pushing to origin/${branch}...`).start();
|
|
50
|
+
try {
|
|
51
|
+
(0, child_process_1.execSync)(`git push origin ${branch}`, {
|
|
52
|
+
encoding: 'utf-8',
|
|
53
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
54
|
+
});
|
|
55
|
+
gitSpinner.succeed(`Pushed to origin/${branch}`);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
// Check if it's just "Everything up-to-date"
|
|
59
|
+
if (error.stderr?.includes('Everything up-to-date') || error.stdout?.includes('Everything up-to-date')) {
|
|
60
|
+
gitSpinner.succeed('Already up to date');
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
gitSpinner.warn('Git push failed (continuing with deploy)');
|
|
64
|
+
console.log(chalk_1.default.dim(error.message));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Trigger deployment
|
|
69
|
+
const deploySpinner = (0, ora_1.default)('Triggering deployment...').start();
|
|
70
|
+
try {
|
|
71
|
+
const result = await api_1.api.deploy(config.projectSlug, config.serviceSlug, { branch });
|
|
72
|
+
deploySpinner.succeed('Deployment started!');
|
|
73
|
+
console.log(chalk_1.default.dim(`\n Deployment ID: ${result.deployment?.id || 'N/A'}`));
|
|
74
|
+
// Poll for deployment status
|
|
75
|
+
console.log(chalk_1.default.cyan('\n Building...\n'));
|
|
76
|
+
let lastStatus = '';
|
|
77
|
+
let dots = 0;
|
|
78
|
+
const maxPolls = 120; // 2 minutes max
|
|
79
|
+
let polls = 0;
|
|
80
|
+
while (polls < maxPolls) {
|
|
81
|
+
polls++;
|
|
82
|
+
try {
|
|
83
|
+
const status = await api_1.api.getServiceStatus(config.projectSlug, config.serviceSlug);
|
|
84
|
+
const deployment = status.deployments?.[0];
|
|
85
|
+
if (!deployment) {
|
|
86
|
+
await sleep(1000);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const currentStatus = deployment.status;
|
|
90
|
+
if (currentStatus !== lastStatus) {
|
|
91
|
+
lastStatus = currentStatus;
|
|
92
|
+
if (currentStatus === 'building') {
|
|
93
|
+
process.stdout.write(chalk_1.default.yellow(' ⏳ Building'));
|
|
94
|
+
}
|
|
95
|
+
else if (currentStatus === 'deploying') {
|
|
96
|
+
process.stdout.write(chalk_1.default.blue('\n 🚀 Deploying'));
|
|
97
|
+
}
|
|
98
|
+
else if (currentStatus === 'success') {
|
|
99
|
+
console.log(chalk_1.default.green('\n\n ✓ Deployed successfully!\n'));
|
|
100
|
+
console.log(chalk_1.default.dim(' URL: ') + chalk_1.default.cyan(`https://${status.domain}`));
|
|
101
|
+
console.log();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
else if (currentStatus === 'failed') {
|
|
105
|
+
console.log(chalk_1.default.red('\n\n ✗ Deployment failed\n'));
|
|
106
|
+
console.log(chalk_1.default.dim(' Run `dockup logs` to see error details'));
|
|
107
|
+
console.log();
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// Show progress dots
|
|
113
|
+
dots++;
|
|
114
|
+
if (dots % 3 === 0) {
|
|
115
|
+
process.stdout.write('.');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
await sleep(1000);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
await sleep(1000);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
console.log(chalk_1.default.yellow('\n\n ⚠ Deployment is taking longer than expected'));
|
|
125
|
+
console.log(chalk_1.default.dim(' Run `dockup status` to check progress'));
|
|
126
|
+
console.log();
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
deploySpinner.fail(`Deployment failed: ${error.message}`);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function sleep(ms) {
|
|
134
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
135
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function startService(): Promise<void>;
|
|
2
|
+
export declare function stopService(): Promise<void>;
|
|
3
|
+
export declare function restartService(): Promise<void>;
|
|
4
|
+
export declare function deployService(): Promise<void>;
|
|
5
|
+
export declare function serviceLogs(options: {
|
|
6
|
+
tail?: string;
|
|
7
|
+
follow?: boolean;
|
|
8
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startService = startService;
|
|
7
|
+
exports.stopService = stopService;
|
|
8
|
+
exports.restartService = restartService;
|
|
9
|
+
exports.deployService = deployService;
|
|
10
|
+
exports.serviceLogs = serviceLogs;
|
|
11
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const ora_1 = __importDefault(require("ora"));
|
|
13
|
+
const config_1 = require("../lib/config");
|
|
14
|
+
const api_1 = require("../lib/api");
|
|
15
|
+
async function getServiceContext() {
|
|
16
|
+
const config = (0, config_1.getProjectConfig)();
|
|
17
|
+
if (!config) {
|
|
18
|
+
console.log(chalk_1.default.red('Not linked to any service. Run `dockup link` first.'));
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return { projectSlug: config.projectSlug, serviceSlug: config.serviceSlug };
|
|
22
|
+
}
|
|
23
|
+
async function startService() {
|
|
24
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
25
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const ctx = await getServiceContext();
|
|
29
|
+
if (!ctx)
|
|
30
|
+
process.exit(1);
|
|
31
|
+
const spinner = (0, ora_1.default)('Starting service...').start();
|
|
32
|
+
try {
|
|
33
|
+
await api_1.api.post(`/projects/${ctx.projectSlug}/services/${ctx.serviceSlug}/start`);
|
|
34
|
+
spinner.succeed('Service started!');
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
spinner.fail(`Failed to start: ${error.message}`);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function stopService() {
|
|
42
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
43
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
const ctx = await getServiceContext();
|
|
47
|
+
if (!ctx)
|
|
48
|
+
process.exit(1);
|
|
49
|
+
const spinner = (0, ora_1.default)('Stopping service...').start();
|
|
50
|
+
try {
|
|
51
|
+
await api_1.api.post(`/projects/${ctx.projectSlug}/services/${ctx.serviceSlug}/stop`);
|
|
52
|
+
spinner.succeed('Service stopped!');
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
spinner.fail(`Failed to stop: ${error.message}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function restartService() {
|
|
60
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
61
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const ctx = await getServiceContext();
|
|
65
|
+
if (!ctx)
|
|
66
|
+
process.exit(1);
|
|
67
|
+
const spinner = (0, ora_1.default)('Restarting service...').start();
|
|
68
|
+
try {
|
|
69
|
+
await api_1.api.post(`/projects/${ctx.projectSlug}/services/${ctx.serviceSlug}/restart`);
|
|
70
|
+
spinner.succeed('Service restarted!');
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
spinner.fail(`Failed to restart: ${error.message}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function deployService() {
|
|
78
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
79
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
const ctx = await getServiceContext();
|
|
83
|
+
if (!ctx)
|
|
84
|
+
process.exit(1);
|
|
85
|
+
const spinner = (0, ora_1.default)('Triggering deployment...').start();
|
|
86
|
+
try {
|
|
87
|
+
const result = await api_1.api.deploy(ctx.projectSlug, ctx.serviceSlug);
|
|
88
|
+
spinner.succeed('Deployment started!');
|
|
89
|
+
console.log(chalk_1.default.dim(`\n Deployment ID: ${result.deploymentId || 'N/A'}`));
|
|
90
|
+
console.log(chalk_1.default.dim(' Run `dockup logs` to watch progress\n'));
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
spinner.fail(`Failed to deploy: ${error.message}`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function serviceLogs(options) {
|
|
98
|
+
if (!(0, config_1.isLoggedIn)()) {
|
|
99
|
+
console.log(chalk_1.default.red('Not logged in. Run `dockup login` first.'));
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
const ctx = await getServiceContext();
|
|
103
|
+
if (!ctx)
|
|
104
|
+
process.exit(1);
|
|
105
|
+
const spinner = (0, ora_1.default)('Fetching logs...').start();
|
|
106
|
+
try {
|
|
107
|
+
const lines = parseInt(options.tail || '100', 10);
|
|
108
|
+
const logs = await api_1.api.get(`/projects/${ctx.projectSlug}/services/${ctx.serviceSlug}/logs?lines=${lines}`);
|
|
109
|
+
spinner.stop();
|
|
110
|
+
if (!logs.logs || logs.logs.length === 0) {
|
|
111
|
+
console.log(chalk_1.default.yellow('\n No logs available\n'));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
console.log(chalk_1.default.cyan('\n Service Logs:\n'));
|
|
115
|
+
const logLines = logs.logs.split('\n');
|
|
116
|
+
for (const line of logLines) {
|
|
117
|
+
if (line.toLowerCase().includes('error')) {
|
|
118
|
+
console.log(chalk_1.default.red(` ${line}`));
|
|
119
|
+
}
|
|
120
|
+
else if (line.toLowerCase().includes('warn')) {
|
|
121
|
+
console.log(chalk_1.default.yellow(` ${line}`));
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
console.log(chalk_1.default.dim(` ${line}`));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
console.log();
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
spinner.fail(`Failed to fetch logs: ${error.message}`);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|