@output.ai/cli 0.7.5 → 0.7.6
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.
|
@@ -6,6 +6,7 @@ export default class Dev extends Command {
|
|
|
6
6
|
static flags: {
|
|
7
7
|
'compose-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
8
|
'no-watch': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
'image-pull-policy': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
10
|
};
|
|
10
11
|
private dockerProcess;
|
|
11
12
|
run(): Promise<void>;
|
|
@@ -73,7 +73,8 @@ export default class Dev extends Command {
|
|
|
73
73
|
static examples = [
|
|
74
74
|
'<%= config.bin %> <%= command.id %>',
|
|
75
75
|
'<%= config.bin %> <%= command.id %> --no-watch',
|
|
76
|
-
'<%= config.bin %> <%= command.id %> --compose-file ./custom-docker-compose.yml'
|
|
76
|
+
'<%= config.bin %> <%= command.id %> --compose-file ./custom-docker-compose.yml',
|
|
77
|
+
'<%= config.bin %> <%= command.id %> --image-pull-policy missing'
|
|
77
78
|
];
|
|
78
79
|
static args = {};
|
|
79
80
|
static flags = {
|
|
@@ -85,6 +86,11 @@ export default class Dev extends Command {
|
|
|
85
86
|
'no-watch': Flags.boolean({
|
|
86
87
|
description: 'Disable automatic container restart on file changes',
|
|
87
88
|
default: false
|
|
89
|
+
}),
|
|
90
|
+
'image-pull-policy': Flags.string({
|
|
91
|
+
description: 'Image pull policy for docker compose (always, missing, never)',
|
|
92
|
+
options: ['always', 'missing', 'never'],
|
|
93
|
+
default: 'always'
|
|
88
94
|
})
|
|
89
95
|
};
|
|
90
96
|
dockerProcess = null;
|
|
@@ -120,8 +126,9 @@ export default class Dev extends Command {
|
|
|
120
126
|
};
|
|
121
127
|
process.on('SIGINT', cleanup);
|
|
122
128
|
process.on('SIGTERM', cleanup);
|
|
129
|
+
const pullPolicy = flags['image-pull-policy'];
|
|
123
130
|
try {
|
|
124
|
-
const { process: dockerProc, waitForHealthy } = await startDockerCompose(dockerComposePath, !flags['no-watch']);
|
|
131
|
+
const { process: dockerProc, waitForHealthy } = await startDockerCompose(dockerComposePath, !flags['no-watch'], pullPolicy);
|
|
125
132
|
this.dockerProcess = dockerProc;
|
|
126
133
|
dockerProc.on('error', error => {
|
|
127
134
|
this.error(`Docker process error: ${getErrorMessage(error)}`, { exit: 1 });
|
|
@@ -78,6 +78,11 @@ describe('dev command', () => {
|
|
|
78
78
|
expect(Dev.flags['compose-file'].required).toBe(false);
|
|
79
79
|
expect(Dev.flags['compose-file'].char).toBe('f');
|
|
80
80
|
});
|
|
81
|
+
it('should have image-pull-policy flag defined', () => {
|
|
82
|
+
expect(Dev.flags).toBeDefined();
|
|
83
|
+
expect(Dev.flags['image-pull-policy']).toBeDefined();
|
|
84
|
+
expect(Dev.flags['image-pull-policy'].description).toContain('pull policy');
|
|
85
|
+
});
|
|
81
86
|
});
|
|
82
87
|
describe('command instantiation', () => {
|
|
83
88
|
it('should be instantiable', () => {
|
|
@@ -130,14 +135,15 @@ describe('dev command', () => {
|
|
|
130
135
|
cmd.error = vi.fn();
|
|
131
136
|
// Mock parse to return flags
|
|
132
137
|
Object.defineProperty(cmd, 'parse', {
|
|
133
|
-
value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined }, args: {} }),
|
|
138
|
+
value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined, 'image-pull-policy': 'always' }, args: {} }),
|
|
134
139
|
configurable: true
|
|
135
140
|
});
|
|
136
141
|
// Run the command but don't await it since it waits forever after startup
|
|
137
142
|
const runPromise = cmd.run();
|
|
138
143
|
// Wait a tick for startDockerCompose to be called
|
|
139
144
|
await new Promise(resolve => setImmediate(resolve));
|
|
140
|
-
expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', true // enableWatch should be true
|
|
145
|
+
expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', true, // enableWatch should be true
|
|
146
|
+
'always' // default pull policy
|
|
141
147
|
);
|
|
142
148
|
expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('File watching enabled'));
|
|
143
149
|
// Cancel the promise (it will be rejected but we don't care)
|
|
@@ -149,14 +155,15 @@ describe('dev command', () => {
|
|
|
149
155
|
cmd.error = vi.fn();
|
|
150
156
|
// Mock parse to return flags
|
|
151
157
|
Object.defineProperty(cmd, 'parse', {
|
|
152
|
-
value: vi.fn().mockResolvedValue({ flags: { 'no-watch': true, 'compose-file': undefined }, args: {} }),
|
|
158
|
+
value: vi.fn().mockResolvedValue({ flags: { 'no-watch': true, 'compose-file': undefined, 'image-pull-policy': 'always' }, args: {} }),
|
|
153
159
|
configurable: true
|
|
154
160
|
});
|
|
155
161
|
// Run the command but don't await it since it waits forever after startup
|
|
156
162
|
const runPromise = cmd.run();
|
|
157
163
|
// Wait a tick for startDockerCompose to be called
|
|
158
164
|
await new Promise(resolve => setImmediate(resolve));
|
|
159
|
-
expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', false // enableWatch should be false
|
|
165
|
+
expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', false, // enableWatch should be false
|
|
166
|
+
'always' // default pull policy
|
|
160
167
|
);
|
|
161
168
|
expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('File watching disabled'));
|
|
162
169
|
// Cancel the promise (it will be rejected but we don't care)
|
|
@@ -176,11 +183,47 @@ describe('dev command', () => {
|
|
|
176
183
|
cmd.error = vi.fn();
|
|
177
184
|
// Mock parse to return flags
|
|
178
185
|
Object.defineProperty(cmd, 'parse', {
|
|
179
|
-
value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined }, args: {} }),
|
|
186
|
+
value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined, 'image-pull-policy': 'always' }, args: {} }),
|
|
180
187
|
configurable: true
|
|
181
188
|
});
|
|
182
189
|
await cmd.run();
|
|
183
190
|
expect(cmd.error).toHaveBeenCalledWith('Docker error', { exit: 1 });
|
|
184
191
|
});
|
|
185
192
|
});
|
|
193
|
+
describe('image pull policy', () => {
|
|
194
|
+
it('should pass pull policy to startDockerCompose', async () => {
|
|
195
|
+
const cmd = new Dev([], {});
|
|
196
|
+
cmd.log = vi.fn();
|
|
197
|
+
cmd.error = vi.fn();
|
|
198
|
+
// Mock parse to return flags with missing pull policy
|
|
199
|
+
Object.defineProperty(cmd, 'parse', {
|
|
200
|
+
value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined, 'image-pull-policy': 'missing' }, args: {} }),
|
|
201
|
+
configurable: true
|
|
202
|
+
});
|
|
203
|
+
// Run the command but don't await it since it waits forever after startup
|
|
204
|
+
const runPromise = cmd.run();
|
|
205
|
+
// Wait a tick for startDockerCompose to be called
|
|
206
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
207
|
+
expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', true, 'missing');
|
|
208
|
+
// Cancel the promise (it will be rejected but we don't care)
|
|
209
|
+
runPromise.catch(() => { });
|
|
210
|
+
});
|
|
211
|
+
it('should use never pull policy when specified', async () => {
|
|
212
|
+
const cmd = new Dev([], {});
|
|
213
|
+
cmd.log = vi.fn();
|
|
214
|
+
cmd.error = vi.fn();
|
|
215
|
+
// Mock parse to return flags with never pull policy
|
|
216
|
+
Object.defineProperty(cmd, 'parse', {
|
|
217
|
+
value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined, 'image-pull-policy': 'never' }, args: {} }),
|
|
218
|
+
configurable: true
|
|
219
|
+
});
|
|
220
|
+
// Run the command but don't await it since it waits forever after startup
|
|
221
|
+
const runPromise = cmd.run();
|
|
222
|
+
// Wait a tick for startDockerCompose to be called
|
|
223
|
+
await new Promise(resolve => setImmediate(resolve));
|
|
224
|
+
expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', true, 'never');
|
|
225
|
+
// Cancel the promise (it will be rejected but we don't care)
|
|
226
|
+
runPromise.catch(() => { });
|
|
227
|
+
});
|
|
228
|
+
});
|
|
186
229
|
});
|
|
@@ -32,6 +32,7 @@ export interface DockerComposeProcess {
|
|
|
32
32
|
process: ChildProcess;
|
|
33
33
|
waitForHealthy: () => Promise<void>;
|
|
34
34
|
}
|
|
35
|
-
export
|
|
35
|
+
export type PullPolicy = 'always' | 'missing' | 'never';
|
|
36
|
+
export declare function startDockerCompose(dockerComposePath: string, enableWatch?: boolean, pullPolicy?: PullPolicy): Promise<DockerComposeProcess>;
|
|
36
37
|
export declare function stopDockerCompose(dockerComposePath: string): Promise<void>;
|
|
37
38
|
export { isDockerInstalled, isDockerComposeAvailable, isDockerDaemonRunning, DockerValidationError };
|
package/dist/services/docker.js
CHANGED
|
@@ -121,13 +121,16 @@ export async function waitForServicesHealthy(dockerComposePath, timeoutMs = 1200
|
|
|
121
121
|
logUpdate.done();
|
|
122
122
|
throw new Error('Timeout waiting for services to become healthy');
|
|
123
123
|
}
|
|
124
|
-
export async function startDockerCompose(dockerComposePath, enableWatch = false) {
|
|
124
|
+
export async function startDockerCompose(dockerComposePath, enableWatch = false, pullPolicy) {
|
|
125
125
|
const args = [
|
|
126
126
|
'compose',
|
|
127
127
|
'-f', dockerComposePath,
|
|
128
128
|
'--project-directory', process.cwd(),
|
|
129
129
|
'up'
|
|
130
130
|
];
|
|
131
|
+
if (pullPolicy) {
|
|
132
|
+
args.push('--pull', pullPolicy);
|
|
133
|
+
}
|
|
131
134
|
if (enableWatch) {
|
|
132
135
|
args.push('--watch');
|
|
133
136
|
}
|