feat: Compute the kubectl download version map at build time

- Allows for bundled kubectl config to be changed without code changes

- Introduce @k8slens/kubectl-versions

  - Compile time fetching of versions

- Update @swc/* packages

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton committed 2023-04-14 14:42:00 -04:00
1 parent 2debe61359
commit 0bd7b1fe92
26 files changed
+348 -70

No files matched your search

+1 -1
View File
@@ -25,7 +25,7 @@
"semver": "^7.3.8"
},
"devDependencies": {
"@swc/cli": "^0.1.61",
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.44",
"@types/node": "^16.18.11",
"@types/semver": "^7.3.13",
+1 -8
View File
@@ -17,16 +17,9 @@
"scripts": {
"clean": "rimraf dist/",
"generate-types": "tsc --d --declarationDir ./dist --declarationMap --emitDeclarationOnly",
"build": "npm run generate-types && swc ./src/index.ts -d ./dist",
"prepare": "npm run build",
"prepare:dev": "npm run build",
"prepare:test": "npm run build"
"build": "npm run generate-types && swc ./src/index.ts -d ./dist"
},
"devDependencies": {
"@swc/cli": "^0.1.61",
"@swc/core": "^1.3.44",
"@types/node": "^16.18.11",
"@types/semver": "^7.3.13",
"rimraf": "^4.4.1"
},
"peerDependencies": {
+2 -1
View File
@@ -184,7 +184,7 @@
"@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60",
"@sentry/types": "^6.19.7",
"@swc/cli": "^0.1.61",
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.44",
"@swc/jest": "^0.2.24",
"@testing-library/dom": "^8.19.0",
@@ -313,6 +313,7 @@
"@k8slens/application": "^6.5.0-alpha.0",
"@k8slens/application-for-electron-main": "^6.5.0-alpha.0",
"@k8slens/cluster-settings": "^6.5.0-alpha.1",
"@k8slens/kubectl-versions": "^6.5.0-alpha.1",
"@k8slens/legacy-extensions": "^1.0.0-alpha.0",
"@k8slens/messaging": "^1.0.0-alpha.1",
"@k8slens/messaging-for-main": "^1.0.0-alpha.1",
@@ -34,7 +34,7 @@ const createKubectlInjectable = getInjectable({
normalizedDownloadPlatform: di.inject(normalizedPlatformInjectable),
kubectlBinaryName: di.inject(kubectlBinaryNameInjectable),
bundledKubectlBinaryPath: di.inject(bundledKubectlBinaryPathInjectable),
baseBundeledBinariesDirectory: di.inject(baseBundledBinariesDirectoryInjectable),
baseBundledBinariesDirectory: di.inject(baseBundledBinariesDirectoryInjectable),
bundledKubectlVersion: di.inject(bundledKubectlVersionInjectable),
kubectlVersionMap: di.inject(kubectlVersionMapInjectable),
logger: di.inject(loggerInjectable),
+2 -2
View File
@@ -29,7 +29,7 @@ export interface KubectlDependencies {
readonly normalizedDownloadArch: "amd64" | "arm64" | "386";
readonly kubectlBinaryName: string;
readonly bundledKubectlBinaryPath: string;
readonly baseBundeledBinariesDirectory: string;
readonly baseBundledBinariesDirectory: string;
readonly state: {
readonly kubectlBinariesPath?: string;
readonly downloadBinariesPath?: string;
@@ -302,7 +302,7 @@ export class Kubectl {
}
protected async writeInitScripts() {
const binariesDir = this.dependencies.baseBundeledBinariesDirectory;
const binariesDir = this.dependencies.baseBundledBinariesDirectory;
const kubectlPath = this.dependencies.state.downloadKubectlBinaries
? this.dirname
: this.dependencies.getDirnameOfPath(this.getPathFromPreferences());
@@ -3,31 +3,19 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { SemVer } from "semver";
import bundledKubectlVersionInjectable from "../../common/vars/bundled-kubectl-version.injectable";
import { kubectlVersions } from "@k8slens/kubectl-versions";
const kubectlVersionMapInjectable = getInjectable({
id: "kubectl-version-map",
instantiate: (di) => {
const bundledKubectlVersion = di.inject(bundledKubectlVersionInjectable);
const bundledKubectlSemVer = new SemVer(bundledKubectlVersion);
return new Map([
["1.7", "1.8.15"],
["1.8", "1.9.10"],
["1.9", "1.10.13"],
["1.10", "1.11.10"],
["1.11", "1.12.10"],
["1.12", "1.13.12"],
["1.13", "1.13.12"],
["1.14", "1.14.10"],
["1.15", "1.15.11"],
["1.16", "1.16.15"],
["1.17", "1.17.17"],
["1.18", "1.18.20"],
["1.19", "1.19.12"],
["1.20", "1.20.8"],
["1.21", "1.21.9"],
["1.22", "1.22.6"],
["1.23", bundledKubectlVersion],
...kubectlVersions,
[`${bundledKubectlSemVer.major}.${bundledKubectlSemVer.minor}`, bundledKubectlVersion],
]);
},
});
+2 -4
View File
@@ -9,9 +9,7 @@
"build": "swc ./src/index.ts -d ./dist/",
"postbuild": "run-script-os",
"postbuild:windows": "",
"postbuild:nix": "chmod u+x ./dist/index.js",
"prepare:dev": "npm run build",
"prepare": "npm run build"
"postbuild:nix": "chmod u+x ./dist/index.js"
},
"bin": {
"ensure-binaries": "./dist/index.js"
@@ -34,7 +32,7 @@
"zod": "^3.20.6"
},
"devDependencies": {
"@swc/cli": "^0.1.61",
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.44",
"@types/cli-progress": "^3.11.0",
"@types/gunzip-maybe": "^1.4.0",
+1 -2
View File
@@ -22,8 +22,7 @@
"scripts": {
"build": "webpack --config webpack/extensions.ts",
"build:docs": "typedoc",
"clean": "rimraf dist/",
"prepare:dev": "npm run build"
"clean": "rimraf dist/"
},
"dependencies": {
"@k8slens/core": "^6.5.0-alpha.6"
+2 -4
View File
@@ -8,9 +8,7 @@
"build": "swc ./src/index.ts -d ./dist/",
"postbuild": "run-script-os",
"postbuild:windows": "",
"postbuild:nix": "chmod u+x ./dist/index.js",
"prepare:dev": "npm run build",
"prepare": "npm run build"
"postbuild:nix": "chmod u+x ./dist/index.js"
},
"bin": {
"generate-tray-icons": "./dist/index.js"
@@ -25,7 +23,7 @@
"registry": "https://registry.npmjs.org/"
},
"devDependencies": {
"@swc/cli": "^0.1.61",
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.44",
"@types/jsdom": "^20.0.1",
"@types/node": "^18.11.18",
+5 -2
View File
@@ -20,8 +20,6 @@
"license": "MIT",
"homepage": "https://github.com/lensapp/lens",
"dependencies": {
"@swc/core": "^1.3.44",
"@swc/jest": "^0.2.23",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.5",
"@types/jest": "^29.2.2",
@@ -32,5 +30,10 @@
"jest-watch-typeahead": "^2.2.1",
"lodash": "^4.17.21",
"ts-jest": "^29.0.3"
},
"devDependencies": {
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.44",
"@swc/jest": "^0.2.24"
}
}
+1
View File
@@ -0,0 +1 @@
build/*.mjs
+9
View File
@@ -0,0 +1,9 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2022"
}
}
+8
View File
@@ -0,0 +1,8 @@
# @k8slens/kubectl-versions
This package contains a JSON array of entries of the format ["MAJOR.MINOR", "MAJOR.MINOR.PATCH"].
This object represents the current (at time of building each release of this package)
greatest PATCH version for each "MAJOR.MINOR" version of `kubectl`.
This is done at compile time so that we don't have to worry about being able to access this data on user machines.
Furthermore, this is dynamic in that the previous method was to update the table by hand.
@@ -0,0 +1,63 @@
import fetch from "node-fetch";
import { TypedRegEx } from "typed-regex";
import { XMLParser } from "fast-xml-parser";
import semver from "semver";
import { writeFile } from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";
const { SemVer } = semver;
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const expectedResponseForm = TypedRegEx("v(?<version>\\d+\\.\\d+\\.\\d+)");
async function requestGreatestKubectlPatchVersion(majorMinor: string): Promise<string | undefined> {
const response = await fetch(`https://dl.k8s.io/release/stable-${majorMinor}.txt`);
if (response.status !== 200) {
try {
const parser = new XMLParser();
const errorBody = parser.parse(await response.text());
throw new Error(`failed to get stable version for ${majorMinor}: ${errorBody?.Error?.Message ?? response.statusText}`);
} catch {
throw new Error(`failed to get stable version for ${majorMinor}: ${response.statusText}`);
}
}
const body = await response.text();
const match = expectedResponseForm.captures(body);
if (!match) {
throw new Error(`failed to get stable version for ${majorMinor}: unexpected response shape. body="${body}"`);
}
return match.version;
}
async function requestAllVersions(): Promise<[string, string][]> {
const greatestVersion = await requestGreatestKubectlPatchVersion("1");
if (!greatestVersion) {
return [];
}
const greatestSemVer = new SemVer(greatestVersion);
const majorMinorRequests = new Array<string>(greatestSemVer.minor + 1)
.fill("")
.map((value, index) => `1.${index}`)
.map(async (majorMinor) => [majorMinor, await requestGreatestKubectlPatchVersion(majorMinor)] as const);
return (await Promise.all(majorMinorRequests))
.filter((entry): entry is [string, string] => !!entry[1]);
}
async function main() {
const versions = await requestAllVersions();
await writeFile(path.join(__dirname, "versions.json"), JSON.stringify(versions, null, 4));
}
await main();
@@ -0,0 +1,110 @@
[
[
"1.0",
"1.0.7"
],
[
"1.1",
"1.1.8"
],
[
"1.2",
"1.2.7"
],
[
"1.3",
"1.3.10"
],
[
"1.4",
"1.4.12"
],
[
"1.5",
"1.5.8"
],
[
"1.6",
"1.6.13"
],
[
"1.7",
"1.7.16"
],
[
"1.8",
"1.8.15"
],
[
"1.9",
"1.9.11"
],
[
"1.10",
"1.10.13"
],
[
"1.11",
"1.11.10"
],
[
"1.12",
"1.12.10"
],
[
"1.13",
"1.13.12"
],
[
"1.14",
"1.14.10"
],
[
"1.15",
"1.15.12"
],
[
"1.16",
"1.16.15"
],
[
"1.17",
"1.17.17"
],
[
"1.18",
"1.18.20"
],
[
"1.19",
"1.19.16"
],
[
"1.20",
"1.20.15"
],
[
"1.21",
"1.21.14"
],
[
"1.22",
"1.22.17"
],
[
"1.23",
"1.23.16"
],
[
"1.24",
"1.24.10"
],
[
"1.25",
"1.25.6"
],
[
"1.26",
"1.26.1"
]
]
+3
View File
@@ -0,0 +1,3 @@
import raw from "./build/versions.json";
export const kubectlVersions = raw as [string, string][];
+1
View File
@@ -0,0 +1 @@
module.exports = require("@k8slens/jest").monorepoPackageConfig(__dirname).configForReact;
+40
View File
@@ -0,0 +1,40 @@
{
"name": "@k8slens/kubectl-versions",
"private": false,
"version": "6.5.0-alpha.1",
"description": "Package of kubectl versions at build time",
"files": [
"dist"
],
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"author": {
"name": "OpenLens Authors",
"email": "info@k8slens.dev"
},
"license": "MIT",
"homepage": "https://github.com/lensapp/lens",
"scripts": {
"precompute-versions": "swc ./build/compute-versions.ts -o ./build/compute-versions.mjs",
"compute-versions": "node ./build/compute-versions.mjs",
"prebuild": "npm run compute-versions",
"build": "webpack",
"dev": "webpack --mode=development --watch",
"test": "jest --coverage --runInBand"
},
"peerDependencies": {
"@ogre-tools/injectable": "^15.1.1"
},
"devDependencies": {
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.44",
"fast-xml-parser": "^4.1.2",
"semver": "^7.3.8",
"ts-node": "^10.9.1",
"typed-regex": "^0.0.8"
}
}
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "@k8slens/typescript/config/base.json"
}
@@ -0,0 +1 @@
module.exports = require("@k8slens/webpack").configForNode;
+1 -5
View File
@@ -15,11 +15,7 @@
],
"scripts": {
"clean": "rimraf dist/",
"build": "webpack --config webpack.ts",
"lint": "exit 0",
"prepare": "npm run build",
"prepare:dev": "npm run build",
"prepare:test": "npm run build"
"build": "webpack --config webpack.ts"
},
"dependencies": {
"node-fetch": "^3.3.0",
+1 -1
View File
@@ -214,7 +214,7 @@
"@k8slens/generate-tray-icons": "^6.5.0-alpha.2",
"@k8slens/test-utils": "^1.0.0-alpha.2",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@swc/cli": "^0.1.61",
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.44",
"@swc/jest": "^0.2.24",
"@types/byline": "^4.2.33",
+1 -1
View File
@@ -17,7 +17,7 @@
},
"type": "module",
"devDependencies": {
"@swc/cli": "^0.1.61",
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.44",
"@types/inquirer": "^9.0.3",
"@types/node": "^16.18.11",
+1 -1
View File
@@ -14,7 +14,7 @@
"semver": "^7.3.8"
},
"devDependencies": {
"@swc/cli": "^0.1.61",
"@swc/cli": "^0.1.62",
"@swc/core": "^1.3.44",
"@types/command-line-args": "^5.2.0",
"@types/node": "^16.18.11",
@@ -1,3 +1,4 @@
/* eslint-disable prettier/prettier */
import { getInjectable, getInjectionToken } from "@ogre-tools/injectable";
import { runManyFor } from "@k8slens/run-many";
import * as timeSlots from "./time-slots";