From 22472b52dadccfc60230327edd618965de77b47e Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Mon, 23 Sep 2024 22:29:06 +0200 Subject: [PATCH 1/4] Check if remote server components actually needs an update Signed-off-by: Seb Julliand --- src/connection/serverComponent.ts | 78 +++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/src/connection/serverComponent.ts b/src/connection/serverComponent.ts index f67195cf..e80ca4ee 100644 --- a/src/connection/serverComponent.ts +++ b/src/connection/serverComponent.ts @@ -1,12 +1,16 @@ import { getInstance } from "../base"; -import { Config } from "../config"; import path from "path"; import { OutputChannel, extensions, window } from "vscode"; +import { Config } from "../config"; import { stat } from "fs/promises"; import { SERVER_VERSION_FILE } from "./SCVersion"; +import IBMi from "@halcyontech/vscode-ibmi-types/api/IBMi"; +import Crypto from 'crypto'; +import { readFileSync } from "fs"; + const ExecutablePathDir = `$HOME/.vscode/`; export enum UpdateStatus { @@ -33,7 +37,7 @@ export class ServerComponent { if (show) { this.outputChannel.show(); } - + if (this.outputChannel) { this.outputChannel.appendLine(jsonString); } @@ -43,7 +47,7 @@ export class ServerComponent { return this.installed; } - static getInitCommand(): string|undefined { + static getInitCommand(): string | undefined { const path = this.getComponentPath(); if (path) { @@ -51,7 +55,7 @@ export class ServerComponent { } } - static getComponentPath(): string|undefined { + static getComponentPath(): string | undefined { if (Config.ready) { const installedVersion = Config.getServerComponentName(); @@ -107,13 +111,13 @@ export class ServerComponent { const assetPath = path.join(extensionPath, `dist`, SERVER_VERSION_FILE); const assetExistsLocally = await exists(assetPath); - ServerComponent.writeOutput(JSON.stringify({assetPath, assetExists: assetExistsLocally})); + ServerComponent.writeOutput(JSON.stringify({ assetPath, assetExists: assetExistsLocally })); if (assetExistsLocally) { const basename = SERVER_VERSION_FILE; const lastInstalledName = Config.getServerComponentName(); - ServerComponent.writeOutput(JSON.stringify({basename, lastInstalledName})); + ServerComponent.writeOutput(JSON.stringify({ basename, lastInstalledName })); await this.initialise(); @@ -132,26 +136,44 @@ export class ServerComponent { const stuffInStderr = commandResult.stderr.length > 0; const remotePath = path.posix.join(commandResult.stdout, basename); - ServerComponent.writeOutput(JSON.stringify({remotePath, ExecutablePathDir})); + ServerComponent.writeOutput(JSON.stringify({ remotePath, ExecutablePathDir })); + + const remoteExists = await connection.content.testStreamFile(remotePath, "f"); + const md5IsEqual = remoteExists && await compareMD5Hash(connection, assetPath, remotePath); + if (!remoteExists || !md5IsEqual) { + if (remoteExists) { + const allowWrite = await connection.sendCommand({ + command: `chmod 600 ${remotePath}` + }); + if (allowWrite.code !== 0) { + this.writeOutput(JSON.stringify(allowWrite)); + window.showErrorMessage(`Remote file ${remotePath} cannot be written; try to delete it and reconnect.`, 'Show') + .then(show => { + if (show) { + this.outputChannel.show(); + } + }); + return UpdateStatus.FAILED; + } + } + await connection.uploadFiles([{ local: assetPath, remote: remotePath }]); - await connection.uploadFiles([{local: assetPath, remote: remotePath}]); + const scAuth = await connection.sendCommand({ + command: `chmod 400 ${remotePath}` + }); - const scAuth = await connection.sendCommand({ - command: `chmod 400 ${remotePath}` - }); + this.writeOutput(JSON.stringify(scAuth)); - this.writeOutput(JSON.stringify(scAuth)); + await Config.setServerComponentName(basename); - await Config.setServerComponentName(basename); + if (stuffInStderr) { + ServerComponent.writeOutput(`Server component was uploaded to ${remotePath} but there was something in stderr, which is not right. It might be worth seeing your user profile startup scripts.`); + } - if (stuffInStderr) { - ServerComponent.writeOutput(`Server component was uploaded to ${remotePath} but there was something in stderr, which is not right. It might be worth seeing your user profile startup scripts.`); + window.showInformationMessage(`Db2 for IBM i extension server component has been updated!`); + this.installed = true; + updateResult = UpdateStatus.JUST_UPDATED; } - - window.showInformationMessage(`Db2 for IBM i extension server component has been updated!`); - this.installed = true; - updateResult = UpdateStatus.JUST_UPDATED; - } else { updateResult = UpdateStatus.FAILED; @@ -196,4 +218,20 @@ async function exists(path: string) { } catch (e) { return false; } +} + +async function compareMD5Hash(connection: IBMi, local: string, remote: string) { + const localMD5 = Crypto.createHash("md5") + .update(readFileSync(local)) + .digest("hex") + .toLowerCase(); + + const remoteMD5Result = (await connection.sendCommand({ command: `${connection.remoteFeatures.md5sum} ${remote}` })); + if (remoteMD5Result.code === 0) { + return localMD5 === remoteMD5Result.stdout.split(/\s+/).at(0); + } + else { + ServerComponent.writeOutput(JSON.stringify(remoteMD5Result)); + return false; + } } \ No newline at end of file From 700708bb675ed56c11a9c2d981f8bdc9014b9725 Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Mon, 23 Sep 2024 22:42:25 +0200 Subject: [PATCH 2/4] Update local configuration Signed-off-by: Seb Julliand --- src/connection/serverComponent.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/connection/serverComponent.ts b/src/connection/serverComponent.ts index e80ca4ee..2f3b110a 100644 --- a/src/connection/serverComponent.ts +++ b/src/connection/serverComponent.ts @@ -174,6 +174,11 @@ export class ServerComponent { this.installed = true; updateResult = UpdateStatus.JUST_UPDATED; } + else{ + await Config.setServerComponentName(basename); + this.installed = true; + updateResult = UpdateStatus.UP_TO_DATE; + } } else { updateResult = UpdateStatus.FAILED; From a188a4ad54520544697ca1c507736b29d633a41f Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Tue, 8 Oct 2024 22:12:34 +0200 Subject: [PATCH 3/4] Made dsc command OS agnostic Signed-off-by: Seb Julliand --- package.json | 4 ++-- src/dsc.ts | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 4aaff651..2bdb712c 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "lint": "eslint .", "pretest": "npm run lint", "language:test": "vitest", - "dsc": "mkdir -p dist && npx tsx src/dsc", + "dsc": "npx tsx src/dsc", "package": "vsce package", "vscode:prepublish": "rm -rf dist && npm run webpack && npm run dsc", "webpack": "vscd --clean && webpack --mode development", @@ -1247,4 +1247,4 @@ } ] } -} +} \ No newline at end of file diff --git a/src/dsc.ts b/src/dsc.ts index d69b90ab..197df242 100644 --- a/src/dsc.ts +++ b/src/dsc.ts @@ -1,8 +1,9 @@ import path from "path"; +import { existsSync, mkdirSync } from "fs"; +import { writeFile } from "fs/promises"; import fetch from "node-fetch"; import { Octokit } from "octokit"; -import { writeFile } from "fs/promises"; import { SERVER_VERSION_FILE, SERVER_VERSION_TAG } from "./connection/SCVersion"; async function work() { @@ -26,12 +27,15 @@ async function work() { console.log(`Asset found: ${newAsset.name}`); const url = newAsset.browser_download_url; + const distDirectory = path.join(`.`, `dist`); + if (!existsSync(distDirectory)) { + mkdirSync(distDirectory); + } - const dist = path.join(`.`, `dist`, SERVER_VERSION_FILE); - - await downloadFile(url, dist); + const serverFile = path.join(distDirectory, SERVER_VERSION_FILE); + await downloadFile(url, serverFile); - console.log(`Asset downloaded.`); + console.log(`Asset downloaded: ${serverFile}`); } else { console.log(`Release found but no asset found.`); @@ -45,8 +49,8 @@ async function work() { function downloadFile(url, outputPath) { return fetch(url) - .then(x => x.arrayBuffer()) - .then(x => writeFile(outputPath, Buffer.from(x))); + .then(x => x.arrayBuffer()) + .then(x => writeFile(outputPath, Buffer.from(x))); } work(); \ No newline at end of file From d7e9c188dff7b77c5d583cc3683a061ec5354617 Mon Sep 17 00:00:00 2001 From: Seb Julliand Date: Tue, 8 Oct 2024 23:08:16 +0200 Subject: [PATCH 4/4] Reset ServerComponent when disconnecting Signed-off-by: Seb Julliand --- src/connection/serverComponent.ts | 6 +++++- src/extension.ts | 30 ++++++++++++++++-------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/connection/serverComponent.ts b/src/connection/serverComponent.ts index 2f3b110a..f9cc2c9b 100644 --- a/src/connection/serverComponent.ts +++ b/src/connection/serverComponent.ts @@ -86,6 +86,10 @@ export class ServerComponent { return this.installed; } + static reset(){ + this.installed = false; + } + static async isAlreadyInstalled() { const instance = getInstance(); const connection = instance.getConnection(); @@ -233,7 +237,7 @@ async function compareMD5Hash(connection: IBMi, local: string, remote: string) { const remoteMD5Result = (await connection.sendCommand({ command: `${connection.remoteFeatures.md5sum} ${remote}` })); if (remoteMD5Result.code === 0) { - return localMD5 === remoteMD5Result.stdout.split(/\s+/).at(0); + return localMD5 === remoteMD5Result.stdout.split(/\s+/)[0]; } else { ServerComponent.writeOutput(JSON.stringify(remoteMD5Result)); diff --git a/src/extension.ts b/src/extension.ts index 1830ccda..cc626839 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,27 +1,27 @@ // The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below -import vscode from "vscode" +import vscode from "vscode"; import schemaBrowser from "./views/schemaBrowser"; import * as JSONServices from "./language/json"; import * as resultsProvider from "./views/results"; +import { JDBCOptions } from "@ibm/mapepire-js/dist/src/types"; import { getInstance, loadBase } from "./base"; -import { JobManager, onConnectOrServerInstall, initConfig } from "./config"; -import { queryHistory } from "./views/queryHistoryView"; -import { ExampleBrowser } from "./views/examples/exampleBrowser"; -import { languageInit } from "./language/providers"; -import { initialiseTestSuite } from "./testing"; -import { JobManagerView } from "./views/jobManager/jobManagerView"; -import { ServerComponent } from "./connection/serverComponent"; +import { JobManager, initConfig, onConnectOrServerInstall } from "./config"; +import Configuration from "./configuration"; import { SQLJobManager } from "./connection/manager"; +import { ServerComponent } from "./connection/serverComponent"; import { OldSQLJob } from "./connection/sqlJob"; +import { languageInit } from "./language/providers"; +import { DbCache } from "./language/providers/logic/cache"; import { notebookInit } from "./notebooks/IBMiSerializer"; -import { SelfTreeDecorationProvider, selfCodesResultsView } from "./views/jobManager/selfCodes/selfCodesResultsView"; -import Configuration from "./configuration"; -import { JDBCOptions } from "@ibm/mapepire-js/dist/src/types"; +import { initialiseTestSuite } from "./testing"; import { Db2iUriHandler, getStatementUri } from "./uriHandler"; -import { DbCache } from "./language/providers/logic/cache"; +import { ExampleBrowser } from "./views/examples/exampleBrowser"; +import { JobManagerView } from "./views/jobManager/jobManagerView"; +import { SelfTreeDecorationProvider, selfCodesResultsView } from "./views/jobManager/selfCodes/selfCodesResultsView"; +import { queryHistory } from "./views/queryHistoryView"; export interface Db2i { sqlJobManager: SQLJobManager, @@ -32,7 +32,7 @@ export interface Db2i { // your extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext): Db2i { - + // Use the console to output diagnostic information (console.log) and errors (console.error) // This line of code will only be executed once when your extension is activated console.log(`Congratulations, your extension "vscode-db2i" is now active!`); @@ -80,7 +80,7 @@ export function activate(context: vscode.ExtensionContext): Db2i { console.log(`Developer environment: ${process.env.DEV}`); const devMode = process.env.DEV !== undefined; - let runTests: Function|undefined; + let runTests: Function | undefined; if (devMode) { // Run tests if not in production build runTests = initialiseTestSuite(context); @@ -103,6 +103,8 @@ export function activate(context: vscode.ExtensionContext): Db2i { }); }); + instance.subscribe(context, `disconnected`, `db2i-disconnected`, () => ServerComponent.reset()); + return { sqlJobManager: JobManager, sqlJob: (options?: JDBCOptions) => new OldSQLJob(options) }; }