@constructive-io/cli 0.0.3 → 5.1.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.
Files changed (130) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +346 -153
  3. package/commands/{analyze.d.ts → codegen.d.ts} +2 -2
  4. package/commands/codegen.js +131 -0
  5. package/commands/explorer.js +94 -0
  6. package/commands/get-graphql-schema.js +73 -0
  7. package/commands/server.js +187 -0
  8. package/commands.d.ts +0 -1
  9. package/commands.js +42 -61
  10. package/esm/commands/codegen.js +126 -0
  11. package/esm/commands/explorer.js +92 -0
  12. package/esm/commands/get-graphql-schema.js +71 -0
  13. package/esm/commands/server.js +185 -0
  14. package/esm/commands.js +41 -59
  15. package/esm/index.js +16 -36
  16. package/esm/utils/display.js +26 -23
  17. package/esm/utils/index.js +3 -6
  18. package/index.d.ts +0 -22
  19. package/index.js +16 -72
  20. package/package.json +50 -25
  21. package/utils/display.d.ts +1 -1
  22. package/utils/display.js +26 -23
  23. package/utils/index.d.ts +3 -6
  24. package/utils/index.js +7 -20
  25. package/commands/add.d.ts +0 -7
  26. package/commands/add.js +0 -86
  27. package/commands/admin-users/add.d.ts +0 -4
  28. package/commands/admin-users/add.js +0 -89
  29. package/commands/admin-users/bootstrap.d.ts +0 -4
  30. package/commands/admin-users/bootstrap.js +0 -50
  31. package/commands/admin-users/remove.d.ts +0 -4
  32. package/commands/admin-users/remove.js +0 -82
  33. package/commands/admin-users.d.ts +0 -4
  34. package/commands/admin-users.js +0 -68
  35. package/commands/analyze.js +0 -21
  36. package/commands/clear.d.ts +0 -3
  37. package/commands/clear.js +0 -59
  38. package/commands/deploy.d.ts +0 -4
  39. package/commands/deploy.js +0 -146
  40. package/commands/docker.d.ts +0 -3
  41. package/commands/docker.js +0 -194
  42. package/commands/env.d.ts +0 -4
  43. package/commands/env.js +0 -124
  44. package/commands/export.js +0 -129
  45. package/commands/extension.js +0 -48
  46. package/commands/init/index.d.ts +0 -7
  47. package/commands/init/index.js +0 -47
  48. package/commands/init/module.d.ts +0 -4
  49. package/commands/init/module.js +0 -71
  50. package/commands/init/workspace.d.ts +0 -4
  51. package/commands/init/workspace.js +0 -52
  52. package/commands/install.d.ts +0 -4
  53. package/commands/install.js +0 -37
  54. package/commands/kill.js +0 -107
  55. package/commands/migrate/deps.d.ts +0 -4
  56. package/commands/migrate/deps.js +0 -186
  57. package/commands/migrate/init.d.ts +0 -4
  58. package/commands/migrate/init.js +0 -65
  59. package/commands/migrate/list.d.ts +0 -4
  60. package/commands/migrate/list.js +0 -85
  61. package/commands/migrate/status.d.ts +0 -4
  62. package/commands/migrate/status.js +0 -94
  63. package/commands/migrate.d.ts +0 -4
  64. package/commands/migrate.js +0 -69
  65. package/commands/package.d.ts +0 -3
  66. package/commands/package.js +0 -65
  67. package/commands/plan.d.ts +0 -3
  68. package/commands/plan.js +0 -62
  69. package/commands/remove.d.ts +0 -3
  70. package/commands/remove.js +0 -42
  71. package/commands/rename.d.ts +0 -4
  72. package/commands/rename.js +0 -35
  73. package/commands/revert.d.ts +0 -3
  74. package/commands/revert.js +0 -107
  75. package/commands/tag.d.ts +0 -6
  76. package/commands/tag.js +0 -168
  77. package/commands/verify.d.ts +0 -3
  78. package/commands/verify.js +0 -85
  79. package/dist/README.md +0 -412
  80. package/dist/package.json +0 -64
  81. package/esm/commands/add.js +0 -51
  82. package/esm/commands/admin-users/add.js +0 -87
  83. package/esm/commands/admin-users/bootstrap.js +0 -48
  84. package/esm/commands/admin-users/remove.js +0 -80
  85. package/esm/commands/admin-users.js +0 -63
  86. package/esm/commands/analyze.js +0 -16
  87. package/esm/commands/clear.js +0 -54
  88. package/esm/commands/deploy.js +0 -144
  89. package/esm/commands/docker.js +0 -192
  90. package/esm/commands/env.js +0 -122
  91. package/esm/commands/export.js +0 -127
  92. package/esm/commands/extension.js +0 -46
  93. package/esm/commands/init/index.js +0 -42
  94. package/esm/commands/init/module.js +0 -68
  95. package/esm/commands/init/workspace.js +0 -46
  96. package/esm/commands/install.js +0 -35
  97. package/esm/commands/kill.js +0 -105
  98. package/esm/commands/migrate/deps.js +0 -184
  99. package/esm/commands/migrate/init.js +0 -63
  100. package/esm/commands/migrate/list.js +0 -83
  101. package/esm/commands/migrate/status.js +0 -92
  102. package/esm/commands/migrate.js +0 -64
  103. package/esm/commands/package.js +0 -63
  104. package/esm/commands/plan.js +0 -60
  105. package/esm/commands/remove.js +0 -40
  106. package/esm/commands/rename.js +0 -30
  107. package/esm/commands/revert.js +0 -105
  108. package/esm/commands/tag.js +0 -133
  109. package/esm/commands/verify.js +0 -83
  110. package/esm/package.js +0 -26
  111. package/esm/utils/argv.js +0 -92
  112. package/esm/utils/cli-error.js +0 -48
  113. package/esm/utils/database.js +0 -78
  114. package/esm/utils/deployed-changes.js +0 -68
  115. package/esm/utils/module-utils.js +0 -51
  116. package/package.d.ts +0 -1
  117. package/package.js +0 -29
  118. package/utils/argv.d.ts +0 -46
  119. package/utils/argv.js +0 -100
  120. package/utils/cli-error.d.ts +0 -8
  121. package/utils/cli-error.js +0 -52
  122. package/utils/database.d.ts +0 -21
  123. package/utils/database.js +0 -83
  124. package/utils/deployed-changes.d.ts +0 -4
  125. package/utils/deployed-changes.js +0 -72
  126. package/utils/module-utils.d.ts +0 -8
  127. package/utils/module-utils.js +0 -54
  128. /package/commands/{export.d.ts → explorer.d.ts} +0 -0
  129. /package/commands/{extension.d.ts → get-graphql-schema.d.ts} +0 -0
  130. /package/commands/{kill.d.ts → server.d.ts} +0 -0
