-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/973 provide configuration via env vars #988
Feat/973 provide configuration via env vars #988
Conversation
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
src/util.ts
Outdated
group.toUpperCase().replace('-', '').replace('_', '') | ||
) | ||
|
||
export function extractFromEnv<T>(prefix: string = 'ZX_') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I'd suggest
getZxDefaults
- Let's extend signature for more convenient testing:
(defs: Options, prefix: string = 'ZX_', env = process.env)
- The reduce API might be better here:
return Object.entries(env).reduce((m, [k, v]) => {
if (k.startsWith(prefix)) {
const _k = snakeToCamel(key.slice(prefix.length))
const _v = ({true: true, false: false})[v.toLowerCase()] ?? v
if (typeof _v === typeof defs[_k])
m[_k] = _v
}
return m
}, defs)
// core.ts
const defaults = getZxDefaults({
[CWD]: process.cwd(),
[SYNC]: false,
verbose: false,
//..
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty nice. Thank you. I will rewrite it ASAP)
There is only one thing that worries me in default value type checking: mixed types. For example, shell
and preferLocal
. In defaults it a boolean
, but we can pass a string
programmatically. This condition rejects such values.
What do you think about this? @antongolub
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated code with some potentially improvements:
- I iterate through
defaults
cause it contains less keys then env (i hope))) - I use parameter
extra
to determine optional fields types and additional type forlocalPrefer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k.startsWith(prefix)
is cheap, so its fine to traverse the whole env
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can avoid extra
in arguments. Fn internal constant is fine here:
const types = {
shell: ['string', 'boolean']
}
if ([typeof defs[_k], ...types[_k]].includes(typeof _v))
Hey, @pafik13,
|
…com:pafik13/zx into feat/973-provide-configuration-via-env-vars
src/core.ts
Outdated
} | ||
|
||
// prettier-ignore | ||
export const defaults: Options = getZxDefaults( | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep the original formatting here for git blame
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// prettier-ignore
export const defaults: Options = getZxDefaults({
// ... to keep git blame
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I restored formatting, but it didn't help (:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prettier-ignore
should go above the export const defaults
declaration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was there and extended only to the closest expression - the function call...
JSON declaration was out of scope)
Now, JSON isn't formatted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was hard, but I did it.
Thank you)
src/core.ts
Outdated
} | ||
|
||
// prettier-ignore | ||
export const defaults: Options = getZxDefaults( | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// prettier-ignore
export const defaults: Options = getZxDefaults({
// ... to keep git blame
})
src/core.ts
Outdated
const _k = snakeToCamel(k.slice(prefix.length)) | ||
const _v = { true: true, false: false }[v.toLowerCase()] ?? v | ||
if ( | ||
[typeof defs[_k as keyof Options], ...(types[_k] ?? [])].includes( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{...undefined}
undefined is spreadable, afair, isnt it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the same, but output from my Browser console
[...undefined]
VM153:1 Uncaught TypeError: undefined is not iterable
at <anonymous>:1:5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but in your case it works)
> {...undefined}
< {}
src/core.ts
Outdated
|
||
const o = Object.entries(env).reduce<Record<string, string | boolean>>( | ||
(m, [k, v]) => { | ||
if (k.startsWith(prefix) && v) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor optimization: v && k.startsWith(prefix)
src/core.ts
Outdated
shell: ['string', 'boolean'], | ||
halt: ['boolean'], | ||
preferLocal: ['string', 'boolean'], | ||
input: ['string'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess we should restrict the accepted env opts: sync, nothrow, input, halt, stdio
can break the script logic, while verbose, shell, quiet, preferLocal
just change its behaviour. Moreover, timeout
does not belong to defaults
, but it looks reasonable to set externally.
const allowed = new Set(['timeout', 'shell', ...])
// ...
if (allowed.has(_k)) {
}
And if somebody is setting ZX_CWD=true
, wrong typecast to bool is the least of problems. So maybe we can skip the type check at all
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plz clarify what you suggest:
-
determine only one list of acceptable properties (
verbose, shell, quiet, preferLocal
) and try it extract from env with type-checking -
determine two lists:
- acceptable - extract w/o type check
- unacceptable - extract with strict type check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this?
(defs: Options, prefix = 'ZX_', env = process.env) => Object.entries(env).reduce<Options>((m, [k, v]) => {
const allowed = new Set(['preferLocal', 'detached', 'verbose', 'quiet', 'timeout', 'timeoutSignal', 'prefix', 'postfix'])
if (v && k.startsWith(prefix)) {
const _k = snakeToCamel(key.slice(prefix.length))
const _v = ({true: true, false: false})[v.toLowerCase()] ?? v
if (allowed.has(_k))
m[_k] = _v
}
return m
}, defs)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this?
(defs: Options, prefix = 'ZX_', env = process.env) => Object.entries(env).reduce<Options>((m, [k, v]) => { const allowed = new Set(['preferLocal', 'detached', 'verbose', 'quiet', 'timeout', 'timeoutSignal', 'prefix', 'postfix']) if (v && k.startsWith(prefix)) { const _k = snakeToCamel(key.slice(prefix.length)) const _v = ({true: true, false: false})[v.toLowerCase()] ?? v if (allowed.has(_k)) m[_k] = _v } return m }, defs)
In that case we can assign to verbose
and quiet
some string value. It's not good, in my opinion.
So, I combined 'allowed' and type-check together into types
and use only it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Thanks for the contribution.
Thanks for the mentorship) |
Fixes #973