Support yanked versions for Bzlmod modules - If present, parse the `metadata.json` of registry modules and cause the build to fail if any yanked version is present in your dep graph. - `--allow_yanked_versions` flag and `BZLMOD_ALLOWED_YANKED_VERSIONS` env var which allowlist the specified yanked versions from causing the build to fail. `all` keyword allowed to disable completely. https://github.com/bazelbuild/bazel/issues/16180 Closes #16180. PiperOrigin-RevId: 479043848 Change-Id: Ie883349a67083623c5c78fba68d1eaf8387409e3
diff --git a/src/MODULE.tools b/src/MODULE.tools index fdd9993..5afa295 100644 --- a/src/MODULE.tools +++ b/src/MODULE.tools
@@ -7,7 +7,7 @@ bazel_dep(name = "rules_python", version = "0.4.0") bazel_dep(name = "platforms", version = "0.0.4") -bazel_dep(name = "protobuf", version = "3.19.0", repo_name = "com_google_protobuf") +bazel_dep(name = "protobuf", version = "3.19.2", repo_name = "com_google_protobuf") cc_configure = use_extension("//tools/cpp:cc_configure.bzl", "cc_configure_extension") use_repo(cc_configure, "local_config_cc", "local_config_cc_toolchains")
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java index 1175e3c..60a1bec 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java
@@ -138,6 +138,7 @@ private final AtomicBoolean ignoreDevDeps = new AtomicBoolean(false); private CheckDirectDepsMode checkDirectDepsMode = CheckDirectDepsMode.WARNING; private BazelCompatibilityMode bazelCompatibilityMode = BazelCompatibilityMode.ERROR; + private List<String> allowedYankedVersions = ImmutableList.of(); private SingleExtensionEvalFunction singleExtensionEvalFunction; public BazelRepositoryModule() { @@ -417,6 +418,7 @@ ignoreDevDeps.set(repoOptions.ignoreDevDependency); checkDirectDepsMode = repoOptions.checkDirectDependencies; bazelCompatibilityMode = repoOptions.bazelCompatibilityMode; + allowedYankedVersions = repoOptions.allowedYankedVersions; if (repoOptions.registries != null && !repoOptions.registries.isEmpty()) { registries = repoOptions.registries; @@ -491,7 +493,9 @@ PrecomputedValue.injected( BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, checkDirectDepsMode), PrecomputedValue.injected( - BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, bazelCompatibilityMode)); + BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, bazelCompatibilityMode), + PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, allowedYankedVersions)); } @Override
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD index 9fb8cc3..ae357a4 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD
@@ -156,6 +156,8 @@ "//src/main/java/com/google/devtools/build/lib/rules:repository/repository_directory_value", "//src/main/java/com/google/devtools/build/lib/rules:repository/repository_function", "//src/main/java/com/google/devtools/build/lib/skyframe:bzl_load_value", + "//src/main/java/com/google/devtools/build/lib/skyframe:client_environment_function", + "//src/main/java/com/google/devtools/build/lib/skyframe:client_environment_value", "//src/main/java/com/google/devtools/build/lib/skyframe:precomputed_value", "//src/main/java/com/google/devtools/build/lib/skyframe:skyframe_cluster", "//src/main/java/com/google/devtools/build/lib/vfs",
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java index 93762a8..750c3c4 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunction.java
@@ -19,16 +19,20 @@ import static com.google.common.collect.ImmutableMap.toImmutableMap; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableCollection; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableTable; import com.google.devtools.build.lib.analysis.BlazeVersionInfo; import com.google.devtools.build.lib.bazel.BazelVersion; import com.google.devtools.build.lib.bazel.bzlmod.ModuleFileValue.RootModuleFileValue; import com.google.devtools.build.lib.bazel.bzlmod.Selection.SelectionResult; +import com.google.devtools.build.lib.bazel.bzlmod.Version.ParseException; import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.BazelCompatibilityMode; import com.google.devtools.build.lib.bazel.repository.RepositoryOptions.CheckDirectDepsMode; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; @@ -36,8 +40,11 @@ import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; +import com.google.devtools.build.lib.events.ExtendedEventHandler; import com.google.devtools.build.lib.packages.LabelConverter; import com.google.devtools.build.lib.server.FailureDetails.ExternalDeps.Code; +import com.google.devtools.build.lib.skyframe.ClientEnvironmentFunction; +import com.google.devtools.build.lib.skyframe.ClientEnvironmentValue; import com.google.devtools.build.lib.skyframe.PrecomputedValue.Precomputed; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.skyframe.SkyFunction; @@ -45,8 +52,11 @@ import com.google.devtools.build.skyframe.SkyFunctionException.Transience; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; +import java.io.IOException; +import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import javax.annotation.Nullable; /** @@ -61,10 +71,20 @@ public static final Precomputed<BazelCompatibilityMode> BAZEL_COMPATIBILITY_MODE = new Precomputed<>("bazel_compatibility_mode"); + public static final Precomputed<List<String>> ALLOWED_YANKED_VERSIONS = + new Precomputed<>("allowed_yanked_versions"); + private static final String BZLMOD_ALLOWED_YANKED_VERSIONS_ENV = "BZLMOD_ALLOW_YANKED_VERSIONS"; + @Override @Nullable public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException { + ClientEnvironmentValue allowedYankedVersionsFromEnv = + (ClientEnvironmentValue) + env.getValue(ClientEnvironmentFunction.key(BZLMOD_ALLOWED_YANKED_VERSIONS_ENV)); + if (allowedYankedVersionsFromEnv == null) { + return null; + } RootModuleFileValue root = (RootModuleFileValue) env.getValue(ModuleFileValue.KEY_FOR_ROOT_MODULE); if (root == null) { @@ -83,23 +103,24 @@ } ImmutableMap<ModuleKey, Module> resolvedDepGraph = selectionResult.getResolvedDepGraph(); - try { - checkCompatibility( - resolvedDepGraph.values(), - Objects.requireNonNull(BAZEL_COMPATIBILITY_MODE.get(env)), - env.getListener()); - } catch (ExternalDepsException e) { - throw new BazelModuleResolutionFunctionException(e, Transience.PERSISTENT); - } - + checkBazelCompatibility( + resolvedDepGraph.values(), + Objects.requireNonNull(BAZEL_COMPATIBILITY_MODE.get(env)), + env.getListener()); + verifyYankedVersions( + resolvedDepGraph, + parseYankedVersions( + allowedYankedVersionsFromEnv.getValue(), + Objects.requireNonNull(ALLOWED_YANKED_VERSIONS.get(env))), + env.getListener()); verifyRootModuleDirectDepsAreAccurate( env, initialDepGraph.get(ModuleKey.ROOT), resolvedDepGraph.get(ModuleKey.ROOT)); return createValue(resolvedDepGraph, selectionResult.getUnprunedDepGraph(), overrides); } - public static void checkCompatibility( + public static void checkBazelCompatibility( ImmutableCollection<Module> modules, BazelCompatibilityMode mode, EventHandler eventHandler) - throws ExternalDepsException { + throws BazelModuleResolutionFunctionException { if (mode == BazelCompatibilityMode.OFF) { return; } @@ -126,14 +147,150 @@ eventHandler.handle(Event.warn(message)); } else { eventHandler.handle(Event.error(message)); - throw ExternalDepsException.withMessage( - Code.VERSION_RESOLUTION_ERROR, "Bazel compatibility check failed"); + throw new BazelModuleResolutionFunctionException( + ExternalDepsException.withMessage( + Code.VERSION_RESOLUTION_ERROR, "Bazel compatibility check failed"), + Transience.PERSISTENT); } } } } } + /** + * Parse a set of allowed yanked version from command line flag (--allowed_yanked_versions) and + * environment variable (ALLOWED_YANKED_VERSIONS). If `all` is specified, return Optional.empty(); + * otherwise returns the set of parsed modulel key. + */ + private Optional<ImmutableSet<ModuleKey>> parseYankedVersions( + String allowedYankedVersionsFromEnv, List<String> allowedYankedVersionsFromFlag) + throws BazelModuleResolutionFunctionException { + ImmutableSet.Builder<ModuleKey> allowedYankedVersionBuilder = new ImmutableSet.Builder<>(); + if (allowedYankedVersionsFromEnv != null) { + if (parseModuleKeysFromString( + allowedYankedVersionsFromEnv, + allowedYankedVersionBuilder, + String.format( + "envirnoment variable %s=%s", + BZLMOD_ALLOWED_YANKED_VERSIONS_ENV, allowedYankedVersionsFromEnv))) { + return Optional.empty(); + } + } + for (String allowedYankedVersions : allowedYankedVersionsFromFlag) { + if (parseModuleKeysFromString( + allowedYankedVersions, + allowedYankedVersionBuilder, + String.format("command line flag --allow_yanked_versions=%s", allowedYankedVersions))) { + return Optional.empty(); + } + } + return Optional.of(allowedYankedVersionBuilder.build()); + } + + /** + * Parse of a comma-separated list of module version(s) of the form '<module name>@<version>' or + * 'all' from the string. Returns true if 'all' is present, otherwise returns false. + */ + private boolean parseModuleKeysFromString( + String input, ImmutableSet.Builder<ModuleKey> allowedYankedVersionBuilder, String context) + throws BazelModuleResolutionFunctionException { + ImmutableList<String> moduleStrs = ImmutableList.copyOf(Splitter.on(',').split(input)); + + for (String moduleStr : moduleStrs) { + if (moduleStr.equals("all")) { + return true; + } + + if (moduleStr.isEmpty()) { + continue; + } + + String[] pieces = moduleStr.split("@", 2); + + if (pieces.length != 2) { + throw new BazelModuleResolutionFunctionException( + ExternalDepsException.withMessage( + Code.VERSION_RESOLUTION_ERROR, + "Parsing %s failed, module versions must be of the form '<module name>@<version>'", + context), + Transience.PERSISTENT); + } + + if (!RepositoryName.VALID_MODULE_NAME.matcher(pieces[0]).matches()) { + throw new BazelModuleResolutionFunctionException( + ExternalDepsException.withMessage( + Code.VERSION_RESOLUTION_ERROR, + "Parsing %s failed, invalid module name '%s': valid names must 1) only contain" + + " lowercase letters (a-z), digits (0-9), dots (.), hyphens (-), and" + + " underscores (_); 2) begin with a lowercase letter; 3) end with a lowercase" + + " letter or digit.", + context, + pieces[0]), + Transience.PERSISTENT); + } + + Version version; + try { + version = Version.parse(pieces[1]); + } catch (ParseException e) { + throw new BazelModuleResolutionFunctionException( + ExternalDepsException.withCauseAndMessage( + Code.VERSION_RESOLUTION_ERROR, + e, + "Parsing %s failed, invalid version specified for module: %s", + context, + pieces[1]), + Transience.PERSISTENT); + } + + allowedYankedVersionBuilder.add(ModuleKey.create(pieces[0], version)); + } + return false; + } + + private void verifyYankedVersions( + ImmutableMap<ModuleKey, Module> depGraph, + Optional<ImmutableSet<ModuleKey>> allowedYankedVersions, + ExtendedEventHandler eventHandler) + throws BazelModuleResolutionFunctionException, InterruptedException { + // Check whether all resolved modules are either not yanked or allowed. Modules with a + // NonRegistryOverride are ignored as their metadata is not available whatsoever. + for (Module m : depGraph.values()) { + if (m.getKey().equals(ModuleKey.ROOT) || m.getRegistry() == null) { + continue; + } + Optional<ImmutableMap<Version, String>> yankedVersions; + try { + yankedVersions = m.getRegistry().getYankedVersions(m.getKey().getName(), eventHandler); + } catch (IOException e) { + eventHandler.handle( + Event.warn( + String.format( + "Could not read metadata file for module %s: %s", m.getKey(), e.getMessage()))); + continue; + } + if (yankedVersions.isEmpty()) { + continue; + } + String yankedInfo = yankedVersions.get().get(m.getVersion()); + if (yankedInfo != null + && allowedYankedVersions.isPresent() + && !allowedYankedVersions.get().contains(m.getKey())) { + throw new BazelModuleResolutionFunctionException( + ExternalDepsException.withMessage( + Code.VERSION_RESOLUTION_ERROR, + "Yanked version detected in your resolved dependency graph: %s, for the reason: " + + "%s.\nYanked versions may contain serious vulnerabilities and should not be " + + "used. To fix this, use a bazel_dep on a newer version of this module. To " + + "continue using this version, allow it using the --allow_yanked_versions " + + "flag or the BZLMOD_ALLOW_YANKED_VERSIONS env variable.", + m.getKey(), + yankedInfo), + Transience.PERSISTENT); + } + } + } + private static void verifyRootModuleDirectDepsAreAccurate( Environment env, Module discoveredRootModule, Module resolvedRootModule) throws InterruptedException, BazelModuleResolutionFunctionException {
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/IndexRegistry.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/IndexRegistry.java index 8e2b022..4f885ba 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/IndexRegistry.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/IndexRegistry.java
@@ -20,6 +20,7 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.devtools.build.lib.bazel.bzlmod.Version.ParseException; import com.google.devtools.build.lib.bazel.repository.downloader.DownloadManager; import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.events.ExtendedEventHandler; @@ -33,6 +34,7 @@ import java.net.URI; import java.net.URL; import java.util.Map; +import java.util.Map.Entry; import java.util.Optional; /** @@ -113,7 +115,7 @@ private <T> Optional<T> grabJson(String url, Class<T> klass, ExtendedEventHandler eventHandler) throws IOException, InterruptedException { Optional<byte[]> bytes = grabFile(url, eventHandler); - if (!bytes.isPresent()) { + if (bytes.isEmpty()) { return Optional.empty(); } String jsonString = new String(bytes.get(), UTF_8); @@ -123,7 +125,8 @@ try { return Optional.of(gson.fromJson(jsonString, klass)); } catch (JsonParseException e) { - throw new IOException(String.format("Unable to parse json at url %s", url), e); + throw new IOException( + String.format("Unable to parse json at url %s: %s", url, e.getMessage()), e); } } @@ -140,7 +143,7 @@ getUrl(), "modules", key.getName(), key.getVersion().toString(), "source.json"), SourceJson.class, eventHandler); - if (!sourceJson.isPresent()) { + if (sourceJson.isEmpty()) { throw new FileNotFoundException( String.format("Module %s's source information not found in registry %s", key, getUrl())); } @@ -194,4 +197,39 @@ .setRemotePatchStrip(sourceJson.get().patchStrip) .build(); } + + @Override + public Optional<ImmutableMap<Version, String>> getYankedVersions( + String moduleName, ExtendedEventHandler eventHandler) + throws IOException, InterruptedException { + Optional<MetadataJson> metadataJson = + grabJson( + constructUrl(getUrl(), "modules", moduleName, "metadata.json"), + MetadataJson.class, + eventHandler); + if (metadataJson.isEmpty()) { + return Optional.empty(); + } + + try { + ImmutableMap.Builder<Version, String> yankedVersionsBuilder = new ImmutableMap.Builder<>(); + if (metadataJson.get().yankedVersions != null) { + for (Entry<String, String> e : metadataJson.get().yankedVersions.entrySet()) { + yankedVersionsBuilder.put(Version.parse(e.getKey()), e.getValue()); + } + } + return Optional.of(yankedVersionsBuilder.buildOrThrow()); + } catch (ParseException e) { + throw new IOException( + String.format( + "Could not parse module %s's metadata file: %s", moduleName, e.getMessage())); + } + } + + /** Represents fields available in {@code metadata.json} for each module. */ + static class MetadataJson { + // There are other attributes in the metadata.json file, but for now, we only care about + // the yanked_version attribute. + Map<String, String> yankedVersions; + } }
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Registry.java b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Registry.java index a6eff34..547bba7 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Registry.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/bzlmod/Registry.java
@@ -15,6 +15,7 @@ package com.google.devtools.build.lib.bazel.bzlmod; +import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.events.ExtendedEventHandler; import java.io.IOException; @@ -39,4 +40,12 @@ */ RepoSpec getRepoSpec(ModuleKey key, RepositoryName repoName, ExtendedEventHandler eventHandler) throws IOException, InterruptedException; + + /** + * Retrieves yanked versions of the module identified by {@code key.getName()} from the registry. + * Returns {@code Optional.empty()} when the information is not found in the registry. + */ + Optional<ImmutableMap<Version, String>> getYankedVersions( + String moduleName, ExtendedEventHandler eventHandler) + throws IOException, InterruptedException; }
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/repository/BUILD index 8c18a58..32f9580 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/BUILD +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/BUILD
@@ -68,5 +68,6 @@ "//src/main/java/com/google/devtools/build/lib/vfs:pathfragment", "//src/main/java/com/google/devtools/common/options", "//third_party:auto_value", + "//third_party:guava", ], )
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java index f0e52f6..2e515fc 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryOptions.java
@@ -58,6 +58,22 @@ public List<String> registries; @Option( + name = "allow_yanked_versions", + defaultValue = "null", + allowMultiple = true, + documentationCategory = OptionDocumentationCategory.BZLMOD, + effectTags = {OptionEffectTag.LOADING_AND_ANALYSIS}, + help = + "Specified the module versions in the form of" + + " `<module1>@<version1>,<module2>@<version2>` that will be allowed in the resolved" + + " dependency graph even if they are declared yanked in the registry where they come" + + " from (if they are not coming from a NonRegistryOverride). Otherwise, yanked" + + " versions will cause the resolution to fail. You can also define allowed yanked" + + " version with the `BZLMOD_ALLOW_YANKED_VERSIONS` environment variable. You can" + + " disable this check by using the keyword 'all' (not recommended).") + public List<String> allowedYankedVersions; + + @Option( name = "experimental_repository_cache_hardlinks", defaultValue = "false", documentationCategory = OptionDocumentationCategory.BAZEL_CLIENT_OPTIONS,
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/StarlarkRuleTransitionProviderTest.java b/src/test/java/com/google/devtools/build/lib/analysis/StarlarkRuleTransitionProviderTest.java index fa4b6bb..5acdfe6 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/StarlarkRuleTransitionProviderTest.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/StarlarkRuleTransitionProviderTest.java
@@ -70,6 +70,8 @@ PrecomputedValue.injected(ModuleFileFunction.IGNORE_DEV_DEPS, false), PrecomputedValue.injected(ModuleFileFunction.MODULE_OVERRIDES, ImmutableMap.of()), PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of()), + PrecomputedValue.injected( BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), PrecomputedValue.injected( BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR));
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java index bde660f..55fca94 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java
@@ -36,6 +36,7 @@ import com.google.devtools.build.lib.rules.repository.RepositoryDelegatorFunction; import com.google.devtools.build.lib.rules.repository.RepositoryFunction; import com.google.devtools.build.lib.skyframe.BazelSkyframeExecutorConstants; +import com.google.devtools.build.lib.skyframe.ClientEnvironmentFunction; import com.google.devtools.build.lib.skyframe.SkyFunctions; import com.google.devtools.build.lib.skyframe.packages.PackageFactoryBuilderWithSkyframeForTesting; import com.google.devtools.build.lib.testutil.TestConstants; @@ -46,6 +47,7 @@ import java.lang.reflect.Field; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; /** Create a mock client for the analysis phase, as well as a configuration factory. */ public abstract class AnalysisMock extends LoadingMock { @@ -140,6 +142,8 @@ getBuiltinModules(directories)), SkyFunctions.BAZEL_MODULE_RESOLUTION, new BazelModuleResolutionFunction(), + SkyFunctions.CLIENT_ENVIRONMENT_VARIABLE, + new ClientEnvironmentFunction(new AtomicReference<>(ImmutableMap.of())), CcSkyframeFdoSupportValue.SKYFUNCTION, new CcSkyframeFdoSupportFunction(directories)); }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java index 8579f2e..f28ef3a 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestCase.java
@@ -238,6 +238,8 @@ BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of()), + PrecomputedValue.injected( BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR))) .build(ruleClassProvider, fileSystem); @@ -284,6 +286,8 @@ BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of()), + PrecomputedValue.injected( BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.WARNING))); }
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD b/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD index 0976108..d8ff4b6 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD +++ b/src/test/java/com/google/devtools/build/lib/analysis/util/BUILD
@@ -104,6 +104,7 @@ "//src/main/java/com/google/devtools/build/lib/shell", "//src/main/java/com/google/devtools/build/lib/skyframe:aspect_key_creator", "//src/main/java/com/google/devtools/build/lib/skyframe:build_configuration", + "//src/main/java/com/google/devtools/build/lib/skyframe:client_environment_function", "//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_and_data", "//src/main/java/com/google/devtools/build/lib/skyframe:configured_target_key", "//src/main/java/com/google/devtools/build/lib/skyframe:diff_awareness",
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD index 72a1903..71844cb 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BUILD
@@ -55,6 +55,7 @@ "//src/main/java/com/google/devtools/build/lib/skyframe:bzl_load_cycle_reporter", "//src/main/java/com/google/devtools/build/lib/skyframe:bzl_load_value", "//src/main/java/com/google/devtools/build/lib/skyframe:bzlmod_repo_cycle_reporter", + "//src/main/java/com/google/devtools/build/lib/skyframe:client_environment_function", "//src/main/java/com/google/devtools/build/lib/skyframe:containing_package_lookup_function", "//src/main/java/com/google/devtools/build/lib/skyframe:file_function", "//src/main/java/com/google/devtools/build/lib/skyframe:ignored_package_prefixes_function",
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunctionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunctionTest.java index 907ec3b..56d1b10 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BazelModuleResolutionFunctionTest.java
@@ -37,6 +37,7 @@ import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions; import com.google.devtools.build.lib.pkgcache.PathPackageLocator; import com.google.devtools.build.lib.skyframe.BazelSkyframeExecutorConstants; +import com.google.devtools.build.lib.skyframe.ClientEnvironmentFunction; import com.google.devtools.build.lib.skyframe.ExternalFilesHelper; import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction; import com.google.devtools.build.lib.skyframe.FileFunction; @@ -117,6 +118,9 @@ new ModuleFileFunction(registryFactory, rootDirectory, ImmutableMap.of())) .put(SkyFunctions.PRECOMPUTED, new PrecomputedFunction()) .put(SkyFunctions.BAZEL_MODULE_RESOLUTION, new BazelModuleResolutionFunction()) + .put( + SkyFunctions.CLIENT_ENVIRONMENT_VARIABLE, + new ClientEnvironmentFunction(new AtomicReference<>(ImmutableMap.of()))) .buildOrThrow(), differencer); @@ -129,6 +133,7 @@ differencer, CheckDirectDepsMode.OFF); BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE.set( differencer, BazelCompatibilityMode.ERROR); + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS.set(differencer, ImmutableList.of()); } @Test @@ -447,4 +452,68 @@ ModuleFileFunction.REGISTRIES.set(differencer, ImmutableList.of(registry.getUrl())); } + @Test + public void testYankedVersionCheckSuccess() throws Exception { + setupModulesForYankedVersion(); + reporter.removeHandler(failFastHandler); + EvaluationResult<BazelModuleResolutionValue> result = + evaluator.evaluate(ImmutableList.of(BazelModuleResolutionValue.KEY), evaluationContext); + + assertThat(result.hasError()).isTrue(); + assertThat(result.getError().toString()) + .contains( + "Yanked version detected in your resolved dependency graph: b@1.0, for the reason: 1.0" + + " is a bad version!"); + } + + @Test + public void testYankedVersionCheckIgnoredByAll() throws Exception { + setupModulesForYankedVersion(); + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS.set(differencer, ImmutableList.of("all")); + EvaluationResult<BazelModuleResolutionValue> result = + evaluator.evaluate(ImmutableList.of(BazelModuleResolutionValue.KEY), evaluationContext); + assertThat(result.hasError()).isFalse(); + } + + @Test + public void testYankedVersionCheckIgnoredBySpecific() throws Exception { + setupModulesForYankedVersion(); + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS.set( + differencer, ImmutableList.of("b@1.0")); + EvaluationResult<BazelModuleResolutionValue> result = + evaluator.evaluate(ImmutableList.of(BazelModuleResolutionValue.KEY), evaluationContext); + assertThat(result.hasError()).isFalse(); + } + + @Test + public void testBadYankedVersionFormat() throws Exception { + setupModulesForYankedVersion(); + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS.set( + differencer, ImmutableList.of("b~1.0")); + EvaluationResult<BazelModuleResolutionValue> result = + evaluator.evaluate(ImmutableList.of(BazelModuleResolutionValue.KEY), evaluationContext); + assertThat(result.hasError()).isTrue(); + assertThat(result.getError().toString()) + .contains( + "Parsing command line flag --allow_yanked_versions=b~1.0 failed, module versions must" + + " be of the form '<module name>@<version>'"); + } + + private void setupModulesForYankedVersion() throws Exception { + scratch.file( + rootDirectory.getRelative("MODULE.bazel").getPathString(), + "module(name='mod', version='1.0')", + "bazel_dep(name = 'a', version = '1.0')"); + + FakeRegistry registry = + registryFactory + .newFakeRegistry("/bar") + .addModule( + createModuleKey("a", "1.0"), + "module(name='a', version='1.0');", + "bazel_dep(name='b', version='1.0')") + .addModule(createModuleKey("b", "1.0"), "module(name='b', version='1.0');") + .addYankedVersion("b", ImmutableMap.of(Version.parse("1.0"), "1.0 is a bad version!")); + ModuleFileFunction.REGISTRIES.set(differencer, ImmutableList.of(registry.getUrl())); + } }
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodRepoRuleHelperTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodRepoRuleHelperTest.java index 8a71301..eece035 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodRepoRuleHelperTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/BzlmodRepoRuleHelperTest.java
@@ -33,6 +33,7 @@ import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.pkgcache.PathPackageLocator; import com.google.devtools.build.lib.skyframe.BazelSkyframeExecutorConstants; +import com.google.devtools.build.lib.skyframe.ClientEnvironmentFunction; import com.google.devtools.build.lib.skyframe.ExternalFilesHelper; import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction; import com.google.devtools.build.lib.skyframe.FileFunction; @@ -123,12 +124,16 @@ GET_REPO_SPEC_BY_NAME_FUNCTION, new GetRepoSpecByNameFunction(new BzlmodRepoRuleHelperImpl())) .put(SkyFunctions.PRECOMPUTED, new PrecomputedFunction()) + .put( + SkyFunctions.CLIENT_ENVIRONMENT_VARIABLE, + new ClientEnvironmentFunction(new AtomicReference<>(ImmutableMap.of()))) .buildOrThrow(), differencer); PrecomputedValue.STARLARK_SEMANTICS.set(differencer, StarlarkSemantics.DEFAULT); ModuleFileFunction.IGNORE_DEV_DEPS.set(differencer, false); ModuleFileFunction.MODULE_OVERRIDES.set(differencer, ImmutableMap.of()); + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS.set(differencer, ImmutableList.of()); BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES.set( differencer, CheckDirectDepsMode.WARNING); BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE.set(
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/DiscoveryTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/DiscoveryTest.java index 96d082b..40bf514 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/DiscoveryTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/DiscoveryTest.java
@@ -197,6 +197,7 @@ RepositoryDelegatorFunction.RESOLVED_FILE_FOR_VERIFICATION.set(differencer, Optional.empty()); ModuleFileFunction.IGNORE_DEV_DEPS.set(differencer, false); ModuleFileFunction.MODULE_OVERRIDES.set(differencer, ImmutableMap.of()); + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS.set(differencer, ImmutableList.of()); } @Test
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/FakeRegistry.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/FakeRegistry.java index df6aa5a..e731065 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/FakeRegistry.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/FakeRegistry.java
@@ -37,6 +37,7 @@ private final String url; private final String rootPath; private final Map<ModuleKey, String> modules = new HashMap<>(); + private final Map<String, ImmutableMap<Version, String>> yankedVersionMap = new HashMap<>(); public FakeRegistry(String url, String rootPath) { this.url = url; @@ -49,6 +50,13 @@ return this; } + @CanIgnoreReturnValue + public FakeRegistry addYankedVersion( + String moduleName, ImmutableMap<Version, String> yankedVersions) { + yankedVersionMap.put(moduleName, yankedVersions); + return this; + } + @Override public String getUrl() { return url; @@ -71,6 +79,12 @@ } @Override + public Optional<ImmutableMap<Version, String>> getYankedVersions( + String moduleName, ExtendedEventHandler eventHandler) { + return Optional.ofNullable(yankedVersionMap.get(moduleName)); + } + + @Override public boolean equals(Object other) { return other instanceof FakeRegistry && this.url.equals(((FakeRegistry) other).url)
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/IndexRegistryTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/IndexRegistryTest.java index a50a629..fc66fc0 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/IndexRegistryTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/IndexRegistryTest.java
@@ -39,6 +39,7 @@ import java.io.IOException; import java.io.Writer; import java.nio.file.Files; +import java.util.Optional; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -235,4 +236,36 @@ registry.getRepoSpec( createModuleKey("foo", "1.0"), RepositoryName.create("foorepo"), reporter)); } + + @Test + public void testGetYankedVersion() throws Exception { + server.serve( + "/modules/red-pill/metadata.json", + "{\n" + + " 'homepage': 'https://docs.matrix.org/red-pill',\n" + + " 'maintainers': [\n" + + " {\n" + + " 'email': 'neo@matrix.org',\n" + + " 'github': 'neo',\n" + + " 'name': 'Neo'\n" + + " }\n" + + " ],\n" + + " 'versions': [\n" + + " '1.0',\n" + + " '2.0'\n" + + " ],\n" + + " 'yanked_versions': {" + + " '1.0': 'red-pill 1.0 is yanked due to CVE-2000-101, please upgrade to 2.0'\n" + + " }\n" + + "}"); + server.start(); + Registry registry = registryFactory.getRegistryWithUrl(server.getUrl()); + Optional<ImmutableMap<Version, String>> yankedVersion = + registry.getYankedVersions("red-pill", reporter); + assertThat(yankedVersion) + .hasValue( + ImmutableMap.of( + Version.parse("1.0"), + "red-pill 1.0 is yanked due to CVE-2000-101, please upgrade to 2.0")); + } }
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java index 7f1ad04..5f0b49c 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleExtensionResolutionTest.java
@@ -54,6 +54,7 @@ import com.google.devtools.build.lib.skyframe.BzlLoadValue; import com.google.devtools.build.lib.skyframe.BzlmodRepoCycleReporter; import com.google.devtools.build.lib.skyframe.BzlmodRepoRuleFunction; +import com.google.devtools.build.lib.skyframe.ClientEnvironmentFunction; import com.google.devtools.build.lib.skyframe.ContainingPackageLookupFunction; import com.google.devtools.build.lib.skyframe.ExternalFilesHelper; import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction; @@ -243,6 +244,9 @@ .put(SkyFunctions.BAZEL_MODULE_RESOLUTION, new BazelModuleResolutionFunction()) .put(SkyFunctions.SINGLE_EXTENSION_USAGES, new SingleExtensionUsagesFunction()) .put(SkyFunctions.SINGLE_EXTENSION_EVAL, singleExtensionEvalFunction) + .put( + SkyFunctions.CLIENT_ENVIRONMENT_VARIABLE, + new ClientEnvironmentFunction(new AtomicReference<>(ImmutableMap.of()))) .build(), differencer); @@ -261,6 +265,7 @@ RepositoryDelegatorFunction.RESOLVED_FILE_FOR_VERIFICATION.set(differencer, Optional.empty()); ModuleFileFunction.IGNORE_DEV_DEPS.set(differencer, false); ModuleFileFunction.MODULE_OVERRIDES.set(differencer, ImmutableMap.of()); + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS.set(differencer, ImmutableList.of()); ModuleFileFunction.REGISTRIES.set(differencer, ImmutableList.of(registry.getUrl())); BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES.set( differencer, CheckDirectDepsMode.WARNING);
diff --git a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunctionTest.java b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunctionTest.java index e4167a1..cdedc1b 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/bzlmod/ModuleFileFunctionTest.java
@@ -152,9 +152,7 @@ .put( BzlmodRepoRuleValue.BZLMOD_REPO_RULE, new BzlmodRepoRuleFunction( - ruleClassProvider, - directories, - new BzlmodRepoRuleHelperImpl())) + ruleClassProvider, directories, new BzlmodRepoRuleHelperImpl())) .buildOrThrow(), differencer); @@ -173,6 +171,7 @@ RepositoryDelegatorFunction.RESOLVED_FILE_FOR_VERIFICATION.set(differencer, Optional.empty()); ModuleFileFunction.IGNORE_DEV_DEPS.set(differencer, false); ModuleFileFunction.MODULE_OVERRIDES.set(differencer, ImmutableMap.of()); + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS.set(differencer, ImmutableList.of()); } @Test
diff --git a/src/test/java/com/google/devtools/build/lib/query2/testutil/SkyframeQueryHelper.java b/src/test/java/com/google/devtools/build/lib/query2/testutil/SkyframeQueryHelper.java index 12be403..c791ada 100644 --- a/src/test/java/com/google/devtools/build/lib/query2/testutil/SkyframeQueryHelper.java +++ b/src/test/java/com/google/devtools/build/lib/query2/testutil/SkyframeQueryHelper.java
@@ -366,6 +366,8 @@ BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of()), + PrecomputedValue.injected( BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR))) .setEnvironmentExtensions(getEnvironmentExtensions()) @@ -405,6 +407,9 @@ CheckDirectDepsMode.WARNING)) .add( PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of())) + .add( + PrecomputedValue.injected( BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR)) .build());
diff --git a/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java b/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java index 7f81617..78fb2d9 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/LabelBuildSettingTest.java
@@ -53,6 +53,8 @@ PrecomputedValue.injected(ModuleFileFunction.IGNORE_DEV_DEPS, false), PrecomputedValue.injected(ModuleFileFunction.MODULE_OVERRIDES, ImmutableMap.of()), PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of()), + PrecomputedValue.injected( BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), PrecomputedValue.injected( BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR));
diff --git a/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD b/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD index 240b70b..f9aa10a 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD +++ b/src/test/java/com/google/devtools/build/lib/rules/repository/BUILD
@@ -23,7 +23,6 @@ "//src/main/java/com/google/devtools/build/lib/analysis:server_directories", "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:repo_rule_helper", "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:repo_rule_value", - "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:resolution", "//src/main/java/com/google/devtools/build/lib/bazel/bzlmod:resolution_impl", "//src/main/java/com/google/devtools/build/lib/bazel/repository:repository_options", "//src/main/java/com/google/devtools/build/lib/bazel/repository/downloader", @@ -38,6 +37,7 @@ "//src/main/java/com/google/devtools/build/lib/rules:repository/repository_directory_value", "//src/main/java/com/google/devtools/build/lib/rules:repository/repository_function", "//src/main/java/com/google/devtools/build/lib/skyframe:bzl_compile", + "//src/main/java/com/google/devtools/build/lib/skyframe:client_environment_function", "//src/main/java/com/google/devtools/build/lib/skyframe:containing_package_lookup_function", "//src/main/java/com/google/devtools/build/lib/skyframe:file_function", "//src/main/java/com/google/devtools/build/lib/skyframe:ignored_package_prefixes_function",
diff --git a/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java b/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java index 220e7b7..4bc793d 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/repository/RepositoryDelegatorTest.java
@@ -53,6 +53,7 @@ import com.google.devtools.build.lib.skyframe.BzlCompileFunction; import com.google.devtools.build.lib.skyframe.BzlLoadFunction; import com.google.devtools.build.lib.skyframe.BzlmodRepoRuleFunction; +import com.google.devtools.build.lib.skyframe.ClientEnvironmentFunction; import com.google.devtools.build.lib.skyframe.ContainingPackageLookupFunction; import com.google.devtools.build.lib.skyframe.ExternalFilesHelper; import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction; @@ -227,6 +228,9 @@ BzlmodRepoRuleValue.BZLMOD_REPO_RULE, new BzlmodRepoRuleFunction( ruleClassProvider, directories, new BzlmodRepoRuleHelperImpl())) + .put( + SkyFunctions.CLIENT_ENVIRONMENT_VARIABLE, + new ClientEnvironmentFunction(new AtomicReference<>(ImmutableMap.of()))) .build(), differencer); overrideDirectory = scratch.dir("/foo"); @@ -246,6 +250,7 @@ RepositoryDelegatorFunction.RESOLVED_FILE_FOR_VERIFICATION.set(differencer, Optional.empty()); ModuleFileFunction.IGNORE_DEV_DEPS.set(differencer, false); ModuleFileFunction.MODULE_OVERRIDES.set(differencer, ImmutableMap.of()); + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS.set(differencer, ImmutableList.of()); BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES.set( differencer, CheckDirectDepsMode.WARNING); BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE.set(
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/BzlLoadFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/BzlLoadFunctionTest.java index c99f8cb..f0ac79e 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/BzlLoadFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/BzlLoadFunctionTest.java
@@ -87,9 +87,9 @@ packageOptions, Options.getDefaults(BuildLanguageOptions.class), UUID.randomUUID(), - ImmutableMap.<String, String>of(), + ImmutableMap.of(), new TimestampGranularityMonitor(BlazeClock.instance())); - skyframeExecutor.setActionEnv(ImmutableMap.<String, String>of()); + skyframeExecutor.setActionEnv(ImmutableMap.of()); } @Override @@ -106,6 +106,8 @@ PrecomputedValue.injected(ModuleFileFunction.IGNORE_DEV_DEPS, false), PrecomputedValue.injected(ModuleFileFunction.MODULE_OVERRIDES, ImmutableMap.of()), PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of()), + PrecomputedValue.injected( BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), PrecomputedValue.injected( BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR));
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfPatternsFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfPatternsFunctionTest.java index e19df5e..aefc7e6 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfPatternsFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/PrepareDepsOfPatternsFunctionTest.java
@@ -242,6 +242,8 @@ PrecomputedValue.injected( BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of()), + PrecomputedValue.injected( BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR)); }
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredExecutionPlatformsFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredExecutionPlatformsFunctionTest.java index 5ac9ca7..1a3547b 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredExecutionPlatformsFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredExecutionPlatformsFunctionTest.java
@@ -106,6 +106,8 @@ PrecomputedValue.injected(ModuleFileFunction.IGNORE_DEV_DEPS, false), PrecomputedValue.injected(ModuleFileFunction.MODULE_OVERRIDES, ImmutableMap.of()), PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of()), + PrecomputedValue.injected( BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), PrecomputedValue.injected( BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR));
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredToolchainsFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredToolchainsFunctionTest.java index 3db6120..3178509 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredToolchainsFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/RegisteredToolchainsFunctionTest.java
@@ -62,6 +62,8 @@ PrecomputedValue.injected(ModuleFileFunction.IGNORE_DEV_DEPS, false), PrecomputedValue.injected(ModuleFileFunction.MODULE_OVERRIDES, ImmutableMap.of()), PrecomputedValue.injected( + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS, ImmutableList.of()), + PrecomputedValue.injected( BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES, CheckDirectDepsMode.WARNING), PrecomputedValue.injected( BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE, BazelCompatibilityMode.ERROR));
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/RepositoryMappingFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/RepositoryMappingFunctionTest.java index 8de2cb0..b3a0d4d 100644 --- a/src/test/java/com/google/devtools/build/lib/skyframe/RepositoryMappingFunctionTest.java +++ b/src/test/java/com/google/devtools/build/lib/skyframe/RepositoryMappingFunctionTest.java
@@ -75,6 +75,8 @@ ModuleFileFunction.IGNORE_DEV_DEPS.set(getSkyframeExecutor().getDifferencerForTesting(), false); ModuleFileFunction.MODULE_OVERRIDES.set( getSkyframeExecutor().getDifferencerForTesting(), ImmutableMap.of()); + BazelModuleResolutionFunction.ALLOWED_YANKED_VERSIONS.set( + getSkyframeExecutor().getDifferencerForTesting(), ImmutableList.of()); BazelModuleResolutionFunction.CHECK_DIRECT_DEPENDENCIES.set( getSkyframeExecutor().getDifferencerForTesting(), CheckDirectDepsMode.WARNING); BazelModuleResolutionFunction.BAZEL_COMPATIBILITY_MODE.set(
diff --git a/src/test/py/bazel/bzlmod/bazel_module_test.py b/src/test/py/bazel/bzlmod/bazel_module_test.py index 321df9b..58ce6fd 100644 --- a/src/test/py/bazel/bzlmod/bazel_module_test.py +++ b/src/test/py/bazel/bzlmod/bazel_module_test.py
@@ -33,18 +33,24 @@ .createCcModule('aaa', '1.1') \ .createCcModule('bbb', '1.0', {'aaa': '1.0'}, {'aaa': 'com_foo_aaa'}) \ .createCcModule('bbb', '1.1', {'aaa': '1.1'}) \ - .createCcModule('ccc', '1.1', {'aaa': '1.1', 'bbb': '1.1'}) + .createCcModule('ccc', '1.1', {'aaa': '1.1', 'bbb': '1.1'}) \ + .createCcModule('ddd', '1.0', {'yanked1': '1.0', 'yanked2': '1.0'}) \ + .createCcModule('eee', '1.0', {'yanked1': '1.0'}) \ + .createCcModule('yanked1', '1.0') \ + .createCcModule('yanked2', '1.0') \ + .addMetadata('yanked1', yanked_versions={'1.0': 'dodgy'}) \ + .addMetadata('yanked2', yanked_versions={'1.0': 'sketchy'}) self.ScratchFile( '.bazelrc', [ # In ipv6 only network, this has to be enabled. # 'startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true', - 'build --experimental_enable_bzlmod', - 'build --registry=' + self.main_registry.getURL(), + 'common --experimental_enable_bzlmod', + 'common --registry=' + self.main_registry.getURL(), # We need to have BCR here to make sure built-in modules like # bazel_tools can work. - 'build --registry=https://bcr.bazel.build', - 'build --verbose_failures', + 'common --registry=https://bcr.bazel.build', + 'common --verbose_failures', ]) self.ScratchFile('WORKSPACE') # The existence of WORKSPACE.bzlmod prevents WORKSPACE prefixes or suffixes @@ -426,5 +432,105 @@ ]) self.RunBazel(['build', '@no_op//:no_op'], allow_failure=False) + def testNonRegistryOverriddenModulesIgnoreYanked(self): + src_yanked1 = self.main_registry.projects.joinpath('yanked1', '1.0') + self.ScratchFile('MODULE.bazel', [ + 'bazel_dep(name = "yanked1", version = "1.0")', 'local_path_override(', + ' module_name = "yanked1",', + ' path = "%s",' % str(src_yanked1.resolve()).replace('\\', '/'), ')' + ]) + self.ScratchFile('WORKSPACE') + self.ScratchFile('BUILD', [ + 'cc_binary(', + ' name = "main",', + ' srcs = ["main.cc"],', + ' deps = ["@yanked1//:lib_yanked1"],', + ')', + ]) + self.RunBazel(['build', '--nobuild', '//:main'], allow_failure=False) + + def testContainingYankedDepFails(self): + self.ScratchFile('MODULE.bazel', [ + 'bazel_dep(name = "yanked1", version = "1.0")', + ]) + self.ScratchFile('WORKSPACE') + self.ScratchFile('BUILD', [ + 'cc_binary(', + ' name = "main",', + ' srcs = ["main.cc"],', + ' deps = ["@ddd//:lib_ddd"],', + ')', + ]) + exit_code, _, stderr = self.RunBazel(['build', '--nobuild', '//:main'], + allow_failure=True) + self.AssertExitCode(exit_code, 48, stderr) + self.assertIn( + 'Yanked version detected in your resolved dependency graph: ' + + 'yanked1@1.0, for the reason: dodgy.', ''.join(stderr)) + + def testAllowedYankedDepsSuccessByFlag(self): + self.ScratchFile('MODULE.bazel', [ + 'bazel_dep(name = "ddd", version = "1.0")', + ]) + self.ScratchFile('WORKSPACE') + self.ScratchFile('BUILD', [ + 'cc_binary(', + ' name = "main",', + ' srcs = ["main.cc"],', + ' deps = ["@ddd//:lib_ddd"],', + ')', + ]) + self.RunBazel([ + 'build', '--nobuild', '--allow_yanked_versions=yanked1@1.0,yanked2@1.0', + '//:main' + ], + allow_failure=False) + + def testAllowedYankedDepsByEnvVar(self): + self.ScratchFile('MODULE.bazel', [ + 'bazel_dep(name = "ddd", version = "1.0")', + ]) + self.ScratchFile('WORKSPACE') + self.ScratchFile('BUILD', [ + 'cc_binary(', + ' name = "main",', + ' srcs = ["main.cc"],', + ' deps = ["@ddd//:lib_ddd"],', + ')', + ]) + self.RunBazel( + ['build', '--nobuild', '//:main'], + env_add={'BZLMOD_ALLOW_YANKED_VERSIONS': 'yanked1@1.0,yanked2@1.0'}, + allow_failure=False) + + # Test changing the env var, the build should fail again. + exit_code, _, stderr = self.RunBazel( + ['build', '--nobuild', '//:main'], + env_add={'BZLMOD_ALLOW_YANKED_VERSIONS': 'yanked2@1.0'}, + allow_failure=True) + self.AssertExitCode(exit_code, 48, stderr) + self.assertIn( + 'Yanked version detected in your resolved dependency graph: ' + + 'yanked1@1.0, for the reason: dodgy.', ''.join(stderr)) + + def testAllowedYankedDepsSuccessMix(self): + self.ScratchFile('MODULE.bazel', [ + 'bazel_dep(name = "ddd", version = "1.0")', + ]) + self.ScratchFile('WORKSPACE') + self.ScratchFile('BUILD', [ + 'cc_binary(', + ' name = "main",', + ' srcs = ["main.cc"],', + ' deps = ["@ddd//:lib_ddd"],', + ')', + ]) + self.RunBazel([ + 'build', '--nobuild', '--allow_yanked_versions=yanked1@1.0', '//:main' + ], + env_add={'BZLMOD_ALLOW_YANKED_VERSIONS': 'yanked2@1.0'}, + allow_failure=False) + + if __name__ == '__main__': unittest.main()
diff --git a/src/test/py/bazel/bzlmod/test_utils.py b/src/test/py/bazel/bzlmod/test_utils.py index a7ff119..9de182a 100644 --- a/src/test/py/bazel/bzlmod/test_utils.py +++ b/src/test/py/bazel/bzlmod/test_utils.py
@@ -238,3 +238,32 @@ module.set_patches(patches, patch_strip) self.addModule(module) return self + + def addMetadata(self, + name, + homepage=None, + maintainers=None, + versions=None, + yanked_versions=None): + """Generate a module metadata file and add it to the registry.""" + if maintainers is None: + maintainers = [] + if versions is None: + versions = [] + if yanked_versions is None: + yanked_versions = {} + + module_dir = self.root.joinpath('modules', name) + module_dir.mkdir(parents=True, exist_ok=True) + + metadata = { + 'homepage': homepage, + 'maintainers': maintainers, + 'versions': versions, + 'yanked_versions': yanked_versions + } + + with module_dir.joinpath('metadata.json').open('w') as f: + json.dump(metadata, f, indent=4, sort_keys=True) + + return self