@@ -1,127 +0,0 @@
1
- import { exportMigrations, LaunchQLPackage } from '@launchql/core';
2
- import { getEnvOptions } from '@launchql/env';
3
- import { getGitConfigInfo } from '@launchql/types';
4
- import { resolve } from 'path';
5
- import { getPgPool } from 'pg-cache';
6
- const exportUsageText = `
7
- Export Command:
8
-
9
- constructive export [OPTIONS]
10
-
11
- Export database migrations from existing databases.
12
-
13
- Options:
14
- --help, -h Show this help message
15
- --author <name> Project author (default: from git config)
16
- --extensionName <name> Extension name
17
- --metaExtensionName <name> Meta extension name (default: svc)
18
- --cwd <directory> Working directory (default: current directory)
19
-
20
- Examples:
21
- constructive export Export migrations from selected database
22
- `;
23
- export default async (argv, prompter, _options) => {
24
- // Show usage if explicitly requested
25
- if (argv.help || argv.h) {
26
- console.log(exportUsageText);
27
- process.exit(0);
28
- }
29
- const { email, username } = getGitConfigInfo();
30
- const cwd = argv.cwd ?? process.cwd();
31
- const project = new LaunchQLPackage(cwd);
32
- project.ensureWorkspace();
33
- project.resetCwd(project.workspacePath);
34
- const options = getEnvOptions();
35
- const db = await getPgPool({
36
- database: 'postgres'
37
- });
38
- const databasesResult = await db.query(`
39
- SELECT datname FROM pg_catalog.pg_database
40
- WHERE datistemplate = FALSE AND datname NOT IN ('postgres')
41
- AND datname !~ '^pg_';
42
- `);
43
- let databases;
44
- ({ databases } = await prompter.prompt(argv, [
45
- {
46
- type: 'checkbox',
47
- name: 'databases',
48
- message: 'Select a database',
49
- options: databasesResult.rows.map(row => row.datname),
50
- required: true
51
- }
52
- ]));
53
- const dbname = databases.filter(d => d.selected).map(d => d.value)[0];
54
- const selectedDb = await getPgPool({
55
- database: dbname
56
- });
57
- const dbsResult = await selectedDb.query(`
58
- SELECT id, name FROM collections_public.database;
59
- `);
60
- let database_ids;
61
- ({ database_ids } = await prompter.prompt({}, [
62
- {
63
- type: 'checkbox',
64
- name: 'database_ids',
65
- message: 'Select database_id(s)',
66
- options: dbsResult.rows.map(db => db.name),
67
- required: true
68
- }
69
- ]));
70
- const dbInfo = {
71
- dbname,
72
- database_ids: database_ids.map(did => dbsResult.rows.find(db => db.name === did.name).id)
73
- };
74
- const { author, extensionName, metaExtensionName } = await prompter.prompt(argv, [
75
- {
76
- type: 'text',
77
- name: 'author',
78
- message: 'Project author',
79
- default: `${username} <${email}>`,
80
- required: true
81
- },
82
- {
83
- type: 'text',
84
- name: 'extensionName',
85
- message: 'Extension name',
86
- default: dbInfo.database_ids[0],
87
- required: true
88
- },
89
- {
90
- type: 'text',
91
- name: 'metaExtensionName',
92
- message: 'Meta extension name',
93
- default: 'svc',
94
- required: true
95
- }
96
- ]);
97
- const schemasResult = await selectedDb.query(`SELECT * FROM collections_public.schema WHERE database_id = $1`, [dbInfo.database_ids[0]]);
98
- const { schema_names } = await prompter.prompt({}, [
99
- {
100
- type: 'checkbox',
101
- name: 'schema_names',
102
- message: 'Select schema_name(s)',
103
- options: schemasResult.rows.map(s => s.schema_name),
104
- default: schemasResult.rows.map(s => s.schema_name),
105
- required: true
106
- }
107
- ]);
108
- const outdir = resolve(project.workspacePath, 'packages/');
109
- await exportMigrations({
110
- project,
111
- options,
112
- dbInfo,
113
- author,
114
- schema_names,
115
- outdir,
116
- extensionName,
117
- metaExtensionName
118
- });
119
- console.log(`
120
-
121
- |||
122
- (o o)
123
- ooO--(_)--Ooo-
124
-
125
- ✨ finished!
126
- `);
127
- };
@@ -1,46 +0,0 @@
1
- import { LaunchQLPackage } from '@launchql/core';
2
- const extensionUsageText = `
3
- Extension Command:
4
-
5
- constructive extension [OPTIONS]
6
-
7
- Manage module dependencies.
8
-
9
- Options:
10
- --help, -h Show this help message
11
- --cwd <directory> Working directory (default: current directory)
12
-
13
- Examples:
14
- constructive extension Manage dependencies for current module
15
- `;
16
- export default async (argv, prompter, _options) => {
17
- // Show usage if explicitly requested
18
- if (argv.help || argv.h) {
19
- console.log(extensionUsageText);
20
- process.exit(0);
21
- }
22
- const { cwd = process.cwd() } = argv;
23
- const project = new LaunchQLPackage(cwd);
24
- if (!project.isInModule()) {
25
- throw new Error('You must run this command inside a LaunchQL module.');
26
- }
27
- const info = project.getModuleInfo();
28
- const installed = project.getRequiredModules();
29
- const available = project.getAvailableModules();
30
- const filtered = available.filter(name => name !== info.extname);
31
- const questions = [
32
- {
33
- name: 'extensions',
34
- message: 'Which modules does this one depend on?',
35
- type: 'checkbox',
36
- allowCustomOptions: true,
37
- options: filtered,
38
- default: installed
39
- }
40
- ];
41
- const answers = await prompter.prompt(argv, questions);
42
- const selected = answers.extensions
43
- .filter(opt => opt.selected)
44
- .map(opt => opt.name);
45
- project.setModuleDependencies(selected);
46
- };
@@ -1,42 +0,0 @@
1
- import runModuleSetup from './module';
2
- import runWorkspaceSetup from './workspace';
3
- const initUsageText = `
4
- Init Command:
5
-
6
- constructive init [OPTIONS]
7
-
8
- Initialize constructive workspace or module.
9
-
10
- Options:
11
- --help, -h Show this help message
12
- --workspace Initialize workspace instead of module
13
- --cwd <directory> Working directory (default: current directory)
14
- --repo <repo> Use templates from GitHub repository (e.g., owner/repo)
15
- --template-path <path> Use templates from local path
16
- --from-branch <branch> Specify branch when using --repo (default: main)
17
-
18
- Examples:
19
- constructive init Initialize new module in existing workspace
20
- constructive init --workspace Initialize new workspace
21
- constructive init --repo owner/repo Use templates from GitHub repository
22
- constructive init --template-path ./custom-templates Use templates from local path
23
- constructive init --repo owner/repo --from-branch develop Use specific branch
24
- `;
25
- export default async (argv, prompter, _options) => {
26
- // Show usage if explicitly requested
27
- if (argv.help || argv.h) {
28
- console.log(initUsageText);
29
- process.exit(0);
30
- }
31
- return handlePromptFlow(argv, prompter);
32
- };
33
- async function handlePromptFlow(argv, prompter) {
34
- const { workspace } = argv;
35
- switch (workspace) {
36
- case true:
37
- return runWorkspaceSetup(argv, prompter);
38
- case false:
39
- default:
40
- return runModuleSetup(argv, prompter);
41
- }
42
- }
@@ -1,68 +0,0 @@
1
- import { LaunchQLPackage, sluggify } from '@launchql/core';
2
- import { Logger } from '@launchql/logger';
3
- import { errors, getGitConfigInfo } from '@launchql/types';
4
- const log = new Logger('module-init');
5
- export default async function runModuleSetup(argv, prompter) {
6
- const { email, username } = getGitConfigInfo();
7
- const { cwd = process.cwd() } = argv;
8
- const project = new LaunchQLPackage(cwd);
9
- if (!project.workspacePath) {
10
- log.error('Not inside a LaunchQL workspace.');
11
- throw errors.NOT_IN_WORKSPACE({});
12
- }
13
- if (!project.isInsideAllowedDirs(cwd) && !project.isInWorkspace() && !project.isParentOfAllowedDirs(cwd)) {
14
- log.error('You must be inside the workspace root or a parent directory of modules (like packages/).');
15
- throw errors.NOT_IN_WORKSPACE_MODULE({});
16
- }
17
- const availExtensions = project.getAvailableModules();
18
- const moduleQuestions = [
19
- {
20
- name: 'MODULENAME',
21
- message: 'Enter the module name',
22
- required: true,
23
- type: 'text',
24
- },
25
- {
26
- name: 'extensions',
27
- message: 'Which extensions?',
28
- options: availExtensions,
29
- type: 'checkbox',
30
- allowCustomOptions: true,
31
- required: true,
32
- },
33
- ];
34
- const answers = await prompter.prompt(argv, moduleQuestions);
35
- const modName = sluggify(answers.MODULENAME);
36
- const extensions = answers.extensions
37
- .filter((opt) => opt.selected)
38
- .map((opt) => opt.name);
39
- // Determine template source
40
- let templateSource;
41
- if (argv.repo) {
42
- templateSource = {
43
- type: 'github',
44
- path: argv.repo,
45
- branch: argv.fromBranch
46
- };
47
- log.info(`Loading templates from GitHub repository: ${argv.repo}`);
48
- }
49
- else if (argv.templatePath) {
50
- templateSource = {
51
- type: 'local',
52
- path: argv.templatePath
53
- };
54
- log.info(`Loading templates from local path: ${argv.templatePath}`);
55
- }
56
- project.initModule({
57
- ...argv,
58
- ...answers,
59
- name: modName,
60
- // @ts-ignore
61
- USERFULLNAME: username,
62
- USEREMAIL: email,
63
- extensions,
64
- templateSource
65
- });
66
- log.success(`Initialized module: ${modName}`);
67
- return { ...argv, ...answers };
68
- }
@@ -1,46 +0,0 @@
1
- import { sluggify } from '@launchql/core';
2
- import { Logger } from '@launchql/logger';
3
- // @ts-ignore - TypeScript module resolution issue with @launchql/templatizer
4
- import { loadTemplates, workspaceTemplate, writeRenderedTemplates } from '@launchql/templatizer';
5
- import { mkdirSync } from 'fs';
6
- import path from 'path';
7
- const log = new Logger('workspace-init');
8
- export default async function runWorkspaceSetup(argv, prompter) {
9
- const workspaceQuestions = [
10
- {
11
- name: 'name',
12
- message: 'Enter workspace name',
13
- required: true,
14
- type: 'text',
15
- }
16
- ];
17
- const answers = await prompter.prompt(argv, workspaceQuestions);
18
- const { cwd } = argv;
19
- const targetPath = path.join(cwd, sluggify(answers.name));
20
- mkdirSync(targetPath, { recursive: true });
21
- log.success(`Created workspace directory: ${targetPath}`);
22
- // Determine template source
23
- let templates = workspaceTemplate;
24
- if (argv.repo) {
25
- const source = {
26
- type: 'github',
27
- path: argv.repo,
28
- branch: argv.fromBranch
29
- };
30
- log.info(`Loading templates from GitHub repository: ${argv.repo}`);
31
- const compiledTemplates = loadTemplates(source, 'workspace');
32
- templates = compiledTemplates.map((t) => t.render);
33
- }
34
- else if (argv.templatePath) {
35
- const source = {
36
- type: 'local',
37
- path: argv.templatePath
38
- };
39
- log.info(`Loading templates from local path: ${argv.templatePath}`);
40
- const compiledTemplates = loadTemplates(source, 'workspace');
41
- templates = compiledTemplates.map((t) => t.render);
42
- }
43
- writeRenderedTemplates(templates, targetPath, { ...argv, ...answers });
44
- log.success('Workspace templates rendered.');
45
- return { ...argv, ...answers, cwd: targetPath };
46
- }
@@ -1,35 +0,0 @@
1
- import { LaunchQLPackage } from '@launchql/core';
2
- const installUsageText = `
3
- Install Command:
4
-
5
- constructive install <package>...
6
-
7
- Install database modules into current module.
8
-
9
- Arguments:
10
- package One or more package names to install
11
-
12
- Options:
13
- --help, -h Show this help message
14
- --cwd <directory> Working directory (default: current directory)
15
-
16
- Examples:
17
- constructive install @pgpm/base32 Install single package
18
- constructive install @pgpm/base32 @pgpm/utils Install multiple packages
19
- `;
20
- export default async (argv, prompter, _options) => {
21
- // Show usage if explicitly requested
22
- if (argv.help || argv.h) {
23
- console.log(installUsageText);
24
- process.exit(0);
25
- }
26
- const { cwd = process.cwd() } = argv;
27
- const project = new LaunchQLPackage(cwd);
28
- if (!project.isInModule()) {
29
- throw new Error('You must run this command inside a LaunchQL module.');
30
- }
31
- if (argv._.length === 0) {
32
- throw new Error('You must provide a package name to install, e.g. `@pgpm/base32`');
33
- }
34
- await project.installModules(...argv._);
35
- };
@@ -1,105 +0,0 @@
1
- import { Logger } from '@launchql/logger';
2
- import { getPgPool } from 'pg-cache';
3
- const log = new Logger('db-kill');
4
- const killUsageText = `
5
- Kill Command:
6
-
7
- constructive kill [OPTIONS]
8
-
9
- Terminate database connections and optionally drop databases.
10
-
11
- Options:
12
- --help, -h Show this help message
13
- --drop Drop databases after killing connections (default: true)
14
- --no-drop Only kill connections, don't drop databases
15
- --pattern <pattern> Pattern to match database names (supports SQL LIKE syntax)
16
- --cwd <directory> Working directory (default: current directory)
17
-
18
- Examples:
19
- constructive kill Kill connections and drop selected databases (interactive)
20
- constructive kill --no-drop Only kill connections, preserve databases (interactive)
21
- constructive kill --pattern test_% Kill connections to databases matching 'test_%' pattern
22
- constructive kill --pattern %dev --no-drop Kill connections to databases ending with 'dev' but don't drop
23
- `;
24
- export default async (argv, prompter, _options) => {
25
- // Show usage if explicitly requested
26
- if (argv.help || argv.h) {
27
- console.log(killUsageText);
28
- process.exit(0);
29
- }
30
- const db = await getPgPool({
31
- database: 'postgres'
32
- });
33
- let selectedDbNames;
34
- if (argv.pattern) {
35
- // Pattern mode: automatically find databases matching the pattern
36
- const databasesResult = await db.query(`
37
- SELECT datname FROM pg_catalog.pg_database
38
- WHERE datistemplate = FALSE AND datname NOT IN ('postgres')
39
- AND datname !~ '^pg_' AND datname LIKE $1;
40
- `, [argv.pattern]);
41
- if (!databasesResult.rows.length) {
42
- log.info(`ā„¹ļø No databases found matching pattern "${argv.pattern}". Exiting.`);
43
- return;
44
- }
45
- selectedDbNames = databasesResult.rows.map(row => row.datname);
46
- log.info(`šŸŽÆ Found ${selectedDbNames.length} database(s) matching pattern "${argv.pattern}": ${selectedDbNames.join(', ')}`);
47
- }
48
- else {
49
- // Interactive mode: prompt user to select databases
50
- const databasesResult = await db.query(`
51
- SELECT datname FROM pg_catalog.pg_database
52
- WHERE datistemplate = FALSE AND datname NOT IN ('postgres')
53
- AND datname !~ '^pg_';
54
- `);
55
- if (!databasesResult.rows.length) {
56
- log.info('ā„¹ļø No databases found to process. Exiting.');
57
- return;
58
- }
59
- let databases;
60
- ({ databases } = await prompter.prompt(argv, [
61
- {
62
- type: 'checkbox',
63
- name: 'databases',
64
- message: 'Select database(s) to terminate connections and optionally drop',
65
- options: databasesResult.rows.map(row => row.datname),
66
- required: true
67
- }
68
- ]));
69
- selectedDbNames = databases.filter(d => d.selected).map(d => d.value);
70
- }
71
- const actionText = argv.drop === false ? 'kill connections to' : 'kill connections and DROP';
72
- const patternText = argv.pattern ? ` (matched by pattern "${argv.pattern}")` : '';
73
- const { yes } = await prompter.prompt(argv, [
74
- {
75
- type: 'confirm',
76
- name: 'yes',
77
- message: `Are you sure you want to ${actionText}: ${selectedDbNames.join(', ')}${patternText}?`,
78
- default: false
79
- }
80
- ]);
81
- if (!yes) {
82
- log.info('āŒ Aborted. No actions were taken.');
83
- return;
84
- }
85
- for (const dbname of selectedDbNames) {
86
- const killResult = await db.query(`
87
- SELECT pg_terminate_backend(pid)
88
- FROM pg_stat_activity
89
- WHERE datname = $1 AND pid <> pg_backend_pid();
90
- `, [dbname]);
91
- log.warn(`šŸ’€ Terminated ${killResult.rowCount} connection(s) to "${dbname}".`);
92
- if (argv.drop === false) {
93
- log.info(`āš ļø Skipping DROP for "${dbname}" due to --no-drop flag.`);
94
- continue;
95
- }
96
- try {
97
- await db.query(`DROP DATABASE "${dbname}";`);
98
- log.success(`šŸ—‘ļø Dropped database "${dbname}" successfully.`);
99
- }
100
- catch (err) {
101
- log.error(`āŒ Failed to drop "${dbname}": ${err.message}`);
102
- }
103
- }
104
- log.success('āœ… Done processing databases.');
105
- };
@@ -1,184 +0,0 @@
1
- import { LaunchQLMigrate } from '@launchql/core';
2
- import { parsePlanFile } from '@launchql/core';
3
- import { Logger } from '@launchql/logger';
4
- import { existsSync } from 'fs';
5
- import { join } from 'path';
6
- import { getPgEnvOptions } from 'pg-env';
7
- import { getTargetDatabase } from '../../utils/database';
8
- const log = new Logger('migrate-deps');
9
- export default async (argv, prompter, options) => {
10
- const cwd = argv.cwd || process.cwd();
11
- const planPath = join(cwd, 'pgpm.plan');
12
- if (!existsSync(planPath)) {
13
- log.error(`No pgpm.plan found in ${cwd}`);
14
- process.exit(1);
15
- }
16
- // Get specific change to analyze
17
- let changeName = argv._?.[0] || argv.change;
18
- const planResult = parsePlanFile(planPath);
19
- if (!planResult.data || planResult.errors.length > 0) {
20
- log.error('Failed to parse plan file:', planResult.errors);
21
- process.exit(1);
22
- }
23
- const plan = planResult.data;
24
- const allChanges = plan.changes;
25
- // If no change specified, prompt
26
- if (!changeName && !argv.all) {
27
- const answer = await prompter.prompt(argv, [
28
- {
29
- type: 'autocomplete',
30
- name: 'change',
31
- message: 'Which change do you want to analyze?',
32
- options: allChanges.map(c => ({
33
- name: c.name,
34
- value: c.name,
35
- description: c.dependencies.length > 0 ? `Depends on: ${c.dependencies.join(', ')}` : 'No dependencies'
36
- }))
37
- }
38
- ]);
39
- changeName = answer.change;
40
- }
41
- try {
42
- if (argv.all) {
43
- // Show dependency graph for all changes
44
- console.log('\nšŸ”— Dependency Graph\n');
45
- console.log(`Package: ${plan.package}`);
46
- console.log(`Total Changes: ${allChanges.length}\n`);
47
- // Build dependency tree
48
- const dependencyTree = buildDependencyTree(allChanges);
49
- // Show changes with no dependencies first (roots)
50
- const roots = allChanges.filter(c => c.dependencies.length === 0);
51
- console.log('šŸ“Œ Root Changes (no dependencies):\n');
52
- roots.forEach(change => {
53
- console.log(` • ${change.name}`);
54
- showDependents(change.name, dependencyTree, ' ');
55
- });
56
- // Show orphaned changes (have dependencies but aren't depended on)
57
- const orphans = allChanges.filter(c => c.dependencies.length > 0 &&
58
- !allChanges.some(other => other.dependencies.includes(c.name)));
59
- if (orphans.length > 0) {
60
- console.log('\nšŸ”ø Leaf Changes (not depended on by others):\n');
61
- orphans.forEach(change => {
62
- console.log(` • ${change.name} → [${change.dependencies.join(', ')}]`);
63
- });
64
- }
65
- }
66
- else {
67
- // Show dependencies for specific change
68
- const change = allChanges.find(c => c.name === changeName);
69
- if (!change) {
70
- log.error(`Change '${changeName}' not found in plan file`);
71
- process.exit(1);
72
- }
73
- console.log(`\nšŸ” Dependency Analysis: ${changeName}\n`);
74
- // Direct dependencies
75
- if (change.dependencies.length > 0) {
76
- console.log('šŸ“„ Direct Dependencies:');
77
- change.dependencies.forEach(dep => {
78
- console.log(` • ${dep}`);
79
- });
80
- }
81
- else {
82
- console.log('šŸ“„ Direct Dependencies: None');
83
- }
84
- // All dependencies (recursive)
85
- const allDeps = getAllDependencies(change.name, allChanges);
86
- if (allDeps.size > 0) {
87
- console.log(`\nšŸ“¦ All Dependencies (${allDeps.size} total):`);
88
- Array.from(allDeps).forEach(dep => {
89
- console.log(` • ${dep}`);
90
- });
91
- }
92
- // Dependents (what depends on this)
93
- const dependents = allChanges.filter(c => c.dependencies.includes(changeName));
94
- if (dependents.length > 0) {
95
- console.log(`\nšŸ“¤ Depended on by (${dependents.length} changes):`);
96
- dependents.forEach(dep => {
97
- console.log(` • ${dep.name}`);
98
- });
99
- }
100
- else {
101
- console.log('\nšŸ“¤ Depended on by: None');
102
- }
103
- // Check deployment status if connected to database
104
- const pgEnv = getPgEnvOptions();
105
- const targetDatabase = await getTargetDatabase(argv, prompter, {
106
- message: 'Select database to check deployment status'
107
- });
108
- const client = new LaunchQLMigrate({
109
- host: pgEnv.host,
110
- port: pgEnv.port,
111
- user: pgEnv.user,
112
- password: pgEnv.password,
113
- database: pgEnv.database
114
- });
115
- try {
116
- const deployedChanges = await client.getDeployedChanges(targetDatabase, plan.package);
117
- const deployedMap = new Map(deployedChanges.map(c => [c.change_name, c]));
118
- console.log('\nšŸ“Š Deployment Status:');
119
- // Check if this change is deployed
120
- const isDeployed = deployedMap.has(changeName);
121
- console.log(` This change: ${isDeployed ? 'āœ… Deployed' : 'ā³ Not deployed'}`);
122
- // Check dependencies
123
- const undeployedDeps = Array.from(allDeps).filter(dep => !deployedMap.has(dep));
124
- if (undeployedDeps.length > 0) {
125
- console.log(` āš ļø Undeployed dependencies: ${undeployedDeps.join(', ')}`);
126
- }
127
- else if (allDeps.size > 0) {
128
- console.log(' āœ… All dependencies deployed');
129
- }
130
- // Check dependents
131
- const deployedDependents = dependents.filter(d => deployedMap.has(d.name));
132
- if (deployedDependents.length > 0) {
133
- console.log(` āš ļø Deployed dependents: ${deployedDependents.map(d => d.name).join(', ')}`);
134
- }
135
- }
136
- catch (dbError) {
137
- // Database connection optional for dependency analysis
138
- log.debug('Could not connect to database for deployment status');
139
- }
140
- }
141
- }
142
- catch (error) {
143
- log.error('Failed to analyze dependencies:', error);
144
- process.exit(1);
145
- }
146
- };
147
- function buildDependencyTree(changes) {
148
- const tree = new Map();
149
- changes.forEach(change => {
150
- change.dependencies.forEach((dep) => {
151
- if (!tree.has(dep)) {
152
- tree.set(dep, []);
153
- }
154
- tree.get(dep).push(change.name);
155
- });
156
- });
157
- return tree;
158
- }
159
- function showDependents(changeName, tree, indent) {
160
- const dependents = tree.get(changeName) || [];
161
- dependents.forEach(dep => {
162
- console.log(`${indent}└─ ${dep}`);
163
- showDependents(dep, tree, indent + ' ');
164
- });
165
- }
166
- function getAllDependencies(changeName, changes) {
167
- const deps = new Set();
168
- const change = changes.find(c => c.name === changeName);
169
- if (!change)
170
- return deps;
171
- function addDeps(change) {
172
- change.dependencies.forEach((dep) => {
173
- if (!deps.has(dep)) {
174
- deps.add(dep);
175
- const depChange = changes.find(c => c.name === dep);
176
- if (depChange) {
177
- addDeps(depChange);
178
- }
179
- }
180
- });
181
- }
182
- addDeps(change);
183
- return deps;
184
- }