@lsmacedo/envres 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -0
- package/envres +173 -0
- package/package.json +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# envres
|
|
2
|
+
|
|
3
|
+
Resolve environment variable references from the macOS keychain and run a command with the real secrets injected.
|
|
4
|
+
|
|
5
|
+
Set a variable's value to an `envres/<service>` reference instead of a plaintext secret. `envres` rewrites it to the keychain secret (`security find-generic-password -s <service> -w`) just before running your command. Variables without the prefix pass through untouched.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm i -g envres
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
envres run [--env-file <path>] -- <command> [args...]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Reference secrets in your shell profile, then alias the command that needs them:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
export ARTIFACTORY_TOKEN="envres/artifactory-token"
|
|
23
|
+
alias npm='envres run -- npm'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Now `npm install` runs with `ARTIFACTORY_TOKEN` resolved from the keychain.
|
|
27
|
+
|
|
28
|
+
Load references from a `.env` file too (the file is never modified; its values override the environment):
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
envres run --env-file .env -- npm install
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
# .env
|
|
36
|
+
ARTIFACTORY_TOKEN=envres/artifactory-token
|
|
37
|
+
NPM_TOKEN=envres/npm-token
|
|
38
|
+
```
|
package/envres
ADDED
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# envres — resolve environment variable references from a secret backend
|
|
4
|
+
# (currently the macOS keychain) and run a command with them injected.
|
|
5
|
+
#
|
|
6
|
+
# Set an env var's value to a reference, e.g. in your shell profile:
|
|
7
|
+
#
|
|
8
|
+
# export ARTIFACTORY_TOKEN="envres/artifactory-token"
|
|
9
|
+
#
|
|
10
|
+
# Then wrap the command that needs the real secret:
|
|
11
|
+
#
|
|
12
|
+
# alias npm='envres run -- npm'
|
|
13
|
+
#
|
|
14
|
+
# When you run `npm install`, envres rewrites ARTIFACTORY_TOKEN to the output
|
|
15
|
+
# of `security find-generic-password -s artifactory-token -w` before exec'ing
|
|
16
|
+
# the wrapped command. Vars without the prefix pass through untouched.
|
|
17
|
+
#
|
|
18
|
+
# --env-file <path> additionally loads KEY=value lines from a .env file into the
|
|
19
|
+
# command's environment, resolving any `envres/`-prefixed values the same way.
|
|
20
|
+
# The file is read-only; its values override the existing environment.
|
|
21
|
+
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
PREFIX="envres/"
|
|
25
|
+
|
|
26
|
+
usage() {
|
|
27
|
+
cat <<'EOF'
|
|
28
|
+
usage: envres run [--env-file <path>] -- <command> [args...]
|
|
29
|
+
|
|
30
|
+
Resolves environment variables whose value is "envres/<service>" by
|
|
31
|
+
replacing them with `security find-generic-password -s <service> -w`,
|
|
32
|
+
then executes <command>.
|
|
33
|
+
|
|
34
|
+
--env-file <path> Also load KEY=value lines from <path> into the command's
|
|
35
|
+
environment (resolving envres/ values). Values from the
|
|
36
|
+
file override the existing environment. The file is never
|
|
37
|
+
modified.
|
|
38
|
+
|
|
39
|
+
example:
|
|
40
|
+
alias npm='envres run -- npm'
|
|
41
|
+
envres run --env-file .env -- npm install
|
|
42
|
+
EOF
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
resolve_secret() {
|
|
46
|
+
local service="$1"
|
|
47
|
+
if ! security find-generic-password -s "$service" -w 2>/dev/null; then
|
|
48
|
+
echo "envres: no keychain entry for service '$service'" >&2
|
|
49
|
+
return 1
|
|
50
|
+
fi
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Load KEY=value lines from a .env file, exporting each into the environment.
|
|
54
|
+
# Values are exported verbatim (including any envres/ prefix); resolve_env
|
|
55
|
+
# performs keychain substitution afterward over the merged environment.
|
|
56
|
+
load_env_file() {
|
|
57
|
+
local file="$1" line key value
|
|
58
|
+
if [[ ! -r "$file" ]]; then
|
|
59
|
+
echo "envres: cannot read env file '$file'" >&2
|
|
60
|
+
return 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
64
|
+
line="${line#"${line%%[![:space:]]*}"}" # trim leading whitespace
|
|
65
|
+
[[ -z "$line" || "$line" == "#"* ]] && continue
|
|
66
|
+
[[ "$line" == "export "* ]] && line="${line#export }"
|
|
67
|
+
[[ "$line" == *=* ]] || continue
|
|
68
|
+
|
|
69
|
+
key="${line%%=*}"
|
|
70
|
+
value="${line#*=}"
|
|
71
|
+
|
|
72
|
+
if [[ ! "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
|
73
|
+
echo "envres: skipping invalid key '$key' in '$file'" >&2
|
|
74
|
+
continue
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
if [[ "$value" == \"*\" || "$value" == \'*\' ]]; then
|
|
78
|
+
value="${value:1:${#value}-2}" # strip one pair of surrounding quotes
|
|
79
|
+
else
|
|
80
|
+
value="${value%"${value##*[![:space:]]}"}" # trim trailing whitespace
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
export "$key=$value"
|
|
84
|
+
done <"$file"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
resolve_env() {
|
|
88
|
+
local name value service secret
|
|
89
|
+
while IFS= read -r name; do
|
|
90
|
+
value="${!name-}"
|
|
91
|
+
[[ "$value" == "$PREFIX"* ]] || continue
|
|
92
|
+
|
|
93
|
+
service="${value#"$PREFIX"}"
|
|
94
|
+
if [[ -z "$service" ]]; then
|
|
95
|
+
echo "envres: $name has empty service reference ('$value')" >&2
|
|
96
|
+
return 1
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
if ! secret="$(resolve_secret "$service")"; then
|
|
100
|
+
echo "envres: failed to resolve $name from keychain" >&2
|
|
101
|
+
return 1
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
export "$name=$secret"
|
|
105
|
+
done < <(compgen -e)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
main() {
|
|
109
|
+
if [[ $# -eq 0 ]]; then
|
|
110
|
+
usage >&2
|
|
111
|
+
exit 2
|
|
112
|
+
fi
|
|
113
|
+
|
|
114
|
+
case "$1" in
|
|
115
|
+
-h | --help)
|
|
116
|
+
usage
|
|
117
|
+
exit 0
|
|
118
|
+
;;
|
|
119
|
+
run)
|
|
120
|
+
shift
|
|
121
|
+
;;
|
|
122
|
+
*)
|
|
123
|
+
echo "envres: unknown subcommand '$1'" >&2
|
|
124
|
+
usage >&2
|
|
125
|
+
exit 2
|
|
126
|
+
;;
|
|
127
|
+
esac
|
|
128
|
+
|
|
129
|
+
local env_file=""
|
|
130
|
+
while [[ $# -gt 0 ]]; do
|
|
131
|
+
case "$1" in
|
|
132
|
+
--env-file)
|
|
133
|
+
env_file="${2-}"
|
|
134
|
+
if [[ -z "$env_file" ]]; then
|
|
135
|
+
echo "envres: --env-file requires a path" >&2
|
|
136
|
+
exit 2
|
|
137
|
+
fi
|
|
138
|
+
shift 2
|
|
139
|
+
;;
|
|
140
|
+
--env-file=*)
|
|
141
|
+
env_file="${1#--env-file=}"
|
|
142
|
+
shift
|
|
143
|
+
;;
|
|
144
|
+
--)
|
|
145
|
+
shift
|
|
146
|
+
break
|
|
147
|
+
;;
|
|
148
|
+
-*)
|
|
149
|
+
echo "envres: unknown option '$1'" >&2
|
|
150
|
+
usage >&2
|
|
151
|
+
exit 2
|
|
152
|
+
;;
|
|
153
|
+
*)
|
|
154
|
+
break
|
|
155
|
+
;;
|
|
156
|
+
esac
|
|
157
|
+
done
|
|
158
|
+
|
|
159
|
+
if [[ $# -eq 0 ]]; then
|
|
160
|
+
echo "envres: no command given" >&2
|
|
161
|
+
usage >&2
|
|
162
|
+
exit 2
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
if [[ -n "$env_file" ]]; then
|
|
166
|
+
load_env_file "$env_file" || exit 2
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
resolve_env
|
|
170
|
+
exec "$@"
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
main "$@"
|
package/package.json
ADDED