Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check if remote server components actually needs an update #280

Merged
merged 4 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -1247,4 +1247,4 @@
}
]
}
}
}
87 changes: 67 additions & 20 deletions src/connection/serverComponent.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -33,7 +37,7 @@ export class ServerComponent {
if (show) {
this.outputChannel.show();
}

if (this.outputChannel) {
this.outputChannel.appendLine(jsonString);
}
Expand All @@ -43,15 +47,15 @@ export class ServerComponent {
return this.installed;
}

static getInitCommand(): string|undefined {
static getInitCommand(): string | undefined {
const path = this.getComponentPath();

if (path) {
return `/QOpenSys/QIBM/ProdData/JavaVM/jdk80/64bit/bin/java -Dos400.stdio.convert=N -jar ${path} --single`
}
}

static getComponentPath(): string|undefined {
static getComponentPath(): string | undefined {
if (Config.ready) {
const installedVersion = Config.getServerComponentName();

Expand Down Expand Up @@ -82,6 +86,10 @@ export class ServerComponent {
return this.installed;
}

static reset(){
this.installed = false;
}

static async isAlreadyInstalled() {
const instance = getInstance();
const connection = instance.getConnection();
Expand All @@ -107,13 +115,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();

Expand All @@ -132,26 +140,49 @@ 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;
}
else{
await Config.setServerComponentName(basename);
this.installed = true;
updateResult = UpdateStatus.UP_TO_DATE;
}

window.showInformationMessage(`Db2 for IBM i extension server component has been updated!`);
this.installed = true;
updateResult = UpdateStatus.JUST_UPDATED;

} else {
updateResult = UpdateStatus.FAILED;

Expand Down Expand Up @@ -196,4 +227,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+/)[0];
}
else {
ServerComponent.writeOutput(JSON.stringify(remoteMD5Result));
return false;
}
}
18 changes: 11 additions & 7 deletions src/dsc.ts
Original file line number Diff line number Diff line change
@@ -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() {
Expand All @@ -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.`);
Expand All @@ -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();
30 changes: 16 additions & 14 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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!`);
Expand Down Expand Up @@ -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);
Expand All @@ -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) };
}

Expand Down
Loading