Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/ide/gha-update-image/lib/jb-stable-version.ts
2499 views
1
// Copyright (c) 2024 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
import axios from "axios";
6
import { $ } from "bun";
7
import { z } from "zod";
8
import semver from "semver";
9
import { MultipleBuildVersionsError, MultipleMajorVersionsError } from "./errors";
10
import { pathToWorkspaceYaml, pathToBackendPluginGradleStable, readWorkspaceYaml } from "./common";
11
12
$.nothrow(); // git likes to respond with non-zero codes, but it is alright for us
13
14
const JB_PRODUCTS_DATA_URL = "https://data.services.jetbrains.com/products";
15
16
const jbReleaseResponse = z.array(
17
z.object({
18
name: z.string(),
19
link: z.string(),
20
releases: z.array(
21
z.object({
22
majorVersion: z.string(),
23
build: z.string(),
24
downloads: z.object({
25
linux: z
26
.object({
27
link: z.string(),
28
})
29
.optional(),
30
}),
31
}),
32
),
33
}),
34
);
35
36
export interface JetBrainsIDE {
37
productName: string;
38
productId: string;
39
productCode: string;
40
productType: string;
41
exampleRepo: string;
42
}
43
44
export const ides: JetBrainsIDE[] = [
45
{
46
productName: "IntelliJ IDEA Ultimate",
47
productId: "intellij",
48
productCode: "IIU",
49
productType: "release",
50
exampleRepo: "https://github.com/gitpod-samples/spring-petclinic",
51
},
52
{
53
productName: "GoLand",
54
productId: "goland",
55
productCode: "GO",
56
productType: "release",
57
exampleRepo: "https://github.com/gitpod-samples/template-golang-cli",
58
},
59
{
60
productName: "PyCharm Professional Edition",
61
productId: "pycharm",
62
productCode: "PCP",
63
productType: "release",
64
exampleRepo: "https://github.com/gitpod-samples/template-python-django",
65
},
66
{
67
productName: "PhpStorm",
68
productId: "phpstorm",
69
productCode: "PS",
70
productType: "release",
71
exampleRepo: "https://github.com/gitpod-samples/template-php-laravel-mysql",
72
},
73
{
74
productName: "RubyMine",
75
productId: "rubymine",
76
productCode: "RM",
77
productType: "release",
78
exampleRepo: "https://github.com/gitpod-samples/template-ruby-on-rails-postgres",
79
},
80
{
81
productName: "WebStorm",
82
productId: "webstorm",
83
productCode: "WS",
84
productType: "release",
85
exampleRepo: "https://github.com/gitpod-samples/template-typescript-react",
86
},
87
// TODO(hw): ENT-777 We don't upgrade Rider until we can build backend-plugin for it
88
// {
89
// productName: "Rider",
90
// productId: "rider",
91
// productCode: "RD",
92
// productType: "release",
93
// exampleRepo: "https://github.com/gitpod-samples/template-dotnet-core-cli-csharp",
94
// },
95
{
96
productName: "CLion",
97
productId: "clion",
98
productCode: "CL",
99
productType: "release",
100
exampleRepo: "https://github.com/gitpod-samples/template-cpp",
101
},
102
{
103
productName: "RustRover",
104
productId: "rustrover",
105
productCode: "RR",
106
productType: "release",
107
exampleRepo: "https://github.com/gitpod-samples/template-rust-cli",
108
},
109
] as const;
110
111
const workspaceYamlInfo = await readWorkspaceYaml();
112
let rawWorkspace = workspaceYamlInfo.rawText;
113
const workspace = workspaceYamlInfo.parsedObj;
114
115
export const getStableVersionsInfo = async (ides: JetBrainsIDE[]) => {
116
let buildVersion: semver.SemVer | undefined;
117
118
const uniqueMajorVersions = new Set<string>();
119
const uniqueMajorBuildVersions = new Set<string>();
120
const updatedIDEs: string[] = [];
121
122
await Promise.all(
123
ides.map(async (ide) => {
124
const params = new URLSearchParams({
125
code: ide.productCode,
126
"release.type": ide.productType,
127
fields: ["distributions", "link", "name", "releases"].join(","),
128
_: Date.now().toString(),
129
});
130
131
const url = new URL(JB_PRODUCTS_DATA_URL);
132
url.search = params.toString();
133
console.debug(`Fetching data for ${ide.productId.padStart(8, " ")}: ${url.toString()}`);
134
const resp = await axios(url.toString());
135
136
const parsedResponse = jbReleaseResponse.parse(resp.data);
137
const lastRelease = parsedResponse[0].releases[0];
138
console.log(
139
`Latest ${ide.productId.padEnd(8, " ")} majorVersion: ${lastRelease.majorVersion}, buildVersion: ${
140
lastRelease.build
141
}`,
142
);
143
144
uniqueMajorVersions.add(lastRelease.majorVersion);
145
146
const ideKey = `${ide.productId}DownloadUrl` as const;
147
const oldDownloadUrl = workspace.defaultArgs[ideKey];
148
149
const downloadLink = lastRelease.downloads?.linux?.link;
150
if (!downloadLink) {
151
throw new Error("No download link found for the latest release");
152
}
153
rawWorkspace = rawWorkspace.replace(oldDownloadUrl, downloadLink);
154
if (oldDownloadUrl !== downloadLink) {
155
updatedIDEs.push(ide.productId);
156
}
157
158
const currentBuildVersion = semver.parse(lastRelease.build);
159
if (!currentBuildVersion) {
160
throw new Error("Failed to parse the build version: " + lastRelease.build);
161
}
162
uniqueMajorBuildVersions.add(currentBuildVersion.major.toString());
163
// Use minimal common build version, within the same major version there should have no breaking changes
164
if (!buildVersion || semver.lt(currentBuildVersion, buildVersion)) {
165
buildVersion = currentBuildVersion;
166
}
167
}),
168
);
169
170
const majorVersions = [...uniqueMajorVersions];
171
const majorBuildVersions = [...uniqueMajorBuildVersions];
172
console.log({ majorVersions, majorBuildVersions, buildVersion });
173
174
if (!buildVersion) {
175
throw new Error("build version is unresolved");
176
}
177
if (majorBuildVersions.length !== 1) {
178
throw new MultipleBuildVersionsError(majorBuildVersions);
179
}
180
181
if (majorVersions.length !== 1) {
182
throw new MultipleMajorVersionsError(majorVersions);
183
}
184
185
const majorVersion = majorVersions[0];
186
console.log(`All IDEs are in the same major version: ${majorVersion}`);
187
188
return { buildVersion, majorVersion, updatedIDEs };
189
};
190
191
// TODO: remove me and use jb-gradle-task-config.ts
192
export const upgradeStableVersionsInWorkspaceaAndGradle = async () => {
193
try {
194
const { buildVersion, majorVersion, updatedIDEs } = await getStableVersionsInfo(ides);
195
await Bun.write(pathToWorkspaceYaml, rawWorkspace);
196
197
await Bun.write(
198
pathToBackendPluginGradleStable,
199
`# this file is auto generated by components/ide/gha-update-image/index-jb.ts
200
# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
201
# for insight into build numbers and IntelliJ Platform versions.
202
# revert pluginSinceBuild if it's unnecessary
203
pluginSinceBuild=${buildVersion.major}.${buildVersion.minor}
204
pluginUntilBuild=${buildVersion.major}.*
205
# Plugin Verifier integration -> https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl
206
# See https://jb.gg/intellij-platform-builds-list for available build versions.
207
pluginVerifierIdeVersions=${majorVersion}
208
# Version from "com.jetbrains.intellij.idea" which can be found at https://www.jetbrains.com/intellij-repository/snapshots
209
platformVersion=${buildVersion.major}.${buildVersion.minor}-EAP-CANDIDATE-SNAPSHOT
210
`,
211
);
212
213
console.log(`File updated: ${pathToWorkspaceYaml}`);
214
console.log(`File updated: ${pathToBackendPluginGradleStable}`);
215
return updatedIDEs;
216
} catch (e) {
217
if (e instanceof MultipleMajorVersionsError || e instanceof MultipleBuildVersionsError) {
218
console.error(e.message);
219
return;
220
}
221
throw e;
222
}
223
};
224
225