From 239c80e3001f9dee81beeeda1a9a064f95be800a Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 28 Nov 2018 12:08:24 -0800 Subject: [PATCH 1/2] Fix debug adapter and ensure it gets bundled separately --- .travis.yml | 5 ++ build/webpack/webpack.debugadapter.config.js | 75 ++++++++++++++++++ build/webpack/webpack.debugadapter.config.ts | 79 +++++++++++++++++++ build/webpack/webpack.extension.config.js | 3 +- build/webpack/webpack.extension.config.ts | 3 +- gulpfile.js | 1 + src/client/common/variables/environment.ts | 9 +-- src/client/common/variables/types.ts | 2 +- ...ce.test.ts => envVarsService.unit.test.ts} | 7 ++ 9 files changed, 174 insertions(+), 10 deletions(-) create mode 100644 build/webpack/webpack.debugadapter.config.js create mode 100644 build/webpack/webpack.debugadapter.config.ts rename src/test/common/variables/{envVarsService.test.ts => envVarsService.unit.test.ts} (97%) diff --git a/.travis.yml b/.travis.yml index 03beedf62ec3..0eb897e48180 100644 --- a/.travis.yml +++ b/.travis.yml @@ -108,5 +108,10 @@ script: - if [[ $AZURE_STORAGE_ACCOUNT && "$TRAVIS_BRANCH" == release* && "$TRAVIS_PULL_REQUEST" == "false" ]]; then npm run clean; vsce package; + azure storage blob upload python*.vsix $AZURE_STORAGE_CONTAINER ms-python-$TRAVIS_BRANCH-insiders.vsix --account-name $AZURE_STORAGE_ACCOUNT --account-key $AZURE_STORAGE_ACCESS_KEY --quiet; + npm run clean; + npm run package; + npx gulp clean:cleanExceptTests; + npm run testSmoke; azure storage blob upload python*.vsix $AZURE_STORAGE_CONTAINER ms-python-$TRAVIS_BRANCH.vsix --account-name $AZURE_STORAGE_ACCOUNT --account-key $AZURE_STORAGE_ACCESS_KEY --quiet; fi diff --git a/build/webpack/webpack.debugadapter.config.js b/build/webpack/webpack.debugadapter.config.js new file mode 100644 index 000000000000..c9fbb9065c2c --- /dev/null +++ b/build/webpack/webpack.debugadapter.config.js @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; +Object.defineProperty(exports, "__esModule", { value: true }); +const path = require("path"); +const tsconfig_paths_webpack_plugin_1 = require("tsconfig-paths-webpack-plugin"); +const webpack_1 = require("webpack"); +const constants_1 = require("../constants"); +const common_1 = require("./common"); +// tslint:disable-next-line:no-var-requires no-require-imports +const configFileName = path.join(constants_1.ExtensionRootDir, 'tsconfig.extension.json'); +const config = { + mode: 'production', + target: 'node', + entry: { + 'debugger/debugAdapter/main': './src/client/debugger/debugAdapter/main.ts' + }, + devtool: 'source-map', + node: { + __dirname: false + }, + module: { + rules: [ + { + // JupyterServices imports node-fetch using `eval`. + test: /@jupyterlab[\\\/]services[\\\/].*js$/, + use: [ + { + loader: path.join(__dirname, 'loaders', 'fixEvalRequire.js') + } + ] + }, + { + // Do not use __dirname in getos when using require. + test: /getos[\\\/]index.js$/, + use: [ + { + loader: path.join(__dirname, 'loaders', 'fixGetosRequire.js') + } + ] + }, + { + test: /\.ts$/, + exclude: /node_modules/, + use: [ + { + loader: 'ts-loader' + } + ] + } + ] + }, + externals: [ + 'vscode', + 'commonjs' + ], + plugins: [ + ...common_1.getDefaultPlugins('extension'), + new webpack_1.ContextReplacementPlugin(/getos/, /logic\/.*.js/) + ], + resolve: { + extensions: ['.ts', '.js'], + plugins: [ + new tsconfig_paths_webpack_plugin_1.TsconfigPathsPlugin({ configFile: configFileName }) + ] + }, + output: { + filename: '[name].js', + path: path.resolve(constants_1.ExtensionRootDir, 'out', 'client'), + libraryTarget: 'commonjs2', + devtoolModuleFilenameTemplate: '../../[resource-path]' + } +}; +// tslint:disable-next-line:no-default-export +exports.default = config; diff --git a/build/webpack/webpack.debugadapter.config.ts b/build/webpack/webpack.debugadapter.config.ts new file mode 100644 index 000000000000..9b9d77b425e8 --- /dev/null +++ b/build/webpack/webpack.debugadapter.config.ts @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import * as path from 'path'; +import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin'; +import { Configuration, ContextReplacementPlugin } from 'webpack'; +import { ExtensionRootDir } from '../constants'; +import { getDefaultPlugins } from './common'; + +// tslint:disable-next-line:no-var-requires no-require-imports +const configFileName = path.join(ExtensionRootDir, 'tsconfig.extension.json'); + +const config: Configuration = { + mode: 'production', + target: 'node', + entry: { + 'debugger/debugAdapter/main': './src/client/debugger/debugAdapter/main.ts' + }, + devtool: 'source-map', + node: { + __dirname: false + }, + module: { + rules: [ + { + // JupyterServices imports node-fetch using `eval`. + test: /@jupyterlab[\\\/]services[\\\/].*js$/, + use: [ + { + loader: path.join(__dirname, 'loaders', 'fixEvalRequire.js') + } + ] + }, + { + // Do not use __dirname in getos when using require. + test: /getos[\\\/]index.js$/, + use: [ + { + loader: path.join(__dirname, 'loaders', 'fixGetosRequire.js') + } + ] + }, + { + test: /\.ts$/, + exclude: /node_modules/, + use: [ + { + loader: 'ts-loader' + } + ] + } + ] + }, + externals: [ + 'vscode', + 'commonjs' + ], + plugins: [ + ...getDefaultPlugins('extension'), + new ContextReplacementPlugin(/getos/, /logic\/.*.js/) + ], + resolve: { + extensions: ['.ts', '.js'], + plugins: [ + new TsconfigPathsPlugin({ configFile: configFileName }) + ] + }, + output: { + filename: '[name].js', + path: path.resolve(ExtensionRootDir, 'out', 'client'), + libraryTarget: 'commonjs2', + devtoolModuleFilenameTemplate: '../../[resource-path]' + } +}; + +// tslint:disable-next-line:no-default-export +export default config; diff --git a/build/webpack/webpack.extension.config.js b/build/webpack/webpack.extension.config.js index 0f5c12e75937..2b94dcc229fa 100644 --- a/build/webpack/webpack.extension.config.js +++ b/build/webpack/webpack.extension.config.js @@ -18,8 +18,7 @@ const config = { mode: 'production', target: 'node', entry: { - extension: './src/client/extension.ts', - 'debugger/debugAdapter/main': './src/client/debugger/debugAdapter/main.ts' + extension: './src/client/extension.ts' }, devtool: 'source-map', node: { diff --git a/build/webpack/webpack.extension.config.ts b/build/webpack/webpack.extension.config.ts index d63d5b71924b..9e551eac33f9 100644 --- a/build/webpack/webpack.extension.config.ts +++ b/build/webpack/webpack.extension.config.ts @@ -23,8 +23,7 @@ const config: Configuration = { mode: 'production', target: 'node', entry: { - extension: './src/client/extension.ts', - 'debugger/debugAdapter/main': './src/client/debugger/debugAdapter/main.ts' + extension: './src/client/extension.ts' }, devtool: 'source-map', node: { diff --git a/gulpfile.js b/gulpfile.js index 11bc8ccbb264..63583839bdd4 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -196,6 +196,7 @@ gulp.task('webpack', async () => { await spawnAsync('npx', ['webpack', '--mode', 'production']); await spawnAsync('npx', ['webpack', '--config', './build/webpack/webpack.extension.sourceMaps.config.js', '--mode', 'production']); await spawnAsync('npx', ['webpack', '--config', './build/webpack/webpack.extension.config.js', '--mode', 'production']); + await spawnAsync('npx', ['webpack', '--config', './build/webpack/webpack.debugadapter.config.js', '--mode', 'production']); }); gulp.task('prePublishBundle', gulp.series('checkNativeDependencies', 'check-datascience-dependencies', 'compile', 'clean:cleanExceptTests', 'webpack')); diff --git a/src/client/common/variables/environment.ts b/src/client/common/variables/environment.ts index b57f599b0ac8..dd1146f98f04 100644 --- a/src/client/common/variables/environment.ts +++ b/src/client/common/variables/environment.ts @@ -14,13 +14,12 @@ export class EnvironmentVariablesService implements IEnvironmentVariablesService constructor(@inject(IPathUtils) pathUtils: IPathUtils) { this.pathVariable = pathUtils.getPathVariableName(); } - public async parseFile(filePath: string): Promise { - const exists = filePath.length > 0 ? await fs.pathExists(filePath) : undefined; - if (!exists) { - return undefined; + public async parseFile(filePath?: string): Promise { + if (!filePath || !await fs.pathExists(filePath)) { + return; } if (!fs.lstatSync(filePath).isFile()) { - return undefined; + return; } return dotenv.parse(await fs.readFile(filePath)); } diff --git a/src/client/common/variables/types.ts b/src/client/common/variables/types.ts index be137b28cc07..bfdd40e0890c 100644 --- a/src/client/common/variables/types.ts +++ b/src/client/common/variables/types.ts @@ -10,7 +10,7 @@ export type EnvironmentVariables = Object & { export const IEnvironmentVariablesService = Symbol('IEnvironmentVariablesService'); export interface IEnvironmentVariablesService { - parseFile(filePath: string): Promise; + parseFile(filePath?: string): Promise; mergeVariables(source: EnvironmentVariables, target: EnvironmentVariables): void; appendPythonPath(vars: EnvironmentVariables, ...pythonPaths: string[]): void; appendPath(vars: EnvironmentVariables, ...paths: string[]): void; diff --git a/src/test/common/variables/envVarsService.test.ts b/src/test/common/variables/envVarsService.unit.test.ts similarity index 97% rename from src/test/common/variables/envVarsService.test.ts rename to src/test/common/variables/envVarsService.unit.test.ts index b34df2da05ad..4ae22b5ef317 100644 --- a/src/test/common/variables/envVarsService.test.ts +++ b/src/test/common/variables/envVarsService.unit.test.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +'use strict'; + import { expect, use } from 'chai'; import * as chaiAsPromised from 'chai-as-promised'; import * as path from 'path'; @@ -23,6 +25,11 @@ suite('Environment Variables Service', () => { variablesService = new EnvironmentVariablesService(pathUtils); }); + test('Custom variables should be undefined with no argument', async () => { + const vars = await variablesService.parseFile(undefined); + expect(vars).to.equal(undefined, 'Variables should be undefined'); + }); + test('Custom variables should be undefined with non-existent files', async () => { const vars = await variablesService.parseFile(path.join(envFilesFolderPath, 'abcd')); expect(vars).to.equal(undefined, 'Variables should be undefined'); From 34c42e8028882a2339e521e471db8a290e94f049 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 28 Nov 2018 12:21:21 -0800 Subject: [PATCH 2/2] Smoke test for debugging --- src/test/smoke/debugger.smoke.test.ts | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/test/smoke/debugger.smoke.test.ts diff --git a/src/test/smoke/debugger.smoke.test.ts b/src/test/smoke/debugger.smoke.test.ts new file mode 100644 index 000000000000..3d58ca8ec371 --- /dev/null +++ b/src/test/smoke/debugger.smoke.test.ts @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +// tslint:disable:max-func-body-length no-invalid-this no-any + +import { expect } from 'chai'; +import * as fs from 'fs-extra'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { openFile, waitForCondition } from '../common'; +import { EXTENSION_ROOT_DIR_FOR_TESTS, IS_SMOKE_TEST } from '../constants'; +import { closeActiveWindows, initializeTest } from '../initialize'; + +suite('Smoke Test: Debug file', function () { + // Large value to allow for LS to get downloaded. + this.timeout(4 * 60_000); + + suiteSetup(function () { + if (!IS_SMOKE_TEST) { + return this.skip(); + } + }); + setup(initializeTest); + suiteTeardown(closeActiveWindows); + teardown(closeActiveWindows); + + test('Debug', async () => { + const file = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'testMultiRootWkspc', 'smokeTests', 'testExecInTerminal.py'); + const outputFile = path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'testMultiRootWkspc', 'smokeTests', 'testExecInTerminal.log'); + if (await fs.pathExists(outputFile)) { + await fs.unlink(outputFile); + } + await openFile(file); + + const config = { + name: 'Debug', + request: 'launch', + type: 'python', + program: file + }; + + const started = await vscode.debug.startDebugging(vscode.workspace.workspaceFolders![0], config); + expect(started).to.be.equal(true, 'Debugger did not sart'); + const checkIfFileHasBeenCreated = () => fs.pathExists(outputFile); + await waitForCondition(checkIfFileHasBeenCreated, 30_000, '\'testExecInTerminal.log\' file not created'); + }); +});