-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update npm task with install/publish/custom commands
- Loading branch information
Showing
38 changed files
with
1,320 additions
and
695 deletions.
There are no files selected for viewing
52 changes: 39 additions & 13 deletions
52
Tasks/Npm/Strings/resources.resjson/en-US/resources.resjson
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,43 @@ | ||
{ | ||
"loc.friendlyName": "npm", | ||
"loc.helpMarkDown": "[More Information](https://go.microsoft.com/fwlink/?LinkID=613746)", | ||
"loc.description": "Run an npm command", | ||
"loc.description": "Install and publish npm packages, or run an npm command. Supports npmjs.com and authenticated registries like Package Management.", | ||
"loc.instanceNameFormat": "npm $(command)", | ||
"loc.input.label.cwd": "working folder", | ||
"loc.input.help.cwd": "Working directory where the npm command is run. Defaults to the root of the repo.", | ||
"loc.input.label.command": "npm command", | ||
"loc.input.label.arguments": "arguments", | ||
"loc.input.help.arguments": "Additional arguments passed to npm.", | ||
"loc.messages.InvalidCommand": "Only one command should be used for npm. Use 'Arguments' input for additional arguments.", | ||
"loc.messages.NpmReturnCode": "npm exited with return code: %d", | ||
"loc.messages.NpmFailed": "npm failed with error: %s", | ||
"loc.messages.NpmConfigFailed": "Failed to log the npm config information. Error : %s", | ||
"loc.messages.NpmAuthFailed": "Failed to get the required authentication tokens for npm. Error: %s", | ||
"loc.messages.BuildCredentialsWarn": "Could not determine credentials to use for npm" | ||
} | ||
"loc.group.displayName.customRegistries": "Custom registries and authentication", | ||
"loc.group.displayName.publishRegistries": "Destination registry and authentication", | ||
"loc.input.label.command": "Command", | ||
"loc.input.help.command": "The command and arguments which will be passed to npm for execution.", | ||
"loc.input.label.workingDir": "Working folder with package.json", | ||
"loc.input.help.workingDir": "Path to the folder containing the target package.json and .npmrc files. Select the folder, not the file e.g. \"/packages/mypackage\".", | ||
"loc.input.label.customCommand": "Command and arguments", | ||
"loc.input.help.customCommand": "Custom command to run, e.g. \"dist-tag ls mypackage\".", | ||
"loc.input.label.customRegistry": "Registries to use", | ||
"loc.input.help.customRegistry": "You can either commit a .npmrc file to your source code repository and set its path here or select a registry from VSTS here.", | ||
"loc.input.label.customFeed": "Use packages from this VSTS/TFS registry", | ||
"loc.input.help.customFeed": "Include the selected feed in the generated .npmrc.", | ||
"loc.input.label.customEndpoint": "Credentials for registries outside this account/collection", | ||
"loc.input.help.customEndpoint": "Credentials to use for external registries located in the project's .npmrc. For registries in this account/collection, leave this blank; the build’s credentials are used automatically.", | ||
"loc.input.label.publishRegistry": "Registry location", | ||
"loc.input.help.publishRegistry": "Registry the command will target.", | ||
"loc.input.label.publishFeed": "Target registry", | ||
"loc.input.help.publishFeed": "Select a registry hosted in this account. You must have Package Management installed and licensed to select a registry here.", | ||
"loc.input.label.publishEndpoint": "External Registry", | ||
"loc.input.help.publishEndpoint": "Credentials to use for publishing to an external registry.", | ||
"loc.messages.FoundBuildCredentials": "Found build credentials", | ||
"loc.messages.NoBuildCredentials": "Could not find build credentials", | ||
"loc.messages.UnknownCommand": "Unknown command: %s", | ||
"loc.messages.MultipleProjectConfigs": "More than one project .npmrc found in $s", | ||
"loc.messages.ServiceEndpointNotDefined": "Couldn't find Service Endpoint, make sure the selected endpoint still exists.", | ||
"loc.messages.ServiceEndpointUrlNotDefined": "Couldn't find Url for Service Endpoint, make sure Service Endpoint is correctly configured.", | ||
"loc.messages.SavingFile": "Saving file %s", | ||
"loc.messages.RestoringFile": "Restoring file %s", | ||
"loc.messages.PublishFeed": "Publishing to internal feed", | ||
"loc.messages.PublishExternal": "Publishing to external registry", | ||
"loc.messages.UseFeed": "Using internal feed", | ||
"loc.messages.UseNpmrc": "Using registries in .npmrc", | ||
"loc.messages.PublishRegistry": "Publishing to registry: %s", | ||
"loc.messages.UsingRegistry": "Using registry: %s", | ||
"loc.messages.AddingAuthRegistry": "Adding auth for registry: %s", | ||
"loc.messages.FoundLocalRegistries": "Found %d registries in this account/collection", | ||
"loc.messages.ForcePackagingUrl": "Packaging collection url forced to: %s" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,138 +1,256 @@ | ||
import * as path from 'path'; | ||
import * as assert from 'assert'; | ||
import * as fs from 'fs'; | ||
import * as path from 'path'; | ||
import * as Q from 'q'; | ||
|
||
import * as mockery from 'mockery'; | ||
import * as ttm from 'vsts-task-lib/mock-test'; | ||
import {NpmMockHelper} from './NpmMockHelper'; | ||
|
||
import { NpmMockHelper } from './NpmMockHelper'; | ||
|
||
describe('Npm Task', function () { | ||
before(() => { | ||
mockery.enable({ | ||
useCleanCache: true, | ||
warnOnUnregistered: false | ||
} as mockery.MockeryEnableArgs); | ||
}); | ||
|
||
after(() => { | ||
mockery.disable(); | ||
}); | ||
|
||
beforeEach(() => { | ||
mockery.resetCache(); | ||
}); | ||
|
||
afterEach(() => { | ||
mockery.deregisterAll(); | ||
}); | ||
|
||
/* Current behavior */ | ||
it("should execute 'npm config list' successfully", (done: MochaDone) => { | ||
// custom | ||
it('custom command should return npm version', (done: MochaDone) => { | ||
this.timeout(1000); | ||
let tp = path.join(__dirname, 'test-configlist.js') | ||
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); | ||
let tp = path.join(__dirname, 'custom-version.js'); | ||
let tr = new ttm.MockTestRunner(tp); | ||
|
||
tr.run(); | ||
|
||
assert.equal(tr.invokedToolCount, 3, 'should have run vsts-npm-auth, npm config list and npm command'); | ||
assert(tr.ran(`${NpmMockHelper.NpmCmdPath} config list`), 'it should have run npm'); | ||
assert(tr.stdOutContained('; cli configs'), "should have npm config output"); | ||
assert.equal(tr.errorIssues.length, 0, "should have no errors"); | ||
// This assert is skipped due to a test mocking issue on non windows platforms. | ||
// assert.equal(tr.warningIssues.length, 0, "should have no warnings: " + tr.warningIssues.join(',')); | ||
assert(tr.succeeded, 'should have succeeded'); | ||
assert.equal(tr.invokedToolCount, 2, 'task should have run npm'); | ||
assert(tr.succeeded, 'task should have succeeded'); | ||
assert(tr.stdOutContained('; debug cli configs'), 'should have debug npm config output'); | ||
assert(tr.stdOutContained('; cli configs') === false, 'should not have regular npm config output'); | ||
|
||
done(); | ||
}); | ||
|
||
it('should pass when no arguments are supplied', (done: MochaDone) => { | ||
|
||
// show config | ||
it('should execute \'npm config list\' without debug switch', (done: MochaDone) => { | ||
this.timeout(1000); | ||
let tp = path.join(__dirname, 'test-commandWithoutArguments.js') | ||
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); | ||
let tp = path.join(__dirname, 'config-noDebug.js'); | ||
let tr = new ttm.MockTestRunner(tp); | ||
|
||
tr.run(); | ||
|
||
assert.equal(tr.invokedToolCount, 3, 'should have run npm'); | ||
assert(tr.ran(`${NpmMockHelper.NpmCmdPath} root`), 'it should have run npm'); | ||
assert(tr.stdOutContained(`${NpmMockHelper.FakeWorkingDirectory}`), "should have npm root output - working directory"); | ||
assert(tr.stdOutContained("node_modules"), "should have npm root output - 'node_modules' directory"); | ||
assert.equal(tr.errorIssues.length, 0, "should have no errors"); | ||
// This assert is skipped due to a test mocking issue on non windows platforms. | ||
// assert.equal(tr.warningIssues.length, 0, "should have no warnings: " + tr.warningIssues.join(',')); | ||
assert(tr.succeeded, 'should have succeeded'); | ||
assert.equal(tr.invokedToolCount, 2, 'task should have run npm'); | ||
assert(tr.succeeded, 'task should have succeeded'); | ||
assert(tr.stdOutContained('; cli configs'), 'should have regular npm config output'); | ||
assert(tr.stdOutContained('; debug cli configs') === false, 'should not have debug npm config output'); | ||
|
||
done(); | ||
}); | ||
|
||
it('should fail when command contains spaces', (done: MochaDone) => { | ||
|
||
// install command | ||
it('should fail when npm fails', (done: MochaDone) => { | ||
this.timeout(1000); | ||
let tp = path.join(__dirname, 'test-commandContainsSpaces.js') | ||
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); | ||
let tp = path.join(__dirname, 'install-npmFailure.js'); | ||
let tr = new ttm.MockTestRunner(tp); | ||
|
||
tr.run(); | ||
|
||
assert.equal(tr.invokedToolCount, 0, 'should not have run npm'); | ||
assert(tr.failed, 'should have failed'); | ||
assert(tr.failed, 'task should have failed'); | ||
|
||
done(); | ||
}); | ||
it('should fail when task fails', (done: MochaDone) => { | ||
|
||
it ('install using local feed', (done: MochaDone) => { | ||
this.timeout(1000); | ||
let tp = path.join(__dirname, 'test-npmFailure.js') | ||
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); | ||
let tp = path.join(__dirname, 'install-feed.js'); | ||
let tr = new ttm.MockTestRunner(tp); | ||
|
||
tr.run(); | ||
|
||
assert.equal(tr.invokedToolCount, 3, 'should have run npm'); | ||
assert(tr.failed, 'should have failed'); | ||
assert.equal(tr.invokedToolCount, 2, 'task should have run npm'); | ||
assert(tr.stdOutContained('npm install successful'), 'npm should have installed the package'); | ||
assert(tr.succeeded, 'task should have succeeded'); | ||
|
||
done(); | ||
}); | ||
|
||
/* Deprecated behavior */ | ||
it("should execute 'npm config list' successfully (deprecated task)", (done: MochaDone) => { | ||
it ('install using npmrc', (done: MochaDone) => { | ||
this.timeout(1000); | ||
let tp = path.join(__dirname, 'test-configlist-deprecated.js') | ||
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); | ||
let tp = path.join(__dirname, 'install-npmrc.js'); | ||
let tr = new ttm.MockTestRunner(tp); | ||
|
||
tr.run(); | ||
|
||
assert.equal(tr.invokedToolCount, 1, 'should have run npm'); | ||
assert(tr.ran(`${NpmMockHelper.NpmCmdPath} config list`), 'it should have run npm'); | ||
assert(tr.stdOutContained('; cli configs'), "should have npm config output"); | ||
assert.equal(tr.errorIssues.length, 0, "should have no errors"); | ||
assert.equal(tr.warningIssues.length, 0, "should have no warnings: " + tr.warningIssues.join(',')); | ||
assert(tr.succeeded, 'should have succeeded'); | ||
assert.equal(tr.invokedToolCount, 2, 'task should have run npm'); | ||
assert(tr.stdOutContained('npm install successful'), 'npm should have installed the package'); | ||
assert(tr.succeeded, 'task should have succeeded'); | ||
|
||
done(); | ||
}); | ||
|
||
it('should pass when no arguments are supplied (deprecated task)', (done: MochaDone) => { | ||
|
||
// publish | ||
it ('publish using feed', (done: MochaDone) => { | ||
this.timeout(1000); | ||
let tp = path.join(__dirname, 'test-commandWithoutArguments-deprecated.js') | ||
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); | ||
let tp = path.join(__dirname, 'publish-feed.js'); | ||
let tr = new ttm.MockTestRunner(tp); | ||
|
||
tr.run(); | ||
|
||
assert.equal(tr.invokedToolCount, 1, 'should have run npm'); | ||
assert(tr.ran(`${NpmMockHelper.NpmCmdPath} root`), 'it should have run npm'); | ||
assert(tr.stdOutContained(`${NpmMockHelper.FakeWorkingDirectory}`), "should have npm root output - working directory"); | ||
assert(tr.stdOutContained("node_modules"), "should have npm root output - 'node_modules' directory"); | ||
assert.equal(tr.errorIssues.length, 0, "should have no errors"); | ||
assert.equal(tr.warningIssues.length, 0, "should have no warnings: " + tr.warningIssues.join(',')); | ||
assert(tr.succeeded, 'should have succeeded'); | ||
assert.equal(tr.invokedToolCount, 2, 'task should have run npm'); | ||
assert(tr.stdOutContained('npm publish successful'), 'npm should have installed the package'); | ||
assert(tr.succeeded, 'task should have succeeded'); | ||
|
||
done(); | ||
}); | ||
it('should fail when command contains spaces (deprecated task)', (done: MochaDone) => { | ||
|
||
it ('publish using external registry', (done: MochaDone) => { | ||
this.timeout(1000); | ||
let tp = path.join(__dirname, 'test-commandContainsSpaces-deprecated.js') | ||
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); | ||
let tp = path.join(__dirname, 'publish-external.js'); | ||
let tr = new ttm.MockTestRunner(tp); | ||
|
||
tr.run(); | ||
|
||
assert.equal(tr.invokedToolCount, 0, 'should not have run npm'); | ||
assert(tr.failed, 'should have failed'); | ||
assert.equal(tr.invokedToolCount, 2, 'task should have run npm'); | ||
assert(tr.stdOutContained('npm publish successful'), 'npm should have installed the package'); | ||
assert(tr.succeeded, 'task should have succeeded'); | ||
|
||
done(); | ||
}); | ||
|
||
it('should fail when task fails (deprecated task)', (done: MochaDone) => { | ||
this.timeout(1000); | ||
let tp = path.join(__dirname, 'test-npmFailure-deprecated.js') | ||
let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp); | ||
|
||
tr.run(); | ||
// util | ||
it('gets npm registries', (done: MochaDone) => { | ||
let mockTask = { | ||
writeFile: (file: string, data: string | Buffer) => { | ||
// no-op | ||
} | ||
}; | ||
mockery.registerMock('vsts-task-lib/task', mockTask); | ||
let npmrc = `registry=http://example.com | ||
always-auth=true | ||
@scoped:registry=http://scoped.com | ||
//scoped.com/:_authToken=thisIsASecretToken | ||
@scopedTwo:registry=http://scopedTwo.com | ||
; some comments | ||
@scoped:always-auth=true | ||
# more comments`; | ||
|
||
let mockFs = { | ||
readFileSync: (path: string) => npmrc | ||
}; | ||
mockery.registerMock('fs', mockFs); | ||
|
||
assert.equal(tr.invokedToolCount, 1, 'should have run npm'); | ||
assert(tr.failed, 'should have failed'); | ||
let npmrcParser = require('../npmrcparser'); | ||
let registries = npmrcParser.GetRegistries(''); | ||
|
||
assert.equal(registries.length, 3); | ||
assert.equal(registries[0], 'http://example.com/'); | ||
assert.equal(registries[1], 'http://scoped.com/'); | ||
assert.equal(registries[2], 'http://scopedTwo.com/'); | ||
|
||
done(); | ||
}); | ||
|
||
it('gets feed id from VSTS registry', (done: MochaDone) => { | ||
mockery.registerMock('vsts-task-lib/task', {}); | ||
let util = require('../util'); | ||
|
||
assert.equal(util.getFeedIdFromRegistry( | ||
'http://account.visualstudio.com/_packaging/feedId/npm/registry'), | ||
'feedId'); | ||
assert.equal(util.getFeedIdFromRegistry( | ||
'http://account.visualstudio.com/_packaging/feedId/npm/registry/'), | ||
'feedId'); | ||
assert.equal(util.getFeedIdFromRegistry( | ||
'http://TFSSERVER/_packaging/feedId/npm/registry'), | ||
'feedId'); | ||
assert.equal(util.getFeedIdFromRegistry( | ||
'http://TFSSERVER:1234/_packaging/feedId/npm/registry'), | ||
'feedId'); | ||
|
||
done(); | ||
}); | ||
}); | ||
|
||
it('gets correct packaging Url', () => { | ||
let mockTask = { | ||
getVariable: (v: string) => { | ||
if (v === 'System.TeamFoundationCollectionUri') { | ||
return 'http://example.visualstudio.com'; | ||
} | ||
}, | ||
debug: (message: string) => { | ||
// no-op | ||
}, | ||
loc: (key: string) => { | ||
// no-op | ||
} | ||
}; | ||
mockery.registerMock('vsts-task-lib/task', mockTask); | ||
let util = require('../util'); | ||
|
||
return util.getPackagingCollectionUrl().then(u => { | ||
assert.equal(u, 'http://example.pkgs.visualstudio.com/'.toLowerCase()); | ||
|
||
mockTask.getVariable = (v: string) => 'http://TFSSERVER.com/'; | ||
return util.getPackagingCollectionUrl().then(u => { | ||
assert.equal(u, 'http://TFSSERVER.com/'.toLowerCase()); | ||
|
||
mockTask.getVariable = (v: string) => 'http://serverWithPort:1234'; | ||
return util.getPackagingCollectionUrl().then(u => { | ||
assert.equal(u, 'http://serverWithPort:1234/'.toLowerCase()); | ||
|
||
return; | ||
}); | ||
}); | ||
}); | ||
}); | ||
|
||
it('gets correct local registries', () => { | ||
let mockParser = { | ||
GetRegistries: (npmrc: string) => [ | ||
'http://registry.com/npmRegistry/', | ||
'http://example.pkgs.visualstudio.com/npmRegistry/', | ||
'http://localTFSServer/npmRegistry/' | ||
] | ||
}; | ||
mockery.registerMock('./npmrcparser', mockParser); | ||
let mockTask = { | ||
getVariable: (v: string) => { | ||
if (v === 'System.TeamFoundationCollectionUri') { | ||
return 'http://example.visualstudio.com'; | ||
} | ||
}, | ||
debug: (message: string) => { | ||
// no-op | ||
}, | ||
loc: (key: string) => { | ||
// no-op | ||
} | ||
}; | ||
mockery.registerMock('vsts-task-lib/task', mockTask); | ||
let util = require('../util'); | ||
|
||
return util.getLocalRegistries('').then((registries: string[]) => { | ||
assert.equal(registries.length, 1); | ||
assert.equal(registries[0], 'http://example.pkgs.visualstudio.com/npmRegistry/'); | ||
|
||
mockTask.getVariable = () => 'http://localTFSServer/'; | ||
return util.getLocalRegistries('').then((registries: string[]) => { | ||
assert.equal(registries.length, 1); | ||
assert.equal(registries[0], 'http://localTFSServer/npmRegistry/'); | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.