diff --git a/.azure-pipelines/jobs/ci1.linux.yml b/.azure-pipelines/jobs/ci1.linux.yml index 8e16ce8a07..7549c774d9 100644 --- a/.azure-pipelines/jobs/ci1.linux.yml +++ b/.azure-pipelines/jobs/ci1.linux.yml @@ -2,10 +2,10 @@ steps: - script: npx nps format.and.lint.check displayName: 'format and lint check' - - script: npx nps prepare.electron + - script: npx nps build.ci.electron displayName: 'build electron' - - script: npx nps test.affected.origin-master + - script: npx nx affected:test --base=origin/master --ci displayName: 'affected unit-tests' - script: npx nps e2e.ci1.fixtures diff --git a/.azure-pipelines/jobs/ci2.linux.yml b/.azure-pipelines/jobs/ci2.linux.yml index fd705f84d0..7d7292dd45 100644 --- a/.azure-pipelines/jobs/ci2.linux.yml +++ b/.azure-pipelines/jobs/ci2.linux.yml @@ -1,5 +1,5 @@ steps: - - script: npx nps prepare.electron + - script: npx nps build.ci.electron displayName: build electron - script: npx nps e2e.ci2.fixtures diff --git a/.azure-pipelines/jobs/windows.yml b/.azure-pipelines/jobs/windows.yml index ebb5b8b2d8..50adb29758 100644 --- a/.azure-pipelines/jobs/windows.yml +++ b/.azure-pipelines/jobs/windows.yml @@ -1,3 +1,6 @@ steps: - script: npx nps prepare.electron - displayName: build electron + displayName: prepare electron + + - script: npx nps package.electronWin + displayName: package electron diff --git a/.azure-pipelines/steps/install-dependencies.yml b/.azure-pipelines/steps/install-dependencies.yml index ec19d58e17..0d8a03ef77 100644 --- a/.azure-pipelines/steps/install-dependencies.yml +++ b/.azure-pipelines/steps/install-dependencies.yml @@ -5,5 +5,5 @@ steps: versionSpec: '12.x' - script: | - yarn install --ignore-engines --frozen-lockfile --ignore-optional + yarn install --frozen-lockfile displayName: yarn install diff --git a/.vscode/launch.json b/.vscode/launch.json index bec567aa51..816c7646b5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,7 @@ "version": "0.2.0", "configurations": [ { - "name": "Extension", + "name": "Run Extension In Dev Mode", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", @@ -14,9 +14,8 @@ "--extensionDevelopmentPath=${workspaceFolder}/dist/apps/vscode" ], "trace": "false", - "sourceMaps": false, - "stopOnEntry": false, "outFiles": ["${workspaceFolder}/**/*"], + "preLaunchTask": "npm: build-vs-code" } ] } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 932e2572fc..4ac19b7c68 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,7 +6,7 @@ { "type": "npm", "script": "build-vs-code", - "isBackground": false, + "isBackground": true, "presentation": { "reveal": "never" }, diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ac8eb1321..9782f8977f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,18 +42,9 @@ You can build the electron app by running `nps package.electronMac` or `nps pack ## Building VSCode Plugin -You can build the vscode extension by running `nps prepare.and.package.vscode`. -You can install it by running `code --install-extension dist/apps/vscode/angular-console.vsix` -Reload the vscode window to use the newly installed build of the extension. +You can build the vscode extension and run it in development mode by opening up this repo in Visual Studio code and hitting the f5 function key. This will launch `nps build.dev.vscode` in the background and spawn an extension development host version of VSCode so that you can try out your code. -If you are working on the plugin, run: - -- `nps build.dev.vscode` (This builds the client/server in watch mode) -- Wait for both client and server to be built. -- Hit F5 - -Now you can make changes to your code and the client/server builds will trigger. -When you want to see those reflected in VSCode, click the refresh button or hit F5 again. +When you want to update the extension with a new set of changes, go back to the editor you launched the extension host from and click the refresh button (its green and looks like a browser refresh icon). ## Submitting a PR diff --git a/apps/intellij/src/app/start-server.ts b/apps/intellij/src/app/start-server.ts index 6201958b5d..237b469fe0 100644 --- a/apps/intellij/src/app/start-server.ts +++ b/apps/intellij/src/app/start-server.ts @@ -6,8 +6,8 @@ import { } from '@angular-console/server'; import { NestFactory } from '@nestjs/core'; import { Store } from '@nrwl/angular-console-enterprise-electron'; -import { join } from 'path'; import { existsSync, writeFile } from 'fs'; +import { join } from 'path'; export interface IntellijTerminal { onDataWrite(callback: (data: string) => void): void; @@ -49,9 +49,6 @@ function wsPseudoTerminalFactory( }, kill: () => { terminal.kill(); - }, - setCols: () => { - // No-op, we defer to vscode so as to match its display } }; } diff --git a/apps/vscode/src/app/pseudo-terminal.factory.ts b/apps/vscode/src/app/pseudo-terminal.factory.ts index 8790eca0a2..e750d32a73 100644 --- a/apps/vscode/src/app/pseudo-terminal.factory.ts +++ b/apps/vscode/src/app/pseudo-terminal.factory.ts @@ -5,10 +5,17 @@ import { readSettings } from '@angular-console/server'; import { platform } from 'os'; -import { ExtensionContext, Terminal, window } from 'vscode'; +import { Disposable, ExtensionContext, Terminal, window } from 'vscode'; import { getStoreForContext } from './get-store-for-context'; +let terminalsToReuse: Array = []; +window.onDidCloseTerminal(e => { + terminalsToReuse = terminalsToReuse.filter(t => t.processId !== e.processId); +}); + +const DISPOSE_MESSAGE = 'Press any key to close this terminal'; + export function getPseudoTerminalFactory( context: ExtensionContext ): PseudoTerminalFactory { @@ -18,26 +25,29 @@ export function getPseudoTerminalFactory( if (platform() === 'win32') { const isWsl = readSettings(store).isWsl; if (isWsl) { - return wslPseudoTerminalFactory(context, config); + return wslPseudoTerminalFactory(config); } else { - return win32PseudoTerminalFactory(context, config); + return win32PseudoTerminalFactory(config); } } - return unixPseudoTerminalFactory(context, config); + return unixPseudoTerminalFactory(config); }; } -function win32PseudoTerminalFactory( - context: ExtensionContext, - { name, program, args, cwd, displayCommand }: PseudoTerminalConfig -): PseudoTerminal { +function win32PseudoTerminalFactory({ + name, + program, + args, + cwd, + displayCommand +}: PseudoTerminalConfig): PseudoTerminal { const successMessage = 'Process completed #woot'; const failureMessage = 'Process failed #failwhale'; const fullCommand = [ `echo '${displayCommand}\n'; Try {`, `& '${program}' ${args.join(' ')};`, - `if($?) { echo '\n\r${successMessage}' };`, - `if(!$?) { echo '\n\r${failureMessage}' };`, + `if($?) { echo '\n\n${successMessage}\n\n${DISPOSE_MESSAGE}' };`, + `if(!$?) { echo '\n\n${failureMessage}\n\n${DISPOSE_MESSAGE}' };`, `} Catch { `, `echo '\n\r${failureMessage}'`, `}; $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');` @@ -50,110 +60,119 @@ function win32PseudoTerminalFactory( shellArgs: `-Sta -NoLogo -NonInteractive -C "& {${fullCommand}}"` }); - return renderVsCodeTerminal( - context, - terminal, - successMessage, - failureMessage - ); + return renderVsCodeTerminal(terminal, successMessage, failureMessage); } function wslPseudoTerminalFactory( - context: ExtensionContext, - { name, program, args, cwd, displayCommand }: PseudoTerminalConfig + config: PseudoTerminalConfig ): PseudoTerminal { const successMessage = 'Process completed #woot'; const failureMessage = 'Process failed #failwhale'; - const fullCommand = - `echo "${displayCommand}\n" && ${program} ${args.join( - ' ' - )} && read -n 1 -s -r -p $"\n\n${successMessage}\n"` + - ` || read -n 1 -s -r -p $"\n\n${failureMessage}\n"`; const terminal = window.createTerminal({ name, - cwd, + cwd: config.cwd, shellPath: 'C:\\Windows\\System32\\wsl.exe', - shellArgs: ['-e', 'bash', '-l', '-i', '-c', fullCommand] + shellArgs: [ + '-e', + 'bash', + '-l', + '-c', + getBashScriptForCommand(config, successMessage, failureMessage) + ] }); - return renderVsCodeTerminal( - context, - terminal, - successMessage, - failureMessage - ); + return renderVsCodeTerminal(terminal, successMessage, failureMessage); } function unixPseudoTerminalFactory( - context: ExtensionContext, - { name, program, args, cwd, displayCommand }: PseudoTerminalConfig + config: PseudoTerminalConfig ): PseudoTerminal { const successMessage = 'Process completed 🙏'; const failureMessage = 'Process failed 🐳'; - const fullCommand = - `echo "${displayCommand}\n" && ${program} ${args.join( - ' ' - )} && read -n 1 -s -r -p $"\n\n${successMessage}\n"` + - ` || read -n 1 -s -r -p $"\n\n${failureMessage}\n"`; const terminal = window.createTerminal({ name, - cwd, + cwd: config.cwd, shellPath: '/bin/bash', - shellArgs: ['-l', '-i', '-c', fullCommand] + shellArgs: [ + '-l', + '-i', + '-c', + getBashScriptForCommand(config, successMessage, failureMessage) + ] }); - return renderVsCodeTerminal( - context, - terminal, - successMessage, - failureMessage + return renderVsCodeTerminal(terminal, successMessage, failureMessage); +} + +function getBashScriptForCommand( + config: PseudoTerminalConfig, + successMessage: string, + failureMessage: string +) { + const { displayCommand, program, args } = config; + return ( + `echo "${displayCommand}\n" && ${program} ${args.join( + ' ' + )} && read -n 1 -s -r -p $"\n\n${successMessage}\n\n${DISPOSE_MESSAGE}"` + + ` || read -n 1 -s -r -p $"\n\n${failureMessage}\n\n${DISPOSE_MESSAGE}"` ); } function renderVsCodeTerminal( - context: ExtensionContext, terminal: Terminal, successMessage: string, failureMessage: string ): PseudoTerminal { + const reusableTerminal = terminalsToReuse.pop(); + if (reusableTerminal) { + reusableTerminal.dispose(); + } + terminal.show(); - context.subscriptions.push(terminal); let onDidWriteData: ((data: string) => void) | undefined; let onExit: ((code: number) => void) | undefined; + let disposeOnDidWriteData: Disposable | undefined; const disposeTerminal = (code: number) => { - onDidWriteData = undefined; if (onExit) { onExit(code); - onExit = undefined; } + if (disposeOnDidWriteData) { + disposeOnDidWriteData.dispose(); + } + onExit = undefined; + onDidWriteData = undefined; + disposeOnDidWriteData = undefined; }; - context.subscriptions.push( - (terminal).onDidWriteData((data: string) => { + disposeOnDidWriteData = (terminal).onDidWriteData((data: string) => { + // Screen scrape for successMessage or failureMessage as the signal that the + // process has exited. + const success = data.includes(successMessage); + const failed = data.includes(failureMessage); + if (success || failed) { if (onDidWriteData) { - onDidWriteData(data); + onDidWriteData(data.replace(DISPOSE_MESSAGE, '')); } - // Screen scrape for successMessage or failureMessage as the signal that the - // process has exited. - const success = data.includes(successMessage); - const failed = data.includes(failureMessage); - if (success || failed) { - disposeTerminal(success ? 0 : 1); - } - }) - ); + disposeTerminal(success ? 0 : 1); + } else if (onDidWriteData) { + onDidWriteData(data); + } + }); return { onDidWriteData: callback => { onDidWriteData = callback; }, onExit: callback => { - onExit = callback; + onExit = code => { + terminalsToReuse.push(terminal); + callback(code); + }; }, kill: () => { if (onDidWriteData) { @@ -163,9 +182,6 @@ function renderVsCodeTerminal( terminal.dispose(); } disposeTerminal(1); - }, - setCols: () => { - // No-op, we defer to vscode so as to match its display } }; } diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b6f542891f..1859fe24c6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,14 +1,14 @@ jobs: - job: linux_shard_1 pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-latest steps: - template: .azure-pipelines/steps/install-dependencies.yml - template: .azure-pipelines/jobs/ci1.linux.yml - job: linux_shard_2 pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-latest steps: - template: .azure-pipelines/steps/install-dependencies.yml - template: .azure-pipelines/jobs/ci2.linux.yml diff --git a/libs/feature-action-bar/src/lib/action-bar.component.html b/libs/feature-action-bar/src/lib/action-bar.component.html index cef9da0bda..e55d994b09 100644 --- a/libs/feature-action-bar/src/lib/action-bar.component.html +++ b/libs/feature-action-bar/src/lib/action-bar.component.html @@ -6,7 +6,7 @@
{ + if (c.commandRunning) { + c.commandRunning.kill(); + c.commandRunning = null; + } if (c.status === 'in-progress') { c.status = 'terminated'; c.detailedStatusCalculator.setStatus('terminated'); - if (c.commandRunning) { - c.commandRunning.kill(); - c.commandRunning = null; - } } }); } diff --git a/libs/server/src/lib/api/run-command.ts b/libs/server/src/lib/api/run-command.ts index 77c96a57d5..92792ebf99 100644 --- a/libs/server/src/lib/api/run-command.ts +++ b/libs/server/src/lib/api/run-command.ts @@ -72,7 +72,7 @@ export interface PseudoTerminal { onExit(callback: (code: number) => void): void; - setCols(cols: number): void; + setCols?(cols: number): void; kill(): void; } diff --git a/libs/server/src/lib/resolvers/query.resolver.ts b/libs/server/src/lib/resolvers/query.resolver.ts index e4766b57dd..1964f07cd6 100644 --- a/libs/server/src/lib/resolvers/query.resolver.ts +++ b/libs/server/src/lib/resolvers/query.resolver.ts @@ -1,4 +1,3 @@ -import { Inject } from '@nestjs/common'; import { CommandResponse, EditorSupport, @@ -8,7 +7,18 @@ import { Settings, Workspace } from '@angular-console/schema'; +import { Inject } from '@nestjs/common'; +import { Args, Context, Query, Resolver } from '@nestjs/graphql'; + +import { CommandInformation } from '../api/commands'; +import { readDependencies } from '../api/read-dependencies'; +import { readEditors } from '../api/read-editors'; +import { availableExtensions, readExtensions } from '../api/read-extensions'; import { schematicCollectionsForNgNew } from '../api/read-ngnews'; +import { readNpmScripts } from '../api/read-npm-scripts'; +import { readProjects } from '../api/read-projects'; +import { readSettings } from '../api/read-settings'; +import { commands } from '../api/run-command'; import { cacheFiles, exists, @@ -16,15 +26,6 @@ import { filterByName, readJsonFile } from '../utils/utils'; -import { readDependencies } from '../api/read-dependencies'; -import { availableExtensions, readExtensions } from '../api/read-extensions'; -import { readProjects } from '../api/read-projects'; -import { readNpmScripts } from '../api/read-npm-scripts'; -import { readEditors } from '../api/read-editors'; -import { commands } from '../api/run-command'; -import { Args, Context, Query, Resolver } from '@nestjs/graphql'; -import { CommandInformation } from '../api/commands'; -import { readSettings } from '../api/read-settings'; @Resolver() export class QueryResolver { @@ -134,7 +135,7 @@ function serializeIndividualCommand( cols: number, includeDetailedStatus: boolean ) { - if (c.commandRunning) { + if (c.commandRunning && c.commandRunning.setCols) { c.commandRunning.setCols(cols); } return { diff --git a/package-scripts.js b/package-scripts.js index 3b9e52b847..64802214ed 100644 --- a/package-scripts.js +++ b/package-scripts.js @@ -12,8 +12,8 @@ function forEachApplication(command) { function affected(affectedCommand) { return { - 'origin-master': `nx affected:${affectedCommand} --base=origin/master --parallel`, - 'upstream-master': `nx affected:${affectedCommand} --base=upstream/master --parallel` + 'origin-master': `nx affected:${affectedCommand} --base=origin/master --parallel --silent --ci`, + 'upstream-master': `nx affected:${affectedCommand} --base=upstream/master --parallel --silent --ci` }; } @@ -236,6 +236,18 @@ module.exports = { }) ) ), + ci: { + ...forEachApplication( + nps.series( + 'nps dev.gen-graphql', + nps.concurrent({ + server: 'ng build APPLICATION --noSourceMap', + client: + 'ng build angular-console --configuration=APPLICATION --noSourceMap --optimization=false --noCommonChunk --aot=false --buildOptimizer=false' + }) + ) + ) + }, dev: { ...forEachApplication( nps.series( diff --git a/package.json b/package.json index 1c13a34591..fb6253e2f2 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "ng": "ng", "start": "nps", "format": "nps format.write", - "build-vs-code": "ng build vscode --maxWorkers=4", + "build-vs-code": "nps build.dev.vscode", "postinstall": "node ./tools/scripts/postinstall.js", "update": "ng update @nrwl/workspace" }, diff --git a/tools/scripts/e2e.js b/tools/scripts/e2e.js index 934210ba52..86e7a62e8f 100644 --- a/tools/scripts/e2e.js +++ b/tools/scripts/e2e.js @@ -7,19 +7,19 @@ let flags = process.argv.slice(2).join(' '); function runE2eTests() { try { - cp.execSync(`ng e2e ${flags}`, { + const x = cp.execSync(`ng e2e ${flags}`, { stdio: [0, 1, 2], env: { ...process.env, CYPRESS_projectsRoot: path.join(__dirname, '../../tmp') } }); + dev.kill(); + process.exit(0); } catch (e) { console.error(e); + dev.kill('SIGKILL'); process.exit(1); - } finally { - dev.kill(); - process.exit(0); } } @@ -34,7 +34,7 @@ try { } catch (e) { console.error(e); if (dev) { - dev.kill(); + dev.kill('SIGKILL'); } process.exit(1); }