@celilo/e2e 0.5.1 → 0.6.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/bin/cele2e +14 -0
- package/bin/e2e-cache-images +57 -0
- package/bin/e2e-shell +36 -13
- package/bin/e2e-status +8 -2
- package/bin/e2e-up +5 -5
- package/completions/install-zsh-completion.sh +78 -0
- package/config/dns/iamtheinternet.org.zone +1 -1
- package/config/routing/docker-image-preload.service +14 -0
- package/config/routing/docker-image-preload.sh +24 -0
- package/config/routing/management-routes.sh +5 -2
- package/docker/Dockerfile.management +2 -0
- package/docker/Dockerfile.target-machine +20 -0
- package/docker/Dockerfile.target-machine-docker +11 -19
- package/docker/Dockerfile.target-technitium +97 -0
- package/netapps/authentik.netapp +0 -0
- package/netapps/caddy.netapp +0 -0
- package/netapps/greenwave.netapp +0 -0
- package/netapps/homebridge.netapp +0 -0
- package/netapps/iptables.netapp +0 -0
- package/netapps/knot-unbound-internal.netapp +0 -0
- package/netapps/namecheap.netapp +0 -0
- package/netapps/technitium.netapp +0 -0
- package/netapps/test-ansible-output.netapp +0 -0
- package/package.json +7 -1
- package/simulators/greenwave/state.ts +7 -12
- package/src/cli/build.ts +204 -0
- package/src/cli/command-registry.ts +99 -0
- package/src/cli/command-tree-parser.ts +52 -0
- package/src/cli/completion.ts +171 -0
- package/src/cli/index.ts +203 -0
- package/src/cli/scaffold.ts +109 -0
- package/src/container-manager.ts +16 -6
- package/src/docker-compose-generator.ts +12 -10
- package/src/network-builder.ts +21 -0
- package/src/parse-line.ts +136 -0
- package/src/runner.ts +81 -88
- package/src/types.ts +4 -0
- package/bin/cache-docker-images +0 -46
package/src/cli/build.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cele2e build — build Docker images and package .netapp modules.
|
|
3
|
+
*
|
|
4
|
+
* Steps:
|
|
5
|
+
* 1. Package each specified module directory as a .netapp file
|
|
6
|
+
* 2. Build all Docker images from the @celilo/e2e docker/ directory
|
|
7
|
+
* 3. Optionally save images to tarball (--save)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { execSync, spawnSync } from 'node:child_process';
|
|
11
|
+
import { existsSync, readdirSync, mkdirSync } from 'node:fs';
|
|
12
|
+
import { basename, join, resolve } from 'node:path';
|
|
13
|
+
|
|
14
|
+
/** Walk up from pkgDir to find the monorepo root (contains a `modules/` dir). */
|
|
15
|
+
function findMonorepoRoot(pkgDir: string): string | null {
|
|
16
|
+
let dir = pkgDir;
|
|
17
|
+
for (let i = 0; i < 10; i++) {
|
|
18
|
+
if (existsSync(join(dir, 'modules'))) return dir;
|
|
19
|
+
const parent = resolve(dir, '..');
|
|
20
|
+
if (parent === dir) break;
|
|
21
|
+
dir = parent;
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Return paths to all modules that have a manifest.yml. */
|
|
27
|
+
function discoverModuleDirs(repoRoot: string): string[] {
|
|
28
|
+
const modulesDir = join(repoRoot, 'modules');
|
|
29
|
+
return readdirSync(modulesDir)
|
|
30
|
+
.map((name) => join(modulesDir, name))
|
|
31
|
+
.filter((dir) => existsSync(join(dir, 'manifest.yml')));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const bold = '\x1b[1m';
|
|
35
|
+
const dim = '\x1b[2m';
|
|
36
|
+
const green = '\x1b[32m';
|
|
37
|
+
const red = '\x1b[31m';
|
|
38
|
+
const reset = '\x1b[0m';
|
|
39
|
+
|
|
40
|
+
interface BuildOptions {
|
|
41
|
+
pkgDir: string;
|
|
42
|
+
moduleDirs: string[];
|
|
43
|
+
save: boolean;
|
|
44
|
+
skipModules: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function packageNetapp(moduleDir: string, netappsDir: string): void {
|
|
48
|
+
const absDir = resolve(moduleDir);
|
|
49
|
+
if (!existsSync(absDir)) {
|
|
50
|
+
console.error(` ${red}skip${reset} ${moduleDir} ${dim}(not found)${reset}`);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const name = basename(absDir);
|
|
55
|
+
const out = join(netappsDir, `${name}.netapp`);
|
|
56
|
+
|
|
57
|
+
process.stdout.write(` ${String(name).padEnd(20)} `);
|
|
58
|
+
const start = Date.now();
|
|
59
|
+
|
|
60
|
+
// Find celilo CLI by locating the monorepo root from the module's directory.
|
|
61
|
+
const repoRoot = findMonorepoRoot(absDir);
|
|
62
|
+
const celiloWrapper = repoRoot ? join(repoRoot, 'celilo') : null;
|
|
63
|
+
const celiloTs = repoRoot ? join(repoRoot, 'apps/celilo/src/cli/index.ts') : null;
|
|
64
|
+
|
|
65
|
+
let result: ReturnType<typeof spawnSync>;
|
|
66
|
+
if (celiloWrapper && existsSync(celiloWrapper)) {
|
|
67
|
+
result = spawnSync(celiloWrapper, ['package', absDir, '--output', out], { stdio: 'pipe' });
|
|
68
|
+
} else if (celiloTs && existsSync(celiloTs)) {
|
|
69
|
+
result = spawnSync('bun', ['run', celiloTs, 'package', absDir, '--output', out], {
|
|
70
|
+
stdio: 'pipe',
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
console.error(` ${red}skip${reset} ${name} ${dim}(celilo CLI not found)${reset}`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const elapsed = Math.round((Date.now() - start) / 1000);
|
|
78
|
+
if (result.status !== 0) {
|
|
79
|
+
console.log(`${red}✗${reset} ${dim}${result.stderr?.toString().trim()}${reset}`);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const size = (() => {
|
|
84
|
+
try {
|
|
85
|
+
return execSync(`du -h "${out}"`).toString().split('\t')[0];
|
|
86
|
+
} catch {
|
|
87
|
+
return '?';
|
|
88
|
+
}
|
|
89
|
+
})();
|
|
90
|
+
|
|
91
|
+
console.log(`${green}✔${reset} ${dim}${elapsed}s ${size}${reset}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildDockerImages(pkgDir: string): void {
|
|
95
|
+
const dockerDir = join(pkgDir, 'docker');
|
|
96
|
+
if (!existsSync(dockerDir)) {
|
|
97
|
+
console.error(`${red}Docker directory not found: ${dockerDir}${reset}`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const dockerfiles = readdirSync(dockerDir)
|
|
102
|
+
.filter((f) => f.startsWith('Dockerfile.'))
|
|
103
|
+
.sort();
|
|
104
|
+
|
|
105
|
+
if (dockerfiles.length === 0) {
|
|
106
|
+
console.log(`${dim}No Dockerfiles found in ${dockerDir}${reset}`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const total = dockerfiles.length;
|
|
111
|
+
const buildStart = Date.now();
|
|
112
|
+
|
|
113
|
+
for (let i = 0; i < total; i++) {
|
|
114
|
+
const file = dockerfiles[i];
|
|
115
|
+
const name = file.replace('Dockerfile.', '');
|
|
116
|
+
const tag = `celilo-e2e/${name}`;
|
|
117
|
+
|
|
118
|
+
process.stdout.write(` [${i + 1}/${total}] ${String(name).padEnd(25)} `);
|
|
119
|
+
|
|
120
|
+
const start = Date.now();
|
|
121
|
+
const result = spawnSync(
|
|
122
|
+
'docker',
|
|
123
|
+
['build', '-t', tag, '-f', join(dockerDir, file), '.'],
|
|
124
|
+
{ cwd: pkgDir, stdio: 'pipe' },
|
|
125
|
+
);
|
|
126
|
+
const elapsed = Math.round((Date.now() - start) / 1000);
|
|
127
|
+
|
|
128
|
+
if (result.status !== 0) {
|
|
129
|
+
console.log(`${red}✗${reset} ${dim}${elapsed}s${reset}`);
|
|
130
|
+
console.error(result.stderr?.toString());
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log(`${green}✔${reset} ${dim}${elapsed}s${reset}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const totalElapsed = Math.round((Date.now() - buildStart) / 1000);
|
|
138
|
+
console.log(`\n${green}All ${total} images built in ${totalElapsed}s${reset}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function saveImages(pkgDir: string): void {
|
|
142
|
+
const cacheDir = join(pkgDir, '.docker-cache');
|
|
143
|
+
mkdirSync(cacheDir, { recursive: true });
|
|
144
|
+
const tarball = join(cacheDir, 'celilo-e2e-images.tar');
|
|
145
|
+
|
|
146
|
+
console.log(`\n${bold}Saving images to tarball...${reset}`);
|
|
147
|
+
|
|
148
|
+
const images = execSync('docker images --filter "reference=celilo-e2e/*" --format "{{.Repository}}:{{.Tag}}"')
|
|
149
|
+
.toString()
|
|
150
|
+
.trim()
|
|
151
|
+
.split('\n')
|
|
152
|
+
.filter(Boolean)
|
|
153
|
+
.sort();
|
|
154
|
+
|
|
155
|
+
if (images.length === 0) {
|
|
156
|
+
console.error(`${red}No celilo-e2e/* images found to save${reset}`);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const result = spawnSync('docker', ['save', ...images, '-o', tarball], { stdio: 'inherit' });
|
|
161
|
+
if (result.status !== 0) process.exit(1);
|
|
162
|
+
|
|
163
|
+
const size = (() => {
|
|
164
|
+
try {
|
|
165
|
+
return execSync(`du -h "${tarball}"`).toString().split('\t')[0];
|
|
166
|
+
} catch {
|
|
167
|
+
return '?';
|
|
168
|
+
}
|
|
169
|
+
})();
|
|
170
|
+
|
|
171
|
+
console.log(`${green}Saved to ${tarball} (${size})${reset}`);
|
|
172
|
+
console.log(`\nTo restore after colima restart:\n cele2e load`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function runBuild(options: BuildOptions): void {
|
|
176
|
+
const { pkgDir, moduleDirs, save, skipModules } = options;
|
|
177
|
+
const netappsDir = join(pkgDir, 'netapps');
|
|
178
|
+
mkdirSync(netappsDir, { recursive: true });
|
|
179
|
+
|
|
180
|
+
if (!skipModules) {
|
|
181
|
+
// Explicit dirs take precedence; fall back to auto-discovering all modules.
|
|
182
|
+
const dirs = moduleDirs.length > 0
|
|
183
|
+
? moduleDirs
|
|
184
|
+
: (() => {
|
|
185
|
+
const root = findMonorepoRoot(pkgDir);
|
|
186
|
+
return root ? discoverModuleDirs(root) : [];
|
|
187
|
+
})();
|
|
188
|
+
|
|
189
|
+
if (dirs.length > 0) {
|
|
190
|
+
console.log(`${bold}Packaging modules...${reset}\n`);
|
|
191
|
+
for (const dir of dirs) {
|
|
192
|
+
packageNetapp(dir, netappsDir);
|
|
193
|
+
}
|
|
194
|
+
console.log('');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log(`${bold}Building E2E Docker images...${reset}\n`);
|
|
199
|
+
buildDockerImages(pkgDir);
|
|
200
|
+
|
|
201
|
+
if (save) {
|
|
202
|
+
saveImages(pkgDir);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command registry — single source of truth for cele2e's command structure.
|
|
3
|
+
* Used by completion, help, and coverage tests.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface CommandDef {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
subcommands?: SubcommandDef[];
|
|
10
|
+
flags?: FlagDef[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SubcommandDef {
|
|
14
|
+
name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface FlagDef {
|
|
19
|
+
name: string;
|
|
20
|
+
description: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const COMMANDS: CommandDef[] = [
|
|
24
|
+
{
|
|
25
|
+
name: 'run',
|
|
26
|
+
description: 'Run e2e tests (pattern filters by test file name)',
|
|
27
|
+
flags: [
|
|
28
|
+
{ name: '--keep', description: 'Keep network running after tests' },
|
|
29
|
+
{ name: '--reuse', description: 'Reuse existing network if running' },
|
|
30
|
+
{ name: '--live', description: 'Use live (non-simulated) internet' },
|
|
31
|
+
{ name: '--published', description: 'Use published .netapp packages' },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'up',
|
|
36
|
+
description: 'Start interactive test network',
|
|
37
|
+
subcommands: [
|
|
38
|
+
{ name: '--caddy', description: 'Start with caddy machine in DMZ' },
|
|
39
|
+
{ name: '--full-stack', description: 'Start with caddy, IDP, and DB machines' },
|
|
40
|
+
{ name: '--infrastructure', description: 'Start infrastructure only (default)' },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'down',
|
|
45
|
+
description: 'Tear down running network',
|
|
46
|
+
flags: [
|
|
47
|
+
{ name: '--keep', description: 'Stop containers but preserve volumes' },
|
|
48
|
+
{ name: '--all', description: 'Also stop competing module containers' },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'build-infra',
|
|
53
|
+
description: 'Rebuild Docker images and package all modules as .netapps',
|
|
54
|
+
flags: [
|
|
55
|
+
{ name: '--save', description: 'Save Docker images to tarball after build' },
|
|
56
|
+
{ name: '--skip-modules', description: 'Skip .netapp packaging (only rebuild Docker images)' },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'clear-timing',
|
|
61
|
+
description: 'Clear saved timing history for test ETAs',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'shell',
|
|
65
|
+
description: 'Shell into a running container (default: management)',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'status',
|
|
69
|
+
description: 'Show network status and DNS health',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'load',
|
|
73
|
+
description: 'Load cached Docker images from tarball',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: 'scaffold',
|
|
77
|
+
description: 'Generate a new test file from template',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'version',
|
|
81
|
+
description: 'Show cele2e version',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'completion',
|
|
85
|
+
description: 'Generate shell completion script',
|
|
86
|
+
subcommands: [
|
|
87
|
+
{ name: 'zsh', description: 'Generate zsh completion script' },
|
|
88
|
+
{ name: 'bash', description: 'Generate bash completion script' },
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
|
|
93
|
+
export const UP_PRESETS = ['--caddy', '--full-stack', '--infrastructure'];
|
|
94
|
+
|
|
95
|
+
export const RUN_FLAGS = ['--keep', '--reuse', '--live', '--published'];
|
|
96
|
+
|
|
97
|
+
export const DOWN_FLAGS = ['--keep', '--all'];
|
|
98
|
+
|
|
99
|
+
export const BUILD_INFRA_FLAGS = ['--save'];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discovers cele2e commands by parsing --help output.
|
|
3
|
+
* Used by completion coverage tests to verify registry ↔ help sync.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
|
|
9
|
+
const CLI = resolve(import.meta.dir, '../..', 'src/cli/index.ts');
|
|
10
|
+
|
|
11
|
+
function runHelp(args = ''): string {
|
|
12
|
+
try {
|
|
13
|
+
return execSync(`bun ${CLI} ${args} --help 2>&1`, { encoding: 'utf-8' });
|
|
14
|
+
} catch (e: unknown) {
|
|
15
|
+
return (e as { stdout?: string }).stdout?.toString() ?? '';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseCommandsFromHelp(text: string): string[] {
|
|
20
|
+
const commands: string[] = [];
|
|
21
|
+
let inCommandsSection = false;
|
|
22
|
+
|
|
23
|
+
for (const line of text.split('\n')) {
|
|
24
|
+
if (/^\s*Commands?:\s*$/i.test(line)) { inCommandsSection = true; continue; }
|
|
25
|
+
if (inCommandsSection && /^\s*(Options|Examples|Usage):/i.test(line)) { inCommandsSection = false; continue; }
|
|
26
|
+
if (!inCommandsSection) continue;
|
|
27
|
+
|
|
28
|
+
// Match " command-name" at 2-3 spaces indent, followed by space/bracket/end
|
|
29
|
+
const match = line.match(/^ {2,3}([a-z][a-z0-9-]+)(\s|$)/);
|
|
30
|
+
if (match) commands.push(match[1]);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return commands;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface CommandNode {
|
|
37
|
+
name: string;
|
|
38
|
+
subcommands: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Discovers top-level commands from --help output.
|
|
43
|
+
* cele2e doesn't implement per-command --help pages, so subcommand
|
|
44
|
+
* discovery is not attempted — use the COMMANDS registry for that.
|
|
45
|
+
*/
|
|
46
|
+
export function discoverCommands(): Map<string, CommandNode> {
|
|
47
|
+
const tree = new Map<string, CommandNode>();
|
|
48
|
+
for (const name of parseCommandsFromHelp(runHelp())) {
|
|
49
|
+
tree.set(name, { name, subcommands: [] });
|
|
50
|
+
}
|
|
51
|
+
return tree;
|
|
52
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell completion support for cele2e.
|
|
3
|
+
* All completions are static (no DB lookups needed).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
BUILD_INFRA_FLAGS,
|
|
8
|
+
COMMANDS,
|
|
9
|
+
DOWN_FLAGS,
|
|
10
|
+
RUN_FLAGS,
|
|
11
|
+
UP_PRESETS,
|
|
12
|
+
} from './command-registry';
|
|
13
|
+
|
|
14
|
+
export function getCompletions(words: string[], current: number): string[] {
|
|
15
|
+
const args = words[0] === 'cele2e' ? words.slice(1) : words;
|
|
16
|
+
const currentIndex = words[0] === 'cele2e' ? current - 1 : current;
|
|
17
|
+
|
|
18
|
+
if (currentIndex === 0) {
|
|
19
|
+
return filterSuggestions(
|
|
20
|
+
COMMANDS.map((c) => c.name),
|
|
21
|
+
args[0] || '',
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const command = args[0];
|
|
26
|
+
|
|
27
|
+
if (command === 'run' && currentIndex >= 1) {
|
|
28
|
+
const used = new Set(args.slice(1));
|
|
29
|
+
return filterSuggestions(
|
|
30
|
+
RUN_FLAGS.filter((f) => !used.has(f)),
|
|
31
|
+
args[currentIndex] || '',
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (command === 'up' && currentIndex === 1) {
|
|
36
|
+
return filterSuggestions(UP_PRESETS, args[1] || '');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (command === 'down' && currentIndex >= 1) {
|
|
40
|
+
const used = new Set(args.slice(1));
|
|
41
|
+
return filterSuggestions(
|
|
42
|
+
DOWN_FLAGS.filter((f) => !used.has(f)),
|
|
43
|
+
args[currentIndex] || '',
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (command === 'build-infra' && currentIndex >= 1) {
|
|
48
|
+
const used = new Set(args.slice(1).filter((a) => a.startsWith('--')));
|
|
49
|
+
return filterSuggestions(
|
|
50
|
+
BUILD_INFRA_FLAGS.filter((f) => !used.has(f)),
|
|
51
|
+
args[currentIndex] || '',
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (command === 'completion' && currentIndex === 1) {
|
|
56
|
+
return filterSuggestions(['zsh', 'bash'], args[1] || '');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function filterSuggestions(options: string[], current: string): string[] {
|
|
63
|
+
if (!current) return options;
|
|
64
|
+
return options.filter((opt) => opt.startsWith(current));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function generateBashCompletion(): string {
|
|
68
|
+
return `# cele2e bash completion
|
|
69
|
+
_cele2e_completion() {
|
|
70
|
+
local cur prev words cword
|
|
71
|
+
_init_completion || return
|
|
72
|
+
|
|
73
|
+
local completions
|
|
74
|
+
completions=$(cele2e --get-completions "\${COMP_WORDS[@]}" "\${COMP_CWORD}")
|
|
75
|
+
|
|
76
|
+
COMPREPLY=( $(compgen -W "$completions" -- "$cur") )
|
|
77
|
+
return 0
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
complete -F _cele2e_completion cele2e
|
|
81
|
+
`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function generateZshCompletion(): string {
|
|
85
|
+
return `#compdef cele2e
|
|
86
|
+
|
|
87
|
+
_cele2e() {
|
|
88
|
+
local curcontext="$curcontext" state line
|
|
89
|
+
typeset -A opt_args
|
|
90
|
+
|
|
91
|
+
_arguments -C \\
|
|
92
|
+
'1: :_cele2e_commands' \\
|
|
93
|
+
'*::arg:->args'
|
|
94
|
+
|
|
95
|
+
case $state in
|
|
96
|
+
args)
|
|
97
|
+
case $line[1] in
|
|
98
|
+
run)
|
|
99
|
+
_arguments \\
|
|
100
|
+
'--keep[Keep network running after tests]' \\
|
|
101
|
+
'--reuse[Reuse existing network if running]' \\
|
|
102
|
+
'--live[Use live (non-simulated) internet]' \\
|
|
103
|
+
'--published[Use published .netapp packages]' \\
|
|
104
|
+
'*:test pattern:'
|
|
105
|
+
;;
|
|
106
|
+
up)
|
|
107
|
+
_arguments '1: :_cele2e_up_presets'
|
|
108
|
+
;;
|
|
109
|
+
down)
|
|
110
|
+
_arguments \\
|
|
111
|
+
'--keep[Stop containers but preserve volumes]' \\
|
|
112
|
+
'--all[Also stop competing module containers]'
|
|
113
|
+
;;
|
|
114
|
+
build-infra)
|
|
115
|
+
_arguments \\
|
|
116
|
+
'--save[Save Docker images to tarball after build]' \\
|
|
117
|
+
'*:module directory:_directories'
|
|
118
|
+
;;
|
|
119
|
+
shell)
|
|
120
|
+
_arguments '1:container name:'
|
|
121
|
+
;;
|
|
122
|
+
scaffold)
|
|
123
|
+
_arguments '1:test name:'
|
|
124
|
+
;;
|
|
125
|
+
completion)
|
|
126
|
+
_arguments '1: :_cele2e_completion_shells'
|
|
127
|
+
;;
|
|
128
|
+
status|load|clear-timing)
|
|
129
|
+
;;
|
|
130
|
+
esac
|
|
131
|
+
;;
|
|
132
|
+
esac
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_cele2e_commands() {
|
|
136
|
+
local commands=(
|
|
137
|
+
'run:Run e2e tests (pattern filters by test file name)'
|
|
138
|
+
'up:Start interactive test network'
|
|
139
|
+
'down:Tear down running network'
|
|
140
|
+
'build-infra:Rebuild @celilo/e2e Docker images and standard module .netapps'
|
|
141
|
+
'clear-timing:Clear saved timing history for test ETAs'
|
|
142
|
+
'shell:Shell into a running container (default: management)'
|
|
143
|
+
'status:Show network status and DNS health'
|
|
144
|
+
'load:Load cached Docker images from tarball'
|
|
145
|
+
'scaffold:Generate a new test file from template'
|
|
146
|
+
'version:Show cele2e version'
|
|
147
|
+
'completion:Generate shell completion script (zsh|bash)'
|
|
148
|
+
)
|
|
149
|
+
_describe 'command' commands
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
_cele2e_up_presets() {
|
|
153
|
+
local presets=(
|
|
154
|
+
'--caddy:Start with caddy machine in DMZ'
|
|
155
|
+
'--full-stack:Start with caddy, IDP, and DB machines'
|
|
156
|
+
'--infrastructure:Start infrastructure only (default)'
|
|
157
|
+
)
|
|
158
|
+
_describe 'preset' presets
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
_cele2e_completion_shells() {
|
|
162
|
+
local shells=(
|
|
163
|
+
'zsh:Generate zsh completion script'
|
|
164
|
+
'bash:Generate bash completion script'
|
|
165
|
+
)
|
|
166
|
+
_describe 'shell' shells
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
_cele2e "$@"
|
|
170
|
+
`;
|
|
171
|
+
}
|