Skip to content

Commit fc003bd

Browse files
authored
Merge pull request #2 from seek-oss/token-from-file
Add env and file options to extend support for different injection mechanisms
2 parents d24cfb6 + 44f3d62 commit fc003bd

File tree

4 files changed

+226
-18
lines changed

4 files changed

+226
-18
lines changed

README.md

+55-10
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,84 @@
33
A [Buildkite plugin](https://buildkite.com/docs/agent/v3/plugins) to allow pipeline steps to easily install
44
private packages from an [npm](https://www.npmjs.com) repository.
55

6-
Note this plugin should work equally well despite your personal preferences for either `yarn` or `npm`.
6+
Note this plugin should work equally well despite any personal preferences for either `yarn` or `npm`.
77

88
## Example
99

10-
The following pipeline will run `yarn install` (and presumably some private packages).
10+
To read the value from an environment variable named `MY_TOKEN` when the plugin executes, use the `env` fiels.
1111

1212
```yml
1313
steps:
1414
- command: yarn install
1515
plugins:
16-
seek-oss/private-npm#v1.0.1:
17-
token: ${NPM_TOKEN}
16+
seek-oss/private-npm#v1.1.1:
17+
env: "MY_TOKEN"
1818
```
1919
20+
To read the value from a file named `my_token_file`, use the `file` field.
21+
22+
```yml
23+
steps:
24+
- command: yarn install
25+
plugins:
26+
seek-oss/private-npm#v1.1.1:
27+
file: "my_token_file"
28+
```
29+
30+
Alternatively you can read the token directly from any value exposed toxs your `pipeline.yml` file. However, this
31+
approach is discoraged in favour of using with the `env` or `file` fields. This functionality remains in the interest
32+
of backwards compatibility.
33+
34+
```yml
35+
steps:
36+
- command: yarn install
37+
plugins:
38+
seek-oss/private-npm#v1.1.1:
39+
token: ${MY_TOKEN}
40+
```
41+
42+
2043
You can also specify a custom npm registry if you are using your own mirror.
2144

2245
```yml
2346
steps:
2447
- command: yarn install
2548
plugins:
26-
seek-oss/private-npm#v1.0.1:
27-
token: ${NPM_TOKEN}
49+
seek-oss/private-npm#v1.1.1:
50+
env: "MY_TOKEN"
2851
registry: //myprivatenpm.com/
2952
```
3053

3154
## Configuration
3255

33-
### `token` (required)
34-
The value of the NPM token.
56+
> **NOTE** Even thought `env`, `file` and `token` are described as optional, _at least one must be set_ or the plugin
57+
> will fail.
58+
59+
### `env` (optional)
60+
61+
The value of the NPM token will be read from the agent environment when the plugin executes. This is useful in working
62+
around cases where eager binding of variables in `pipeline.yml` means some variables are not present in the
63+
environment when the configuration file is parsed.
64+
65+
### `file` (optional)
66+
67+
The value of the NPM token will be read from a file on the agent when the plugin executes. This is useful when working
68+
with secret that are created as files on the filesystem when a build is initiated.
69+
70+
### `token` (optional)
71+
72+
The value of the NPM token will be read from a variable which is available to the Buildkite YAML parsing context.
73+
This value is interpolated when the YAML configuration is parsed by the Buildgent agent and provided to the plugin "as
74+
is".
3575

36-
Example: `${NPM_TOKEN}`
76+
Example: `${MY_TOKEN}`
77+
> **NOTE:** Don't put your tokens into source control. Don't use web interfaces you don't control to inject them into
78+
> your environment either. Rather use a Secrets Manager. If you are an AWS user, perhaps consider the
79+
> [aws-sm-buildkite-plugin](https://github.com/seek-oss/aws-sm-buildkite-plugin) which works well with this plugin.
3780

38-
> *NOTE* It's bad security practise to put your secrets into source control. A better idea is to use environment variables.
81+
> **NOTE:** There is anecdotal evidence to suggest that using `NPM_TOKEN` as the variable name containing the
82+
> token can intermittently cause the token to become empty. It is advised to use a different name as has been done in
83+
> these docs.
3984

4085
### `registry` (optional)
4186
The path to a private npm repository. Please ensure you supply the trailing `/`!

hooks/pre-command

+28-4
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,36 @@
22
set -euo pipefail
33
IFS=$'\n\t'
44

5-
echo '--- Setting up access for :no_entry_sign: :npm: :package:'
6-
75
REGISTRY=${BUILDKITE_PLUGIN_PRIVATE_NPM_REGISTRY:-'//registry.npmjs.org/'}
8-
NPM_TOKEN=${BUILDKITE_PLUGIN_PRIVATE_NPM_TOKEN}
6+
TOKEN=${BUILDKITE_PLUGIN_PRIVATE_NPM_TOKEN:-''}
7+
FILE=${BUILDKITE_PLUGIN_PRIVATE_NPM_FILE:-''}
8+
ENV=${BUILDKITE_PLUGIN_PRIVATE_NPM_ENV:-''}
9+
10+
if { [[ -n "${FILE}" ]] && [[ -n "${ENV}" ]]; } \
11+
|| { [[ -n "${FILE}" ]] && [[ -n "${TOKEN}" ]]; } \
12+
|| { [[ -n "${TOKEN}" ]] && [[ -n "${ENV}" ]]; }
13+
then
14+
echo ':no_entry_sign: :npm: :package: Failed! Only one of file, env or token parameters may be set'
15+
exit 1
16+
fi
17+
18+
if [[ -n "${FILE}" ]]
19+
then
20+
TOKEN=$(cat "${FILE}")
21+
elif [[ -n "${ENV}" ]]
22+
then
23+
TOKEN="${!ENV}"
24+
fi
25+
26+
if [[ -z $TOKEN ]]
27+
then
28+
echo ':no_entry_sign: :npm: :package: Failed! A valid NPM_TOKEN could not be determined'
29+
exit 1
30+
fi
31+
32+
echo '--- Setting up access for :no_entry_sign: :npm: :package:'
933

1034
cat > .npmrc << EOF
11-
${REGISTRY}:_authToken=${NPM_TOKEN}
35+
${REGISTRY}:_authToken=${TOKEN}
1236
save-exact=true
1337
EOF

plugin.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,8 @@ configuration:
1010
type: string
1111
token:
1212
type: string
13-
required:
14-
- token
13+
env:
14+
type: string
15+
file:
16+
type: string
17+

tests/pre-command.bats

+138-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@
33
load "$BATS_PATH/load.bash"
44
teardown() {
55
rm -f .npmrc
6+
unset BUILDKITE_PLUGIN_PRIVATE_NPM_ENV
7+
unset BUILDKITE_PLUGIN_PRIVATE_NPM_TOKEN
8+
unset BUILDKITE_PLUGIN_PRIVATE_NPM_FILE
9+
unset BUILDKITE_PLUGIN_PRIVATE_NPM_REGISTRY
10+
unset MY_ENV_VAR
11+
rm -fr my_token_file
12+
rm -fr my_empty_file
613
}
714

815

@@ -16,7 +23,65 @@ teardown() {
1623
assert_equal "$(head -n1 .npmrc)" '//registry.npmjs.org/:_authToken=abc123'
1724
}
1825

19-
@test "crates a npmrc file with supplied registry path and token" {
26+
@test "reads the token from a file if the file parameter is used" {
27+
export BUILDKITE_PLUGIN_PRIVATE_NPM_FILE='my_token_file'
28+
echo 'abc123' > my_token_file
29+
30+
run $PWD/hooks/pre-command
31+
32+
assert_success
33+
assert [ -e '.npmrc' ]
34+
assert_equal "$(head -n1 .npmrc)" '//registry.npmjs.org/:_authToken=abc123'
35+
}
36+
37+
@test "fails if the file parameter is used but no file exists" {
38+
export BUILDKITE_PLUGIN_PRIVATE_NPM_FILE='my_missing_file'
39+
40+
run $PWD/hooks/pre-command
41+
42+
assert_failure
43+
}
44+
45+
@test "fails if the file parameter is used and the file exists but is empty" {
46+
export BUILDKITE_PLUGIN_PRIVATE_NPM_FILE='my_empty_file'
47+
48+
touch my_empty_file
49+
50+
run $PWD/hooks/pre-command
51+
52+
assert_failure
53+
}
54+
55+
@test "reads the token from the environment if the env parameter is used" {
56+
export BUILDKITE_PLUGIN_PRIVATE_NPM_ENV='MY_ENV_VAR'
57+
export MY_ENV_VAR='abc123'
58+
59+
run $PWD/hooks/pre-command
60+
61+
assert_success
62+
assert [ -e '.npmrc' ]
63+
assert_equal "$(head -n1 .npmrc)" '//registry.npmjs.org/:_authToken=abc123'
64+
}
65+
66+
@test "fails if the env parameter is used but no such variable is defined" {
67+
export BUILDKITE_PLUGIN_PRIVATE_NPM_ENV='MY_MISSING_VAR'
68+
69+
run $PWD/hooks/pre-command
70+
71+
assert_failure
72+
}
73+
74+
@test "fails if the env parameter is used but the value of the variable is empty" {
75+
export BUILDKITE_PLUGIN_PRIVATE_NPM_ENV='MY_EMPTY_VAR'
76+
77+
export MY_EMPTY_VAR=""
78+
79+
run $PWD/hooks/pre-command
80+
81+
assert_failure
82+
}
83+
84+
@test "creates a npmrc file with supplied registry path and token" {
2085
export BUILDKITE_PLUGIN_PRIVATE_NPM_TOKEN='abc123'
2186
export BUILDKITE_PLUGIN_PRIVATE_NPM_REGISTRY='//myprivateregistry.org/'
2287

@@ -27,9 +92,80 @@ teardown() {
2792
assert_equal "$(head -n1 .npmrc)" '//myprivateregistry.org/:_authToken=abc123'
2893
}
2994

30-
@test "the command fails if the token is not set" {
95+
@test "creates a npmrc file with supplied registry path and env" {
96+
export BUILDKITE_PLUGIN_PRIVATE_NPM_ENV='MY_ENV_VAR'
97+
export MY_ENV_VAR='abc123'
98+
export BUILDKITE_PLUGIN_PRIVATE_NPM_REGISTRY='//myprivateregistry.org/'
99+
100+
run $PWD/hooks/pre-command
101+
102+
assert_success
103+
assert [ -e '.npmrc' ]
104+
assert_equal "$(head -n1 .npmrc)" '//myprivateregistry.org/:_authToken=abc123'
105+
}
106+
107+
@test "creates a npmrc file with supplied registry path and file" {
108+
export BUILDKITE_PLUGIN_PRIVATE_NPM_FILE='my_token_file'
109+
echo 'abc123' > my_token_file
110+
export BUILDKITE_PLUGIN_PRIVATE_NPM_REGISTRY='//myprivateregistry.org/'
111+
112+
run $PWD/hooks/pre-command
113+
114+
assert_success
115+
assert [ -e '.npmrc' ]
116+
assert_equal "$(head -n1 .npmrc)" '//myprivateregistry.org/:_authToken=abc123'
117+
}
118+
119+
@test "the command fails if none of the fields are not set" {
120+
run $PWD/hooks/pre-command
121+
122+
assert_failure
123+
refute [ -e '.npmrc' ]
124+
}
125+
126+
# There is an exclusive relationship between file, env, and token. These tests ensure only value is set and fail with
127+
# a meaninful message otherwise
128+
@test "fails if env and file are both set" {
129+
export BUILDKITE_PLUGIN_PRIVATE_NPM_FILE='my_token_file'
130+
export BUILDKITE_PLUGIN_PRIVATE_NPM_ENV='MY_ENV_VAR'
131+
132+
run $PWD/hooks/pre-command
133+
134+
assert_failure
135+
assert_output ':no_entry_sign: :npm: :package: Failed! Only one of file, env or token parameters may be set'
136+
refute [ -e '.npmrc' ]
137+
}
138+
139+
@test "fails if token and file are both set" {
140+
export BUILDKITE_PLUGIN_PRIVATE_NPM_FILE='my_token_file'
141+
export BUILDKITE_PLUGIN_PRIVATE_NPM_TOKEN='abc123'
142+
143+
run $PWD/hooks/pre-command
144+
145+
assert_failure
146+
assert_output ':no_entry_sign: :npm: :package: Failed! Only one of file, env or token parameters may be set'
147+
refute [ -e '.npmrc' ]
148+
}
149+
150+
@test "fails if env and token are both set" {
151+
export BUILDKITE_PLUGIN_PRIVATE_NPM_TOKEN='abc123'
152+
export BUILDKITE_PLUGIN_PRIVATE_NPM_ENV='MY_ENV_VAR'
153+
154+
run $PWD/hooks/pre-command
155+
156+
assert_failure
157+
assert_output ':no_entry_sign: :npm: :package: Failed! Only one of file, env or token parameters may be set'
158+
refute [ -e '.npmrc' ]
159+
}
160+
161+
@test "fails if env, file and token are all set" {
162+
export BUILDKITE_PLUGIN_PRIVATE_NPM_FILE='my_token_file'
163+
export BUILDKITE_PLUGIN_PRIVATE_NPM_ENV='MY_ENV_VAR'
164+
export BUILDKITE_PLUGIN_PRIVATE_NPM_TOKEN='abc123'
165+
31166
run $PWD/hooks/pre-command
32167

33168
assert_failure
169+
assert_output ':no_entry_sign: :npm: :package: Failed! Only one of file, env or token parameters may be set'
34170
refute [ -e '.npmrc' ]
35171
}

0 commit comments

Comments
 (0)