Skip to content

Commit

Permalink
Add strict-include-framework-vars mode.
Browse files Browse the repository at this point in the history
  • Loading branch information
nathanhammond committed Apr 27, 2023
1 parent 517b9b7 commit f1b82e3
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 25 deletions.
2 changes: 1 addition & 1 deletion cli/internal/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func (g *CompleteGraph) GetPackageTaskVisitor(
useOldTaskHashable := false
if taskEnvMode == util.Infer {
if taskDefinition.PassthroughEnv != nil {
taskEnvMode = util.Strict
taskEnvMode = util.StrictIncludeFrameworkVars
} else {
// If we're in infer mode we have just detected non-usage of strict env vars.
// Since we haven't stabilized this we don't want to break their cache.
Expand Down
4 changes: 3 additions & 1 deletion cli/internal/run/global_hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func calculateGlobalHashFromHashable(full GlobalHashable) (string, error) {
// In infer mode, if there is any passThru config (even if it is an empty array)
// we'll hash the whole object, so we can detect changes to that config
// Further, resolve the envMode to the concrete value.
full.envMode = util.Strict
full.envMode = util.StrictIncludeFrameworkVars
return fs.HashObject(full)
}

Expand All @@ -72,6 +72,8 @@ func calculateGlobalHashFromHashable(full GlobalHashable) (string, error) {
// Remove the passthroughs from hash consideration if we're explicitly loose.
full.envVarPassthroughs = nil
return fs.HashObject(full)
case util.StrictIncludeFrameworkVars:
fallthrough
case util.Strict:
// Collapse `nil` and `[]` in strict mode.
if full.envVarPassthroughs == nil {
Expand Down
2 changes: 1 addition & 1 deletion cli/internal/run/real_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ func (ec *execContext) exec(ctx gocontext.Context, packageTask *nodes.PackageTas
currentState := env.GetEnvMap()
passthroughEnv := env.EnvironmentVariableMap{}

if packageTask.EnvMode == util.Strict {
if packageTask.EnvMode.IsStrict() {
defaultPassthrough := []string{
"PATH",
"SHELL",
Expand Down
2 changes: 1 addition & 1 deletion cli/internal/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ func (r *run) run(ctx gocontext.Context, targets []string) error {

globalEnvMode := rs.Opts.runOpts.EnvMode
if globalEnvMode == util.Infer && turboJSON.GlobalPassthroughEnv != nil {
globalEnvMode = util.Strict
globalEnvMode = util.StrictIncludeFrameworkVars
}

// RunSummary contains information that is statically analyzable about
Expand Down
4 changes: 3 additions & 1 deletion cli/internal/taskhash/taskhash.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,8 @@ func calculateTaskHashFromHashable(full *taskHashable, useOldTaskHashable bool)
// Remove the passthroughs from hash consideration if we're explicitly loose.
full.passthroughEnv = nil
return fs.HashObject(full)
case util.StrictIncludeFrameworkVars:
fallthrough
case util.Strict:
// Collapse `nil` and `[]` in strict mode.
if full.passthroughEnv == nil {
Expand Down Expand Up @@ -382,7 +384,7 @@ func (th *Tracker) CalculateTaskHash(packageTask *nodes.PackageTask, dependencyS
var framework *inference.Framework
envVarContainingExcludePrefix := ""

// In strict environment variable mode we don't do framework env var inference.
// In Strict environment variable mode we don't do framework env var inference.
// You must instead:
// - Specify all environment variables that you want available in your build environment.
// - Specify whether you want those considered for the hash or merely passed through.
Expand Down
15 changes: 13 additions & 2 deletions cli/internal/util/run_opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,24 @@ const (
Infer EnvMode = "Infer"
// Loose - environment variables are unconstrained
Loose EnvMode = "Loose"
// StrictIncludeFrameworkVars - environment variables are limited.
// They include vars from detected frameworks.
StrictIncludeFrameworkVars EnvMode = "StrictIncludeFrameworkVars"
// Strict - environment variables are limited
Strict EnvMode = "Strict"
)

// MarshalText implements TextMarshaler for the struct.
func (s EnvMode) MarshalText() (text []byte, err error) {
return []byte(strings.ToLower(string(s))), nil
func (em EnvMode) MarshalText() (text []byte, err error) {
if em == StrictIncludeFrameworkVars {
return []byte("strict-include-framework-vars"), nil
}
return []byte(strings.ToLower(string(em))), nil
}

// IsStrict collapses the two Strict variants.
func (em EnvMode) IsStrict() bool {
return em == Strict || em == StrictIncludeFrameworkVars
}

// RunOpts holds the options that control the execution of a turbo run
Expand Down
21 changes: 21 additions & 0 deletions crates/turborepo-lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub enum DryRunMode {
pub enum EnvMode {
Infer,
Loose,
StrictIncludeFrameworkVars,
Strict,
}

Expand Down Expand Up @@ -726,6 +727,26 @@ mod test {
"env_mode: specified strict"
);

assert_eq!(
Args::try_parse_from([
"turbo",
"run",
"build",
"--experimental-env-mode",
"strict-include-framework-vars"
])
.unwrap(),
Args {
command: Some(Command::Run(Box::new(RunArgs {
tasks: vec!["build".to_string()],
env_mode: EnvMode::StrictIncludeFrameworkVars,
..get_default_run_args()
}))),
..Args::default()
},
"env_mode: specified strict"
);

assert_eq!(
Args::try_parse_from(["turbo", "run", "build", "lint", "test"]).unwrap(),
Args {
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/repo/docs/core-concepts/caching.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ To alter the cache for _all_ tasks, you can declare environment variables in the
### Automatic environment variable inclusion

<Callout type="warning">
Turborepo only does automatic environment variable inclusion when the environment variable mode is set to `loose` or when using the default environment variable mode of `infer` without specifying `passthroughEnv`.
Turborepo does automatic environment variable inclusion unless you pass `--env-mode=strict` in order to disable this behavior.

In `strict` environment variable mode you must specify every environment variable you want available in your execution environment via `env` or `globalEnv` if you want them considered for the hash and `passthroughEnv` or `globalPassthroughEnv` if not.

Expand Down
35 changes: 26 additions & 9 deletions docs/pages/repo/docs/reference/command-line-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -188,20 +188,21 @@ Task details include:

Controls the available environment variables to your tasks.

| option | description |
| ------ | ---------------------------------------------------------- |
| infer | infers strict mode or loose mode based on allowlist config |
| loose | allows all environment variables |
| strict | only allow declared variables |
| option | description |
| ----------------------------- | -------------------------------------------------------------------------------------------------------- |
| infer | infers loose or strict-include-framework-vars mode based upon existence of configuration |
| loose | allows all environment variables |
| strict-include-framework-vars | allows only environment variables detected from your framework and those specified by your configuration |
| strict | allows only environment variables specified by your configuration |

`PATH`, `SHELL`, and `SYSTEMROOT` are always available to all tasks.

##### `infer`

In infer mode, Turborepo will look for [`experimentalPassThroughEnv`][1] for
each task config, and [`experimentalGlobalPassThroughEnv`][2] in the root of the
`turbo.json` config. If either is defined, "strict" mode is inferred. If
neither is defined, then `loose` mode is inferred.
`turbo.json` config. If either is defined, "strict-include-framework-vars" mode
is inferred. If neither is defined, then `loose` mode is inferred.

In this mode, the value of `experimentalGlobalPassThroughEnv` and the flag's
value ("infer") will only be incorporated into the global hash if
Expand All @@ -210,9 +211,25 @@ value ("infer") will only be incorporated into the global hash if
##### `loose`

In loose mode, all environment variables are made available to the task.
The value of `experimentalGlobalPassThroughEnv` and the flag's value ("loose")
The flag's value ("loose") itself will be incorporated into the global hash.

##### `strict-include-framework-vars`

In strict-include-framework-vars mode, environment variables specified in the following keys are
available to the task:

- `env` and `experimentalPassThroughEnv` in each task configuration
- `globalEnv` and `experimentalGlobalPassThroughEnv` in the root of your config

In addition, values detected by [automatic environment variable inclusion](../caching#automatic-environment-variable-inclusion)
will also be available to the task.

The value of `experimentalGlobalPassThroughEnv` and the flag's value ("strict-include-framework-vars")
itself will be incorporated into the global hash.

If strict-include-framework-vars mode is specified or inferred _all_ tasks are run in
strict-include-framework-vars mode regardless of their configuration.

##### `strict`

In strict mode, only environment variables specified in the following keys are
Expand All @@ -224,7 +241,7 @@ available to the task:
The value of `experimentalGlobalPassThroughEnv` and the flag's value ("strict")
itself will be incorporated into the global hash.

If strict mode is specified or inferred, _all_ tasks are run in strict mode,
If strict mode is specified or inferred _all_ tasks are run in strict mode,
regardless of their configuration.

#### `--filter`
Expand Down
27 changes: 27 additions & 0 deletions turborepo-tests/integration/tests/framework_inference.t
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ Check next
"NEXT_PUBLIC_HELLO=7719a612cd8fb5d22a8207ee9dba3e55d2f7d712e3dfd40af2d9080a545ff427"
]

Check next + strict-include-framework-vars
$ rm -rf .turbo/runs
$ ${TURBO} build --filter=next-app --experimental-env-mode=strict-include-framework-vars --summarize | grep "next-app:build: hello from "
next-app:build: hello from
$ cat .turbo/runs/*.json | jq -r '.tasks[0].environmentVariables.inferred'
[
"NEXT_PUBLIC_HELLO=7719a612cd8fb5d22a8207ee9dba3e55d2f7d712e3dfd40af2d9080a545ff427"
]

Check next + strict
$ rm -rf .turbo/runs
$ ${TURBO} build --filter=next-app --experimental-env-mode=strict --summarize | grep "next-app:build: hello from "
Expand All @@ -32,6 +41,15 @@ Check vite
"VITE_HELLO=7e153fd3e8ad33597a4ecf6d1a96a91dfba32cbb695e2729228cb9af8f615567"
]

Check vite + strict-include-framework-vars
$ rm -rf .turbo/runs
$ ${TURBO} build --filter=vite-app --experimental-env-mode=strict-include-framework-vars --summarize | grep "vite-app:build: hello from "
vite-app:build: hello from
$ cat .turbo/runs/*.json | jq -r '.tasks[0].environmentVariables.inferred'
[
"VITE_HELLO=7e153fd3e8ad33597a4ecf6d1a96a91dfba32cbb695e2729228cb9af8f615567"
]

Check vite + strict
$ rm -rf .turbo/runs
$ ${TURBO} build --filter=vite-app --experimental-env-mode=strict --summarize | grep "vite-app:build: hello from "
Expand All @@ -48,6 +66,15 @@ Check vue
"VUE_APP_HELLO=429b02cec7c8171389c5d0eaff1ef36b47e418bb96050979f9cf2bea34c07539"
]

Check vue + strict-include-framework-vars
$ rm -rf .turbo/runs
$ ${TURBO} build --filter=vue-app --experimental-env-mode=strict-include-framework-vars --summarize | grep "vue-app:build: hello from "
vue-app:build: hello from
$ cat .turbo/runs/*.json | jq -r '.tasks[0].environmentVariables.inferred'
[
"VUE_APP_HELLO=429b02cec7c8171389c5d0eaff1ef36b47e418bb96050979f9cf2bea34c07539"
]

Check vue + strict
$ rm -rf .turbo/runs
$ ${TURBO} build --filter=vue-app --experimental-env-mode=strict --summarize | grep "vue-app:build: hello from "
Expand Down
29 changes: 21 additions & 8 deletions turborepo-tests/integration/tests/run_summary/strict_env.t
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,27 @@ Run as `loose`
"globalPassthrough": null
}

Run as `strict-include-framework-vars`
$ rm -rf .turbo/runs
$ ${TURBO} run build --experimental-env-mode=strict-include-framework-vars --summarize > /dev/null
$ cat .turbo/runs/*.json | jq -r '.envMode'
strict-include-framework-vars
$ cat .turbo/runs/*.json | jq -r '.tasks[0].envMode'
strict-include-framework-vars
$ cat .turbo/runs/*.json | jq -r '.tasks[0].environmentVariables | {passthrough, globalPassthrough}'
{
"passthrough": null,
"globalPassthrough": null
}

All specified + infer
$ rm -rf .turbo/runs
$ cp "$TESTDIR/../_fixtures/strict_env_vars_configs/all.json" "$(pwd)/turbo.json" && git commit --allow-empty -am "no comment" --quiet
$ ${TURBO} run build --summarize > /dev/null
$ cat .turbo/runs/*.json | jq -r '.envMode'
strict
strict-include-framework-vars
$ cat .turbo/runs/*.json | jq -r '.tasks[0].envMode'
strict
strict-include-framework-vars
$ cat .turbo/runs/*.json | jq -r '.tasks[0].environmentVariables | {passthrough, globalPassthrough}'
{
"passthrough": [
Expand Down Expand Up @@ -90,9 +103,9 @@ Global passthrough specified empty array + infer
$ cp "$TESTDIR/../_fixtures/strict_env_vars_configs/global_pt-empty.json" "$(pwd)/turbo.json" && git commit --allow-empty -am "no comment" --quiet
$ ${TURBO} run build --summarize > /dev/null
$ cat .turbo/runs/*.json | jq -r '.envMode'
strict
strict-include-framework-vars
$ cat .turbo/runs/*.json | jq -r '.tasks[0].envMode'
strict
strict-include-framework-vars
$ cat .turbo/runs/*.json | jq -r '.tasks[0].environmentVariables | {passthrough, globalPassthrough}'
{
"passthrough": null,
Expand All @@ -104,9 +117,9 @@ Global passthrough specified value + infer
$ cp "$TESTDIR/../_fixtures/strict_env_vars_configs/global_pt.json" "$(pwd)/turbo.json" && git commit --allow-empty -am "no comment" --quiet
$ ${TURBO} run build --summarize > /dev/null
$ cat .turbo/runs/*.json | jq -r '.envMode'
strict
strict-include-framework-vars
$ cat .turbo/runs/*.json | jq -r '.tasks[0].envMode'
strict
strict-include-framework-vars
$ cat .turbo/runs/*.json | jq -r '.tasks[0].environmentVariables | {passthrough, globalPassthrough}'
{
"passthrough": null,
Expand Down Expand Up @@ -152,7 +165,7 @@ Task passthrough specified empty array + infer
$ cat .turbo/runs/*.json | jq -r '.envMode'
infer
$ cat .turbo/runs/*.json | jq -r '.tasks[0].envMode'
strict
strict-include-framework-vars
$ cat .turbo/runs/*.json | jq -r '.tasks[0].environmentVariables | {passthrough, globalPassthrough}'
{
"passthrough": [],
Expand All @@ -166,7 +179,7 @@ Task passthrough specified value + infer
$ cat .turbo/runs/*.json | jq -r '.envMode'
infer
$ cat .turbo/runs/*.json | jq -r '.tasks[0].envMode'
strict
strict-include-framework-vars
$ cat .turbo/runs/*.json | jq -r '.tasks[0].environmentVariables | {passthrough, globalPassthrough}'
{
"passthrough": [
Expand Down

0 comments on commit f1b82e3

Please sign in to comment.