diff --git a/maven-plugin/src/main/java/org/wildfly/galleon/maven/AbstractFeaturePackBuildMojo.java b/maven-plugin/src/main/java/org/wildfly/galleon/maven/AbstractFeaturePackBuildMojo.java index 4ed2eaef..f7abd2b8 100644 --- a/maven-plugin/src/main/java/org/wildfly/galleon/maven/AbstractFeaturePackBuildMojo.java +++ b/maven-plugin/src/main/java/org/wildfly/galleon/maven/AbstractFeaturePackBuildMojo.java @@ -35,13 +35,16 @@ import java.nio.file.attribute.BasicFileAttributes; import java.time.Clock; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Properties; import java.util.Set; @@ -58,6 +61,7 @@ import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.DefaultProjectBuildingRequest; @@ -83,6 +87,7 @@ import org.jboss.galleon.layout.FeaturePackDescription; import org.jboss.galleon.maven.plugin.FpMavenErrors; import org.jboss.galleon.maven.plugin.util.MavenArtifactRepositoryManager; +import org.jboss.galleon.spec.ConfigLayerSpec; import org.jboss.galleon.spec.FeaturePackPlugin; import org.jboss.galleon.spec.FeaturePackSpec; import org.jboss.galleon.spec.FeatureSpec; @@ -231,6 +236,12 @@ static boolean isProvided(String module) { @Parameter(alias = "config-stability-level", required = false) protected String configStabilityLevel; + /** + * Enforce that no package at a lower stability level than the minimum-stability-level is referenced from Galleon constructs. + */ + @Parameter(alias = "forbid-lower-stability-level-package-reference", required = false, defaultValue = "false") + protected boolean forbidLowerStatibilityLevelPackageReference; + /** * The default stability level used at provisioning time when installing packages/JBoss Modules modules. * Can't be used when {@code stability-level} is set. @@ -255,6 +266,8 @@ static boolean isProvided(String module) { private Stability defaultConfigStabilityLevel; private Stability defaultPackageStabilityLevel; + private final Set lowerStabilityPackages = new HashSet<>(); + @Override public void execute() throws MojoExecutionException, MojoFailureException { try { @@ -433,7 +446,6 @@ protected void buildFeaturePack(FeaturePackDescription.Builder fpBuilder, WildFl ArtifactListBuilder builder = new ArtifactListBuilder(new MavenArtifactRepositoryManager(repoSystem, noWorkspaceSession, repositories), repoSession.getLocalRepository().getBasedir().toPath(), getLog()); buildArtifactList(builder); - addConfigPackages(resourcesDir.resolve(Constants.PACKAGES), fpDir.resolve(Constants.PACKAGES), fpBuilder); Util.copyIfExists(resourcesDir, fpDir, Constants.LAYERS); Util.copyIfExists(resourcesDir, fpDir, Constants.CONFIGS); @@ -489,8 +501,10 @@ protected void buildFeaturePack(FeaturePackDescription.Builder fpBuilder, WildFl } try { //Validate that we can parse this feature-pack... - FeaturePackDescriber.describeFeaturePack(versionDir,"UTF-8"); - } catch (ProvisioningDescriptionException ex) { + FeaturePackDescription desc = FeaturePackDescriber.describeFeaturePack(versionDir, "UTF-8"); + checkFeaturePackContentStability(forbidLowerStatibilityLevelPackageReference, lowerStabilityPackages, + desc.getPackages(), desc.getLayers(), desc.getFeatures(), desc.getConfigs(), getLog()); + } catch (Exception ex) { throw new RuntimeException(ex); } ZipUtils.zip(versionDir, target); @@ -521,6 +535,80 @@ protected void buildFeaturePack(FeaturePackDescription.Builder fpBuilder, WildFl } } + static void checkFeaturePackContentStability(boolean forbidLowerStatibilityLevelPackageReference, + Set lowerStabilityPackages, + Collection packages, + Collection layers, + Collection features, + Map> configs, Log log) throws Exception { + + // Validate that no packages at a lower stability level are referenced + for (PackageSpec spec : packages) { + if (spec.hasLocalPackageDeps()) { + for (PackageDependencySpec pds : spec.getLocalPackageDeps()) { + if (lowerStabilityPackages.contains(pds.getName())) { + String message = "package " + spec.getName() + " references the package " + pds.getName() + + " that has not been packaged due to its stability level being lower than the feature-pack minimum stability level. You should remove this reference."; + if (forbidLowerStatibilityLevelPackageReference) { + throw new Exception(message); + } else { + log.warn(message); + } + } + } + } + } + for (ConfigLayerSpec spec : layers) { + if (spec.hasLocalPackageDeps()) { + for (PackageDependencySpec pds : spec.getLocalPackageDeps()) { + if (lowerStabilityPackages.contains(pds.getName())) { + String message = "package " + spec.getName() + " references the package " + pds.getName() + + " that has not been packaged due to its stability level being lower than the feature-pack minimum stability level. You should remove this reference."; + if (forbidLowerStatibilityLevelPackageReference) { + throw new Exception(message); + } else { + log.warn(message); + } + } + } + } + } + for (FeatureSpec spec : features) { + if (spec.hasLocalPackageDeps()) { + for (PackageDependencySpec pds : spec.getLocalPackageDeps()) { + if (lowerStabilityPackages.contains(pds.getName())) { + String message = "feature " + spec.getName() + " references the package " + pds.getName() + + " that has not been packaged due to its stability level being lower than the feature-pack minimum stability level. You should remove this reference."; + if (forbidLowerStatibilityLevelPackageReference) { + throw new Exception(message); + } else { + log.warn(message); + } + } + } + } + } + for (Entry> configModel : configs.entrySet()) { + String modelName = configModel.getKey(); + for (Entry cm : configModel.getValue().entrySet()) { + ConfigModel config = cm.getValue(); + String configName = cm.getKey(); + if (config.hasLocalPackageDeps()) { + for (PackageDependencySpec pds : config.getLocalPackageDeps()) { + if (lowerStabilityPackages.contains(pds.getName())) { + String message = "config " + modelName + "/" + configName + " references the package " + pds.getName() + + " that has not been packaged due to its stability level being lower than the feature-pack minimum stability level. You should remove this reference."; + if (forbidLowerStatibilityLevelPackageReference) { + throw new Exception(message); + } else { + log.warn(message); + } + } + } + } + } + } + } /** * Create a YAML Channel Manifest that defines streams for all the feature-pack * dependencies. The feature-pack itself is added to the channel's streams. @@ -582,6 +670,7 @@ private void addConfigPackages(final Path configDir, final Path packagesDir, fin + "Package stability '" + packageStability + "' is not enabled by the '" + buildTimestabilityLevel + "' stability level that is the feature-pack minimum stability level."); + lowerStabilityPackages.add(pkgSpec.getName()); continue; } } @@ -869,6 +958,7 @@ private void packageModules(FeaturePackDescription.Builder fpBuilder, + "Package stability '" + packageStability + "' is not enabled by the '" + buildTimestabilityLevel + "' stability level that is the feature-pack minimum stability level."); + lowerStabilityPackages.add(packageName); continue; } pkgSpecBuilder.setStability(stab); diff --git a/maven-plugin/src/test/java/org/wildfly/galleon/maven/StabilityLevelTestCase.java b/maven-plugin/src/test/java/org/wildfly/galleon/maven/StabilityLevelTestCase.java new file mode 100644 index 00000000..5d6eb556 --- /dev/null +++ b/maven-plugin/src/test/java/org/wildfly/galleon/maven/StabilityLevelTestCase.java @@ -0,0 +1,244 @@ +/* + * Copyright 2016-2024 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.wildfly.galleon.maven; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.apache.maven.plugin.logging.Log; +import org.jboss.galleon.Constants; +import org.jboss.galleon.config.ConfigModel; +import org.jboss.galleon.spec.ConfigLayerSpec; +import org.jboss.galleon.spec.FeatureSpec; +import org.jboss.galleon.spec.PackageSpec; +import org.junit.Test; + +public class StabilityLevelTestCase { + + @Test + public void testPackageAtLowerStabilityLevel() throws Exception { + Set lowerStabilityPackages = new HashSet<>(); + lowerStabilityPackages.add("exp1"); + lowerStabilityPackages.add("exp2"); + lowerStabilityPackages.add("exp3"); + + Set packages = new HashSet<>(); + packages.add(PackageSpec.builder("pkg1").addPackageDep("exp1").build()); + + Set layers = new HashSet<>(); + layers.add(ConfigLayerSpec.builder().setName("layer1").addPackageDep("exp2").build()); + + Set features = new HashSet<>(); + features.add(FeatureSpec.builder().setName("feat1").addPackageDep("exp3").build()); + + Map> configs = new HashMap<>(); + Map map = new HashMap<>(); + configs.put("standalone", map); + map.put(Constants.MODEL_XML, ConfigModel.builder("standalone", Constants.MODEL_XML).addPackageDep("exp1").build()); + Log log = new Log() { + @Override + public boolean isDebugEnabled() { + return false; + } + + @Override + public void debug(CharSequence cs) { + System.out.println(cs); + } + + @Override + public void debug(CharSequence cs, Throwable thrwbl) { + System.out.println(cs); + } + + @Override + public void debug(Throwable thrwbl) { + System.out.println(thrwbl.toString()); + } + + @Override + public boolean isInfoEnabled() { + return true; + } + + @Override + public void info(CharSequence cs) { + System.out.println(cs); + } + + @Override + public void info(CharSequence cs, Throwable thrwbl) { + System.out.println(cs); + } + + @Override + public void info(Throwable thrwbl) { + System.out.println(thrwbl.toString()); + } + + @Override + public boolean isWarnEnabled() { + return true; + } + + @Override + public void warn(CharSequence cs) { + System.out.println(cs); + } + + @Override + public void warn(CharSequence cs, Throwable thrwbl) { + System.out.println(cs); + } + + @Override + public void warn(Throwable thrwbl) { + System.out.println(thrwbl.toString()); + } + + @Override + public boolean isErrorEnabled() { + return false; + } + + @Override + public void error(CharSequence cs) { + System.out.println(cs); + } + + @Override + public void error(CharSequence cs, Throwable thrwbl) { + System.out.println(cs); + } + + @Override + public void error(Throwable thrwbl) { + System.out.println(thrwbl.toString()); + } + }; + // No check, should pass + AbstractFeaturePackBuildMojo.checkFeaturePackContentStability(false, + lowerStabilityPackages, + packages, + layers, + features, + configs, + log); + + // No package at lower stability level, should pass + AbstractFeaturePackBuildMojo.checkFeaturePackContentStability(false, + Collections.emptySet(), + packages, + layers, + features, + configs, + log); + + { + boolean failed = false; + try { + AbstractFeaturePackBuildMojo.checkFeaturePackContentStability(true, + lowerStabilityPackages, + packages, + Collections.emptySet(), + Collections.emptySet(), + Collections.emptyMap(), + log); + failed = true; + } catch (Exception ex) { + // XXX expected + } + if (failed) { + throw new Exception("The test case should have failed"); + } + } + { + boolean failed = false; + try { + AbstractFeaturePackBuildMojo.checkFeaturePackContentStability(true, + lowerStabilityPackages, + Collections.emptySet(), + layers, + Collections.emptySet(), + Collections.emptyMap(), + log); + failed = true; + } catch (Exception ex) { + // XXX expected + } + if (failed) { + throw new Exception("The test case should have failed"); + } + } + { + boolean failed = false; + try { + AbstractFeaturePackBuildMojo.checkFeaturePackContentStability(true, + lowerStabilityPackages, + Collections.emptySet(), + Collections.emptySet(), + features, + Collections.emptyMap(), + log); + failed = true; + } catch (Exception ex) { + // XXX expected + } + if (failed) { + throw new Exception("The test case should have failed"); + } + } + { + boolean failed = false; + try { + AbstractFeaturePackBuildMojo.checkFeaturePackContentStability(true, + lowerStabilityPackages, + Collections.emptySet(), + Collections.emptySet(), + Collections.emptySet(), + configs, + log); + failed = true; + } catch (Exception ex) { + // XXX expected + } + if (failed) { + throw new Exception("The test case should have failed"); + } + } + { + boolean failed = false; + try { + AbstractFeaturePackBuildMojo.checkFeaturePackContentStability(true, + lowerStabilityPackages, + packages, + layers, + features, + configs, + log); + failed = true; + } catch (Exception ex) { + // XXX expected + } + if (failed) { + throw new Exception("The test case should have failed"); + } + } + } +} diff --git a/pom.xml b/pom.xml index 5f4efe2f..762ab04c 100644 --- a/pom.xml +++ b/pom.xml @@ -76,7 +76,7 @@ 1.0.1 3.1.0 1.1.0 - 6.0.0.Final + 6.0.1.Final 1.5.0.Final 1.0.5.Final diff --git a/testsuite/test-feature-packs/community-feature-pack/pom.xml b/testsuite/test-feature-packs/community-feature-pack/pom.xml index c6d7ad73..b17ed153 100644 --- a/testsuite/test-feature-packs/community-feature-pack/pom.xml +++ b/testsuite/test-feature-packs/community-feature-pack/pom.xml @@ -117,6 +117,7 @@ compile + true target/resources/build/wildfly-feature-pack-build.xml community community diff --git a/testsuite/test-feature-packs/default-feature-pack/pom.xml b/testsuite/test-feature-packs/default-feature-pack/pom.xml index 9f9bb716..1a11fcef 100644 --- a/testsuite/test-feature-packs/default-feature-pack/pom.xml +++ b/testsuite/test-feature-packs/default-feature-pack/pom.xml @@ -117,6 +117,7 @@ compile + true target/resources/build/wildfly-feature-pack-build.xml default default diff --git a/testsuite/test-feature-packs/preview-feature-pack/pom.xml b/testsuite/test-feature-packs/preview-feature-pack/pom.xml index 87185cc9..9500b0c1 100644 --- a/testsuite/test-feature-packs/preview-feature-pack/pom.xml +++ b/testsuite/test-feature-packs/preview-feature-pack/pom.xml @@ -117,6 +117,7 @@ compile + true target/resources/build/wildfly-feature-pack-build.xml preview preview