From 97667cbbe0b0eb219440639c1be0ae4348ec9576 Mon Sep 17 00:00:00 2001 From: Alon Dahari Date: Wed, 17 Jan 2024 14:19:48 +0000 Subject: [PATCH 1/8] Initial npm plugin setup This is the initial commit to handle integration with NPM, with the official registry. After running the boiler plate I updated the relevant fields, just enough to do manual testing of the basic use case (npm install). Still left to do: - Parse existing .npmrc file - Define when auth is needed - testing --- plugins/npm/access_token.go | 68 ++++++++++++++++++++++++++++++++ plugins/npm/access_token_test.go | 55 ++++++++++++++++++++++++++ plugins/npm/npm.go | 20 ++++++++++ plugins/npm/plugin.go | 22 +++++++++++ 4 files changed, 165 insertions(+) create mode 100644 plugins/npm/access_token.go create mode 100644 plugins/npm/access_token_test.go create mode 100644 plugins/npm/npm.go create mode 100644 plugins/npm/plugin.go diff --git a/plugins/npm/access_token.go b/plugins/npm/access_token.go new file mode 100644 index 000000000..873c279ba --- /dev/null +++ b/plugins/npm/access_token.go @@ -0,0 +1,68 @@ +package npm + +import ( + "context" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/importer" + "github.com/1Password/shell-plugins/sdk/provision" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func AccessToken() schema.CredentialType { + return schema.CredentialType{ + Name: credname.AccessToken, + DocsURL: sdk.URL("https://docs.npmjs.com/creating-and-viewing-access-tokens"), + ManagementURL: sdk.URL("https://www.npmjs.com/settings//tokens/"), + Fields: []schema.CredentialField{ + { + Name: fieldname.Token, + MarkdownDescription: "Token used to authenticate to NPM.", + Secret: true, + Composition: &schema.ValueComposition{ + Length: 36, + Prefix: "npm_", + Charset: schema.Charset{ + Uppercase: true, + Lowercase: true, + Digits: true, + }, + }, + }, + }, + DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), + } +} + +var defaultEnvVarMapping = map[string]sdk.FieldName{ + "NPM_CONFIG_//registry.npmjs.org/:_authToken": fieldname.Token, +} + +// TODO: Check if the platform stores the Access Token in a local config file, and if so, +// implement the function below to add support for importing it. +func TryNPMConfigFile() sdk.Importer { + return importer.TryFile("~/path/to/config/file.yml", func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) { + // var config Config + // if err := contents.ToYAML(&config); err != nil { + // out.AddError(err) + // return + // } + + // if config.Token == "" { + // return + // } + + // out.AddCandidate(sdk.ImportCandidate{ + // Fields: map[sdk.FieldName]string{ + // fieldname.Token: config.Token, + // }, + // }) + }) +} + +// TODO: Implement the config file schema +// type Config struct { +// Token string +// } diff --git a/plugins/npm/access_token_test.go b/plugins/npm/access_token_test.go new file mode 100644 index 000000000..69b2cdf90 --- /dev/null +++ b/plugins/npm/access_token_test.go @@ -0,0 +1,55 @@ +package npm + +import ( + "testing" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/plugintest" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func TestAccessTokenProvisioner(t *testing.T) { + plugintest.TestProvisioner(t, AccessToken().DefaultProvisioner, map[string]plugintest.ProvisionCase{ + "default": { + ItemFields: map[sdk.FieldName]string{ // TODO: Check if this is correct + fieldname.Token: "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Environment: map[string]string{ + "NPM_TOKEN": "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + }, + }, + }, + }) +} + +func TestAccessTokenImporter(t *testing.T) { + plugintest.TestImporter(t, AccessToken().Importer, map[string]plugintest.ImportCase{ + "environment": { + Environment: map[string]string{ // TODO: Check if this is correct + "NPM_TOKEN": "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.Token: "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + }, + }, + }, + }, + // TODO: If you implemented a config file importer, add a test file example in npm/test-fixtures + // and fill the necessary details in the test template below. + "config file": { + Files: map[string]string{ + // "~/path/to/config.yml": plugintest.LoadFixture(t, "config.yml"), + }, + ExpectedCandidates: []sdk.ImportCandidate{ + // { + // Fields: map[sdk.FieldName]string{ + // fieldname.Token: "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + // }, + // }, + }, + }, + }) +} diff --git a/plugins/npm/npm.go b/plugins/npm/npm.go new file mode 100644 index 000000000..3d9405174 --- /dev/null +++ b/plugins/npm/npm.go @@ -0,0 +1,20 @@ +package npm + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" +) + +func NPMCLI() schema.Executable { + return schema.Executable{ + Name: "NPM CLI", + Runs: []string{"npm"}, + DocsURL: sdk.URL("https://docs.npmjs.com/cli"), + Uses: []schema.CredentialUsage{ + { + Name: credname.AccessToken, + }, + }, + } +} diff --git a/plugins/npm/plugin.go b/plugins/npm/plugin.go new file mode 100644 index 000000000..393162a90 --- /dev/null +++ b/plugins/npm/plugin.go @@ -0,0 +1,22 @@ +package npm + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema" +) + +func New() schema.Plugin { + return schema.Plugin{ + Name: "npm", + Platform: schema.PlatformInfo{ + Name: "NPM", + Homepage: sdk.URL("https://npmjs.com"), + }, + Credentials: []schema.CredentialType{ + AccessToken(), + }, + Executables: []schema.Executable{ + NPMCLI(), + }, + } +} From bd53b79a60269fd4a34e0b5b44ae5fbe60d9182b Mon Sep 17 00:00:00 2001 From: Alon Dahari Date: Sun, 21 Jan 2024 17:14:30 +0000 Subject: [PATCH 2/8] Add needsauth specification Add a list of commands that don't require authentication. When running those commands with the cli, the plugin will not try to fetch the token from the vault. I opted for a negative list of commands over a positive one since the latter would end up being much longer, would need to be more complete, and would probably need more updates with time, with newer npm versions. --- plugins/npm/npm.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/plugins/npm/npm.go b/plugins/npm/npm.go index 3d9405174..d66f54cac 100644 --- a/plugins/npm/npm.go +++ b/plugins/npm/npm.go @@ -2,6 +2,7 @@ package npm import ( "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/needsauth" "github.com/1Password/shell-plugins/sdk/schema" "github.com/1Password/shell-plugins/sdk/schema/credname" ) @@ -11,6 +12,30 @@ func NPMCLI() schema.Executable { Name: "NPM CLI", Runs: []string{"npm"}, DocsURL: sdk.URL("https://docs.npmjs.com/cli"), + NeedsAuth: needsauth.IfAll( + // not a complete list of commands that don't + // need auth, but probably the main ones + needsauth.NotForHelpOrVersion(), + needsauth.NotWhenContainsArgs("init"), + needsauth.NotWhenContainsArgs("?"), + needsauth.NotWhenContainsArgs("config"), + needsauth.NotWhenContainsArgs("help-search"), + needsauth.NotWhenContainsArgs("login"), + needsauth.NotWhenContainsArgs("logout"), + needsauth.NotWhenContainsArgs("prune"), + needsauth.NotWhenContainsArgs("shrinkwrap"), + needsauth.NotWhenContainsArgs("start"), + needsauth.NotWhenContainsArgs("run-script"), + needsauth.NotWhenContainsArgs("run"), + needsauth.NotWhenContainsArgs("rum"), + needsauth.NotWhenContainsArgs("urn"), + needsauth.NotWhenContainsArgs("uninstall"), + needsauth.NotWhenContainsArgs("unlink"), + needsauth.NotWhenContainsArgs("remove"), + needsauth.NotWhenContainsArgs("rm"), + needsauth.NotWhenContainsArgs("r"), + needsauth.NotWhenContainsArgs("un"), + ), Uses: []schema.CredentialUsage{ { Name: credname.AccessToken, From 3970931815516164d044acf965f402499ccfc59c Mon Sep 17 00:00:00 2001 From: Alon Dahari Date: Wed, 24 Jan 2024 18:17:45 +0000 Subject: [PATCH 3/8] Parse token from existing .npmrc Update the access_token specfication to find the relevant .npmrc config file and create the 1password secret from it when initializing the plugin. --- plugins/npm/access_token.go | 47 ++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/plugins/npm/access_token.go b/plugins/npm/access_token.go index 873c279ba..f39047440 100644 --- a/plugins/npm/access_token.go +++ b/plugins/npm/access_token.go @@ -2,6 +2,7 @@ package npm import ( "context" + "strings" "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/importer" @@ -9,6 +10,7 @@ import ( "github.com/1Password/shell-plugins/sdk/schema" "github.com/1Password/shell-plugins/sdk/schema/credname" "github.com/1Password/shell-plugins/sdk/schema/fieldname" + "gopkg.in/ini.v1" ) func AccessToken() schema.CredentialType { @@ -33,6 +35,9 @@ func AccessToken() schema.CredentialType { }, }, DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), + Importer: importer.TryAll( + TryNPMConfigFile(), + ), } } @@ -40,29 +45,29 @@ var defaultEnvVarMapping = map[string]sdk.FieldName{ "NPM_CONFIG_//registry.npmjs.org/:_authToken": fieldname.Token, } -// TODO: Check if the platform stores the Access Token in a local config file, and if so, -// implement the function below to add support for importing it. func TryNPMConfigFile() sdk.Importer { - return importer.TryFile("~/path/to/config/file.yml", func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) { - // var config Config - // if err := contents.ToYAML(&config); err != nil { - // out.AddError(err) - // return - // } + return importer.TryFile("~/.npmrc", func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) { + // don't use colon as a delimiter, since it is used in the .npmrc file as a delimiter + // between the scope, registry and configuration key + configs, err := ini.LoadSources(ini.LoadOptions{KeyValueDelimiters: "="}, []byte(contents)) + if err != nil { + out.AddError(err) + } - // if config.Token == "" { - // return - // } + // sections are not supported in .npmrc + section, err := configs.GetSection(ini.DefaultSection) + if err != nil { + out.AddError(err) + } + for _, key := range section.Keys() { + if strings.Contains(key.Name(), "_authToken") { - // out.AddCandidate(sdk.ImportCandidate{ - // Fields: map[sdk.FieldName]string{ - // fieldname.Token: config.Token, - // }, - // }) + out.AddCandidate(sdk.ImportCandidate{ + Fields: map[sdk.FieldName]string{ + fieldname.Token: key.Value(), + }, + }) + } + } }) } - -// TODO: Implement the config file schema -// type Config struct { -// Token string -// } From df153a59e6f4f742e05048d38a5f4daab9c8c03f Mon Sep 17 00:00:00 2001 From: Alon Dahari Date: Thu, 25 Jan 2024 13:08:32 +0000 Subject: [PATCH 4/8] Parse project level cofig file The .npmrc can live in the project root, as well as the home directory. Decided to only support the per-project and per-user config files, since the global and the builtin configs should not contain the auth config probably. Reference: https://docs.npmjs.com/cli/v10/configuring-npm/npmrc#global-config-file --- plugins/npm/access_token.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/plugins/npm/access_token.go b/plugins/npm/access_token.go index f39047440..34525db6d 100644 --- a/plugins/npm/access_token.go +++ b/plugins/npm/access_token.go @@ -2,6 +2,7 @@ package npm import ( "context" + "os" "strings" "github.com/1Password/shell-plugins/sdk" @@ -11,6 +12,7 @@ import ( "github.com/1Password/shell-plugins/sdk/schema/credname" "github.com/1Password/shell-plugins/sdk/schema/fieldname" "gopkg.in/ini.v1" + "path/filepath" ) func AccessToken() schema.CredentialType { @@ -36,7 +38,8 @@ func AccessToken() schema.CredentialType { }, DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), Importer: importer.TryAll( - TryNPMConfigFile(), + TryNPMConfigFile(""), + TryGlobalNPMConfigFile("NPM_CONFIG_USERCONFIG", "~"), ), } } @@ -45,8 +48,17 @@ var defaultEnvVarMapping = map[string]sdk.FieldName{ "NPM_CONFIG_//registry.npmjs.org/:_authToken": fieldname.Token, } -func TryNPMConfigFile() sdk.Importer { - return importer.TryFile("~/.npmrc", func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) { +func TryGlobalNPMConfigFile(env string, defaultPath string) sdk.Importer { + path := os.Getenv(env) + if path == "" { + path = defaultPath + } + return TryNPMConfigFile(path) +} + +func TryNPMConfigFile(path string) sdk.Importer { + return importer.TryFile(filepath.Join(path, ".npmrc"), func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) { + // don't use colon as a delimiter, since it is used in the .npmrc file as a delimiter // between the scope, registry and configuration key configs, err := ini.LoadSources(ini.LoadOptions{KeyValueDelimiters: "="}, []byte(contents)) From 994299a64ac719d101cd012ea64c5fd4ddbf70fc Mon Sep 17 00:00:00 2001 From: Alon Dahari Date: Thu, 25 Jan 2024 13:58:09 +0000 Subject: [PATCH 5/8] Support different registries NPM packages can live in different registries, not just the official one. This commit adds support for them by setting the env var according to the right backend. To support that, I had to use a temp file and not the env var provisioner, since the key name would be different per registry. I also added support for different scopes along the way. I decided to separate the credentials instead of having a single line ithe 1password vault, to allow users to configure their secrets more easily. --- plugins/npm/access_token.go | 58 +++++++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/plugins/npm/access_token.go b/plugins/npm/access_token.go index 34525db6d..0317641c1 100644 --- a/plugins/npm/access_token.go +++ b/plugins/npm/access_token.go @@ -5,6 +5,8 @@ import ( "os" "strings" + "path/filepath" + "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/importer" "github.com/1Password/shell-plugins/sdk/provision" @@ -12,7 +14,6 @@ import ( "github.com/1Password/shell-plugins/sdk/schema/credname" "github.com/1Password/shell-plugins/sdk/schema/fieldname" "gopkg.in/ini.v1" - "path/filepath" ) func AccessToken() schema.CredentialType { @@ -35,8 +36,23 @@ func AccessToken() schema.CredentialType { }, }, }, + { + Name: fieldname.Organization, + MarkdownDescription: "The organization the access token is scoped for.", + Optional: true, + }, + { + Name: fieldname.Host, + MarkdownDescription: "The registry host for the npm packages.", + Optional: true, + }, }, - DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping), + DefaultProvisioner: provision.TempFile(configFile, + provision.Filename(".npmrc"), + provision.AddArgs( + "--userconfig", "{{ .Path }}", + ), + ), Importer: importer.TryAll( TryNPMConfigFile(""), TryGlobalNPMConfigFile("NPM_CONFIG_USERCONFIG", "~"), @@ -44,8 +60,23 @@ func AccessToken() schema.CredentialType { } } -var defaultEnvVarMapping = map[string]sdk.FieldName{ - "NPM_CONFIG_//registry.npmjs.org/:_authToken": fieldname.Token, +func configFile(in sdk.ProvisionInput) ([]byte, error) { + contents := "" + + if org, ok := in.ItemFields[fieldname.Organization]; ok && org != "" { + contents += "@" + strings.Trim(org, "@") + ":" + } + if host, ok := in.ItemFields[fieldname.Host]; ok && host != "" { + contents += "//" + strings.Trim(host, "/") + "/:" + } + + contents += "_authToken=" + + if token, ok := in.ItemFields[fieldname.Token]; ok { + contents += token + } + + return []byte(contents), nil } func TryGlobalNPMConfigFile(env string, defaultPath string) sdk.Importer { @@ -74,10 +105,27 @@ func TryNPMConfigFile(path string) sdk.Importer { for _, key := range section.Keys() { if strings.Contains(key.Name(), "_authToken") { + keyParts := strings.Split(key.Name(), ":") + + registry := "" + scope := "" + hint := "" + if len(keyParts) == 2 { + registry = strings.Trim(keyParts[0], "/") + hint = registry + } else if len(keyParts) == 3 { + registry = strings.Trim(keyParts[1], "/") + scope = strings.Trim(keyParts[0], "@") + hint = "@" + scope + ":" + registry + } + out.AddCandidate(sdk.ImportCandidate{ Fields: map[sdk.FieldName]string{ - fieldname.Token: key.Value(), + fieldname.Token: key.Value(), + fieldname.Host: registry, + fieldname.Organization: scope, }, + NameHint: importer.SanitizeNameHint(hint), }) } } From 940bcfaacd4e919b789e62d5f3c8718ab4fc0864 Mon Sep 17 00:00:00 2001 From: Alon Dahari Date: Thu, 25 Jan 2024 21:21:15 +0000 Subject: [PATCH 6/8] Default registry to npmjs.org Let's support best practice by defaulting to the official npm registry if the secret doesn't contain a host. --- plugins/npm/access_token.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/npm/access_token.go b/plugins/npm/access_token.go index 0317641c1..26413932e 100644 --- a/plugins/npm/access_token.go +++ b/plugins/npm/access_token.go @@ -68,6 +68,8 @@ func configFile(in sdk.ProvisionInput) ([]byte, error) { } if host, ok := in.ItemFields[fieldname.Host]; ok && host != "" { contents += "//" + strings.Trim(host, "/") + "/:" + } else { + contents += "//registry.npmjs.org/:" } contents += "_authToken=" From 5e037aace4dfe7a53f7ed663795f53c707544484 Mon Sep 17 00:00:00 2001 From: Alon Dahari Date: Thu, 25 Jan 2024 21:29:17 +0000 Subject: [PATCH 7/8] Add test fixtures This will be useful when testing the access token --- plugins/npm/test-fixtures/.npmrc-custom-registry | 1 + plugins/npm/test-fixtures/.npmrc-default-registry | 1 + plugins/npm/test-fixtures/.npmrc-scoped | 1 + 3 files changed, 3 insertions(+) create mode 100644 plugins/npm/test-fixtures/.npmrc-custom-registry create mode 100644 plugins/npm/test-fixtures/.npmrc-default-registry create mode 100644 plugins/npm/test-fixtures/.npmrc-scoped diff --git a/plugins/npm/test-fixtures/.npmrc-custom-registry b/plugins/npm/test-fixtures/.npmrc-custom-registry new file mode 100644 index 000000000..7fd6cdab4 --- /dev/null +++ b/plugins/npm/test-fixtures/.npmrc-custom-registry @@ -0,0 +1 @@ +//my.registry.com/:_authToken=npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE diff --git a/plugins/npm/test-fixtures/.npmrc-default-registry b/plugins/npm/test-fixtures/.npmrc-default-registry new file mode 100644 index 000000000..c9e04c5ab --- /dev/null +++ b/plugins/npm/test-fixtures/.npmrc-default-registry @@ -0,0 +1 @@ +//registry.npmjs.org/:_authToken=npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE diff --git a/plugins/npm/test-fixtures/.npmrc-scoped b/plugins/npm/test-fixtures/.npmrc-scoped new file mode 100644 index 000000000..b78c49745 --- /dev/null +++ b/plugins/npm/test-fixtures/.npmrc-scoped @@ -0,0 +1 @@ +@op://my.registry.com/:_authToken=npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE From ff754bba835037e9d8df5e42ab7ca9eb0000be9f Mon Sep 17 00:00:00 2001 From: Alon Dahari Date: Thu, 25 Jan 2024 21:49:08 +0000 Subject: [PATCH 8/8] Test access token Add some tests around importing and provisioning the credentials. --- plugins/npm/access_token_test.go | 61 ++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/plugins/npm/access_token_test.go b/plugins/npm/access_token_test.go index 69b2cdf90..3a3209a38 100644 --- a/plugins/npm/access_token_test.go +++ b/plugins/npm/access_token_test.go @@ -2,22 +2,48 @@ package npm import ( "testing" - + "github.com/1Password/shell-plugins/sdk" "github.com/1Password/shell-plugins/sdk/plugintest" "github.com/1Password/shell-plugins/sdk/schema/fieldname" ) - + func TestAccessTokenProvisioner(t *testing.T) { plugintest.TestProvisioner(t, AccessToken().DefaultProvisioner, map[string]plugintest.ProvisionCase{ "default": { - ItemFields: map[sdk.FieldName]string{ // TODO: Check if this is correct + ItemFields: map[sdk.FieldName]string{ fieldname.Token: "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", }, ExpectedOutput: sdk.ProvisionOutput{ - Environment: map[string]string{ - "NPM_TOKEN": "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + Files: map[string]sdk.OutputFile{ + "/tmp/.npmrc": {Contents: []byte("//registry.npmjs.org/:_authToken=npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE")}, }, + CommandLine: []string{"--userconfig", "/tmp/.npmrc"}, + }, + }, + "custom registry": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Token: "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + fieldname.Host: "my.registry.com", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Files: map[string]sdk.OutputFile{ + "/tmp/.npmrc": {Contents: []byte("//my.registry.com/:_authToken=npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE")}, + }, + CommandLine: []string{"--userconfig", "/tmp/.npmrc"}, + }, + }, + "scoped": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Token: "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + fieldname.Host: "my.registry.com", + fieldname.Organization: "op", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Files: map[string]sdk.OutputFile{ + "/tmp/.npmrc": {Contents: []byte("@op://my.registry.com/:_authToken=npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE")}, + }, + CommandLine: []string{"--userconfig", "/tmp/.npmrc"}, }, }, }) @@ -25,31 +51,20 @@ func TestAccessTokenProvisioner(t *testing.T) { func TestAccessTokenImporter(t *testing.T) { plugintest.TestImporter(t, AccessToken().Importer, map[string]plugintest.ImportCase{ - "environment": { - Environment: map[string]string{ // TODO: Check if this is correct - "NPM_TOKEN": "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + "config file": { + Files: map[string]string{ + "~/.npmrc": plugintest.LoadFixture(t, ".npmrc-default-registry"), }, ExpectedCandidates: []sdk.ImportCandidate{ { + NameHint: "registry.npmjs.org", Fields: map[sdk.FieldName]string{ - fieldname.Token: "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + fieldname.Token: "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", + fieldname.Host: "registry.npmjs.org", + fieldname.Organization: "", }, }, }, }, - // TODO: If you implemented a config file importer, add a test file example in npm/test-fixtures - // and fill the necessary details in the test template below. - "config file": { - Files: map[string]string{ - // "~/path/to/config.yml": plugintest.LoadFixture(t, "config.yml"), - }, - ExpectedCandidates: []sdk.ImportCandidate{ - // { - // Fields: map[sdk.FieldName]string{ - // fieldname.Token: "npm_4F1h0xp0lBn0XvZ4RGfpnpoawECBMEXAMPLE", - // }, - // }, - }, - }, }) }