@git.zone/cli 1.16.8 → 1.17.1
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_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/gitzone.cli.js +8 -1
- package/dist_ts/mod_format/classes.baseformatter.js +12 -2
- package/dist_ts/mod_format/format.gitignore.js +27 -7
- package/dist_ts/mod_format/format.packagejson.js +3 -3
- package/dist_ts/mod_format/formatters/prettier.formatter.js +61 -37
- package/dist_ts/mod_format/index.js +1 -1
- package/dist_ts/mod_services/classes.dockercontainer.d.ts +68 -0
- package/dist_ts/mod_services/classes.dockercontainer.js +194 -0
- package/dist_ts/mod_services/classes.serviceconfiguration.d.ts +69 -0
- package/dist_ts/mod_services/classes.serviceconfiguration.js +195 -0
- package/dist_ts/mod_services/classes.servicemanager.d.ts +49 -0
- package/dist_ts/mod_services/classes.servicemanager.js +363 -0
- package/dist_ts/mod_services/helpers.d.ts +24 -0
- package/dist_ts/mod_services/helpers.js +108 -0
- package/dist_ts/mod_services/index.d.ts +1 -0
- package/dist_ts/mod_services/index.js +184 -0
- package/dist_ts/mod_services/mod.plugins.d.ts +7 -0
- package/dist_ts/mod_services/mod.plugins.js +8 -0
- package/dist_ts/plugins.d.ts +6 -1
- package/dist_ts/plugins.js +7 -2
- package/npmextra.json +1 -1
- package/package.json +29 -24
- package/readme.hints.md +8 -3
- package/readme.md +151 -17
- package/readme.plan.md +119 -168
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/gitzone.cli.ts +8 -0
- package/ts/mod_format/classes.baseformatter.ts +13 -1
- package/ts/mod_format/format.gitignore.ts +31 -6
- package/ts/mod_format/format.packagejson.ts +2 -2
- package/ts/mod_format/formatters/prettier.formatter.ts +76 -43
- package/ts/mod_format/index.ts +4 -1
- package/ts/mod_services/classes.dockercontainer.ts +227 -0
- package/ts/mod_services/classes.serviceconfiguration.ts +246 -0
- package/ts/mod_services/classes.servicemanager.ts +423 -0
- package/ts/mod_services/helpers.ts +123 -0
- package/ts/mod_services/index.ts +219 -0
- package/ts/mod_services/mod.plugins.ts +9 -0
- package/ts/plugins.ts +10 -0
package/ts/gitzone.cli.ts
CHANGED
|
@@ -131,6 +131,14 @@ export let run = async () => {
|
|
|
131
131
|
modHelpers.run(argvArg);
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* manage development services (MongoDB, S3/MinIO)
|
|
136
|
+
*/
|
|
137
|
+
gitzoneSmartcli.addCommand('services').subscribe(async (argvArg) => {
|
|
138
|
+
const modServices = await import('./mod_services/index.js');
|
|
139
|
+
await modServices.run(argvArg);
|
|
140
|
+
});
|
|
141
|
+
|
|
134
142
|
// start parsing of the cli
|
|
135
143
|
gitzoneSmartcli.startParse();
|
|
136
144
|
return await done.promise;
|
|
@@ -53,7 +53,19 @@ export abstract class BaseFormatter {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
protected async modifyFile(filepath: string, content: string): Promise<void> {
|
|
56
|
-
|
|
56
|
+
// Validate filepath before writing
|
|
57
|
+
if (!filepath || filepath.trim() === '') {
|
|
58
|
+
throw new Error(`Invalid empty filepath in modifyFile`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Ensure we have a proper path with directory component
|
|
62
|
+
// If the path has no directory component (e.g., "package.json"), prepend "./"
|
|
63
|
+
let normalizedPath = filepath;
|
|
64
|
+
if (!plugins.path.parse(filepath).dir) {
|
|
65
|
+
normalizedPath = './' + filepath;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
await plugins.smartfile.memory.toFs(content, normalizedPath);
|
|
57
69
|
}
|
|
58
70
|
|
|
59
71
|
protected async createFile(filepath: string, content: string): Promise<void> {
|
|
@@ -8,15 +8,40 @@ const gitignorePath = plugins.path.join(paths.cwd, './.gitignore');
|
|
|
8
8
|
|
|
9
9
|
export const run = async (projectArg: Project) => {
|
|
10
10
|
const gitignoreExists = await plugins.smartfile.fs.fileExists(gitignorePath);
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
let customContent = '';
|
|
12
|
+
|
|
13
13
|
if (gitignoreExists) {
|
|
14
14
|
// lets get the existing gitignore file
|
|
15
15
|
const existingGitIgnoreString =
|
|
16
16
|
plugins.smartfile.fs.toStringSync(gitignorePath);
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
// Check for different custom section markers
|
|
19
|
+
const customMarkers = ['#------# custom', '# custom'];
|
|
20
|
+
for (const marker of customMarkers) {
|
|
21
|
+
const splitResult = existingGitIgnoreString.split(marker);
|
|
22
|
+
if (splitResult.length > 1) {
|
|
23
|
+
// Get everything after the marker (excluding the marker itself)
|
|
24
|
+
customContent = splitResult[1].trim();
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Write the template
|
|
31
|
+
const templateModule = await import('../mod_template/index.js');
|
|
32
|
+
const ciTemplate = await templateModule.getTemplate('gitignore');
|
|
33
|
+
await ciTemplate.writeToDisk(paths.cwd);
|
|
34
|
+
|
|
35
|
+
// Append the custom content if it exists
|
|
36
|
+
if (customContent) {
|
|
37
|
+
const newGitignoreContent =
|
|
38
|
+
plugins.smartfile.fs.toStringSync(gitignorePath);
|
|
39
|
+
// The template already ends with "#------# custom", so just append the content
|
|
40
|
+
const finalContent =
|
|
41
|
+
newGitignoreContent.trimEnd() + '\n' + customContent + '\n';
|
|
42
|
+
await plugins.smartfile.fs.toFs(finalContent, gitignorePath);
|
|
43
|
+
logger.log('info', 'Updated .gitignore while preserving custom section!');
|
|
44
|
+
} else {
|
|
45
|
+
logger.log('info', 'Added a .gitignore!');
|
|
19
46
|
}
|
|
20
|
-
ciTemplate.writeToDisk(paths.cwd);
|
|
21
|
-
logger.log('info', 'Added a .gitignore!');
|
|
22
47
|
};
|
|
@@ -83,10 +83,10 @@ export const run = async (projectArg: Project) => {
|
|
|
83
83
|
type: 'git',
|
|
84
84
|
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}.git`,
|
|
85
85
|
};
|
|
86
|
-
(packageJson.bugs = {
|
|
86
|
+
((packageJson.bugs = {
|
|
87
87
|
url: `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}/issues`,
|
|
88
88
|
}),
|
|
89
|
-
(packageJson.homepage = `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}#readme`);
|
|
89
|
+
(packageJson.homepage = `https://${gitzoneData.module.githost}/${gitzoneData.module.gitscope}/${gitzoneData.module.gitrepo}#readme`));
|
|
90
90
|
|
|
91
91
|
// Check for module type
|
|
92
92
|
if (!packageJson.type) {
|
|
@@ -29,8 +29,9 @@ export class PrettierFormatter extends BaseFormatter {
|
|
|
29
29
|
'README.md',
|
|
30
30
|
'changelog.md',
|
|
31
31
|
'CHANGELOG.md',
|
|
32
|
-
'
|
|
33
|
-
'
|
|
32
|
+
// Skip files without extensions as prettier can't infer parser
|
|
33
|
+
// 'license',
|
|
34
|
+
// 'LICENSE',
|
|
34
35
|
'*.md',
|
|
35
36
|
];
|
|
36
37
|
|
|
@@ -102,40 +103,26 @@ export class PrettierFormatter extends BaseFormatter {
|
|
|
102
103
|
try {
|
|
103
104
|
await this.preExecute();
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
const batchSize = 10; // Process 10 files at a time
|
|
107
|
-
const batches: IPlannedChange[][] = [];
|
|
106
|
+
logVerbose(`Processing ${changes.length} files sequentially`);
|
|
108
107
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
logVerbose(
|
|
114
|
-
`Processing ${changes.length} files in ${batches.length} batches`,
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
for (let i = 0; i < batches.length; i++) {
|
|
118
|
-
const batch = batches[i];
|
|
108
|
+
// Process files sequentially to avoid prettier cache/state issues
|
|
109
|
+
for (let i = 0; i < changes.length; i++) {
|
|
110
|
+
const change = changes[i];
|
|
119
111
|
logVerbose(
|
|
120
|
-
`Processing
|
|
112
|
+
`Processing file ${i + 1}/${changes.length}: ${change.path}`,
|
|
121
113
|
);
|
|
122
114
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
// Don't throw - continue with other files
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
await Promise.all(promises);
|
|
115
|
+
try {
|
|
116
|
+
await this.applyChange(change);
|
|
117
|
+
this.stats.recordFileOperation(this.name, change.type, true);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
this.stats.recordFileOperation(this.name, change.type, false);
|
|
120
|
+
logger.log(
|
|
121
|
+
'error',
|
|
122
|
+
`Failed to format ${change.path}: ${error.message}`,
|
|
123
|
+
);
|
|
124
|
+
// Don't throw - continue with other files
|
|
125
|
+
}
|
|
139
126
|
}
|
|
140
127
|
|
|
141
128
|
await this.postExecute();
|
|
@@ -151,25 +138,71 @@ export class PrettierFormatter extends BaseFormatter {
|
|
|
151
138
|
if (change.type !== 'modify') return;
|
|
152
139
|
|
|
153
140
|
try {
|
|
141
|
+
// Validate the path before processing
|
|
142
|
+
if (!change.path || change.path.trim() === '') {
|
|
143
|
+
logger.log(
|
|
144
|
+
'error',
|
|
145
|
+
`Invalid empty path in change: ${JSON.stringify(change)}`,
|
|
146
|
+
);
|
|
147
|
+
throw new Error('Invalid empty path');
|
|
148
|
+
}
|
|
149
|
+
|
|
154
150
|
// Read current content
|
|
155
151
|
const content = plugins.smartfile.fs.toStringSync(change.path);
|
|
156
152
|
|
|
157
153
|
// Format with prettier
|
|
158
154
|
const prettier = await import('prettier');
|
|
159
|
-
const formatted = await prettier.format(content, {
|
|
160
|
-
filepath: change.path,
|
|
161
|
-
...(await this.getPrettierConfig()),
|
|
162
|
-
});
|
|
163
155
|
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
156
|
+
// Skip files that prettier can't parse without explicit parser
|
|
157
|
+
const fileExt = plugins.path.extname(change.path).toLowerCase();
|
|
158
|
+
if (!fileExt || fileExt === '') {
|
|
159
|
+
// Files without extensions need explicit parser
|
|
160
|
+
logVerbose(
|
|
161
|
+
`Skipping ${change.path} - no file extension for parser inference`,
|
|
162
|
+
);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const formatted = await prettier.format(content, {
|
|
168
|
+
filepath: change.path,
|
|
169
|
+
...(await this.getPrettierConfig()),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Only write if content actually changed
|
|
173
|
+
if (formatted !== content) {
|
|
174
|
+
// Debug: log the path being written
|
|
175
|
+
logVerbose(`Writing formatted content to: ${change.path}`);
|
|
176
|
+
await this.modifyFile(change.path, formatted);
|
|
177
|
+
logVerbose(`Formatted ${change.path}`);
|
|
178
|
+
} else {
|
|
179
|
+
logVerbose(`No formatting changes for ${change.path}`);
|
|
180
|
+
}
|
|
181
|
+
} catch (prettierError) {
|
|
182
|
+
// Check if it's a parser error
|
|
183
|
+
if (
|
|
184
|
+
prettierError.message &&
|
|
185
|
+
prettierError.message.includes('No parser could be inferred')
|
|
186
|
+
) {
|
|
187
|
+
logVerbose(`Skipping ${change.path} - ${prettierError.message}`);
|
|
188
|
+
return; // Skip this file silently
|
|
189
|
+
}
|
|
190
|
+
throw prettierError;
|
|
170
191
|
}
|
|
171
192
|
} catch (error) {
|
|
172
|
-
|
|
193
|
+
// Log the full error stack for debugging mkdir issues
|
|
194
|
+
if (error.message && error.message.includes('mkdir')) {
|
|
195
|
+
logger.log(
|
|
196
|
+
'error',
|
|
197
|
+
`Failed to format ${change.path}: ${error.message}`,
|
|
198
|
+
);
|
|
199
|
+
logger.log('error', `Error stack: ${error.stack}`);
|
|
200
|
+
} else {
|
|
201
|
+
logger.log(
|
|
202
|
+
'error',
|
|
203
|
+
`Failed to format ${change.path}: ${error.message}`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
173
206
|
throw error;
|
|
174
207
|
}
|
|
175
208
|
}
|
package/ts/mod_format/index.ts
CHANGED
|
@@ -185,5 +185,8 @@ export const handleListBackups = async (): Promise<void> => {
|
|
|
185
185
|
};
|
|
186
186
|
|
|
187
187
|
export const handleCleanBackups = async (): Promise<void> => {
|
|
188
|
-
logger.log(
|
|
188
|
+
logger.log(
|
|
189
|
+
'info',
|
|
190
|
+
'Backup cleaning has been disabled - backup system removed',
|
|
191
|
+
);
|
|
189
192
|
};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import * as plugins from './mod.plugins.js';
|
|
2
|
+
import * as helpers from './helpers.js';
|
|
3
|
+
import { logger } from '../gitzone.logging.js';
|
|
4
|
+
|
|
5
|
+
export type ContainerStatus = 'running' | 'stopped' | 'not_exists';
|
|
6
|
+
|
|
7
|
+
export interface IDockerRunOptions {
|
|
8
|
+
name: string;
|
|
9
|
+
image: string;
|
|
10
|
+
ports?: { [key: string]: string };
|
|
11
|
+
volumes?: { [key: string]: string };
|
|
12
|
+
environment?: { [key: string]: string };
|
|
13
|
+
restart?: string;
|
|
14
|
+
command?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class DockerContainer {
|
|
18
|
+
private smartshell: plugins.smartshell.Smartshell;
|
|
19
|
+
|
|
20
|
+
constructor() {
|
|
21
|
+
this.smartshell = new plugins.smartshell.Smartshell({
|
|
22
|
+
executor: 'bash',
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check if Docker is installed and available
|
|
28
|
+
*/
|
|
29
|
+
public async checkDocker(): Promise<boolean> {
|
|
30
|
+
try {
|
|
31
|
+
const result = await this.smartshell.exec('docker --version');
|
|
32
|
+
return result.exitCode === 0;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get container status
|
|
40
|
+
*/
|
|
41
|
+
public async getStatus(containerName: string): Promise<ContainerStatus> {
|
|
42
|
+
try {
|
|
43
|
+
// Check if running
|
|
44
|
+
const runningResult = await this.smartshell.exec(
|
|
45
|
+
`docker ps --format '{{.Names}}' | grep -q "^${containerName}$"`
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (runningResult.exitCode === 0) {
|
|
49
|
+
return 'running';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check if exists but stopped
|
|
53
|
+
const existsResult = await this.smartshell.exec(
|
|
54
|
+
`docker ps -a --format '{{.Names}}' | grep -q "^${containerName}$"`
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (existsResult.exitCode === 0) {
|
|
58
|
+
return 'stopped';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return 'not_exists';
|
|
62
|
+
} catch (error) {
|
|
63
|
+
return 'not_exists';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Start a container
|
|
69
|
+
*/
|
|
70
|
+
public async start(containerName: string): Promise<boolean> {
|
|
71
|
+
try {
|
|
72
|
+
const result = await this.smartshell.exec(`docker start ${containerName}`);
|
|
73
|
+
return result.exitCode === 0;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Stop a container
|
|
81
|
+
*/
|
|
82
|
+
public async stop(containerName: string): Promise<boolean> {
|
|
83
|
+
try {
|
|
84
|
+
const result = await this.smartshell.exec(`docker stop ${containerName}`);
|
|
85
|
+
return result.exitCode === 0;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Remove a container
|
|
93
|
+
*/
|
|
94
|
+
public async remove(containerName: string, force: boolean = false): Promise<boolean> {
|
|
95
|
+
try {
|
|
96
|
+
const forceFlag = force ? '-f' : '';
|
|
97
|
+
const result = await this.smartshell.exec(`docker rm ${forceFlag} ${containerName}`);
|
|
98
|
+
return result.exitCode === 0;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Run a new container
|
|
106
|
+
*/
|
|
107
|
+
public async run(options: IDockerRunOptions): Promise<boolean> {
|
|
108
|
+
let command = 'docker run -d';
|
|
109
|
+
|
|
110
|
+
// Add name
|
|
111
|
+
command += ` --name ${options.name}`;
|
|
112
|
+
|
|
113
|
+
// Add ports
|
|
114
|
+
if (options.ports) {
|
|
115
|
+
for (const [hostPort, containerPort] of Object.entries(options.ports)) {
|
|
116
|
+
command += ` -p ${hostPort}:${containerPort}`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Add volumes
|
|
121
|
+
if (options.volumes) {
|
|
122
|
+
for (const [hostPath, containerPath] of Object.entries(options.volumes)) {
|
|
123
|
+
command += ` -v "${hostPath}:${containerPath}"`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Add environment variables
|
|
128
|
+
if (options.environment) {
|
|
129
|
+
for (const [key, value] of Object.entries(options.environment)) {
|
|
130
|
+
command += ` -e ${key}="${value}"`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Add restart policy
|
|
135
|
+
if (options.restart) {
|
|
136
|
+
command += ` --restart ${options.restart}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Add image
|
|
140
|
+
command += ` ${options.image}`;
|
|
141
|
+
|
|
142
|
+
// Add command if provided
|
|
143
|
+
if (options.command) {
|
|
144
|
+
command += ` ${options.command}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const result = await this.smartshell.exec(command);
|
|
149
|
+
return result.exitCode === 0;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
logger.log('error', `Failed to run container: ${error.message}`);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Execute a command in a running container
|
|
158
|
+
*/
|
|
159
|
+
public async exec(containerName: string, command: string): Promise<string> {
|
|
160
|
+
try {
|
|
161
|
+
const result = await this.smartshell.exec(`docker exec ${containerName} ${command}`);
|
|
162
|
+
if (result.exitCode === 0) {
|
|
163
|
+
return result.stdout;
|
|
164
|
+
}
|
|
165
|
+
return '';
|
|
166
|
+
} catch (error) {
|
|
167
|
+
return '';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get container logs
|
|
173
|
+
*/
|
|
174
|
+
public async logs(containerName: string, lines?: number): Promise<string> {
|
|
175
|
+
try {
|
|
176
|
+
const tailFlag = lines ? `--tail ${lines}` : '';
|
|
177
|
+
const result = await this.smartshell.exec(`docker logs ${tailFlag} ${containerName}`);
|
|
178
|
+
return result.stdout;
|
|
179
|
+
} catch (error) {
|
|
180
|
+
return `Error getting logs: ${error.message}`;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Check if a container exists
|
|
186
|
+
*/
|
|
187
|
+
public async exists(containerName: string): Promise<boolean> {
|
|
188
|
+
const status = await this.getStatus(containerName);
|
|
189
|
+
return status !== 'not_exists';
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Check if a container is running
|
|
194
|
+
*/
|
|
195
|
+
public async isRunning(containerName: string): Promise<boolean> {
|
|
196
|
+
const status = await this.getStatus(containerName);
|
|
197
|
+
return status === 'running';
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Wait for a container to be ready
|
|
202
|
+
*/
|
|
203
|
+
public async waitForReady(containerName: string, maxAttempts: number = 30): Promise<boolean> {
|
|
204
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
205
|
+
if (await this.isRunning(containerName)) {
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
await plugins.smartdelay.delayFor(1000);
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Get container information
|
|
215
|
+
*/
|
|
216
|
+
public async inspect(containerName: string): Promise<any> {
|
|
217
|
+
try {
|
|
218
|
+
const result = await this.smartshell.exec(`docker inspect ${containerName}`);
|
|
219
|
+
if (result.exitCode === 0) {
|
|
220
|
+
return JSON.parse(result.stdout);
|
|
221
|
+
}
|
|
222
|
+
return null;
|
|
223
|
+
} catch (error) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|