How to run a system command from VSCode extension - visual-studio-code

I am trying to create a simple VSCode extension to run a set of commands when I open a folder. Basically these commands will set up our development environment. I have started off creating the boilerplace and ran through the example that VSCode provided but I am not clear how to run system commands. Appreciate any help or point me to some documentation about this topic.

Your extension environment has access to node.js libraries, so you can just use child_process or any helper libraries to execute commands:
const cp = require('child_process')
cp.exec('pwd', (err, stdout, stderr) => {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (err) {
console.log('error: ' + err);
}
});

One alternative could be to use the Terminal API which is the best option if you have the need for the process to be fully observable and controllable to/by the user.
Biggest downside: The Terminal API does not yet offer a way to introspect the processes that run inside of it.
If you actually want to run the process in the terminal, the only way to do this safely for now, would be to use a two-layer approach, where you start a wrapper process that in turn launches and observes the actual process (taken in via command line args).
Our self-made TerminalWrapper
We tried this ourselves.
In our first approach, the wrapper used a socket.io connection that allows for communication with and control by the extension.
In our second approach, we simplified and instead created the terminal using bash -c (non-interactive shell), and used a file watcher to get the results instead. Easier this way but after the process is done, the user won't be able to use the Terminal window (because its non-interactive). A lot less error-prone and does not require fulfilling socket.io dependency.
Implementation Details
In our extension, we use a TerminalWrapper which runs the command inside a wrapper process, and waits for a file to contain the results.
The wrapper process is here. It writes the result to a file.
Usage example here:
const cwd = '.';
const command = `node -e "console.log('hi!');"`;
const { code } = await TerminalWrapper.execInTerminal(cwd, command, {}).waitForResult();
if (code) {
const processExecMsg = `${cwd}$ ${command}`;
throw new Error(`Process failed with exit code ${code} (${processExecMsg})`);
}
Biggest downside with the second approach is that we now need bash to be present, however (i) we do have a dependency checker that will warn you if you don't and explain how to get it, and (ii) using a unified shell, makes running commands a lot easier, as we now have a pretty strong unified feature set, we know we can rely on, rather than only being able to use common command execution syntax, and (iii) we can even run *.sh files without having to worry.
Introducing: VSCode Terminal API
VSCode API Documentation: Terminal
Official Terminal API Samples
All of the following imagery and excerpts are just straight up copy-and-paste'd from their official sample repository:
Create a terminal and run a command in it
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createAndSend', () => {
const terminal = vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`);
terminal.sendText("echo 'Sent text immediately after creating'");
}));
Terminal activation event
vscode.window.onDidChangeActiveTerminal(e => {
console.log(`Active terminal changed, name=${e ? e.name : 'undefined'}`);
});
TerminalQuickPickItem
function selectTerminal(): Thenable<vscode.Terminal | undefined> {
interface TerminalQuickPickItem extends vscode.QuickPickItem {
terminal: vscode.Terminal;
}
const terminals = <vscode.Terminal[]>(<any>vscode.window).terminals;
const items: TerminalQuickPickItem[] = terminals.map(t => {
return {
label: `name: ${t.name}`,
terminal: t
};
});
return vscode.window.showQuickPick(items).then(item => {
return item ? item.terminal : undefined;
});
}
...and a lot more!...
(<3 for the VSCode team and their hard work.)

What I did was to create a promise based utility function to run all shell command with child_process
import * as cp from "child_process";
const execShell = (cmd: string) =>
new Promise<string>((resolve, reject) => {
cp.exec(cmd, (err, out) => {
if (err) {
return reject(err);
}
return resolve(out);
});
});
To get current directory
const currentDir = await execShell('pwd');
To get current git branch name
const branchName = await execShell('git rev-parse --abbrev-ref HEAD');

Related

VSCode extension testing: Use `vscode.executeDefinitionProvider` in test

Background
I'm trying to auto-test my VSCode extension. The extension works with python files and uses vscode.executeDefinitionProvider and vscode.executeDocumentSymbolProvider on them.
Problem
vscode.executeDefinitionProvider always returns [], vscode.executeDocumentSymbolProvider always returns undefined.
Notes
When running the same code in a debug session of the extension (no test session), the commands work flawless.
I ensured the extensions to be available during the test and even manually activated them with
let ext = vscode.extensions.getExtension("ms-python.python");
assert.notStrictEqual (ext, undefined);
await ext?.activate ();
ext = vscode.extensions.getExtension("ms-python.vscode-pylance");
assert.notStrictEqual (ext, undefined);
await ext?.activate ();
Question
How do I get the commands to succeed during automated test.
Edit: Workaround
Apparently VSCode takes its time to really activate the extensions. I could get it working placing a await sleep (10000); in index.ts::run () before return new Promise((c, e) => {.
While this is working, it's a really unstable workaround, Is there any way to make the code wait until the whole environment is fully loaded?
In the end nothing really stably worked for me, so I resorted to the following (perfectly fine working) solution.
My auto tests are run from the productive environment, like any other extension.
In package.json I created a new command _test.
the command would run ./test/suite/index.ts : run().
Extension<T>::activate(): Thenable<T>
Returns: Thenable<T> - A promise that will resolve when this extension has been activated.
await ext?.activate();

Issue with installing Facebook CTF

I am trying to install Facebook CTF from https://github.com/facebook/fbctf
Following the instructions, I execute ./extra/provision.sh -m prod -s $PWD
All goes well, until it gets to the section where it runs grunt. It's hitting this code in a javascript file
const proto = Object.defineProperties(() => {}, {
...styles,
level: {
enumerable: true,
get() {
return this._generator.level;
},
set(level) {
this._generator.level = level;
}
}
});
It's balking at the ellipsis in front of styles.
It's giving this error.
...styles,
^^^
SyntaxError: Unexpected token ...
Has anyone run into this error when install fbctf or can spot a Javascript error? Thanks for your help
I'm using the Quick Setup instructions and ran into a similar issue.
I resolved by it upgrading my nodejs version using npm in the instructions below.
https://phoenixnap.com/kb/update-node-js-version
Note: I did the nodejs installation on command line before running the install command.

Q# : QDK Errors

I've recently tried to install the QDK via the VSCode extension in my Windows 10 Desktop and VSCode wasn't able to find the Microsoft libraries even after I was able to execute the code by the dotnet run command on the terminal. The code was the sample project code described in the create new project part of the tutorial. I also didn't have .NET SDK so I installed it but it seems to be working fine. In computers I got problems all the code, all related to not finding the namespaces.
namespace QuantumRNG {
open Microsoft.Quantum.Canon;
open Microsoft.Quantum.Intrinsic;
open Microsoft.Quantum.Measurement;
open Microsoft.Quantum.Math;
open Microsoft.Quantum.Convert;
operation GenerateRandomBit() : Result {
using (q = Qubit()) {
H(q);
return MResetZ(q);
}
}
operation SampleRandomNumberInRange(max : Int) : Int {
mutable output = 0;
repeat {
mutable bits = new Result[0];
for (idxBit in 1..BitSizeI(max)) {
set bits += [GenerateRandomBit()];
}
set output = ResultArrayAsInt(bits);
} until (output <= max);
return output;
}
#EntryPoint()
operation SampleRandomNumber() : Int {
let max = 50;
Message($"Sampling a random number between 0 and {max}: ");
return SampleRandomNumberInRange(max);
}
}
Do you see any error messages in the output console?
To see the output console select "View: Toggle Output" (Ctrl + Shift + U) and select "Q# Language Extension" from the drop down list.
If the drop down list doesn't show "Q# Language Extension" then it probably means that the language-server that gets downloaded on first run is still downloading, so give it a minute or so (depending on your internet connection).
I think I also had the same problem. The .Net SDk which you have downloaded automatically creates a path in the environment variable by the name "%USERPROFILE%.dotnet\tools" so delete this and add "C:\Program Files\dotnet\sdk" or copy the path of dotnet SDK from the drive where you have installed dotnet and create a new environment variable under user variable. This might solve your namespace problem.

parcel watch only detects first file change

I have the following in ./js/parcel/build-js.js (it is more or less a simplification of exactly what the API docs example does, except that it takes an optional --watch argument):
#!/usr/bin/env node
const Bundler = require('parcel-bundler');
const path = require('path');
const watch = process.argv.indexOf('--watch') > 0;
if (watch) console.log('Watching files...');
(async function bundleJs() {
const jsBundler = new Bundler(path.join(__dirname, '../src/common.js'), {
watch,
hmr: false,
});
jsBundler.on('bundled', () => {
console.log('bundled!');
});
const bundle = await jsBundler.bundle();
console.log('done');
})();
When I run node js/parcel/build-js.js --watch, it detects the first change to src/common.js and prints:
Watching files...
✨ Built in 585ms.
bundled!
done
This is as I'd expect. When I edit and save src/common.js, it sees that and then the total output becomes (done gets deleted):
Watching files...
✨ Built in 585ms.
bundled!
✨ Built in 86ms.
bundled!
But after that, no file changes are detected. I make changes and save but it just sits there, producing no more output or updating the build. Why only once?
Note: If I do strace node js/parcel/build-js.js --watch, it seems to just sit on an unfinished epoll_wait(3,, which I guess means it's waiting for something, but maybe watching the wrong file...
Edit: Versions!
parcel-bundler: 1.12.3
node: 10.15.1
Ubuntu 18.04
Edit: using parcel watch
This appears to be a system-wide thing for me. I did yarn globals add parcel (which also installed 1.12.3), and now watching any JS file with parcel watch path/to/file.js does the same thing.
It turned out to be a conflict between Parcel's change detection and the default Vim setup. From the Hot Module Replacement docs:
Some text editors and IDE's have a feature called safe write that basically prevents data loss, by taking a copy of the file and renaming it when saved.
When using Hot Module Reload (HMR) this feature blocks the automatic detection of file updates, to disable safe write use the options provided below:
I added set backupcopy=yes to my .vimrc and it started working.
The solution for other editors is documented there as well.
It is a Parcel issue! I dropped it (until they fix it)
IMHO: I do not have to change my editor's behavior just to make bundler work correctly. (webpack works fine in the situation)

How do I run only certain tests in karma?

I have karma config set up correctly, config file, running in the background, just great. As soon as I change and save a file, it reruns the tests.... all 750 of the unit tests. I want to be able to run just a few. Short of manually hacking the config file or commenting out hundreds of tests across many files, is there any easy way to do it?
E.g. when running command line server tests using say mocha, I just use regexp: mocha -g 'only tests that I want'. Makes it much easier to debug and quickly check.
So now I feel foolish. mocha supports a very narrow version of regexp matching.
This runs all tests
describe('all tests',function(){
describe('first tests',function(){
});
describe('second tests',function(){
});
});
This runs just 'first tests'
describe('all tests',function(){
describe.only('first tests',function(){
});
describe('second tests',function(){
});
});
You can also do it.only()
I should have noticed that. Sigh.
You can do that at karma startup time unfortunately, not at runtime.
If you want to change it dynamically you have to put some more effort.
Say you want to focus on a specific set/suite of tests from the beginning, on the karma-mocha plugin page there's this snippet of code to do what you want:
module.exports = function(config) {
config.set({
// karma configuration here
...
// this is a mocha configuration object
client: {
// The pattern string will be passed to mocha
args: ['--grep', '<pattern>'],
...
}
});
};
In order to make the <pattern> parametric you have to wrap the configuration file in a Configurator that will listen CLI and customize the karma configuration for you.
Have a look to this SO answer to know how to setup a very simple Configurator.
I have same question and this is my workround by a little change on karma.conf.js.
In fact, take an argument from command line and modify the pattern in "files".
I use minimist to parse the argument list.
In config file:
/* Begin */
var minimist = require('minimist');
var argv = minimist(process.argv);
var testBase="test/unit";
var testExt=".spec.js";
var unitTestPattern = testBase+'/**/*'+testExt;
if ("test" in argv){
unitTestPattern = testBase+"/"+argv["test"]+testExt;
}
/* End */
module.exports = function(config){
config.set({
//....
files : [
//....
unitTestPattern, //place here
// 'test/unit/**/*.spec.js', //replace this
//....
],
//....
});
};
run in command prompt:
karma start test/karma.conf.js --single-run --test #TEST_CASE_FILE#
a nice extension that can help here is karma-jasmine-html-reporter-livereload
https://www.npmjs.com/package/karma-jasmine-html-reporter-livereload
or karma-jasmine-html-reporter https://www.npmjs.com/package/karma-jasmine-html-reporter?__hstc=72727564.86845f057bb4d741f59d578059e30644.1443860954685.1453095135802.1453138187458.37&__hssc=72727564.1.1453138187458&__hsfp=2285154675
It creates a debug page in which you can run each test individually. very useful for large projects!
1) In your karma.conf.js get the params from the terminal:
var files = (process.env.npm_config_single_file) ? process.env.npm_config_single_file : 'test/test_index.js';
2) In order to run a single test you will need to set an option object with all your configuration (Without files and preprocessors):
var option = {
webpack: {
// webpack configuration
},
// more configuration......
};
3) Set your files path and preprocessors:
option.files = [
{pattern: files, watch: false}
];
option.preprocessors = {};
option.preprocessors[files] = [ 'webpack', 'sourcemap' ];
// call config.set function
config.set(option);
4) Run in the terminal:
npm test --single_file=**/my-specific-file-spec.js
For more information check this PR:
https://github.com/webpack/karma-webpack/pull/178
There are different ways to do it.
Use --grep option. The disadvantage of this is that all the tests are preprocessed before running the specific test suite.
Use .only method. Disadvantage same as no. 1. Using both 1 and 2 method my node process used to crash often saying out of memory.
Limit the files options for processing. This is super fast.
Limit preprocessing to certain folder like Unit or Integration folder.
For this I have used custom cli option --only and in the karma config
const modules = config.only;
and in the the files pattern
files: typeof modules === 'string ? '[`tests/**/${module}/**/*.(test|spec).js`]: 'tests/**/*.(test|spec).js'
Advantage: Developers can run only certain tests when they make a small change way faster by limiting in the preprocessing phase.
You can also use combination of no.3 and no.1 or 2.