-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Propagate project-scoped flags through exec transitions #29232
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -671,6 +671,188 @@ public void createKey_withScopedBuildOptions_outOfScopeFlag_flagSetInTheBaseline | |
| .containsExactlyEntriesIn(expectedScopeTypeMap); | ||
| } | ||
|
|
||
| @Test | ||
| public void createKey_withScopedBuildOptions_projectFlag_isPreservedForInScopeTarget() | ||
| throws Exception { | ||
| // Tests that when a target is evaluated within a flag's project boundary, the project-scoped | ||
| // flag's value is correctly preserved. | ||
| createStarlarkFlagRule(); | ||
| scratch.file( | ||
| "flag/BUILD", | ||
| """ | ||
| load(":def.bzl", "basic_flag") | ||
| basic_flag( | ||
| name = "foo", | ||
| scope = "project", | ||
| build_setting_default = "default", | ||
| ) | ||
| """); | ||
| scratch.file( | ||
| "flag/PROJECT.scl", | ||
| """ | ||
| load("//test:project_proto.scl", "project_pb2") | ||
| project = project_pb2.Project.create( | ||
| project_directories = ["//my_project"], | ||
| ) | ||
| """); | ||
|
|
||
| invalidatePackages(false); | ||
|
|
||
| // The target being built is inside the flag's project boundary. | ||
| BuildOptions baseOptions = createBuildOptions("--//flag:foo=foo"); | ||
| BuildConfigurationKey result = | ||
| fetch(baseOptions, Label.parseCanonicalUnchecked("//my_project:my_target")); | ||
| assertThat(result).isNotNull(); | ||
|
|
||
| // The flag is in scope for //my_project:my_target, so its value should be preserved. | ||
| assertThat( | ||
| result | ||
| .getOptions() | ||
| .getStarlarkOptions() | ||
| .get(Label.parseCanonicalUnchecked("//flag:foo"))) | ||
| .isEqualTo("foo"); | ||
| } | ||
|
|
||
| @Test | ||
| public void createKey_withScopedBuildOptions_projectFlag_resetsOutsideProject() | ||
| throws Exception { | ||
| // Tests that when a project-scoped flag arrives at a target evaluation, | ||
| // if the target is outside the flag's project boundary, the project-boundary | ||
| // enforcement resets the flag to its baseline value. | ||
| createStarlarkFlagRule(); | ||
| scratch.file( | ||
| "flag/BUILD", | ||
| """ | ||
| load(":def.bzl", "basic_flag") | ||
| basic_flag( | ||
| name = "foo", | ||
| scope = "project", | ||
| build_setting_default = "default", | ||
| ) | ||
| """); | ||
| scratch.file( | ||
| "flag/PROJECT.scl", | ||
| """ | ||
| load("//test:project_proto.scl", "project_pb2") | ||
| project = project_pb2.Project.create( | ||
| project_directories = ["//my_project"], | ||
| ) | ||
| """); | ||
|
|
||
| invalidatePackages(false); | ||
|
|
||
| // The target being built is outside the flag's project boundary. | ||
| BuildOptions baseOptions = createBuildOptions("--//flag:foo=foo"); | ||
| BuildConfigurationKey result = | ||
| fetch(baseOptions, Label.parseCanonicalUnchecked("//other_project:my_target")); | ||
| assertThat(result).isNotNull(); | ||
|
|
||
| // The flag is out of scope and resets to baseline. | ||
| // Since the baseline doesn't have //flag:foo set, it should be absent from the options. | ||
| assertThat( | ||
| result | ||
| .getOptions() | ||
| .getStarlarkOptions() | ||
| .get(Label.parseCanonicalUnchecked("//flag:foo"))) | ||
| .isNull(); | ||
| } | ||
|
|
||
| @Test | ||
| public void createKey_withNullLabel_resetsProjectScopedFlags() throws Exception { | ||
| createStarlarkFlagRule(); | ||
| scratch.file( | ||
| "flag/BUILD", | ||
| """ | ||
| load(":def.bzl", "basic_flag") | ||
| basic_flag( | ||
| name = "foo", | ||
| scope = "project", | ||
| build_setting_default = "default", | ||
| ) | ||
| """); | ||
| scratch.file( | ||
| "flag/PROJECT.scl", | ||
| """ | ||
| load("//test:project_proto.scl", "project_pb2") | ||
| project = project_pb2.Project.create( | ||
| project_directories = ["//my_project"], | ||
| ) | ||
| """); | ||
|
|
||
| invalidatePackages(false); | ||
|
|
||
| // Provide a non-default flag value | ||
| BuildOptions baseOptions = createBuildOptions("--//flag:foo=foo"); | ||
|
|
||
| // Fetching with a null label acts as if evaluating a top-level config or | ||
| // an exec transitions headless config. The flag should be aggressively reset to baseline | ||
| // because no project boundary could possibly be validated. | ||
| BuildConfigurationKey result = fetch(baseOptions, null); | ||
|
|
||
| assertThat(result).isNotNull(); | ||
| // It should have reset to baseline, meaning //flag:foo is absent. | ||
| assertThat( | ||
| result | ||
| .getOptions() | ||
| .getStarlarkOptions() | ||
| .get(Label.parseCanonicalUnchecked("//flag:foo"))) | ||
| .isNull(); | ||
| } | ||
|
|
||
| @Test | ||
| public void createKey_withMissingScopeTypeMap_doesNotCrash() throws Exception { | ||
| createStarlarkFlagRule(); | ||
| scratch.overwriteFile( | ||
| "flag/def.bzl", | ||
| """ | ||
| def _impl(ctx): | ||
| return [] | ||
|
|
||
| basic_flag_without_restrictions = rule( | ||
| implementation = _impl, | ||
| build_setting = config.string(flag = True), | ||
| attrs = { | ||
| "scope": attr.string( | ||
| doc = "The scope", | ||
| default = "universal", | ||
| ), | ||
| }, | ||
| ) | ||
| """); | ||
| scratch.file( | ||
| "flag/BUILD", | ||
| """ | ||
| load(":def.bzl", "basic_flag_without_restrictions") | ||
| basic_flag_without_restrictions( | ||
| name = "another_flag", | ||
| build_setting_default = "default", | ||
| ) | ||
| basic_flag_without_restrictions( | ||
| name = "ref_flag", | ||
| scope = "exec:--//flag:another_flag", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What exactly dos this test method cover? I'd try to keep If this is testing missing scope doesn't crash, and that's just a path toward that, is that a new vulnerability you think deserves more scrutiny here? |
||
| build_setting_default = "default", | ||
| ) | ||
| """); | ||
|
|
||
| invalidatePackages(false); | ||
|
|
||
| // Provide --//flag:ref_flag=ref but DON'T provide --//flag:another_flag in the options. | ||
| BuildOptions baseOptions = createBuildOptions("--//flag:ref_flag=ref"); | ||
|
|
||
| // This fetch invokes BuildOptionsScopeFunction which specifically injects //flag:another_flag | ||
| // into the evaluated options, BUT fails to add it to fullyResolvedScopes and scopeTypeMap. | ||
| // Ensure that resetFlags correctly validates this injected un-scoped flag without hitting NPE. | ||
| BuildConfigurationKey result = fetch(baseOptions, null); | ||
|
|
||
| assertThat(result).isNotNull(); | ||
| assertThat( | ||
| result | ||
| .getOptions() | ||
| .getStarlarkOptions() | ||
| .get(Label.parseCanonicalUnchecked("//flag:another_flag"))) | ||
| .isEqualTo("default"); | ||
| } | ||
|
|
||
| @Test | ||
| public void checkFinalizeBuildOptions_haveCorrectScopeTypeMap_noScopingApplied() | ||
| throws Exception { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.