diff --git a/src/configureWorkspace/configure.ts b/src/configureWorkspace/configure.ts index 08a8f4bbf6..d52d486fa1 100644 --- a/src/configureWorkspace/configure.ts +++ b/src/configureWorkspace/configure.ts @@ -24,12 +24,11 @@ import { ConfigureTelemetryProperties, genCommonDockerIgnoreFile, getSubfolderDe import { openFilesIfRequired, registerScaffolder, scaffold, Scaffolder, ScaffolderContext, ScaffoldFile } from './scaffolding'; export interface PackageInfo { - npmStart: boolean; // has npm start - cmd: string; - fullCommand: string; // full command + cmd: string | string[]; author: string; version: string; artifactName: string; + main?: string; } interface JsonPackageContents { @@ -155,12 +154,11 @@ async function getPackageJson(folderPath: string): Promise { function getDefaultPackageInfo(): PackageInfo { return { - npmStart: true, - fullCommand: 'npm start', - cmd: 'npm start', + cmd: ['npm', 'start'], author: 'author', version: '0.0.1', - artifactName: '' + artifactName: '', + main: 'index.js', }; } @@ -175,15 +173,15 @@ async function readPackageJson(folderPath: string): Promise<{ packagePath?: stri const json = JSON.parse(fse.readFileSync(packagePath, 'utf8')); if (json.scripts && typeof json.scripts.start === "string") { - packageInfo.npmStart = true; - packageInfo.fullCommand = json.scripts.start; - packageInfo.cmd = 'npm start'; + packageInfo.cmd = ['npm', 'start']; + + const matches = /node (.+)/i.exec(json.scripts.start); + if (matches?.[1]) { + packageInfo.main = matches[1]; + } } else if (typeof json.main === "string") { - packageInfo.npmStart = false; - packageInfo.fullCommand = 'node' + ' ' + json.main; - packageInfo.cmd = packageInfo.fullCommand; - } else { - packageInfo.fullCommand = ''; + packageInfo.cmd = ['node', json.main]; + packageInfo.main = json.main; } if (typeof json.author === "string") { diff --git a/src/configureWorkspace/configureGo.ts b/src/configureWorkspace/configureGo.ts index 664c881a3c..83c425c078 100644 --- a/src/configureWorkspace/configureGo.ts +++ b/src/configureWorkspace/configureGo.ts @@ -45,7 +45,7 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], _: Partial): string { return `version: '3.4' services: diff --git a/src/configureWorkspace/configureJava.ts b/src/configureWorkspace/configureJava.ts index d1c8473c9f..fcedf65662 100644 --- a/src/configureWorkspace/configureJava.ts +++ b/src/configureWorkspace/configureJava.ts @@ -40,7 +40,7 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], _: Partial): string { return `version: '3.4' services: diff --git a/src/configureWorkspace/configureNode.ts b/src/configureWorkspace/configureNode.ts index 9f7038e721..b2cda4a0e8 100644 --- a/src/configureWorkspace/configureNode.ts +++ b/src/configureWorkspace/configureNode.ts @@ -18,9 +18,16 @@ export let configureNode: IPlatformGeneratorInfo = { initializeForDebugging, }; -function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { cmd, author, version, artifactName }: Partial): string { +function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { cmd }: Partial): string { let exposeStatements = getExposeStatements(ports); + let cmdDirective: string; + if (Array.isArray(cmd)) { + cmdDirective = `CMD ${toCMDArray(cmd)}`; + } else { + cmdDirective = `CMD ${cmd}`; + } + return `FROM node:12.18-alpine ENV NODE_ENV production WORKDIR /usr/src/app @@ -28,7 +35,7 @@ COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"] RUN npm install --production --silent && mv node_modules ../ COPY . . ${exposeStatements} -CMD ${cmd}`; +${cmdDirective}`; } function genDockerCompose(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[]): string { @@ -43,14 +50,14 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { cmd, main }: Partial): string { const inspectConfig = '--inspect=0.0.0.0:9229'; - const cmdArray: string[] = cmd.split(' '); - if (cmdArray[0].toLowerCase() === 'node') { - cmdArray.splice(1, 0, inspectConfig); - cmd = `command: ${cmdArray.join(' ')}`; + + let cmdDirective: string; + if (main) { + cmdDirective = `command: ${toCMDArray(['node', inspectConfig, main])}`; } else { - cmd = `## set your startup file here\n command: node ${inspectConfig} index.js`; + cmdDirective = `## set your startup file here\n command: ["node", "${inspectConfig}", "index.js"]`; } return `version: '3.4' @@ -62,7 +69,7 @@ services: environment: NODE_ENV: development ${getComposePorts(ports, 9229)} - ${cmd}`; + ${cmdDirective}`; } async function initializeForDebugging(context: IActionContext, folder: WorkspaceFolder, platformOS: PlatformOS, dockerfile: string, packageInfo: PackageInfo): Promise { @@ -75,3 +82,13 @@ async function initializeForDebugging(context: IActionContext, folder: Workspace await dockerDebugScaffoldingProvider.initializeNodeForDebugging(scaffoldContext); } + +function toCMDArray(cmdArray: string[]): string { + return `[${cmdArray.map(part => { + if (part.startsWith('"') && part.endsWith('"')) { + return part; + } + + return `"${part}"`; + }).join(', ')}]`; +} diff --git a/src/configureWorkspace/configureOther.ts b/src/configureWorkspace/configureOther.ts index 48fb9ae717..52c7222da2 100644 --- a/src/configureWorkspace/configureOther.ts +++ b/src/configureWorkspace/configureOther.ts @@ -17,7 +17,7 @@ function genDockerFile(serviceNameAndRelativePath: string, platform: string, os: return `FROM docker/whalesay:latest LABEL Name=${serviceNameAndRelativePath} Version=${version} RUN apt-get -y update && apt-get install -y fortunes -CMD /usr/games/fortune -a | cowsay +CMD ["sh", "-c", "/usr/games/fortune -a | cowsay"] `; } @@ -31,7 +31,7 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], _: Partial): string { return `version: '3.4' services: diff --git a/src/configureWorkspace/configureRuby.ts b/src/configureWorkspace/configureRuby.ts index 18a266f164..f964bba9ed 100644 --- a/src/configureWorkspace/configureRuby.ts +++ b/src/configureWorkspace/configureRuby.ts @@ -44,7 +44,7 @@ services: ${getComposePorts(ports)}`; } -function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], { fullCommand: cmd }: Partial): string { +function genDockerComposeDebug(serviceNameAndRelativePath: string, platform: string, os: string | undefined, ports: number[], _: Partial): string { return `version: '3.4' services: diff --git a/test/configure.test.ts b/test/configure.test.ts index 858d576ba8..4e21d800bd 100644 --- a/test/configure.test.ts +++ b/test/configure.test.ts @@ -463,19 +463,19 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void ); assertFileContains('Dockerfile', 'EXPOSE 1234'); - assertFileContains('Dockerfile', 'CMD npm start'); + assertFileContains('Dockerfile', 'CMD ["npm", "start"]'); assertFileContains('docker-compose.debug.yml', '1234'); assertFileContains('docker-compose.debug.yml', '9229:9229'); assertFileContains('docker-compose.debug.yml', 'image: testoutput'); assertFileContains('docker-compose.debug.yml', 'NODE_ENV: development'); - assertFileContains('docker-compose.debug.yml', 'command: node --inspect=0.0.0.0:9229 index.js'); + assertFileContains('docker-compose.debug.yml', 'command: ["node", "--inspect=0.0.0.0:9229", "index.js"]'); assertFileContains('docker-compose.yml', '1234'); assertNotFileContains('docker-compose.yml', '9229:9229'); assertFileContains('docker-compose.yml', 'image: testoutput'); assertFileContains('docker-compose.yml', 'NODE_ENV: production'); - assertNotFileContains('docker-compose.yml', 'command: node --inspect=0.0.0.0:9229 index.js'); + assertNotFileContains('docker-compose.yml', 'command: ["node", "--inspect=0.0.0.0:9229", "index.js"]'); assertFileContains('.dockerignore', '.vscode'); }); @@ -494,7 +494,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void ); assertFileContains('Dockerfile', 'EXPOSE 1234'); - assertFileContains('Dockerfile', 'CMD npm start'); + assertFileContains('Dockerfile', 'CMD ["npm", "start"]'); assertFileContains('.dockerignore', '.vscode'); }); @@ -534,19 +534,19 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void ); assertFileContains('Dockerfile', 'EXPOSE 4321'); - assertFileContains('Dockerfile', 'CMD npm start'); + assertFileContains('Dockerfile', 'CMD ["npm", "start"]'); assertFileContains('docker-compose.debug.yml', '4321'); assertFileContains('docker-compose.debug.yml', '9229:9229'); assertFileContains('docker-compose.debug.yml', 'image: testoutput'); assertFileContains('docker-compose.debug.yml', 'NODE_ENV: development'); - assertFileContains('docker-compose.debug.yml', 'command: node --inspect=0.0.0.0:9229 ./bin/www'); + assertFileContains('docker-compose.debug.yml', 'command: ["node", "--inspect=0.0.0.0:9229", "./bin/www"]'); assertFileContains('docker-compose.yml', '4321'); assertNotFileContains('docker-compose.yml', '9229:9229'); assertFileContains('docker-compose.yml', 'image: testoutput'); assertFileContains('docker-compose.yml', 'NODE_ENV: production'); - assertNotFileContains('docker-compose.yml', 'command: node --inspect=0.0.0.0:9229 ./bin/www'); + assertNotFileContains('docker-compose.yml', 'command: ["node", "--inspect=0.0.0.0:9229", "./bin/www"]'); assertFileContains('.dockerignore', '.vscode'); }); @@ -581,7 +581,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void ['package.json', 'Dockerfile', 'docker-compose.debug.yml', 'docker-compose.yml', '.dockerignore'] ); - assertNotFileContains('docker-compose.yml', 'command: node --inspect=0.0.0.0:9229 index.js'); + assertNotFileContains('docker-compose.yml', 'command: ["node", "--inspect=0.0.0.0:9229", "index.js"]'); }); testInEmptyFolder("Without start script", async () => { @@ -614,7 +614,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void ); assertFileContains('Dockerfile', 'EXPOSE 4321'); - assertFileContains('Dockerfile', 'CMD node ./out/dockerExtension'); + assertFileContains('Dockerfile', 'CMD ["node", "./out/dockerExtension"]'); }); }); @@ -1348,7 +1348,7 @@ suite("Configure (Add Docker files to Workspace)", function (this: Suite): void FROM docker/whalesay:latest LABEL Name=testoutput Version=1.2.3 RUN apt-get -y update && apt-get install -y fortunes - CMD /usr/games/fortune -a | cowsay + CMD ["sh", "-c", "/usr/games/fortune -a | cowsay"] `)); assert.strictEqual(composeContents, removeIndentation(` version: '3.4'