-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.mts
More file actions
161 lines (129 loc) · 5.99 KB
/
script.mts
File metadata and controls
161 lines (129 loc) · 5.99 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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import { chmod, cp, rm } from 'node:fs/promises';
import { basename, join } from 'node:path';
type Repo = (typeof REPOS)[number];
const NO_UPDATES_EXIT_CODE = 2;
const REPOS = ['microsoft/vscode', 'microsoft/vscode-eslint'] as const;
const FORCE = process.argv.includes('--force');
async function writeExecutable(path: string, text: string): Promise<void> {
const withShebang = text.startsWith('#!') ? text : `#!/usr/bin/env node\n${text}`;
await Bun.write(path, withShebang);
await chmod(path, 0o755);
}
async function getLatestReleaseVersion(repo: string): Promise<string> {
const res = await fetch(`https://api.github.com/repos/${repo}/releases/latest`, {
headers: {
Accept: 'application/vnd.github+json',
'X-GitHub-Api-Version': '2026-03-10',
...(Bun.env.GITHUB_TOKEN
? {
Authorization: `Bearer ${Bun.env.GITHUB_TOKEN}`,
}
: {}),
},
});
if (!res.ok) throw new Error(`Failed to fetch the latest release for ${repo}: ${res.status} ${res.statusText}`);
const data = (await res.json()) as { tag_name?: string };
const version = data.tag_name?.match(/\d+\.\d+\.\d+/)?.[0];
if (!version) throw new Error(`Failed to parse a version number from the latest release tag for ${repo}`);
return version;
}
async function getVscodeExtensionsDependencies(version: string): Promise<Record<string, string>> {
const res = await fetch(`https://raw.githubusercontent.com/microsoft/vscode/${version}/extensions/package.json`);
if (!res.ok) {
throw new Error(`Failed to fetch VS Code extension dependencies for ${version}: ${res.status} ${res.statusText}`);
}
const data = (await res.json()) as { dependencies?: Record<string, string> };
return data.dependencies ?? {};
}
function getVsixUrl(publisher: string, extension: string, version: string): string {
return `https://${publisher}.gallery.vsassets.io/_apis/public/gallery/publisher/${publisher}/extension/${extension}/${version}/assetbyname/Microsoft.VisualStudio.Services.VSIXPackage`;
}
async function extractVscode(version: string): Promise<void> {
console.log(`Downloading microsoft/vscode v${version}...`);
// https://code.visualstudio.com/Docs/supporting/FAQ
const res = await fetch(`https://update.code.visualstudio.com/${version}/linux-x64/stable`);
if (!res.ok) throw new Error(`Failed to download microsoft/vscode: ${res.status} ${res.statusText}`);
const archive = new Bun.Archive(await res.blob());
for (const language of ['css', 'html', 'json']) {
const files = await archive.files(`**/${language}-language-features/server/dist/node/*`);
const entrypoint = `${language}ServerMain.js`;
let fileCount = 0;
let entrypointFound = false;
for (const [, file] of files) {
fileCount += 1;
const name = basename(file.name);
const text = await file.text();
if (name === entrypoint) {
entrypointFound = true;
await writeExecutable(join(`./dist/${language}`, name), text);
} else {
await Bun.write(join(`./dist/${language}`, name), text);
}
}
if (fileCount === 0) {
throw new Error(`No files were found for the ${language} language server in the VS Code archive`);
}
if (!entrypointFound) {
throw new Error(`Could not find ${entrypoint} in the VS Code archive`);
}
}
}
async function extractEslint(version: string): Promise<void> {
console.log(`Downloading microsoft/vscode-eslint v${version}...`);
const res = await fetch(getVsixUrl('dbaeumer', 'vscode-eslint', version));
if (!res.ok) throw new Error(`Failed to download vscode-eslint: ${res.status} ${res.statusText}`);
await Bun.write('./tmp/vscode-eslint.vsix', await res.blob());
await rm('./tmp/vscode-eslint', { recursive: true, force: true });
await Bun.$`unzip -oq ./tmp/vscode-eslint.vsix -d ./tmp/vscode-eslint`;
await cp('./tmp/vscode-eslint/extension/server/out', './dist/eslint', { recursive: true });
const serverFile = Bun.file('./dist/eslint/eslintServer.js');
await writeExecutable('./dist/eslint/eslintServer.js', await serverFile.text());
}
async function main(): Promise<void> {
const packageJson = (await Bun.file('./package.json').json()) as {
dependencies: Record<string, string>;
metadata: { versions: Record<Repo, string> };
};
console.log('Checking for updates...');
const latestVersions = Object.fromEntries(
await Promise.all(REPOS.map(async (repo) => [repo, await getLatestReleaseVersion(repo)] as const)),
) as Record<Repo, string>;
const updates = REPOS.filter((repo) => {
const isUpdate = packageJson.metadata.versions[repo] !== latestVersions[repo];
console.log(
isUpdate
? `Update available for ${repo}: ${packageJson.metadata.versions[repo]} -> ${latestVersions[repo]}`
: `${repo} is already at latest version (${latestVersions[repo]})`,
);
return isUpdate;
});
if (updates.length === 0) {
console.log('---');
if (!FORCE) {
console.log('All packages are already up to date. Nothing to do.');
process.exit(NO_UPDATES_EXIT_CODE);
}
console.log('All packages are already up to date. Downloading and extracting anyway because --force was passed.');
}
console.log('---');
console.log('Downloading all packages...');
await rm('./dist', { recursive: true, force: true });
await extractVscode(latestVersions['microsoft/vscode']);
await extractEslint(latestVersions['microsoft/vscode-eslint']);
packageJson.dependencies = await getVscodeExtensionsDependencies(latestVersions['microsoft/vscode']);
Object.assign(packageJson.metadata.versions, latestVersions);
await Bun.write('./package.json', `${JSON.stringify(packageJson, null, 2)}\n`);
console.log('---');
if (updates.length === 0) {
console.log('Rebuilt packages without upstream version changes');
return;
}
console.log(`Updated ${updates.length} package(s)`);
for (const update of updates) {
console.log(update);
}
}
main().catch((error: unknown) => {
console.error(error instanceof Error ? error.message : String(error));
process.exit(1);
});