-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpack-installer.ts
More file actions
139 lines (128 loc) · 4.27 KB
/
pack-installer.ts
File metadata and controls
139 lines (128 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { execFile } from 'child_process';
import { access } from 'fs/promises';
import { constants } from 'fs';
import { join } from 'path';
import { DisposableObject } from '../common/disposable';
import type { Logger } from '../common/logger';
import type { CliResolver } from '../codeql/cli-resolver';
import type { ServerManager } from './server-manager';
export interface PackInstallOptions {
/** Force reinstall even if lock files exist. */
force?: boolean;
/** Install only for specific languages. */
languages?: string[];
}
/**
* Installs CodeQL pack dependencies for the bundled tool query packs
* shipped inside the VSIX (or, as fallback, in the locally-installed
* `codeql-development-mcp-server` npm package).
*
* The VSIX bundles the qlpack source files (`.ql` + lock files) at
* `<extensionRoot>/server/ql/<lang>/tools/src/`, and the npm install
* mirrors them at `globalStorage/mcp-server/node_modules/.../ql/...`.
* The bundled copy is always preferred so that the packs used by
* `codeql pack install` match the server code running from the VSIX.
*
* CodeQL library dependencies (e.g. `codeql/javascript-all`) must be
* fetched from GHCR via `codeql pack install`. This class automates
* the `codeql-development-mcp-server-setup-packs` step documented in
* the getting-started guide.
*/
export class PackInstaller extends DisposableObject {
static readonly SUPPORTED_LANGUAGES = [
'actions',
'cpp',
'csharp',
'go',
'java',
'javascript',
'python',
'ruby',
'swift',
] as const;
constructor(
private readonly cliResolver: CliResolver,
private readonly serverManager: ServerManager,
private readonly logger: Logger,
) {
super();
}
/**
* Get the root directory for qlpack resolution.
*
* Prefers the bundled `server/` directory inside the VSIX so that the
* packs installed match the server version. Falls back to the
* npm-installed package root in `globalStorage` (for local dev or when
* the VSIX bundle is missing).
*/
private getQlpackRoot(): string {
return this.serverManager.getBundledQlRoot()
?? this.serverManager.getPackageRoot();
}
/**
* Get the qlpack source directories for all languages.
*/
getQlpackPaths(): string[] {
const root = this.getQlpackRoot();
return PackInstaller.SUPPORTED_LANGUAGES.map((lang) =>
join(root, 'ql', lang, 'tools', 'src'),
);
}
/**
* Install CodeQL pack dependencies for all (or specified) languages.
* Requires the npm package to be installed locally first (via ServerManager).
*/
async installAll(options?: PackInstallOptions): Promise<void> {
const codeqlPath = await this.cliResolver.resolve();
if (!codeqlPath) {
this.logger.warn(
'CodeQL CLI not found — skipping pack installation. Install the CLI or set CODEQL_PATH.',
);
return;
}
const qlRoot = this.getQlpackRoot();
const languages =
options?.languages ?? [...PackInstaller.SUPPORTED_LANGUAGES];
for (const lang of languages) {
const packDir = join(qlRoot, 'ql', lang, 'tools', 'src');
// Check if the pack directory exists
try {
await access(packDir, constants.R_OK);
} catch {
this.logger.debug(`Pack directory not found, skipping: ${packDir}`);
continue;
}
this.logger.info(`Installing CodeQL pack dependencies for ${lang}...`);
try {
await this.runCodeqlPackInstall(codeqlPath, packDir);
this.logger.info(`Pack dependencies installed for ${lang}.`);
} catch (err) {
this.logger.error(
`Failed to install pack dependencies for ${lang}: ${err instanceof Error ? err.message : String(err)}`,
);
}
}
}
/** Run `codeql pack install` for a single pack directory. */
private runCodeqlPackInstall(
codeqlPath: string,
packDir: string,
): Promise<void> {
return new Promise((resolve, reject) => {
execFile(
codeqlPath,
['pack', 'install', '--no-strict-mode', packDir],
{ timeout: 300_000 },
(err, _stdout, stderr) => {
if (err) {
reject(
new Error(`codeql pack install failed: ${stderr || err.message}`),
);
return;
}
resolve();
},
);
});
}
}